diff --git a/client/css/style.css b/client/css/style.css
index c5763358..7e3789bc 100644
--- a/client/css/style.css
+++ b/client/css/style.css
@@ -1096,6 +1096,19 @@ button,
line-height: 1.8;
}
+#settings #change-password .error,
+#settings #change-password .success {
+ margin-bottom: 1em;
+}
+
+#settings #change-password .error {
+ color: #e74c3c;
+}
+
+#settings #change-password .success {
+ color: #2ecc40;
+}
+
#form {
background: #eee;
border-top: 1px solid #ddd;
diff --git a/client/index.html b/client/index.html
index 0be10248..841a8174 100644
--- a/client/index.html
+++ b/client/index.html
@@ -265,6 +265,31 @@
Enable notification for all messages
+ <% if (!public) { %>
+
+ <% } %>
About The Lounge
diff --git a/client/js/lounge.js b/client/js/lounge.js
index 2404a29b..7ffae757 100644
--- a/client/js/lounge.js
+++ b/client/js/lounge.js
@@ -112,6 +112,31 @@ $(function() {
.show();
});
+ socket.on("change-password", function(data) {
+ var passwordForm = $("#change-password");
+ if (data.error || data.success) {
+ var message = data.success ? data.success : data.error;
+ var feedback = passwordForm.find(".feedback");
+
+ if (data.success) {
+ feedback.addClass("success").removeClass("error");
+ } else {
+ feedback.addClass("error").removeClass("success");
+ }
+
+ feedback.text(message).show();
+ feedback.closest("form").one("submit", function() {
+ feedback.hide();
+ });
+ }
+ passwordForm
+ .find("input")
+ .val("")
+ .end()
+ .find(".btn")
+ .prop("disabled", false);
+ });
+
socket.on("init", function(data) {
if (data.networks.length === 0) {
$("#footer").find(".connect").trigger("click");
@@ -723,7 +748,7 @@ $(function() {
});
var windows = $("#windows");
- var forms = $("#sign-in, #connect");
+ var forms = $("#sign-in, #connect, #change-password");
windows.on("show", "#sign-in", function() {
var self = $(this);
@@ -750,6 +775,8 @@ $(function() {
.end();
if (form.closest(".window").attr("id") === "connect") {
event = "conn";
+ } else if (form.closest("div").attr("id") === "change-password") {
+ event = "change-password";
}
var values = {};
$.each(form.serializeArray(), function(i, obj) {
diff --git a/src/client.js b/src/client.js
index 7b795800..60119846 100644
--- a/src/client.js
+++ b/src/client.js
@@ -1,7 +1,6 @@
var _ = require("lodash");
var Chan = require("./models/chan");
var crypto = require("crypto");
-var fs = require("fs");
var identd = require("./identd");
var log = require("./log");
var net = require("net");
@@ -51,14 +50,15 @@ var inputs = [
"whois"
];
-function Client(sockets, name, config) {
+function Client(manager, name, config) {
_.merge(this, {
activeChannel: -1,
config: config,
id: id++,
name: name,
networks: [],
- sockets: sockets
+ sockets: manager.sockets,
+ manager: manager
});
var client = this;
crypto.randomBytes(48, function(err, buf) {
@@ -221,6 +221,21 @@ Client.prototype.connect = function(args) {
});
};
+Client.prototype.setPassword = function(hash) {
+ var client = this;
+ client.manager.updateUser(client.name, {password:hash});
+ // re-read the hash off disk to ensure we use whatever is saved. this will
+ // prevent situations where the password failed to save properly and so
+ // a restart of the server would forget the change and use the old
+ // password again.
+ var user = client.manager.readUserConfig(client.name);
+ if (user.password === hash) {
+ client.config.password = hash;
+ return true;
+ }
+ return false;
+};
+
Client.prototype.input = function(data) {
var client = this;
var text = data.text.trim();
@@ -353,9 +368,6 @@ Client.prototype.save = function(force) {
return;
}
- var name = this.name;
- var path = Helper.HOME + "/users/" + name + ".json";
-
var networks = _.map(
this.networks,
function(n) {
@@ -364,29 +376,6 @@ Client.prototype.save = function(force) {
);
var json = {};
- fs.readFile(path, "utf-8", function(err, data) {
- if (err) {
- console.log(err);
- return;
- }
-
- try {
- json = JSON.parse(data);
- json.networks = networks;
- } catch (e) {
- console.log(e);
- return;
- }
-
- fs.writeFile(
- path,
- JSON.stringify(json, null, " "),
- {mode: "0777"},
- function(err) {
- if (err) {
- console.log(err);
- }
- }
- );
- });
+ json.networks = networks;
+ client.manager.updateUser(client.name, json);
};
diff --git a/src/clientManager.js b/src/clientManager.js
index f006f848..2b0fbb87 100644
--- a/src/clientManager.js
+++ b/src/clientManager.js
@@ -29,18 +29,14 @@ ClientManager.prototype.loadUsers = function() {
ClientManager.prototype.loadUser = function(name) {
try {
- var json = fs.readFileSync(
- Helper.HOME + "/users/" + name + ".json",
- "utf-8"
- );
- json = JSON.parse(json);
+ var json = this.readUserConfig(name);
} catch (e) {
console.log(e);
return;
}
if (!this.findClient(name)) {
this.clients.push(new Client(
- this.sockets,
+ this,
name,
json
));
@@ -93,6 +89,50 @@ ClientManager.prototype.addUser = function(name, password) {
return true;
};
+ClientManager.prototype.updateUser = function(name, opts) {
+ var users = this.getUsers();
+ if (users.indexOf(name) === -1) {
+ return false;
+ }
+ if (typeof opts === "undefined") {
+ return false;
+ }
+ var path = Helper.HOME + "/users/" + name + ".json";
+ var user = {};
+
+ try {
+ user = this.readUserConfig(name);
+ _.merge(user, opts);
+ } catch (e) {
+ console.log(e);
+ return;
+ }
+
+ fs.writeFileSync(
+ path,
+ JSON.stringify(user, null, " "),
+ {mode: "0777"},
+ function(err) {
+ if (err) {
+ console.log(err);
+ }
+ }
+ );
+ return true;
+};
+
+ClientManager.prototype.readUserConfig = function(name) {
+ var users = this.getUsers();
+ if (users.indexOf(name) === -1) {
+ return false;
+ }
+ var path = Helper.HOME + "/users/" + name + ".json";
+ var user = {};
+ var data = fs.readFileSync(path, "utf-8");
+ user = JSON.parse(data);
+ return user;
+};
+
ClientManager.prototype.removeUser = function(name) {
var users = this.getUsers();
if (users.indexOf(name) === -1) {
diff --git a/src/server.js b/src/server.js
index baca756f..1951bbd6 100644
--- a/src/server.js
+++ b/src/server.js
@@ -107,6 +107,51 @@ function init(socket, client, token) {
client.connect(data);
}
);
+ if (!config.public) {
+ socket.on(
+ "change-password",
+ function(data) {
+ var old = data.old_password;
+ var p1 = data.new_password;
+ var p2 = data.verify_password;
+ if (typeof old === "undefined" || old === "") {
+ socket.emit("change-password", {
+ error: "Please enter your current password"
+ });
+ return;
+ }
+ if (typeof p1 === "undefined" || p1 === "") {
+ socket.emit("change-password", {
+ error: "Please enter a new password"
+ });
+ return;
+ }
+ if (p1 !== p2) {
+ socket.emit("change-password", {
+ error: "Both new password fields must match"
+ });
+ return;
+ }
+ if (!bcrypt.compareSync(old || "", client.config.password)) {
+ socket.emit("change-password", {
+ error: "The current password field does not match your account password"
+ });
+ return;
+ }
+ var salt = bcrypt.genSaltSync(8);
+ var hash = bcrypt.hashSync(p1, salt);
+ if (client.setPassword(hash)) {
+ socket.emit("change-password", {
+ success: "Successfully updated your password"
+ });
+ return;
+ }
+ socket.emit("change-password", {
+ error: "Failed to update your password"
+ });
+ }
+ );
+ }
socket.on(
"open",
function(data) {