Skip to content

Commit

Permalink
Store capability values from CAP v3.2 handshakes + honor SASL v3.2 me…
Browse files Browse the repository at this point in the history
…chanism lists (#322)

* Store capability values in 'CAP LS 302' negotiations.

So it can be retrieved later (eg. to know what SASL mechanisms are available).

https://ircv3.net/specs/extensions/capability-negotiation.html#the-cap-ls-subcommand

* Honor SASL v3.2 mechanism lists

From https://ircv3.net/specs/extensions/sasl-3.2#usage :

> Clients SHOULD pick a mechanism present in the CAP LS reply they get from the server and attempt to use that mechanism for authentication after they request the sasl capability.

* Update handler.network.cap.available on CAP NEW/DEL

* Clear available caps when connecting && cleanup sasl code

Co-authored-by: ItsOnlyBinary <[email protected]>
  • Loading branch information
progval and ItsOnlyBinary authored Dec 13, 2022
1 parent 6effe43 commit a2697c9
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ module.exports = class IrcClient extends EventEmitter {
client.network.cap.negotiating = false;
client.network.cap.requested = [];
client.network.cap.enabled = [];
client.network.cap.available.clear();

client.command_handler.resetCache();
});
Expand Down
33 changes: 27 additions & 6 deletions src/commands/handlers/registration.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,18 +109,25 @@ const handlers = {
const capabilities = command.params[command.params.length - 1]
.replace(/(?:^| )[-~=]/, '')
.split(' ')
.filter((cap) => !!cap)
.map(function(cap) {
// CAPs in 3.2 may be in the form of CAP=VAL. So seperate those out
const sep = cap.indexOf('=');
if (sep === -1) {
capability_values[cap] = '';
if (command.params[1] === 'LS' || command.params[1] === 'NEW') {
handler.network.cap.available.set(cap, '');
}
return cap;
}

const cap_name = cap.substr(0, sep);
const cap_value = cap.substr(sep + 1);

capability_values[cap_name] = cap_value;
if (command.params[1] === 'LS' || command.params[1] === 'NEW') {
handler.network.cap.available.set(cap_name, cap_value);
}
return cap_name;
});

Expand Down Expand Up @@ -191,13 +198,24 @@ const handlers = {
);
}
if (handler.network.cap.negotiating) {
let authenticating = false;
if (handler.network.cap.isEnabled('sasl')) {
if (typeof handler.connection.options.sasl_mechanism === 'string') {
handler.connection.write('AUTHENTICATE ' + handler.connection.options.sasl_mechanism);
} else {
handler.connection.write('AUTHENTICATE PLAIN');
const options_mechanism = handler.connection.options.sasl_mechanism;
const wanted_mechanism = (typeof options_mechanism === 'string') ?
options_mechanism.toUpperCase() :
'PLAIN';

const mechanisms = handler.network.cap.available.get('sasl');
const valid_mechanisms = mechanisms.toUpperCase().split(',');
if (
!mechanisms || // SASL v3.1
valid_mechanisms.includes(wanted_mechanism) // SASL v3.2
) {
handler.connection.write('AUTHENTICATE ' + wanted_mechanism);
authenticating = true;
}
} else if (handler.network.cap.requested.length === 0) {
}
if (!authenticating && handler.network.cap.requested.length === 0) {
// If all of our requested CAPs have been handled, end CAP negotiation
handler.connection.write('CAP END');
handler.network.cap.negotiating = false;
Expand Down Expand Up @@ -244,12 +262,15 @@ const handlers = {
handler.network.cap.enabled,
capabilities
);
for (const cap_name of capabilities) {
handler.network.cap.available.delete(cap_name);
}
break;
}

handler.emit('cap ' + command.params[1].toLowerCase(), {
command: command.params[1],
capabilities: capability_values,
capabilities: capability_values, // for backward-compatibility
});
},

Expand Down
1 change: 1 addition & 0 deletions src/networkinfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ function NetworkInfo() {
negotiating: false,
requested: [],
enabled: [],
available: new Map(),
isEnabled: function(cap_name) {
return this.enabled.indexOf(cap_name) > -1;
}
Expand Down

0 comments on commit a2697c9

Please sign in to comment.