diff --git a/NOTICE b/NOTICE index 98184f91..44e25527 100644 --- a/NOTICE +++ b/NOTICE @@ -228,3 +228,28 @@ is included below: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + +Draupnir also incorperates work from matrix-bot-sdk +https://github.com/turt2live/matrix-bot-sdk +which included the following license notice: +MIT License + +Copyright (c) 2018 - 2022 Travis Ralston + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/ManagementRoomOutput.ts b/src/ManagementRoomOutput.ts index a0eabd18..b65ee494 100644 --- a/src/ManagementRoomOutput.ts +++ b/src/ManagementRoomOutput.ts @@ -26,7 +26,8 @@ limitations under the License. */ import * as Sentry from "@sentry/node"; -import { LogLevel, LogService, MessageType, Permalinks, TextualMessageEventContent, UserID } from "matrix-bot-sdk"; +import { LogLevel, LogService, MessageType, TextualMessageEventContent, UserID } from "matrix-bot-sdk"; +import { Permalinks } from "./commands/interface-manager/Permalinks"; import { IConfig } from "./config"; import { MatrixSendClient } from "./MatrixEmitter"; import { htmlEscape } from "./utils"; diff --git a/src/ProtectedRoomsConfig.ts b/src/ProtectedRoomsConfig.ts index 4211e1a8..89924602 100644 --- a/src/ProtectedRoomsConfig.ts +++ b/src/ProtectedRoomsConfig.ts @@ -26,7 +26,8 @@ limitations under the License. */ import AwaitLock from 'await-lock'; -import { LogService, Permalinks } from "matrix-bot-sdk"; +import { LogService } from "matrix-bot-sdk"; +import { Permalinks } from './commands/interface-manager/Permalinks'; import { IConfig } from "./config"; import { MatrixSendClient } from './MatrixEmitter'; const PROTECTED_ROOMS_EVENT_TYPE = "org.matrix.mjolnir.protected_rooms"; diff --git a/src/ProtectedRoomsSet.ts b/src/ProtectedRoomsSet.ts index ed93b25e..31bdc0af 100644 --- a/src/ProtectedRoomsSet.ts +++ b/src/ProtectedRoomsSet.ts @@ -25,7 +25,8 @@ limitations under the License. * are NOT distributed, contributed, committed, or licensed under the Apache License. */ -import { LogLevel, LogService, MatrixGlob, Permalinks, UserID } from "matrix-bot-sdk"; +import { LogLevel, LogService, MatrixGlob, UserID } from "matrix-bot-sdk"; +import { Permalinks } from "./commands/interface-manager/Permalinks"; import { IConfig } from "./config"; import ErrorCache, { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "./ErrorCache"; import ManagementRoomOutput from "./ManagementRoomOutput"; diff --git a/src/appservice/AccessControl.ts b/src/appservice/AccessControl.ts index 88747bce..4d64f897 100644 --- a/src/appservice/AccessControl.ts +++ b/src/appservice/AccessControl.ts @@ -26,9 +26,9 @@ limitations under the License. */ import { Bridge } from "matrix-appservice-bridge"; +import { Permalinks } from "../commands/interface-manager/Permalinks"; import AccessControlUnit, { EntityAccess } from "../models/AccessControlUnit"; import PolicyList from "../models/PolicyList"; -import { Permalinks } from "matrix-bot-sdk"; /** * Utility to manage which users have access to the application service, diff --git a/src/appservice/MjolnirManager.ts b/src/appservice/MjolnirManager.ts index f69c9ae9..fb83e792 100644 --- a/src/appservice/MjolnirManager.ts +++ b/src/appservice/MjolnirManager.ts @@ -2,13 +2,14 @@ import { Mjolnir } from "../Mjolnir"; import { Request, WeakEvent, BridgeContext, Bridge, Intent, Logger } from "matrix-appservice-bridge"; import { getProvisionedMjolnirConfig } from "../config"; import PolicyList from "../models/PolicyList"; -import { Permalinks, MatrixClient, UserID } from "matrix-bot-sdk"; +import { MatrixClient, UserID } from "matrix-bot-sdk"; import { DataStore, MjolnirRecord } from "./datastore"; import { AccessControl } from "./AccessControl"; import { Access } from "../models/AccessControlUnit"; import { randomUUID } from "crypto"; import EventEmitter from "events"; import { MatrixEmitter } from "../MatrixEmitter"; +import { Permalinks } from "../commands/interface-manager/Permalinks"; const log = new Logger('MjolnirManager'); diff --git a/src/commands/CreateBanListCommand.ts b/src/commands/CreateBanListCommand.ts index 8625b62f..0acd12e0 100644 --- a/src/commands/CreateBanListCommand.ts +++ b/src/commands/CreateBanListCommand.ts @@ -27,7 +27,8 @@ limitations under the License. import { Mjolnir } from "../Mjolnir"; import PolicyList from "../models/PolicyList"; -import { Permalinks, RichReply } from "matrix-bot-sdk"; +import { RichReply } from "matrix-bot-sdk"; +import { Permalinks } from "./interface-manager/Permalinks"; // !mjolnir list create export async function execCreateListCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) { diff --git a/src/commands/ListProtectedRoomsCommand.ts b/src/commands/ListProtectedRoomsCommand.ts index aa113d9d..38701b4c 100644 --- a/src/commands/ListProtectedRoomsCommand.ts +++ b/src/commands/ListProtectedRoomsCommand.ts @@ -26,7 +26,8 @@ limitations under the License. */ import { Mjolnir } from "../Mjolnir"; -import { Permalinks, RichReply } from "matrix-bot-sdk"; +import { RichReply } from "matrix-bot-sdk"; +import { Permalinks } from "./interface-manager/Permalinks"; // !mjolnir rooms export async function execListProtectedRooms(roomId: string, event: any, mjolnir: Mjolnir) { diff --git a/src/commands/RedactCommand.ts b/src/commands/RedactCommand.ts index b001982d..d1c185f5 100644 --- a/src/commands/RedactCommand.ts +++ b/src/commands/RedactCommand.ts @@ -27,7 +27,7 @@ limitations under the License. import { Mjolnir } from "../Mjolnir"; import { redactUserMessagesIn } from "../utils"; -import { Permalinks } from "matrix-bot-sdk"; +import { Permalinks } from "./interface-manager/Permalinks"; // !mjolnir redact [room alias] [limit] export async function execRedactCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) { @@ -49,6 +49,9 @@ export async function execRedactCommand(roomId: string, event: any, mjolnir: Mjo if (userId[0] !== '@') { // Assume it's a permalink const parsed = Permalinks.parseUrl(parts[2]); + if (parsed.roomIdOrAlias === undefined || parsed.eventId === undefined) { + throw new TypeError(`Got a malformed permalink ${parsed}`) + } const targetRoomId = await mjolnir.client.resolveRoom(parsed.roomIdOrAlias); await mjolnir.client.redactEvent(targetRoomId, parsed.eventId); await mjolnir.client.unstableApis.addReactionToEvent(roomId, event['event_id'], '✅'); diff --git a/src/commands/WatchUnwatchCommand.ts b/src/commands/WatchUnwatchCommand.ts index 1828987e..0a339e2a 100644 --- a/src/commands/WatchUnwatchCommand.ts +++ b/src/commands/WatchUnwatchCommand.ts @@ -26,7 +26,8 @@ limitations under the License. */ import { Mjolnir } from "../Mjolnir"; -import { Permalinks, RichReply } from "matrix-bot-sdk"; +import { RichReply } from "matrix-bot-sdk"; +import { Permalinks } from "./interface-manager/Permalinks"; // !mjolnir watch export async function execWatchCommand(roomId: string, event: any, mjolnir: Mjolnir, parts: string[]) { diff --git a/src/commands/interface-manager/MatrixRoomReference.ts b/src/commands/interface-manager/MatrixRoomReference.ts index b47b187a..275b5471 100644 --- a/src/commands/interface-manager/MatrixRoomReference.ts +++ b/src/commands/interface-manager/MatrixRoomReference.ts @@ -3,7 +3,8 @@ * All rights reserved. */ -import { Permalinks, RoomAlias } from "matrix-bot-sdk"; +import { RoomAlias } from "matrix-bot-sdk"; +import { Permalinks } from "./Permalinks"; type JoinRoom = (roomIdOrAlias: string, viaServers?: string[]) => Promise; type ResolveRoom = (roomIdOrAlias: string) => Promise @@ -48,6 +49,9 @@ type ResolveRoom = (roomIdOrAlias: string) => Promise */ public static fromPermalink(permalink: string): MatrixRoomReference { const parts = Permalinks.parseUrl(permalink); + if (parts.roomIdOrAlias === undefined) { + throw new TypeError(`There is no room id or alias in the permalink ${permalink}`); + } return MatrixRoomReference.fromRoomIdOrAlias(parts.roomIdOrAlias, parts.viaServers); } diff --git a/src/commands/interface-manager/Permalinks.ts b/src/commands/interface-manager/Permalinks.ts new file mode 100644 index 00000000..152f4d14 --- /dev/null +++ b/src/commands/interface-manager/Permalinks.ts @@ -0,0 +1,133 @@ +/** + * Copyright (C) 2023 Gnuxie + * All rights reserved. + * + * This file is only really here while we wait for + * https://github.com/turt2live/matrix-bot-sdk/pull/300 + * to be merged and be used by matrix-appservice-bridge too. + * + * This file incorperates work from matrix-bot-sdk + * https://github.com/turt2live/matrix-bot-sdk + * which included the following license notice: +MIT License + +Copyright (c) 2018 - 2022 Travis Ralston + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +/** + * The parts of a permalink. + * @see Permalinks + * @category Utilities + */ +export interface PermalinkParts { + /** + * The room ID or alias the permalink references. May be undefined. + */ + roomIdOrAlias?: string; + + /** + * The user ID the permalink references. May be undefined. + */ + userId?: string; + + /** + * The event ID the permalink references. May be undefined. + */ + eventId?: string; + + /** + * The servers the permalink is routed through. + */ + viaServers: string[]; +} + +/** + * Functions for handling permalinks + * @category Utilities + */ +export class Permalinks { + private constructor() { + } + + private static encodeViaArgs(servers: string[]): string { + if (!servers || !servers.length) return ""; + + return `?via=${servers.join("&via=")}`; + } + + /** + * Creates a room permalink. + * @param {string} roomIdOrAlias The room ID or alias to create a permalink for. + * @param {string[]} viaServers The servers to route the permalink through. + * @returns {string} A room permalink. + */ + public static forRoom(roomIdOrAlias: string, viaServers: string[] = []): string { + return `https://matrix.to/#/${encodeURIComponent(roomIdOrAlias)}${Permalinks.encodeViaArgs(viaServers)}`; + } + + /** + * Creates a user permalink. + * @param {string} userId The user ID to create a permalink for. + * @returns {string} A user permalink. + */ + public static forUser(userId: string): string { + return `https://matrix.to/#/${encodeURIComponent(userId)}`; + } + + /** + * Creates an event permalink. + * @param {string} roomIdOrAlias The room ID or alias to create a permalink in. + * @param {string} eventId The event ID to reference in the permalink. + * @param {string[]} viaServers The servers to route the permalink through. + * @returns {string} An event permalink. + */ + public static forEvent(roomIdOrAlias: string, eventId: string, viaServers: string[] = []): string { + return `https://matrix.to/#/${encodeURIComponent(roomIdOrAlias)}/${encodeURIComponent(eventId)}${Permalinks.encodeViaArgs(viaServers)}`; + } + + /** + * Parses a permalink URL into usable parts. + * @param {string} matrixTo The matrix.to URL to parse. + * @returns {PermalinkParts} The parts of the permalink. + */ + public static parseUrl(matrixTo: string): PermalinkParts { + const matrixToRegexp = /^https:\/\/matrix\.to\/#\/(?[^/?]+)\/?(?[^?]+)?(?\?[^]*)?$/; + + const url = matrixToRegexp.exec(matrixTo)?.groups; + if (!url) { + throw new Error("Not a valid matrix.to URL"); + } + + const entity = decodeURIComponent(url.entity); + if (entity[0] === '@') { + return { userId: entity, roomIdOrAlias: undefined, eventId: undefined, viaServers: [] }; + } else if (entity[0] === '#' || entity[0] === '!') { + return { + userId: undefined, + roomIdOrAlias: entity, + eventId: url.eventId && decodeURIComponent(url.eventId), + viaServers: new URLSearchParams(url.query).getAll('via'), + }; + } else { + throw new Error("Unexpected entity"); + } + } +} diff --git a/src/models/PolicyListManager.ts b/src/models/PolicyListManager.ts index 06045832..215992a7 100644 --- a/src/models/PolicyListManager.ts +++ b/src/models/PolicyListManager.ts @@ -25,11 +25,12 @@ limitations under the License. * are NOT distributed, contributed, committed, or licensed under the Apache License. */ -import { LogLevel, LogService, Permalinks } from "matrix-bot-sdk"; +import { LogLevel, LogService } from "matrix-bot-sdk"; import { Mjolnir } from "../Mjolnir"; import { MatrixDataManager, RawSchemedData, SCHEMA_VERSION_KEY } from "./MatrixDataManager"; import { MatrixRoomReference } from "../commands/interface-manager/MatrixRoomReference"; import { PolicyList, WATCHED_LISTS_EVENT_TYPE, WARN_UNPROTECTED_ROOM_EVENT_PREFIX } from "./PolicyList"; +import { Permalinks } from "../commands/interface-manager/Permalinks"; type WatchedListsEvent = RawSchemedData & { references?: string[]; }; /** diff --git a/src/protections/MessageIsMedia.ts b/src/protections/MessageIsMedia.ts index e2880763..4f5fb006 100644 --- a/src/protections/MessageIsMedia.ts +++ b/src/protections/MessageIsMedia.ts @@ -27,7 +27,8 @@ limitations under the License. import { Protection } from "./IProtection"; import { Mjolnir } from "../Mjolnir"; -import { LogLevel, Permalinks, UserID } from "matrix-bot-sdk"; +import { LogLevel, UserID } from "matrix-bot-sdk"; +import { Permalinks } from "../commands/interface-manager/Permalinks"; export class MessageIsMedia extends Protection { diff --git a/src/protections/MessageIsVoice.ts b/src/protections/MessageIsVoice.ts index 56bd5d0d..3aeea867 100644 --- a/src/protections/MessageIsVoice.ts +++ b/src/protections/MessageIsVoice.ts @@ -27,7 +27,8 @@ limitations under the License. import { Protection } from "./IProtection"; import { Mjolnir } from "../Mjolnir"; -import { LogLevel, Permalinks, UserID } from "matrix-bot-sdk"; +import { LogLevel, UserID } from "matrix-bot-sdk"; +import { Permalinks } from "../commands/interface-manager/Permalinks"; export class MessageIsVoice extends Protection { diff --git a/src/protections/ProtectionManager.ts b/src/protections/ProtectionManager.ts index 79f0f499..a5c232f4 100644 --- a/src/protections/ProtectionManager.ts +++ b/src/protections/ProtectionManager.ts @@ -35,7 +35,7 @@ import { MessageIsMedia } from "./MessageIsMedia"; import { TrustedReporters } from "./TrustedReporters"; import { JoinWaveShortCircuit } from "./JoinWaveShortCircuit"; import { Mjolnir } from "../Mjolnir"; -import { LogLevel, LogService, Permalinks } from "matrix-bot-sdk"; +import { LogLevel, LogService } from "matrix-bot-sdk"; import { ProtectionSettingValidationError } from "./ProtectionSettings"; import { Consequence } from "./consequence"; import { htmlEscape } from "../utils"; @@ -43,6 +43,7 @@ import { ERROR_KIND_FATAL, ERROR_KIND_PERMISSION } from "../ErrorCache"; import { RoomUpdateError } from "../models/RoomUpdateError"; import { BanPropagation } from "./BanPropagation"; import { MatrixDataManager, RawSchemedData, SchemaMigration, SCHEMA_VERSION_KEY } from "../models/MatrixDataManager"; +import { Permalinks } from "../commands/interface-manager/Permalinks"; const PROTECTIONS: Protection[] = [ new FirstMessageIsImage(), diff --git a/src/queues/UnlistedUserRedactionQueue.ts b/src/queues/UnlistedUserRedactionQueue.ts index 62e011bb..d1679b7e 100644 --- a/src/queues/UnlistedUserRedactionQueue.ts +++ b/src/queues/UnlistedUserRedactionQueue.ts @@ -24,7 +24,8 @@ limitations under the License. * However, this file is modified and the modifications in this file * are NOT distributed, contributed, committed, or licensed under the Apache License. */ -import { LogLevel, LogService, Permalinks } from "matrix-bot-sdk"; +import { LogLevel, LogService } from "matrix-bot-sdk"; +import { Permalinks } from "../commands/interface-manager/Permalinks"; import { Mjolnir } from "../Mjolnir"; /** diff --git a/test/integration/utilsTest.ts b/test/integration/utilsTest.ts index 41c396da..bba58bda 100644 --- a/test/integration/utilsTest.ts +++ b/test/integration/utilsTest.ts @@ -27,7 +27,7 @@ describe("Test: utils", function() { }); assert.equal( message.content.formatted_body, - `it's fun here in ${managementRoomAlias}` + `it's fun here in ${managementRoomAlias}` ); }); });