From f0dae6be941ffdc785c06d790204eb4336970771 Mon Sep 17 00:00:00 2001 From: Kai Bleeke Date: Sat, 21 Oct 2023 13:54:35 +0200 Subject: [PATCH] Config option for matrix id character mapping Signed-off-by: Kai Bleeke --- config.sample.yaml | 5 ++++ config.schema.yml | 4 ++++ spec/unit/IrcServer.spec.js | 48 ++++++++++++++++++++++++++++++------- src/irc/IrcServer.ts | 32 +++++++++++++++---------- 4 files changed, 68 insertions(+), 21 deletions(-) diff --git a/config.sample.yaml b/config.sample.yaml index 7b0cb1ed2..ec50309bc 100644 --- a/config.sample.yaml +++ b/config.sample.yaml @@ -315,6 +315,11 @@ ircService: # $NICK => The IRC nick # $SERVER => The IRC server address (e.g. "irc.example.com") matrixClients: + # Character mapping for matrix IDs localparts + # 0: No character mapping + # 1: Mapping according to + # https://spec.matrix.org/v1.8/appendices/#mapping-from-other-character-sets + localPartCharacterMapping: 0 # The user ID template to use when creating virtual matrix users. This # MUST start with an @ and have $NICK somewhere in it. # Optional. Default: "@$SERVER_$NICK". diff --git a/config.schema.yml b/config.schema.yml index 53017ee55..14f30fb50 100644 --- a/config.schema.yml +++ b/config.schema.yml @@ -413,6 +413,10 @@ properties: matrixClients: type: "object" properties: + localPartCharacterMapping: + type: "integer" + minimum: 0 + maximum: 1 userTemplate: type: "string" pattern: "^@.*\\$NICK" diff --git a/spec/unit/IrcServer.spec.js b/spec/unit/IrcServer.spec.js index ec454cddb..2a637fbcc 100644 --- a/spec/unit/IrcServer.spec.js +++ b/spec/unit/IrcServer.spec.js @@ -73,25 +73,41 @@ describe("IrcServer", function() { describe("getUserLocalpart", function() { it("does not touch valid characters", function() { const server = new IrcServer("irc.foobar", - extend(true, IrcServer.DEFAULT_CONFIG, {}) + extend(true, IrcServer.DEFAULT_CONFIG, { + matrixClients: { + localPartCharacterMapping: 1, + } + }) ); expect(server.getUserLocalpart("foobar09.-+")).toEqual("irc.foobar_foobar09.-+"); }); it("encodes capital letters", function() { const server = new IrcServer("irc.foobar", - extend(true, IrcServer.DEFAULT_CONFIG, {}) + extend(true, IrcServer.DEFAULT_CONFIG, { + matrixClients: { + localPartCharacterMapping: 1, + } + }) ); expect(server.getUserLocalpart("foOBaR_09.-+")).toEqual("irc.foobar_fo_o_ba_r__09.-+"); }); it("encodes invalid characters", function() { const server = new IrcServer("irc.foobar", - extend(true, IrcServer.DEFAULT_CONFIG, {}) + extend(true, IrcServer.DEFAULT_CONFIG, { + matrixClients: { + localPartCharacterMapping: 1, + } + }) ); expect(server.getUserLocalpart("foobar=[m]")).toEqual("irc.foobar_foobar=3d=5bm=5d"); }); it("encodes both capital letters and invalid chars", function() { const server = new IrcServer("irc.foobar", - extend(true, IrcServer.DEFAULT_CONFIG, {}) + extend(true, IrcServer.DEFAULT_CONFIG, { + matrixClients: { + localPartCharacterMapping: 1, + } + }) ); expect(server.getUserLocalpart("f_oObAr=[m]")).toEqual("irc.foobar_f__o_ob_ar=3d=5bm=5d"); }); @@ -99,25 +115,41 @@ describe("IrcServer", function() { describe("getNickFromUserId", function() { it("does not touch valid characters", function() { const server = new IrcServer("irc.foobar", - extend(true, IrcServer.DEFAULT_CONFIG, {}) + extend(true, IrcServer.DEFAULT_CONFIG, { + matrixClients: { + localPartCharacterMapping: 1, + } + }) ); expect(server.getNickFromUserId("irc.foobar_foobar09.-+")).toEqual("foobar09.-+"); }); it("encodes capital letters", function() { const server = new IrcServer("irc.foobar", - extend(true, IrcServer.DEFAULT_CONFIG, {}) + extend(true, IrcServer.DEFAULT_CONFIG, { + matrixClients: { + localPartCharacterMapping: 1, + } + }) ); expect(server.getNickFromUserId("irc.foobar_fo_o_ba_r__09.-+")).toEqual("foOBaR_09.-+"); }); it("decodes invalid characters", function() { const server = new IrcServer("irc.foobar", - extend(true, IrcServer.DEFAULT_CONFIG, {}) + extend(true, IrcServer.DEFAULT_CONFIG, { + matrixClients: { + localPartCharacterMapping: 1, + } + }) ); expect(server.getNickFromUserId("irc.foobar_foobar=3d=5bm=5d")).toEqual("foobar=[m]"); }); it("encodes both capital letters and invalid chars", function() { const server = new IrcServer("irc.foobar", - extend(true, IrcServer.DEFAULT_CONFIG, {}) + extend(true, IrcServer.DEFAULT_CONFIG, { + matrixClients: { + localPartCharacterMapping: 1, + } + }) ); expect(server.getNickFromUserId("irc.foobar_f__o_ob_ar=3d=5bm=5d")).toEqual("f_oObAr=[m]"); }); diff --git a/src/irc/IrcServer.ts b/src/irc/IrcServer.ts index 9d1ef30f3..81ad9fc26 100644 --- a/src/irc/IrcServer.ts +++ b/src/irc/IrcServer.ts @@ -78,6 +78,7 @@ export interface IrcServerConfig { federate: boolean; }; matrixClients: { + localPartCharacterMapping: number, userTemplate: string; displayName: string; joinAttempts: number; @@ -533,17 +534,19 @@ export class IrcServer { } public getUserLocalpart(nick: string): string { - // the template is just a literal string with special vars; so find/replace - // the vars and strip the @ - // https://spec.matrix.org/v1.8/appendices/#mapping-from-other-character-sets - const escaped = nick.replaceAll(/[A-Z_]/g, (c) => "_" + c.toLowerCase()); - const escaped2 = escaped.replaceAll(/[^a-z0-9\.\_\-\/+]/g, - (c) => "=" + c.charCodeAt(0).toString(16).padStart(2, '0')); + if (this.config.matrixClients.localPartCharacterMapping == 1) { + // https://spec.matrix.org/v1.8/appendices/#mapping-from-other-character-sets + nick = nick.replaceAll(/[A-Z_]/g, (c) => "_" + c.toLowerCase()); + nick = nick.replaceAll(/[^a-z0-9\.\_\-\/+]/g, + (c) => "=" + c.charCodeAt(0).toString(16).padStart(2, '0')); + } + // the template is just a literal string with special vars; so find/replace + // the vars and strip the @ return renderTemplate(this.config.matrixClients.userTemplate, { server: this.domain, - nick: escaped2, + nick, }).substring(1); // the first character is guaranteed by config schema to be '@' } @@ -579,13 +582,15 @@ export class IrcServer { return null; } - // https://spec.matrix.org/v1.8/appendices/#mapping-from-other-character-sets - const unescaped = match[1].replaceAll(/=([0-9a-f][0-9a-f])/g, - (_m, g1) => String.fromCharCode(parseInt(g1, 16))); - - const unescaped2 = unescaped.replaceAll(/_([a-z_])/g, (m, g1) => g1.toUppercase()); + let nick = match[1]; + if (this.config.matrixClients.localPartCharacterMapping == 1) { + // https://spec.matrix.org/v1.8/appendices/#mapping-from-other-character-sets + nick = match[1].replaceAll(/=([0-9a-f][0-9a-f])/g, + (_m, g1) => String.fromCharCode(parseInt(g1, 16))); + nick = nick.replaceAll(/_([a-z_])/g, (m, g1) => g1.toUppercase()); + } - return unescaped2; + return nick; } public getUserIdFromNick(nick: string): string { @@ -733,6 +738,7 @@ export class IrcServer { mappings: {}, excludedUsers: [], matrixClients: { + localPartCharacterMapping: 0, userTemplate: "@$SERVER_$NICK", displayName: "$NICK", joinAttempts: -1,