259 lines
8.4 KiB
JavaScript
259 lines
8.4 KiB
JavaScript
|
disableEndElseCallbackExecute = true;
|
||
|
|
||
|
var emailregex = /(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/i
|
||
|
var querystring = require("querystring");
|
||
|
var dns = require("dns");
|
||
|
|
||
|
var MongoClient = require('mongodb').MongoClient;
|
||
|
|
||
|
var mongoURL = "mongodb://localhost/newsletter";
|
||
|
var dbname = "newsletter";
|
||
|
var redirectURL = "https://blog.svrjs.org";
|
||
|
var allowedHosts = [
|
||
|
"https://blog.svrjs.org"
|
||
|
];
|
||
|
|
||
|
function antiXSS(string) {
|
||
|
return string.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
||
|
}
|
||
|
|
||
|
function generateUnsubscribeID() {
|
||
|
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||
|
let result = "";
|
||
|
for (let i = 0; i < 32; i++) {
|
||
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
function generateUniqueUnsubscribeID(db, callback) {
|
||
|
var id = generateUnsubscribeID();
|
||
|
db.collection("emails").find({
|
||
|
unsubscribeId: id
|
||
|
}).toArray(function (err, result) {
|
||
|
if (err) {
|
||
|
callServerError(500, err);
|
||
|
return;
|
||
|
}
|
||
|
if (result.length > 0) {
|
||
|
generateUnsubscribeID(db);
|
||
|
} else {
|
||
|
callback(id);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function escapeRegExp(string) {
|
||
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
||
|
}
|
||
|
|
||
|
function connectMongo(callback) {
|
||
|
// customvar3 is a MongoDB database object
|
||
|
if (customvar3) {
|
||
|
callback(customvar3);
|
||
|
} else {
|
||
|
MongoClient.connect(mongoURL, function (err, db) {
|
||
|
if (err) {
|
||
|
callServerError(500, err);
|
||
|
return;
|
||
|
}
|
||
|
var dbo = db.db(dbname);
|
||
|
dbo.createCollection("emails", function (err, dbres) {
|
||
|
customvar3 = dbo;
|
||
|
callback(dbo);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function formatTemplate(templateName, templateData, callback) {
|
||
|
fs.readFile(__dirname + "/../templates/" + templateName + ".template", function (err, data) {
|
||
|
if (err) {
|
||
|
callServerError(500, err);
|
||
|
return;
|
||
|
}
|
||
|
var tD = data.toString();
|
||
|
Object.keys(templateData).forEach(function (key) {
|
||
|
tD = tD.replace(new RegExp("\\{\\{" + escapeRegExp(key) + "\\}\\}", "g"), templateData[key]);
|
||
|
});
|
||
|
callback(tD);
|
||
|
});
|
||
|
}
|
||
|
if (href == "/subscribe.svr") {
|
||
|
if (req.method != "POST") {
|
||
|
callServerError(405, {
|
||
|
"Allow": "POST"
|
||
|
});
|
||
|
return;
|
||
|
}
|
||
|
var baseURL = (req.socket.encrypted ? "https" : "http") + "://" + (req.headers.host ? req.headers.host : req.socket.localAddress);
|
||
|
var isReferrerValid = false;
|
||
|
if ((req.headers.referer + "/").substring(0, baseURL.length + 1) == (baseURL + "/")) isReferrerValid = true;
|
||
|
if (!isReferrerValid) {
|
||
|
allowedHosts.every(function (allowedHost) {
|
||
|
allowedHost = allowedHost.replace(/\/+$/, "");
|
||
|
if ((req.headers.referer + "/").substring(0, allowedHost.length + 1) == (allowedHost + "/")) {
|
||
|
isReferrerValid = true;
|
||
|
return false;
|
||
|
} else {
|
||
|
return true;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
if (!isReferrerValid) {
|
||
|
formatTemplate("index.html", {
|
||
|
"title": "Invalid referrer",
|
||
|
"paragraph": "<p>Invalid referrer has been detected. There may be a CSRF attack.</p>"
|
||
|
}, function (data) {
|
||
|
res.writeHead(400, {
|
||
|
"Content-Type": "text/html; charset=utf-8"
|
||
|
});
|
||
|
res.end(data);
|
||
|
});
|
||
|
return;
|
||
|
}
|
||
|
var postdata = "";
|
||
|
req.on("data", function (data) {
|
||
|
postdata += data.toString();
|
||
|
});
|
||
|
req.on("end", function () {
|
||
|
postdata = querystring.parse(postdata);
|
||
|
if (!postdata.email) {
|
||
|
formatTemplate("index.html", {
|
||
|
"title": "No email address",
|
||
|
"paragraph": "<p>You didn't send any email address.</p>"
|
||
|
}, function (data) {
|
||
|
res.writeHead(400, {
|
||
|
"Content-Type": "text/html; charset=utf-8"
|
||
|
});
|
||
|
res.end(data);
|
||
|
});
|
||
|
return;
|
||
|
} else if (!postdata.email.match(emailregex)) {
|
||
|
formatTemplate("index.html", {
|
||
|
"title": "Invalid email address",
|
||
|
"paragraph": "<p>The email address you sent is invalid.</p>"
|
||
|
}, function (data) {
|
||
|
res.writeHead(400, {
|
||
|
"Content-Type": "text/html; charset=utf-8"
|
||
|
});
|
||
|
res.end(data);
|
||
|
});
|
||
|
return;
|
||
|
} else {
|
||
|
dns.resolveMx(postdata.email.match(/@([\s\S]*)/)[1], function (err, mxRecords) {
|
||
|
if (err || mxRecords.length == 0) {
|
||
|
formatTemplate("index.html", {
|
||
|
"title": "Your email domain is misconfigured",
|
||
|
"paragraph": "<p>The server has determined, that your email domain is misconfigured (MX records not found). Contact the administrator of the domain for the assistance.</p>"
|
||
|
}, function (data) {
|
||
|
res.writeHead(400, {
|
||
|
"Content-Type": "text/html; charset=utf-8"
|
||
|
});
|
||
|
res.end(data);
|
||
|
});
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
connectMongo(function (db) {
|
||
|
db.collection("emails").find({
|
||
|
email: postdata.email
|
||
|
}).toArray(function (err, result) {
|
||
|
if (err) {
|
||
|
callServerError(500, err);
|
||
|
return;
|
||
|
}
|
||
|
if (result.length > 0) {
|
||
|
formatTemplate("index.html", {
|
||
|
"title": "You have already subscribed to the newsletter",
|
||
|
"paragraph": "<p>You have already subscribed to the newsletter.</p>"
|
||
|
}, function (data) {
|
||
|
res.writeHead(200, {
|
||
|
"Content-Type": "text/html; charset=utf-8"
|
||
|
});
|
||
|
res.end(data);
|
||
|
});
|
||
|
} else {
|
||
|
generateUniqueUnsubscribeID(db, function (id) {
|
||
|
db.collection("emails").insertOne({
|
||
|
email: postdata.email,
|
||
|
unsubscribeId: id
|
||
|
}, function (err, dbres) {
|
||
|
if (err) {
|
||
|
callServerError(500, err);
|
||
|
return;
|
||
|
}
|
||
|
formatTemplate("index.html", {
|
||
|
"title": "Thank you for subscribing to our newsletter!",
|
||
|
"paragraph": "<p>You can unsubscribe from the newsletter using <a href=\"" + antiXSS(baseURL + "/unsubscribe.svr?id=" + encodeURIComponent(id)) + "\">this link</a>.</p>"
|
||
|
}, function (data) {
|
||
|
res.writeHead(200, {
|
||
|
"Content-Type": "text/html; charset=utf-8"
|
||
|
});
|
||
|
res.end(data);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
} else if (href == "/unsubscribe.svr") {
|
||
|
if (!uobject.query.id) {
|
||
|
formatTemplate("index.html", {
|
||
|
"title": "Invalid unsubscription link",
|
||
|
"paragraph": "<p>You didn't provide the unsubscription ID.</p>"
|
||
|
}, function (data) {
|
||
|
res.writeHead(400, {
|
||
|
"Content-Type": "text/html; charset=utf-8"
|
||
|
});
|
||
|
res.end(data);
|
||
|
});
|
||
|
}
|
||
|
connectMongo(function (db) {
|
||
|
db.collection("emails").find({
|
||
|
unsubscribeId: uobject.query.id
|
||
|
}).toArray(function (err, result) {
|
||
|
if (err) {
|
||
|
callServerError(500, err);
|
||
|
return;
|
||
|
}
|
||
|
if (result.length == 0) {
|
||
|
formatTemplate("index.html", {
|
||
|
"title": "Invalid unsubscription link",
|
||
|
"paragraph": "<p>You have either already unsubscribed from the newsletter, or the unsubscription ID is invalid.</p>"
|
||
|
}, function (data) {
|
||
|
res.writeHead(400, {
|
||
|
"Content-Type": "text/html; charset=utf-8"
|
||
|
});
|
||
|
res.end(data);
|
||
|
});
|
||
|
} else {
|
||
|
db.collection("emails").deleteOne({
|
||
|
unsubscribeId: uobject.query.id
|
||
|
}, function (err, dbres) {
|
||
|
if (err) {
|
||
|
callServerError(500, err);
|
||
|
return;
|
||
|
}
|
||
|
formatTemplate("index.html", {
|
||
|
"title": "You have been unsubscribed from the newsletter!",
|
||
|
"paragraph": "<p>You have been unsubscribed from the newsletter.</p>"
|
||
|
}, function (data) {
|
||
|
res.writeHead(200, {
|
||
|
"Content-Type": "text/html; charset=utf-8"
|
||
|
});
|
||
|
res.end(data);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
} else if (href == "/") {
|
||
|
redirect(redirectURL);
|
||
|
} else {
|
||
|
elseCallback();
|
||
|
}
|