wip: implement case-mapping

This commit is contained in:
Simon Ser 2021-05-20 19:13:58 +02:00
parent 22e54dac56
commit 7ce98e0e90
2 changed files with 133 additions and 2 deletions

View file

@ -161,7 +161,18 @@ export default class Client extends EventTarget {
break;
case irc.RPL_ISUPPORT:
var tokens = msg.params.slice(1, -1);
irc.parseISUPPORT(tokens, this.isupport);
var changed = irc.parseISUPPORT(tokens, this.isupport);
if (changed.indexOf("CASEMAPPING") >= 0) {
this.setCaseMapping(this.isupport.get("CASEMAPPING"));
}
break;
case irc.RPL_ENDOFMOTD:
case irc.ERR_NOMOTD:
// These messages are used to indicate the end of the ISUPPORT list
if (!this.isupport.has("CASEMAPPING")) {
// Server didn't send any CASEMAPPING token, assume RFC 1459
this.setCaseMapping("rfc1459");
}
break;
case "CAP":
this.handleCap(msg);
@ -358,6 +369,14 @@ export default class Client extends EventTarget {
console.log("Sent:", msg);
}
setCaseMapping(name) {
this.cm = irc.CaseMapping.byName(name);
if (!this.cm) {
console.error("Unsupported case-mapping '" + name + "', falling back to RFC 1459");
this.cm = irc.CaseMapping.RFC1459;
}
}
/* Execute a command that expects a response. `done` is called with message
* events until it returns a truthy value. */
roundtrip(msg, done) {

View file

@ -11,6 +11,7 @@ export const RPL_TOPICWHOTIME = "333";
export const RPL_WHOREPLY = "352";
export const RPL_NAMREPLY = "353";
export const RPL_ENDOFNAMES = "366";
export const RPL_ENDOFMOTD = "376";
export const ERR_NOMOTD = "422";
export const ERR_ERRONEUSNICKNAME = "432";
export const ERR_NICKNAMEINUSE = "433";
@ -320,6 +321,7 @@ export function parseCTCP(msg) {
}
export function parseISUPPORT(tokens, params) {
var changed = [];
tokens.forEach((tok) => {
if (tok.startsWith("-")) {
var k = tok.slice(1);
@ -333,6 +335,116 @@ export function parseISUPPORT(tokens, params) {
k = tok.slice(0, i);
v = tok.slice(i + 1);
}
params.set(k.toUpperCase(), v);
k = k.toUpperCase();
params.set(k, v);
changed.push(k);
});
return changed;
}
export const CaseMapping = {
ASCII(str) {
var out = "";
for (var i = 0; i < out.length; i++) {
var ch = str[i];
if ("A" <= ch && ch <= "Z") {
ch = ch.toLowerCase();
}
out += ch;
}
return out;
}
RFC1459(str) {
var out = "";
for (var i = 0; i < out.length; i++) {
var ch = str[i];
if ("A" <= ch && ch <= "Z") {
ch = ch.toLowerCase();
} else if (ch == "{") {
ch = "[";
} else if (ch == "}") {
ch = "]";
} else if (ch == "\\") {
ch = "|";
} else if (ch == "~") {
ch = "^";
}
out += ch;
}
return out;
}
RFC1459Strict(str) {
var out = "";
for (var i = 0; i < out.length; i++) {
var ch = str[i];
if ("A" <= ch && ch <= "Z") {
ch = ch.toLowerCase();
} else if (ch == "{") {
ch = "[";
} else if (ch == "}") {
ch = "]";
} else if (ch == "\\") {
ch = "|";
}
out += ch;
}
return out;
}
byName(name) {
switch (name) {
case "ascii":
return CaseMapping.ASCII;
case "rfc1459":
return CaseMapping.RFC1459;
case "rfc1459-strict":
return CaseMapping.RFC1459Strict;
}
}
};
export class CaseMapMap {
caseMap: null,
map: new Map(),
constructor(caseMap, iterable) {
this.caseMap = caseMap;
for (var [key, value] of iterable) {
this.set(key, value);
}
}
has(key) {
return this.map.has(this.caseMap(key));
}
get(key) {
var value = this.map.get(this.caseMap(key));
if (value) {
return value.value;
}
return undefined;
}
set(key, value) {
this.map.set(this.caseMap(key), { key, value });
}
[Symbol.iterator]() {
var it = this.map.entries();
return {
next: () => {
var { value, done } = it.next();
if (done) {
return { done: true };
}
return { value: [value[1].key, value[1].value], done: false };
},
};
}
}