Loading src/lib/MultipartMixed.ts 0 → 100644 +49 −0 Original line number Diff line number Diff line import crypto from "node:crypto"; import type Express from "express"; type MultipartMixedPart = { headers: { [k: string]: string; }; body: any; }; export class MultipartMixed { readonly boundary = crypto.randomUUID(); parts: MultipartMixedPart[] = []; add(part: MultipartMixedPart) { this.parts.push(part); return this; } send(res: Express.Response) { res.setHeader( "Content-Type", `multipart/mixed; boundary="${this.boundary}"` ); res.send(this.toString()); } toString() { const lines: string[] = [ "sc07 matrix-discord-bridge multipart/mixed builder", ]; for (const part of this.parts) { lines.push("--" + this.boundary); for (const [header, value] of Object.entries(part.headers)) { lines.push(header + ": " + value); } lines.push(""); lines.push(part.body); lines.push(""); } lines.push("--" + this.boundary + "--"); return lines.join("\r\n"); } } src/routes/media.ts +61 −34 Original line number Diff line number Diff line Loading @@ -6,15 +6,20 @@ import { Signed } from "../lib/signed"; import { SignatureError } from "signed"; import { Matrix } from "../lib/matrix"; import { Router } from "./_router"; import { MultipartMixed } from "../lib/MultipartMixed"; export class MediaRoutes extends Router { override setup(router: e.Router) { router.get( "/_matrix/media/v3/download/:hostname/:name/:filename?", this.handleMedia.bind(this) this.handleLegacyMedia.bind(this) ); router.get( "/_matrix/client/v1/media/download/:hostname/:name/:filename?", this.handleLegacyMedia.bind(this) ); router.get( "/_matrix/federation/v1/media/download/:name", this.handleMedia.bind(this) ); Loading Loading @@ -50,21 +55,12 @@ export class MediaRoutes extends Router { ); } private async handleMedia( req: e.Request<{ hostname: string; name: string }>, res: e.Response ) { if (req.params.hostname !== process.env.HOST) { res.status(400).json({ errcode: "M_ERROR", error: "This service does not proxy external MXCs", }); return; } const parts = Buffer.from(req.params.name, "base64url") .toString() .split(","); /** * Get target url from the media_id * @param media_id base64 encoded media_id */ private async parseURL(media_id: string): Promise<string | null> { const parts = Buffer.from(media_id, "base64url").toString().split(","); const mode = parts.shift(); let targetURL: string; Loading @@ -79,10 +75,7 @@ export class MediaRoutes extends Router { message_id, attachment_id ); if (!attachment) { res.status(404).json({}); return; } if (!attachment) return null; targetURL = attachment; break; Loading @@ -93,19 +86,13 @@ export class MediaRoutes extends Router { const user = await Discord.client.users .fetch(userid) .catch((e) => null); if (!user) { res.status(404).json({}); return; } if (!user) return null; const avatarURL = user.avatarURL({ extension: "png", forceStatic: true, }); if (!avatarURL) { res.status(404).json({}); return; } if (!avatarURL) return null; targetURL = avatarURL; break; Loading @@ -116,10 +103,7 @@ export class MediaRoutes extends Router { const sticker = await Discord.client .fetchSticker(stickerid) .catch((e) => null); if (!sticker) { res.status(404).json({}); return; } if (!sticker) return null; targetURL = sticker.url; break; Loading @@ -133,7 +117,50 @@ export class MediaRoutes extends Router { break; } default: res.status(404).json({ errcode: "M_UNKNOWN", error: "Unknown media" }); return null; } return targetURL; } private async handleMedia(req: e.Request<{ name: string }>, res: e.Response) { const targetURL = await this.parseURL(req.params.name); if (!targetURL) { res.status(404).json({}); return; } new MultipartMixed() .add({ headers: { "Content-Type": "application/json", }, body: JSON.stringify({}), }) .add({ headers: { Location: targetURL, }, body: "", }) .send(res); } private async handleLegacyMedia( req: e.Request<{ hostname: string; name: string }>, res: e.Response ) { if (req.params.hostname !== process.env.HOST) { res.status(400).json({ errcode: "M_ERROR", error: "This service does not proxy external MXCs", }); return; } const targetURL = await this.parseURL(req.params.name); if (!targetURL) { res.status(404).json({}); return; } Loading Loading
src/lib/MultipartMixed.ts 0 → 100644 +49 −0 Original line number Diff line number Diff line import crypto from "node:crypto"; import type Express from "express"; type MultipartMixedPart = { headers: { [k: string]: string; }; body: any; }; export class MultipartMixed { readonly boundary = crypto.randomUUID(); parts: MultipartMixedPart[] = []; add(part: MultipartMixedPart) { this.parts.push(part); return this; } send(res: Express.Response) { res.setHeader( "Content-Type", `multipart/mixed; boundary="${this.boundary}"` ); res.send(this.toString()); } toString() { const lines: string[] = [ "sc07 matrix-discord-bridge multipart/mixed builder", ]; for (const part of this.parts) { lines.push("--" + this.boundary); for (const [header, value] of Object.entries(part.headers)) { lines.push(header + ": " + value); } lines.push(""); lines.push(part.body); lines.push(""); } lines.push("--" + this.boundary + "--"); return lines.join("\r\n"); } }
src/routes/media.ts +61 −34 Original line number Diff line number Diff line Loading @@ -6,15 +6,20 @@ import { Signed } from "../lib/signed"; import { SignatureError } from "signed"; import { Matrix } from "../lib/matrix"; import { Router } from "./_router"; import { MultipartMixed } from "../lib/MultipartMixed"; export class MediaRoutes extends Router { override setup(router: e.Router) { router.get( "/_matrix/media/v3/download/:hostname/:name/:filename?", this.handleMedia.bind(this) this.handleLegacyMedia.bind(this) ); router.get( "/_matrix/client/v1/media/download/:hostname/:name/:filename?", this.handleLegacyMedia.bind(this) ); router.get( "/_matrix/federation/v1/media/download/:name", this.handleMedia.bind(this) ); Loading Loading @@ -50,21 +55,12 @@ export class MediaRoutes extends Router { ); } private async handleMedia( req: e.Request<{ hostname: string; name: string }>, res: e.Response ) { if (req.params.hostname !== process.env.HOST) { res.status(400).json({ errcode: "M_ERROR", error: "This service does not proxy external MXCs", }); return; } const parts = Buffer.from(req.params.name, "base64url") .toString() .split(","); /** * Get target url from the media_id * @param media_id base64 encoded media_id */ private async parseURL(media_id: string): Promise<string | null> { const parts = Buffer.from(media_id, "base64url").toString().split(","); const mode = parts.shift(); let targetURL: string; Loading @@ -79,10 +75,7 @@ export class MediaRoutes extends Router { message_id, attachment_id ); if (!attachment) { res.status(404).json({}); return; } if (!attachment) return null; targetURL = attachment; break; Loading @@ -93,19 +86,13 @@ export class MediaRoutes extends Router { const user = await Discord.client.users .fetch(userid) .catch((e) => null); if (!user) { res.status(404).json({}); return; } if (!user) return null; const avatarURL = user.avatarURL({ extension: "png", forceStatic: true, }); if (!avatarURL) { res.status(404).json({}); return; } if (!avatarURL) return null; targetURL = avatarURL; break; Loading @@ -116,10 +103,7 @@ export class MediaRoutes extends Router { const sticker = await Discord.client .fetchSticker(stickerid) .catch((e) => null); if (!sticker) { res.status(404).json({}); return; } if (!sticker) return null; targetURL = sticker.url; break; Loading @@ -133,7 +117,50 @@ export class MediaRoutes extends Router { break; } default: res.status(404).json({ errcode: "M_UNKNOWN", error: "Unknown media" }); return null; } return targetURL; } private async handleMedia(req: e.Request<{ name: string }>, res: e.Response) { const targetURL = await this.parseURL(req.params.name); if (!targetURL) { res.status(404).json({}); return; } new MultipartMixed() .add({ headers: { "Content-Type": "application/json", }, body: JSON.stringify({}), }) .add({ headers: { Location: targetURL, }, body: "", }) .send(res); } private async handleLegacyMedia( req: e.Request<{ hostname: string; name: string }>, res: e.Response ) { if (req.params.hostname !== process.env.HOST) { res.status(400).json({ errcode: "M_ERROR", error: "This service does not proxy external MXCs", }); return; } const targetURL = await this.parseURL(req.params.name); if (!targetURL) { res.status(404).json({}); return; } Loading