Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions server/lib/matrix-utils/ensure-room-joined.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,29 @@ const assert = require('assert');
const urlJoin = require('url-join');

const { fetchEndpointAsJson } = require('../fetch-endpoint');
const getServerNameFromMatrixRoomIdOrAlias = require('./get-server-name-from-matrix-room-id-or-alias');

const config = require('../config');
const StatusError = require('../status-error');
const matrixServerUrl = config.get('matrixServerUrl');
assert(matrixServerUrl);

async function ensureRoomJoined(accessToken, roomIdOrAlias, viaServers = []) {
async function ensureRoomJoined(accessToken, roomIdOrAlias, viaServers = new Set()) {
// We use a `Set` to ensure that we don't have duplicate servers in the list
assert(viaServers instanceof Set);

// Let's do our best for the user to join the room. Since room ID's are
// unroutable on their own and won't be found if the server doesn't already
// know about the room, we'll try to join the room via the server name that
// we derived from the room ID or alias.
const viaServersWithAssumptions = new Set(viaServers);
const derivedServerName = getServerNameFromMatrixRoomIdOrAlias(roomIdOrAlias);
if (derivedServerName) {
viaServersWithAssumptions.add(derivedServerName);
}

let qs = new URLSearchParams();
[].concat(viaServers).forEach((viaServer) => {
Array.from(viaServersWithAssumptions).forEach((viaServer) => {
qs.append('server_name', viaServer);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use strict';

const assert = require('assert');

// See https://spec.matrix.org/v1.5/appendices/#server-name
function getServerNameFromMatrixRoomIdOrAlias(roomIdOrAlias) {
assert(roomIdOrAlias);

const pieces = roomIdOrAlias.split(':');
// We can only derive the server name if there is a colon in the string. Since room
// IDs are supposed to be treated as opaque strings, there is a future possibility
// that they will not contain a colon.
if (pieces.length < 2) {
return null;
}

const servername = pieces.slice(1).join(':');

return servername;
}

module.exports = getServerNameFromMatrixRoomIdOrAlias;
30 changes: 30 additions & 0 deletions server/lib/parse-via-servers-from-user-input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict';

const StatusError = require('../lib/status-error');

function parseViaServersFromUserInput(rawViaServers) {
// `rawViaServers` could be an array, a single string, or undefined. Turn it into an
// array no matter what
const rawViaServerList = [].concat(rawViaServers || []);
if (rawViaServerList.length === 0) {
return new Set();
}

const viaServerList = rawViaServerList.map((viaServer) => {
// Sanity check to ensure that the via servers are strings (valid enough looking
// host names)
if (typeof viaServer !== 'string') {
throw new StatusError(
400,
`?via server must be a string, got ${viaServer} (${typeof viaServer})`
);
}

return viaServer;
});

// We use a `Set` to ensure that we don't have duplicate servers in the list
return new Set(viaServerList);
}

module.exports = parseViaServersFromUserInput;
21 changes: 17 additions & 4 deletions server/routes/room-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const timeoutMiddleware = require('./timeout-middleware');
const redirectToCorrectArchiveUrlIfBadSigil = require('./redirect-to-correct-archive-url-if-bad-sigil-middleware');

const { HTTPResponseError } = require('../lib/fetch-endpoint');
const parseViaServersFromUserInput = require('../lib/parse-via-servers-from-user-input');
const fetchRoomData = require('../lib/matrix-utils/fetch-room-data');
const fetchEventsFromTimestampBackwards = require('../lib/matrix-utils/fetch-events-from-timestamp-backwards');
const ensureRoomJoined = require('../lib/matrix-utils/ensure-room-joined');
Expand Down Expand Up @@ -173,7 +174,11 @@ router.get(

// We have to wait for the room join to happen first before we can fetch
// any of the additional room info or messages.
const roomId = await ensureRoomJoined(matrixAccessToken, roomIdOrAlias, req.query.via);
const roomId = await ensureRoomJoined(
matrixAccessToken,
roomIdOrAlias,
parseViaServersFromUserInput(req.query.via)
);

// Find the closest day to the current time with messages
const { originServerTs } = await timestampToEvent({
Expand All @@ -192,7 +197,7 @@ router.get(
// We can avoid passing along the `via` query parameter because we already
// joined the room above (see `ensureRoomJoined`).
//
//viaServers: req.query.via,
//viaServers: parseViaServersFromUserInput(req.query.via),
})
);
})
Expand Down Expand Up @@ -245,7 +250,11 @@ router.get(

// We have to wait for the room join to happen first before we can use the jump to
// date endpoint
const roomId = await ensureRoomJoined(matrixAccessToken, roomIdOrAlias, req.query.via);
const roomId = await ensureRoomJoined(
matrixAccessToken,
roomIdOrAlias,
parseViaServersFromUserInput(req.query.via)
);

let eventIdForClosestEvent;
let tsForClosestEvent;
Expand Down Expand Up @@ -536,7 +545,11 @@ router.get(

// We have to wait for the room join to happen first before we can fetch
// any of the additional room info or messages.
const roomId = await ensureRoomJoined(matrixAccessToken, roomIdOrAlias, req.query.via);
const roomId = await ensureRoomJoined(
matrixAccessToken,
roomIdOrAlias,
parseViaServersFromUserInput(req.query.via)
);

// Do these in parallel to avoid the extra time in sequential round-trips
// (we want to display the archive page faster)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use strict';

const assert = require('assert');

const getServerNameFromMatrixRoomIdOrAlias = require('../../../server/lib/matrix-utils/get-server-name-from-matrix-room-id-or-alias');

describe('getServerNameFromMatrixRoomIdOrAlias', () => {
// Some examples from https://spec.matrix.org/v1.5/appendices/#server-name
const testCases = [
{
name: 'can parse normal looking domain name',
input: '!foo:matrix.org',
expected: 'matrix.org',
},
{
name: 'can parse sub-domain',
input: '!foo:archive.matrix.org',
expected: 'archive.matrix.org',
},
{
name: 'can parse domain with port',
input: '!foo:matrix.org:8888',
expected: 'matrix.org:8888',
},
{
name: 'can parse IPv4 address',
input: '!foo:192.168.1.1',
expected: '192.168.1.1',
},
{
name: 'can parse IPv4 address with port',
input: '!foo:192.168.1.1:1234',
expected: '192.168.1.1:1234',
},
{
name: 'can parse IPv6 address',
input: '!foo:[1234:5678::abcd]',
expected: '[1234:5678::abcd]',
},
{
name: 'can parse IPv6 address with port',
input: '!foo:[1234:5678::abcd]:1234',
expected: '[1234:5678::abcd]:1234',
},
{
name: `opaque room ID is *NOT* parsed and we can't derive a server name`,
input: '!foobarbaz',
expected: null,
},
];

testCases.forEach((testCaseMeta) => {
it(testCaseMeta.name, () => {
const actual = getServerNameFromMatrixRoomIdOrAlias(testCaseMeta.input);
assert.strictEqual(actual, testCaseMeta.expected);
});
});
});