Skip to content

Commit

Permalink
Config option for matrix id character mapping
Browse files Browse the repository at this point in the history
Signed-off-by: Kai Bleeke <[email protected]>
  • Loading branch information
Kai Bleeke committed Oct 21, 2023
1 parent 57e9472 commit f0dae6b
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 21 deletions.
5 changes: 5 additions & 0 deletions config.sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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".
Expand Down
4 changes: 4 additions & 0 deletions config.schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,10 @@ properties:
matrixClients:
type: "object"
properties:
localPartCharacterMapping:
type: "integer"
minimum: 0
maximum: 1
userTemplate:
type: "string"
pattern: "^@.*\\$NICK"
Expand Down
48 changes: 40 additions & 8 deletions spec/unit/IrcServer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,51 +73,83 @@ 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");
});
});
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]");
});
Expand Down
32 changes: 19 additions & 13 deletions src/irc/IrcServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export interface IrcServerConfig {
federate: boolean;
};
matrixClients: {
localPartCharacterMapping: number,
userTemplate: string;
displayName: string;
joinAttempts: number;
Expand Down Expand Up @@ -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 '@'
}

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -733,6 +738,7 @@ export class IrcServer {
mappings: {},
excludedUsers: [],
matrixClients: {
localPartCharacterMapping: 0,
userTemplate: "@$SERVER_$NICK",
displayName: "$NICK",
joinAttempts: -1,
Expand Down

0 comments on commit f0dae6b

Please sign in to comment.