diff --git a/lib/channel/filters.js b/lib/channel/filters.js
index 8b47c8a1..fbf1ab2c 100644
--- a/lib/channel/filters.js
+++ b/lib/channel/filters.js
@@ -1,26 +1,34 @@
-var FilterList = require('cytubefilters');
+var FilterList = require("cytubefilters");
var ChannelModule = require("./module");
var XSS = require("../xss");
var Logger = require("../logger");
+/*
+ * Converts JavaScript-style replacements ($1, $2, etc.) with
+ * PCRE-style (\1, \2, etc.)
+ */
+function fixReplace(replace) {
+ return replace.replace(/\$(\d)/g, "\\$1");
+}
+
function validateFilter(f) {
if (typeof f.source !== "string" || typeof f.flags !== "string" ||
typeof f.replace !== "string") {
- return false;
+ return null;
}
if (typeof f.name !== "string") {
f.name = f.source;
}
- f.replace = f.replace.substring(0, 1000);
+ f.replace = fixReplace(f.replace.substring(0, 1000));
f.replace = XSS.sanitizeHTML(f.replace);
f.flags = f.flags.substring(0, 4);
try {
- new RegExp(f.source, f.flags);
+ FilterList.checkValidRegex(f.source);
} catch (e) {
- return false;
+ return null;
}
var filter = {
@@ -35,58 +43,47 @@ function validateFilter(f) {
return filter;
}
-function fixReplace(replace) {
- return replace.replace(/\$(\d)/g, '\\$1');
-}
-
function makeDefaultFilter(name, source, flags, replace) {
return {
name: name,
source: source,
flags: flags,
- replace: fixReplace(replace),
+ replace: replace,
active: true,
filterlinks: false
};
}
const DEFAULT_FILTERS = [
- makeDefaultFilter("monospace", "`(.+?)`", "g", "$1"),
- makeDefaultFilter("bold", "\\*(.+?)\\*", "g", "$1"),
- makeDefaultFilter("italic", "_(.+?)_", "g", "$1"),
- makeDefaultFilter("strike", "~~(.+?)~~", "g", "$1"),
- makeDefaultFilter("inline spoiler", "\\[sp\\](.*?)\\[\\/sp\\]", "ig", "$1")
+ makeDefaultFilter("monospace", "`(.+?)`", "g", "\\1"),
+ makeDefaultFilter("bold", "\\*(.+?)\\*", "g", "\\1"),
+ makeDefaultFilter("italic", "_(.+?)_", "g", "\\1"),
+ makeDefaultFilter("strike", "~~(.+?)~~", "g", "\\1"),
+ makeDefaultFilter("inline spoiler", "\\[sp\\](.*?)\\[\\/sp\\]", "ig",
+ "\\1")
];
function ChatFilterModule(channel) {
ChannelModule.apply(this, arguments);
- this.filters = new FilterList(DEFAULT_FILTERS);
+ this.filters = new FilterList();
}
ChatFilterModule.prototype = Object.create(ChannelModule.prototype);
ChatFilterModule.prototype.load = function (data) {
if ("filters" in data) {
- for (var i = 0; i < data.filters.length; i++) {
- var f = validateFilter(data.filters[i]);
- if (f) {
- try {
- this.filters.updateFilter(f);
- } catch (e) {
- if (e.message.match(/does not exist/i)) {
- try {
- this.filters.addFilter(f);
- } catch (e) {
- Logger.errlog.log("Filter load failed: " +
- JSON.stringify(f) + " c:" + this.channel.name);
- }
- } else {
- Logger.errlog.log("Filter load failed: " +
- JSON.stringify(f) + " c:" + this.channel.name);
- }
- }
- }
+ var filters = data.filters.map(validateFilter).filter(function (f) {
+ return f !== null;
+ });
+ try {
+ this.filters = new FilterList(filters);
+ } catch (e) {
+ Logger.errlog.log("Filter load failed: " + e + " (channel:" +
+ this.channel.name);
+ this.channel.logger.log("Failed to load filters: " + e);
}
+ } else {
+ this.filters = new FilterList(DEFAULT_FILTERS);
}
};
@@ -128,6 +125,16 @@ ChatFilterModule.prototype.handleAddFilter = function (user, data) {
return;
}
+ try {
+ FilterList.checkValidRegex(data.source);
+ } catch (e) {
+ user.socket.emit("errorMsg", {
+ msg: "Invalid regex: " + e.message,
+ alert: true
+ });
+ return;
+ }
+
data = validateFilter(data);
if (!data) {
return;
@@ -142,6 +149,9 @@ ChatFilterModule.prototype.handleAddFilter = function (user, data) {
});
return;
}
+
+ user.socket.emit("addFilterSuccess");
+
var chan = this.channel;
chan.users.forEach(function (u) {
if (chan.modules.permissions.canEditFilters(u)) {
@@ -150,8 +160,8 @@ ChatFilterModule.prototype.handleAddFilter = function (user, data) {
});
chan.logger.log("[mod] " + user.getName() + " added filter: " + data.name + " -> " +
- "s/" + data.source + "/" + data.replace + "/" + data.flags + " active: " +
- data.active + ", filterlinks: " + data.filterlinks);
+ "s/" + data.source + "/" + data.replace + "/" + data.flags +
+ " active: " + data.active + ", filterlinks: " + data.filterlinks);
};
ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
@@ -163,6 +173,16 @@ ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
return;
}
+ try {
+ FilterList.checkValidRegex(data.source);
+ } catch (e) {
+ user.socket.emit("errorMsg", {
+ msg: "Invalid regex: " + e.message,
+ alert: true
+ });
+ return;
+ }
+
data = validateFilter(data);
if (!data) {
return;
@@ -171,14 +191,10 @@ ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
try {
this.filters.updateFilter(data);
} catch (e) {
- if (e.message.match(/filter to be updated does not exist/i)) {
- this.handleAddFilter(user, data);
- } else {
- user.socket.emit("errorMsg", {
- msg: "Filter update failed: " + e.message,
- alert: true
- });
- }
+ user.socket.emit("errorMsg", {
+ msg: "Filter update failed: " + e.message,
+ alert: true
+ });
return;
}
@@ -190,8 +206,8 @@ ChatFilterModule.prototype.handleUpdateFilter = function (user, data) {
});
chan.logger.log("[mod] " + user.getName() + " updated filter: " + data.name + " -> " +
- "s/" + data.source + "/" + data.replace + "/" + data.flags + " active: " +
- data.active + ", filterlinks: " + data.filterlinks);
+ "s/" + data.source + "/" + data.replace + "/" + data.flags +
+ " active: " + data.active + ", filterlinks: " + data.filterlinks);
};
ChatFilterModule.prototype.handleImportFilters = function (user, data) {
@@ -207,7 +223,7 @@ ChatFilterModule.prototype.handleImportFilters = function (user, data) {
try {
this.filters = new FilterList(data.map(validateFilter).filter(function (f) {
- return f !== false;
+ return f !== null;
}));
} catch (e) {
user.socket.emit("errorMsg", {
@@ -249,6 +265,7 @@ ChatFilterModule.prototype.handleRemoveFilter = function (user, data) {
u.socket.emit("deleteChatFilter", data);
}
});
+
this.channel.logger.log("[mod] " + user.getName() + " removed filter: " + data.name);
};
diff --git a/www/js/ui.js b/www/js/ui.js
index aa0ff3b9..17016a82 100644
--- a/www/js/ui.js
+++ b/www/js/ui.js
@@ -621,14 +621,7 @@ $("#cs-chatfilters-newsubmit").click(function () {
"match.");
}
- try {
- new RegExp(regex, flags);
- } catch (e) {
- alert("Regex error: " + e);
- return;
- }
-
- socket.emit("updateFilter", {
+ socket.emit("addFilter", {
name: name,
source: regex,
flags: flags,
@@ -636,10 +629,13 @@ $("#cs-chatfilters-newsubmit").click(function () {
active: true
});
- $("#cs-chatfilters-newname").val("");
- $("#cs-chatfilters-newregex").val("");
- $("#cs-chatfilters-newflags").val("");
- $("#cs-chatfilters-newreplace").val("");
+ socket.once("addFilterSuccess", function () {
+ console.log("addFilterSuccess");
+ $("#cs-chatfilters-newname").val("");
+ $("#cs-chatfilters-newregex").val("");
+ $("#cs-chatfilters-newflags").val("");
+ $("#cs-chatfilters-newreplace").val("");
+ });
});
$("#cs-emotes-newsubmit").click(function () {
diff --git a/www/js/util.js b/www/js/util.js
index 8e625b05..0ae644fd 100644
--- a/www/js/util.js
+++ b/www/js/util.js
@@ -2304,14 +2304,12 @@ function formatCSChatFilterList() {
f.flags = flags.val();
f.replace = replace.val();
f.filterlinks = filterlinks.prop("checked");
- try {
- new RegExp(f.source, f.flags);
- } catch (e) {
- alert("Invalid regex: " + e);
- }
socket.emit("updateFilter", f);
- reset();
+ socket.once("updateFilterSuccess", function () {
+ console.log("updateFilterSuccess");
+ reset();
+ });
});
control.data("editor", tr2);