diff --git a/src/claimDefinitions/index.js b/src/claimDefinitions/index.js index dcc7b81..1d2283e 100644 --- a/src/claimDefinitions/index.js +++ b/src/claimDefinitions/index.js @@ -18,6 +18,7 @@ const list = [ 'irc', 'xmpp', 'matrix', + 'matrix_legacy', 'telegram', 'twitter', 'reddit', @@ -41,6 +42,7 @@ const data = { irc: require('./irc'), xmpp: require('./xmpp'), matrix: require('./matrix'), + matrix_legacy: require('./matrix_legacy'), telegram: require('./telegram'), twitter: require('./twitter'), reddit: require('./reddit'), diff --git a/src/claimDefinitions/matrix.js b/src/claimDefinitions/matrix.js index 62d94aa..8b86c68 100644 --- a/src/claimDefinitions/matrix.js +++ b/src/claimDefinitions/matrix.js @@ -27,12 +27,12 @@ const processURI = (uri) => { const params = queryString.parse(match[2]) - if (!('org.keyoxide.e' in params && 'org.keyoxide.r' in params)) { + if (!('org.keyoxide.r' in params)) { return null } const profileUrl = `https://matrix.to/#/@${match[1]}` - const eventUrl = `https://matrix.to/#/${params['org.keyoxide.r']}/${params['org.keyoxide.e']}` + const eventUrl = `https://matrix.to/#/${params['org.keyoxide.r']}` return { serviceprovider: { @@ -41,7 +41,7 @@ const processURI = (uri) => { }, match: { regularExpression: reURI, - isAmbiguous: false + isAmbiguous: true }, profile: { display: `@${match[1]}`, @@ -55,7 +55,6 @@ const processURI = (uri) => { access: E.ProofAccess.GRANTED, format: E.ProofFormat.JSON, data: { - eventId: params['org.keyoxide.e'], roomId: params['org.keyoxide.r'] } } @@ -63,7 +62,7 @@ const processURI = (uri) => { claim: { format: E.ClaimFormat.URI, relation: E.ClaimRelation.CONTAINS, - path: ['content', 'body'] + path: ['chunk', 'content', 'topic'] } } } @@ -71,7 +70,7 @@ const processURI = (uri) => { const tests = [ { uri: - 'matrix:u/alice:matrix.domain.org?org.keyoxide.r=!123:domain.org&org.keyoxide.e=$123', + 'matrix:u/alice:matrix.domain.org?org.keyoxide.r=!123:domain.org', shouldMatch: true }, { diff --git a/src/claimDefinitions/matrix_legacy.js b/src/claimDefinitions/matrix_legacy.js new file mode 100644 index 0000000..c68a2f9 --- /dev/null +++ b/src/claimDefinitions/matrix_legacy.js @@ -0,0 +1,93 @@ +/* +Copyright 2021 Yarmo Mackenbach + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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. +*/ +const E = require('../enums') +const queryString = require('query-string') + +const reURI = /^matrix:u\/(?:@)?([^@:]*:[^?]*)(\?.*)?/ + +const processURI = (uri) => { + const match = uri.match(reURI) + + if (!match[2]) { + return null + } + + const params = queryString.parse(match[2]) + + if (!('org.keyoxide.e' in params && 'org.keyoxide.r' in params)) { + return null + } + + const profileUrl = `https://matrix.to/#/@${match[1]}` + const eventUrl = `https://matrix.to/#/${params['org.keyoxide.r']}/${params['org.keyoxide.e']}` + + return { + serviceprovider: { + type: 'communication', + name: 'matrix' + }, + match: { + regularExpression: reURI, + isAmbiguous: true + }, + profile: { + display: `@${match[1]}`, + uri: profileUrl, + qr: null + }, + proof: { + uri: eventUrl, + request: { + fetcher: E.Fetcher.MATRIX_LEGACY, + access: E.ProofAccess.GRANTED, + format: E.ProofFormat.JSON, + data: { + eventId: params['org.keyoxide.e'], + roomId: params['org.keyoxide.r'] + } + } + }, + claim: { + format: E.ClaimFormat.URI, + relation: E.ClaimRelation.CONTAINS, + path: ['content', 'body'] + } + } +} + +const tests = [ + { + uri: + 'matrix:u/alice:matrix.domain.org?org.keyoxide.r=!123:domain.org&org.keyoxide.e=$123', + shouldMatch: true + }, + { + uri: 'matrix:u/alice:matrix.domain.org', + shouldMatch: true + }, + { + uri: 'xmpp:alice@domain.org', + shouldMatch: false + }, + { + uri: 'https://domain.org/@alice', + shouldMatch: false + } +] + +exports.reURI = reURI +exports.processURI = processURI +exports.tests = tests diff --git a/src/enums.js b/src/enums.js index c2813fe..3e50c68 100644 --- a/src/enums.js +++ b/src/enums.js @@ -49,6 +49,8 @@ const Fetcher = { XMPP: 'xmpp', /** HTTP request to Matrix API */ MATRIX: 'matrix', + /** HTTP request to Matrix API (legacy method) */ + MATRIX_LEGACY: 'matrix_legacy', /** HTTP request to Telegram API */ TELEGRAM: 'telegram', /** HTTP request to Twitter API */ diff --git a/src/fetcher/index.js b/src/fetcher/index.js index df32047..cc23077 100644 --- a/src/fetcher/index.js +++ b/src/fetcher/index.js @@ -18,6 +18,7 @@ exports.dns = require('./dns') exports.http = require('./http') exports.irc = require('./irc') exports.matrix = require('./matrix') +exports.matrix_legacy = require('./matrix_legacy') exports.telegram = require('./telegram') exports.twitter = require('./twitter') exports.xmpp = require('./xmpp') diff --git a/src/fetcher/matrix.js b/src/fetcher/matrix.js index 97a0a26..a9b4f0b 100644 --- a/src/fetcher/matrix.js +++ b/src/fetcher/matrix.js @@ -31,8 +31,7 @@ module.exports.timeout = 5000 * @function * @async * @param {object} data - Data used in the request - * @param {string} data.eventId - The identifier of the targeted post - * @param {string} data.roomId - The identifier of the room containing the targeted post + * @param {string} data.roomId - The identifier of the room containing the proof * @param {object} opts - Options used to enable the request * @param {string} opts.claims.matrix.instance - The server hostname on which the library can log in * @param {string} opts.claims.matrix.accessToken - The access token required to identify the library ({@link https://www.matrix.org/docs/guides/client-server-api|Matrix docs}) @@ -55,7 +54,10 @@ module.exports.fn = async (data, opts) => { throw new Error(`Matrix fetcher was not set up properly (${err.message})`) } - const url = `https://${opts.claims.matrix.instance}/_matrix/client/r0/rooms/${data.roomId}/event/${data.eventId}?access_token=${opts.claims.matrix.accessToken}` + const urlFilter = encodeURI('{"limit": 1,"types": ["m.room.topic"]}') + const url = `https://${opts.claims.matrix.instance}` + + `/_matrix/client/v3/rooms/${data.roomId}/messages` + + `?access_token=${opts.claims.matrix.accessToken}&dir=b&filter=${urlFilter}` axios.get(url, { headers: { Accept: 'application/json' } diff --git a/src/fetcher/matrix_legacy.js b/src/fetcher/matrix_legacy.js new file mode 100644 index 0000000..990e032 --- /dev/null +++ b/src/fetcher/matrix_legacy.js @@ -0,0 +1,78 @@ +/* +Copyright 2021 Yarmo Mackenbach + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +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. +*/ +const axios = require('axios') +const validator = require('validator') + +/** + * @module fetcher/matrix_legacy + */ + +/** + * The request's timeout value in milliseconds + * @constant {number} timeout + */ +module.exports.timeout = 5000 + +/** + * Execute a fetch request + * @function + * @async + * @param {object} data - Data used in the request + * @param {string} data.eventId - The identifier of the targeted post + * @param {string} data.roomId - The identifier of the room containing the targeted post + * @param {object} opts - Options used to enable the request + * @param {string} opts.claims.matrix.instance - The server hostname on which the library can log in + * @param {string} opts.claims.matrix.accessToken - The access token required to identify the library ({@link https://www.matrix.org/docs/guides/client-server-api|Matrix docs}) + * @returns {object} + */ +module.exports.fn = async (data, opts) => { + let timeoutHandle + const timeoutPromise = new Promise((resolve, reject) => { + timeoutHandle = setTimeout( + () => reject(new Error('Request was timed out')), + data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout + ) + }) + + const fetchPromise = new Promise((resolve, reject) => { + try { + validator.isFQDN(opts.claims.matrix.instance) + validator.isAscii(opts.claims.matrix.accessToken) + } catch (err) { + throw new Error(`Matrix_legacy fetcher was not set up properly (${err.message})`) + } + + const url = `https://${opts.claims.matrix.instance}/_matrix/client/r0/rooms/${data.roomId}/event/${data.eventId}?access_token=${opts.claims.matrix.accessToken}` + axios.get(url, + { + headers: { Accept: 'application/json' } + }) + .then(res => { + return res.data + }) + .then((res) => { + resolve(res) + }) + .catch((error) => { + reject(error) + }) + }) + + return Promise.race([fetchPromise, timeoutPromise]).then((result) => { + clearTimeout(timeoutHandle) + return result + }) +}