forked from CringeStudios/gamja
Add support for SASL PLAIN
This commit is contained in:
parent
6787f90bdf
commit
69f0658b1e
103
assets/client.js
103
assets/client.js
@ -4,11 +4,14 @@ var server = {
|
|||||||
realname: null,
|
realname: null,
|
||||||
nick: null,
|
nick: null,
|
||||||
pass: null,
|
pass: null,
|
||||||
|
saslPlain: null,
|
||||||
autojoin: [],
|
autojoin: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
var ws = null;
|
var ws = null;
|
||||||
|
var registered = false;
|
||||||
var availableCaps = {};
|
var availableCaps = {};
|
||||||
|
var enabledCaps = {};
|
||||||
|
|
||||||
var buffers = {};
|
var buffers = {};
|
||||||
var activeBuffer = null;
|
var activeBuffer = null;
|
||||||
@ -188,7 +191,7 @@ function addAvailableCaps(s) {
|
|||||||
l.forEach(function(s) {
|
l.forEach(function(s) {
|
||||||
var parts = s.split("=");
|
var parts = s.split("=");
|
||||||
var k = parts[0];
|
var k = parts[0];
|
||||||
var v = null;
|
var v = "";
|
||||||
if (parts.length > 1) {
|
if (parts.length > 1) {
|
||||||
v = parts[1];
|
v = parts[1];
|
||||||
}
|
}
|
||||||
@ -204,20 +207,74 @@ function handleCap(msg) {
|
|||||||
addAvailableCaps(args[args.length - 1]);
|
addAvailableCaps(args[args.length - 1]);
|
||||||
if (args[0] != "*") {
|
if (args[0] != "*") {
|
||||||
console.log("Available server caps:", availableCaps);
|
console.log("Available server caps:", availableCaps);
|
||||||
|
|
||||||
|
var reqCaps = [];
|
||||||
|
|
||||||
|
var saslCap = availableCaps["sasl"];
|
||||||
|
var supportsSaslPlain = (saslCap !== undefined);
|
||||||
|
if (saslCap.length > 0) {
|
||||||
|
supportsSaslPlain = saslCap.split(",").includes("PLAIN");
|
||||||
|
}
|
||||||
|
|
||||||
|
var capEnd = true;
|
||||||
|
if (server.saslPlain && supportsSaslPlain) {
|
||||||
|
// CAP END is deferred after authentication finishes
|
||||||
|
reqCaps.push("sasl");
|
||||||
|
capEnd = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reqCaps.length > 0) {
|
||||||
|
sendMessage({ command: "CAP", params: ["REQ"].concat(reqCaps) });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!registered && capEnd) {
|
||||||
sendMessage({ command: "CAP", params: ["END"] });
|
sendMessage({ command: "CAP", params: ["END"] });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "NEW":
|
case "NEW":
|
||||||
addAvailableCaps(args[0]);
|
addAvailableCaps(args[0]);
|
||||||
console.log("Server added available caps:", args[0]);
|
console.log("Server added available caps:", args[0]);
|
||||||
break;
|
break;
|
||||||
case "DEL":
|
case "DEL":
|
||||||
args[0].split(" ").forEach(function(k) {
|
args[0].split(" ").forEach(function(cap) {
|
||||||
delete availableCaps[k];
|
delete availableCaps[cap];
|
||||||
|
delete enabledCaps[cap];
|
||||||
});
|
});
|
||||||
console.log("Server removed available caps:", args[0]);
|
console.log("Server removed available caps:", args[0]);
|
||||||
break;
|
break;
|
||||||
|
case "ACK":
|
||||||
|
console.log("Server ack'ed caps:", args[0]);
|
||||||
|
args[0].split(" ").forEach(function(cap) {
|
||||||
|
enabledCaps[cap] = true;
|
||||||
|
|
||||||
|
if (cap == "sasl" && server.saslPlain) {
|
||||||
|
console.log("Starting SASL PLAIN authentication");
|
||||||
|
sendMessage({ command: "AUTHENTICATE", params: ["PLAIN"] });
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "NAK":
|
||||||
|
console.log("Server nak'ed caps:", args[0]);
|
||||||
|
if (!registered) {
|
||||||
|
sendMessage({ command: "CAP", params: ["END"] });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleAuthenticate(msg) {
|
||||||
|
var challengeStr = msg.params[0];
|
||||||
|
|
||||||
|
// For now only PLAIN is supported
|
||||||
|
if (challengeStr != "+") {
|
||||||
|
console.error("Expected an empty challenge, got:", challengeStr);
|
||||||
|
sendMessage({ command: "AUTHENTICATE", params: ["*"] });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var respStr = btoa("\0" + server.saslPlain.username + "\0" + server.saslPlain.password);
|
||||||
|
sendMessage({ command: "AUTHENTICATE", params: [respStr] });
|
||||||
}
|
}
|
||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
@ -249,7 +306,14 @@ function connect() {
|
|||||||
|
|
||||||
switch (msg.command) {
|
switch (msg.command) {
|
||||||
case RPL_WELCOME:
|
case RPL_WELCOME:
|
||||||
|
if (server.saslPlain && availableCaps["sasl"] === undefined) {
|
||||||
|
console.error("Server doesn't support SASL PLAIN");
|
||||||
|
disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log("Registration complete");
|
console.log("Registration complete");
|
||||||
|
registered = true;
|
||||||
connectElt.style.display = "none";
|
connectElt.style.display = "none";
|
||||||
|
|
||||||
if (server.autojoin.length > 0) {
|
if (server.autojoin.length > 0) {
|
||||||
@ -292,6 +356,29 @@ function connect() {
|
|||||||
case "CAP":
|
case "CAP":
|
||||||
handleCap(msg);
|
handleCap(msg);
|
||||||
break;
|
break;
|
||||||
|
case "AUTHENTICATE":
|
||||||
|
handleAuthenticate(msg);
|
||||||
|
break;
|
||||||
|
case RPL_LOGGEDIN:
|
||||||
|
console.log("Logged in");
|
||||||
|
break;
|
||||||
|
case RPL_LOGGEDOUT:
|
||||||
|
console.log("Logged out");
|
||||||
|
break;
|
||||||
|
case RPL_SASLSUCCESS:
|
||||||
|
console.log("SASL authentication success");
|
||||||
|
if (!registered) {
|
||||||
|
sendMessage({ command: "CAP", params: ["END"] });
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ERR_NICKLOCKED:
|
||||||
|
case ERR_SASLFAIL:
|
||||||
|
case ERR_SASLTOOLONG:
|
||||||
|
case ERR_SASLABORTED:
|
||||||
|
case ERR_SASLALREADY:
|
||||||
|
console.error("SASL error:", msg);
|
||||||
|
disconnect();
|
||||||
|
break;
|
||||||
case "NOTICE":
|
case "NOTICE":
|
||||||
case "PRIVMSG":
|
case "PRIVMSG":
|
||||||
var target = msg.params[0];
|
var target = msg.params[0];
|
||||||
@ -369,6 +456,7 @@ function connect() {
|
|||||||
|
|
||||||
function disconnect() {
|
function disconnect() {
|
||||||
ws.close(1000);
|
ws.close(1000);
|
||||||
|
registered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendMessage(msg) {
|
function sendMessage(msg) {
|
||||||
@ -470,10 +558,17 @@ connectFormElt.onsubmit = function(event) {
|
|||||||
|
|
||||||
server.url = connectFormElt.elements.url.value;
|
server.url = connectFormElt.elements.url.value;
|
||||||
server.nick = connectFormElt.elements.nick.value;
|
server.nick = connectFormElt.elements.nick.value;
|
||||||
server.pass = connectFormElt.elements.password.value;
|
|
||||||
server.username = connectFormElt.elements.username.value || server.nick;
|
server.username = connectFormElt.elements.username.value || server.nick;
|
||||||
server.realname = connectFormElt.elements.realname.value || server.nick;
|
server.realname = connectFormElt.elements.realname.value || server.nick;
|
||||||
|
|
||||||
|
server.saslPlain = null;
|
||||||
|
if (connectFormElt.elements.password.value) {
|
||||||
|
server.saslPlain = {
|
||||||
|
username: server.username,
|
||||||
|
password: connectFormElt.elements.password.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
server.autojoin = [];
|
server.autojoin = [];
|
||||||
connectFormElt.elements.autojoin.value.split(",").forEach(function(ch) {
|
connectFormElt.elements.autojoin.value.split(",").forEach(function(ch) {
|
||||||
ch = ch.trim();
|
ch = ch.trim();
|
||||||
|
@ -3,6 +3,15 @@ const RPL_TOPIC = "332";
|
|||||||
const RPL_NAMREPLY = "353";
|
const RPL_NAMREPLY = "353";
|
||||||
const RPL_ENDOFNAMES = "366";
|
const RPL_ENDOFNAMES = "366";
|
||||||
const ERR_PASSWDMISMATCH = "464";
|
const ERR_PASSWDMISMATCH = "464";
|
||||||
|
// https://ircv3.net/specs/extensions/sasl-3.1
|
||||||
|
const RPL_LOGGEDIN = "900";
|
||||||
|
const RPL_LOGGEDOUT = "901";
|
||||||
|
const ERR_NICKLOCKED = "902";
|
||||||
|
const RPL_SASLSUCCESS = "903";
|
||||||
|
const ERR_SASLFAIL = "904";
|
||||||
|
const ERR_SASLTOOLONG = "905";
|
||||||
|
const ERR_SASLABORTED = "906";
|
||||||
|
const ERR_SASLALREADY = "907";
|
||||||
|
|
||||||
function parsePrefix(s) {
|
function parsePrefix(s) {
|
||||||
var prefix = {
|
var prefix = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user