aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/server.js25
-rw-r--r--default-config.json4
-rw-r--r--res/client.js84
-rw-r--r--res/langs/en.json2
-rw-r--r--res/langs/ru.json2
-rw-r--r--src/client/Main.hx25
-rw-r--r--src/client/Player.hx15
-rw-r--r--src/server/Main.hx20
8 files changed, 127 insertions, 50 deletions
diff --git a/build/server.js b/build/server.js
index d6a0c65..52d878d 100644
--- a/build/server.js
+++ b/build/server.js
@@ -637,6 +637,7 @@ var server_Main = function(port,wsPort) {
port = 4200;
}
this.loadedClientsCount = 0;
+ this.htmlChars = new EReg("[&^<>'\"]","");
this.messages = [];
this.videoTimer = new server_VideoTimer();
this.videoList = [];
@@ -797,6 +798,7 @@ server_Main.prototype = {
switch(data.type) {
case "AddVideo":
var item = data.addVideo.item;
+ item.author = client.name;
var localOrigin = "" + this.localIp + ":" + this.port;
if(item.url.indexOf(localOrigin) != -1) {
item.url = StringTools.replace(item.url,localOrigin,"" + this.globalIp + ":" + this.port);
@@ -837,11 +839,11 @@ server_Main.prototype = {
break;
case "Login":
var name = data.login.clientName;
- if(name.length == 0 || name.length > this.config.maxLoginLength || ClientTools.getByName(this.clients,name) != null) {
+ if(this.badNickName(name) || name.length > this.config.maxLoginLength || ClientTools.getByName(this.clients,name) != null) {
this.send(client,{ type : "LoginError"});
return;
}
- client.name = data.login.clientName;
+ client.name = name;
client.setGroupFlag(ClientGroup.User,true);
this.send(client,{ type : data.type, login : { isUnknownClient : true, clientName : client.name, clients : this.clientList()}});
this.sendClientList();
@@ -897,15 +899,16 @@ server_Main.prototype = {
return;
}
var url = data.removeVideo.url;
- if(this.videoList[0].url == url) {
+ var isFirst = this.videoList[0].url == url;
+ if(isFirst) {
this.videoTimer.stop();
- if(this.videoList.length > 0) {
- this.restartWaitTimer();
- }
}
HxOverrides.remove(this.videoList,Lambda.find(this.videoList,function(item1) {
return item1.url == url;
}));
+ if(isFirst && this.videoList.length > 0) {
+ this.restartWaitTimer();
+ }
this.broadcast(data);
break;
case "Rewind":
@@ -993,6 +996,15 @@ server_Main.prototype = {
client.ws.send(json,null);
}
}
+ ,badNickName: function(name) {
+ if(name.length == 0) {
+ return true;
+ }
+ if(this.htmlChars.match(name)) {
+ return true;
+ }
+ return false;
+ }
,restartWaitTimer: function() {
if(this.waitVideoStart != null) {
this.waitVideoStart.stop();
@@ -1153,6 +1165,7 @@ function $getIterator(o) { if( o instanceof Array ) return HxOverrides.iter(o);
function $bind(o,m) { if( m == null ) return null; if( m.__id__ == null ) m.__id__ = $global.$haxeUID++; var f; if( o.hx__closures__ == null ) o.hx__closures__ = {}; else f = o.hx__closures__[m.__id__]; if( f == null ) { f = m.bind(o); o.hx__closures__[m.__id__] = f; } return f; }
$global.$haxeUID |= 0;
var __map_reserved = {};
+if( String.fromCodePoint == null ) String.fromCodePoint = function(c) { return c < 0x10000 ? String.fromCharCode(c) : String.fromCharCode((c>>10)+0xD7C0)+String.fromCharCode((c&0x3FF)+0xDC00); }
String.__name__ = true;
Array.__name__ = true;
Object.defineProperty(js__$Boot_HaxeError.prototype,"message",{ get : function() {
diff --git a/default-config.json b/default-config.json
index efbcf30..ec00b60 100644
--- a/default-config.json
+++ b/default-config.json
@@ -10,8 +10,8 @@
],
"filters": [
{
- "name": "image", "regex": "(http|https)(:\\/\\/.*\\.)(png|jpg|gif|jpeg)", "flags": "g",
- "replace": "<a href='$1$2$3' target='_blank'><img src='$1$2$3' style='max-width:200px; max-height:200px' /></a>"
+ "name": "image", "regex": "(http|https)(:\\/\\/.*\\.)(png|jpg|gif|jpeg)([^ ,]*)", "flags": "g",
+ "replace": "<a href='$1$2$3' target='_blank'><img src='$1$2$3$4' style='max-width:200px; max-height:200px' /></a>"
}
]
}
diff --git a/res/client.js b/res/client.js
index 35e568a..13a9107 100644
--- a/res/client.js
+++ b/res/client.js
@@ -280,6 +280,52 @@ Std.parseInt = function(x) {
};
var StringTools = function() { };
StringTools.__name__ = true;
+StringTools.htmlEscape = function(s,quotes) {
+ var buf_b = "";
+ var _g_offset = 0;
+ var _g_s = s;
+ while(_g_offset < _g_s.length) {
+ var s1 = _g_s;
+ var index = _g_offset++;
+ var c = s1.charCodeAt(index);
+ if(c >= 55296 && c <= 56319) {
+ c = c - 55232 << 10 | s1.charCodeAt(index + 1) & 1023;
+ }
+ var c1 = c;
+ if(c1 >= 65536) {
+ ++_g_offset;
+ }
+ var code = c1;
+ switch(code) {
+ case 34:
+ if(quotes) {
+ buf_b += "&quot;";
+ } else {
+ buf_b += String.fromCodePoint(code);
+ }
+ break;
+ case 38:
+ buf_b += "&amp;";
+ break;
+ case 39:
+ if(quotes) {
+ buf_b += "&#039;";
+ } else {
+ buf_b += String.fromCodePoint(code);
+ }
+ break;
+ case 60:
+ buf_b += "&lt;";
+ break;
+ case 62:
+ buf_b += "&gt;";
+ break;
+ default:
+ buf_b += String.fromCodePoint(code);
+ }
+ }
+ return buf_b;
+};
StringTools.startsWith = function(s,start) {
if(s.length >= start.length) {
return s.lastIndexOf(start,0) == 0;
@@ -752,7 +798,7 @@ client_Main.prototype = {
this.player.addVideoItem(data.addVideo.item,data.addVideo.atEnd);
break;
case "ClearChat":
- window.document.querySelector("#messagebuffer").innerHTML = "";
+ this.clearChat();
break;
case "ClearPlaylist":
this.player.clearItems();
@@ -868,7 +914,7 @@ client_Main.prototype = {
if(guestName.value.length > 0) {
this.send({ type : "Login", login : { clientName : guestName.value}});
}
- window.document.querySelector("#messagebuffer").innerHTML = "";
+ this.clearChat();
this.serverMessage(1);
var _g = 0;
var _g1 = connected.history;
@@ -907,7 +953,7 @@ client_Main.prototype = {
form.focus();
return;
};
- smilesWrap.innerHTML = "";
+ smilesWrap.textContent = "";
var _g4 = 0;
var _g5 = config.emotes;
while(_g4 < _g5.length) {
@@ -963,19 +1009,19 @@ client_Main.prototype = {
switch(type) {
case 1:
div.className = "server-msg-reconnect";
- div.innerHTML = Lang.get("msgConnected");
+ div.textContent = Lang.get("msgConnected");
break;
case 2:
div.className = "server-msg-disconnect";
- div.innerHTML = Lang.get("msgDisconnected");
+ div.textContent = Lang.get("msgDisconnected");
break;
case 3:
div.className = "server-whisper";
- div.innerHTML = time + text + " " + Lang.get("entered");
+ div.textContent = time + text + " " + Lang.get("entered");
break;
case 4:
div.className = "server-whisper";
- div.innerHTML = time + text;
+ div.textContent = time + text;
break;
default:
}
@@ -983,7 +1029,7 @@ client_Main.prototype = {
msgBuf.scrollTop = msgBuf.scrollHeight;
}
,updateUserList: function() {
- window.document.querySelector("#usercount").innerHTML = this.clients.length + " " + Lang.get("online");
+ window.document.querySelector("#usercount").textContent = this.clients.length + " " + Lang.get("online");
window.document.title = this.getPageTitle();
var list_b = "";
var _g = 0;
@@ -1002,6 +1048,9 @@ client_Main.prototype = {
,getPageTitle: function() {
return "" + this.pageTitle + " (" + this.clients.length + ")";
}
+ ,clearChat: function() {
+ window.document.querySelector("#messagebuffer").textContent = "";
+ }
,addMessage: function(name,text,time) {
var _gthis = this;
var msgBuf = window.document.querySelector("#messagebuffer");
@@ -1012,10 +1061,10 @@ client_Main.prototype = {
if(time == null) {
time = "[" + new Date().toTimeString().split(" ")[0] + "] ";
}
- tstamp.innerHTML = time;
+ tstamp.textContent = time;
var nameDiv = window.document.createElement("strong");
nameDiv.className = "username";
- nameDiv.innerHTML = name + ": ";
+ nameDiv.textContent = name + ": ";
var textDiv = window.document.createElement("span");
if(StringTools.startsWith(text,"/")) {
if(name == this.personal.name) {
@@ -1030,6 +1079,7 @@ client_Main.prototype = {
text = text.replace(filter.regex.r,filter.replace);
}
}
+ text = StringTools.htmlEscape(text);
textDiv.innerHTML = text;
var isInChatEnd = msgBuf.scrollHeight - msgBuf.scrollTop == msgBuf.clientHeight;
userDiv.appendChild(tstamp);
@@ -1150,14 +1200,14 @@ client_Player.prototype = {
_gthis.main.send({ type : "Play", play : { time : _gthis.video.currentTime}});
return;
};
- this.player.innerHTML = "";
+ this.player.textContent = "";
this.player.appendChild(this.video);
- window.document.querySelector("#currenttitle").innerHTML = item.title;
+ window.document.querySelector("#currenttitle").textContent = item.title;
}
,addVideoItem: function(item,atEnd) {
var _gthis = this;
this.items.push(item);
- var itemEl = this.nodeFromString("<li class=\"queue_entry pluid-0 queue_temp queue_active\" title=\"" + Lang.get("addedBy") + ": " + item.author + "\">\n\t\t\t\t<a class=\"qe_title\" href=\"" + item.url + "\" target=\"_blank\">" + item.title + "</a>\n\t\t\t\t<span class=\"qe_time\">" + this.duration(item.duration) + "</span>\n\t\t\t\t<div class=\"qe_clear\"></div>\n\t\t\t\t<div class=\"btn-group\" style=\"display: inline-block;\">\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-play\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-play\"></span>" + Lang.get("play") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-next\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-share-alt\"></span>" + Lang.get("skip") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-tmp\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-flag\"></span>" + Lang.get("makePermanent") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-delete\" id=\"btn-delete\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-trash\"></span>" + Lang.get("delete") + "\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</li>");
+ var itemEl = this.nodeFromString("<li class=\"queue_entry pluid-0 queue_temp queue_active\" title=\"" + Lang.get("addedBy") + ": " + item.author + "\">\n\t\t\t\t<a class=\"qe_title\" href=\"" + item.url + "\" target=\"_blank\">" + StringTools.htmlEscape(item.title) + "</a>\n\t\t\t\t<span class=\"qe_time\">" + this.duration(item.duration) + "</span>\n\t\t\t\t<div class=\"qe_clear\"></div>\n\t\t\t\t<div class=\"btn-group\" style=\"display: inline-block;\">\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-play\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-play\"></span>" + Lang.get("play") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-next\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-share-alt\"></span>" + Lang.get("skip") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-tmp\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-flag\"></span>" + Lang.get("makePermanent") + "\n\t\t\t\t\t</button>\n\t\t\t\t\t<button class=\"btn btn-xs btn-default qbtn-delete\" id=\"btn-delete\">\n\t\t\t\t\t\t<span class=\"glyphicon glyphicon-trash\"></span>" + Lang.get("delete") + "\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t</li>");
itemEl.querySelector("#btn-delete").onclick = function(e) {
_gthis.main.send({ type : "RemoveVideo", removeVideo : { url : itemEl.querySelector(".qe_title").getAttribute("href")}});
return;
@@ -1175,7 +1225,7 @@ client_Player.prototype = {
}
this.player.removeChild(this.video);
this.video = null;
- window.document.querySelector("#currenttitle").innerHTML = Lang.get("nothingPlaying");
+ window.document.querySelector("#currenttitle").textContent = Lang.get("nothingPlaying");
}
,removeItem: function(url) {
var _g = 0;
@@ -1204,8 +1254,8 @@ client_Player.prototype = {
,updateCounters: function() {
var tmp = "" + this.items.length + " ";
var tmp1 = Lang.get("videos");
- window.document.querySelector("#plcount").innerHTML = tmp + tmp1;
- window.document.querySelector("#pllength").innerHTML = this.totalDuration();
+ window.document.querySelector("#plcount").textContent = tmp + tmp1;
+ window.document.querySelector("#pllength").textContent = this.totalDuration();
}
,getItems: function() {
return this.items;
@@ -1223,7 +1273,7 @@ client_Player.prototype = {
}
,clearItems: function() {
this.items.length = 0;
- this.videoItemsEl.innerHTML = "";
+ this.videoItemsEl.textContent = "";
this.updateCounters();
}
,refresh: function() {
diff --git a/res/langs/en.json b/res/langs/en.json
index 3e618fb..145e7ae 100644
--- a/res/langs/en.json
+++ b/res/langs/en.json
@@ -5,7 +5,7 @@
"joined": "joined",
"online": "online",
"nothingPlaying": "Nothing Playing",
- "usernameError": "Username must be from 1 to $MAX characters and don't repeat another's.",
+ "usernameError": "Username must be from 1 to $MAX characters and don't repeat another's. Characters &^<>'\" are not allowed.",
"addVideoError": "Failed to add video.",
"rawVideo": "Raw video",
"videos": "videos",
diff --git a/res/langs/ru.json b/res/langs/ru.json
index 5bccce1..ce5d125 100644
--- a/res/langs/ru.json
+++ b/res/langs/ru.json
@@ -5,7 +5,7 @@
"joined": "вошел",
"online": "онлайн",
"nothingPlaying": "Ничего не играет",
- "usernameError": "Ник должен быть от 1 до $MAX символов и не повторять чужие.",
+ "usernameError": "Ник должен быть от 1 до $MAX символов и не повторять чужие. Символы &^<>'\" запрещены.",
"addVideoError": "Не удалось добавить видео.",
"rawVideo": "Исходное видео",
"videos": "видео",
diff --git a/src/client/Main.hx b/src/client/Main.hx
index bfcc877..3a819b2 100644
--- a/src/client/Main.hx
+++ b/src/client/Main.hx
@@ -283,7 +283,7 @@ class Main {
if (isLeader()) player.setTime(player.getTime(), false);
case ClearChat:
- ge("#messagebuffer").innerHTML = "";
+ clearChat();
case ClearPlaylist:
player.clearItems();
@@ -313,7 +313,7 @@ class Main {
clientName: guestName.value
}
});
- ge("#messagebuffer").innerHTML = "";
+ clearChat();
serverMessage(1);
for (message in connected.history) {
addMessage(message.name, message.text, message.time);
@@ -349,7 +349,7 @@ class Main {
form.value += ' ${el.title}';
form.focus();
}
- smilesWrap.innerHTML = "";
+ smilesWrap.textContent = "";
for (emote in config.emotes) {
final img = document.createImageElement();
img.className = "smile-preview";
@@ -402,16 +402,16 @@ class Main {
switch (type) {
case 1:
div.className = "server-msg-reconnect";
- div.innerHTML = Lang.get("msgConnected");
+ div.textContent = Lang.get("msgConnected");
case 2:
div.className = "server-msg-disconnect";
- div.innerHTML = Lang.get("msgDisconnected");
+ div.textContent = Lang.get("msgDisconnected");
case 3:
div.className = "server-whisper";
- div.innerHTML = time + text + " " + Lang.get("entered");
+ div.textContent = time + text + " " + Lang.get("entered");
case 4:
div.className = "server-whisper";
- div.innerHTML = time + text;
+ div.textContent = time + text;
default:
}
msgBuf.appendChild(div);
@@ -420,7 +420,7 @@ class Main {
function updateUserList():Void {
final userCount = ge("#usercount");
- userCount.innerHTML = clients.length + " " + Lang.get("online");
+ userCount.textContent = clients.length + " " + Lang.get("online");
document.title = getPageTitle();
final list = new StringBuf();
@@ -438,6 +438,10 @@ class Main {
return '$pageTitle (${clients.length})';
}
+ function clearChat():Void {
+ ge("#messagebuffer").textContent = "";
+ }
+
function addMessage(name:String, text:String, ?time:String):Void {
final msgBuf = ge("#messagebuffer");
final userDiv = document.createDivElement();
@@ -446,11 +450,11 @@ class Main {
final tstamp = document.createSpanElement();
tstamp.className = "timestamp";
if (time == null) time = "[" + new Date().toTimeString().split(" ")[0] + "] ";
- tstamp.innerHTML = time;
+ tstamp.textContent = time;
final nameDiv = document.createElement("strong");
nameDiv.className = "username";
- nameDiv.innerHTML = name + ": ";
+ nameDiv.textContent = name + ": ";
final textDiv = document.createSpanElement();
if (text.startsWith("/")) {
@@ -460,6 +464,7 @@ class Main {
text = filter.regex.replace(text, filter.replace);
}
}
+ text = text.htmlEscape();
textDiv.innerHTML = text;
final isInChatEnd = msgBuf.scrollHeight - msgBuf.scrollTop == msgBuf.clientHeight;
diff --git a/src/client/Player.hx b/src/client/Player.hx
index c9b10a4..bee9a3c 100644
--- a/src/client/Player.hx
+++ b/src/client/Player.hx
@@ -5,6 +5,7 @@ import js.html.VideoElement;
import js.Browser.document;
import client.Main.ge;
import Types.VideoItem;
+using StringTools;
using Lambda;
class Player {
@@ -63,16 +64,16 @@ class Player {
}
});
}
- player.innerHTML = "";
+ player.textContent = "";
player.appendChild(video);
- ge("#currenttitle").innerHTML = item.title;
+ ge("#currenttitle").textContent = item.title;
}
public function addVideoItem(item:VideoItem, atEnd:Bool):Void {
items.push(item);
final itemEl = nodeFromString(
'<li class="queue_entry pluid-0 queue_temp queue_active" title="${Lang.get("addedBy")}: ${item.author}">
- <a class="qe_title" href="${item.url}" target="_blank">${item.title}</a>
+ <a class="qe_title" href="${item.url}" target="_blank">${item.title.htmlEscape()}</a>
<span class="qe_time">${duration(item.duration)}</span>
<div class="qe_clear"></div>
<div class="btn-group" style="display: inline-block;">
@@ -109,7 +110,7 @@ class Player {
if (video == null) return;
player.removeChild(video);
video = null;
- ge("#currenttitle").innerHTML = Lang.get("nothingPlaying");
+ ge("#currenttitle").textContent = Lang.get("nothingPlaying");
}
public function removeItem(url:String):Void {
@@ -132,8 +133,8 @@ class Player {
}
function updateCounters():Void {
- ge("#plcount").innerHTML = '${items.length} ${Lang.get("videos")}';
- ge("#pllength").innerHTML = totalDuration();
+ ge("#plcount").textContent = '${items.length} ${Lang.get("videos")}';
+ ge("#pllength").textContent = totalDuration();
}
public function getItems():Array<VideoItem> {
@@ -153,7 +154,7 @@ class Player {
public function clearItems():Void {
items.resize(0);
- videoItemsEl.innerHTML = "";
+ videoItemsEl.textContent = "";
updateCounters();
}
diff --git a/src/server/Main.hx b/src/server/Main.hx
index 271f6ea..9f7534c 100644
--- a/src/server/Main.hx
+++ b/src/server/Main.hx
@@ -180,12 +180,12 @@ class Main {
sendClientList();
case Login:
final name = data.login.clientName;
- if (name.length == 0 || name.length > config.maxLoginLength
+ if (badNickName(name) || name.length > config.maxLoginLength
|| clients.getByName(name) != null) {
send(client, {type: LoginError});
return;
}
- client.name = data.login.clientName;
+ client.name = name;
client.isUser = true;
send(client, {
type: data.type,
@@ -228,6 +228,7 @@ class Main {
case AddVideo:
final item = data.addVideo.item;
+ item.author = client.name;
final localOrigin = '$localIp:$port';
if (item.url.indexOf(localOrigin) != -1) {
item.url = item.url.replace(localOrigin, '$globalIp:$port');
@@ -245,13 +246,12 @@ class Main {
case RemoveVideo:
if (videoList.length == 0) return;
final url = data.removeVideo.url;
- if (videoList[0].url == url) {
- videoTimer.stop();
- if (videoList.length > 0) restartWaitTimer();
- }
+ final isFirst = videoList[0].url == url;
+ if (isFirst) videoTimer.stop();
videoList.remove(
videoList.find(item -> item.url == url)
);
+ if (isFirst && videoList.length > 0) restartWaitTimer();
broadcast(data);
case Pause:
@@ -368,6 +368,14 @@ class Main {
}
}
+ final htmlChars = ~/[&^<>'"]/;
+
+ function badNickName(name:String):Bool {
+ if (name.length == 0) return true;
+ if (htmlChars.match(name)) return true;
+ return false;
+ }
+
var waitVideoStart:Timer;
var loadedClientsCount = 0;
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage