Chat Federation: refactoring ActivityPub data:
The data format used by plugin v6.3.0 was not well suited. Here comes a new data format, with S2S informations. The plugin can automatically upgrade old format. It also continues to provide the old format, so than remote instance that did not update the plugin will still work.
This commit is contained in:
@ -1,73 +1,99 @@
|
||||
import type { LiveChatJSONLDInfos, LiveChatJSONLDAttribute } from './types'
|
||||
import type { RegisterServerOptions } from '@peertube/peertube-types'
|
||||
import type { LiveChatJSONLDAttributeV1 } from './types'
|
||||
import { URL } from 'url'
|
||||
|
||||
function sanitizePeertubeLiveChatInfos (chatInfos: any): LiveChatJSONLDAttribute {
|
||||
/**
|
||||
* Use this function for incoming remote informations.
|
||||
* It will sanitize them, by checking everything is ok.
|
||||
* It can also migrate from old format to the new one.
|
||||
*
|
||||
* This function can be used when informations are incoming,
|
||||
* or when reading stored information (to automatically migrate them)
|
||||
*
|
||||
* @param chatInfos remote chat informations
|
||||
* @returns a sanitized version of the remote chat informations
|
||||
*/
|
||||
function sanitizePeertubeLiveChatInfos (options: RegisterServerOptions, chatInfos: any): LiveChatJSONLDAttributeV1 {
|
||||
if (chatInfos === false) { return false }
|
||||
if (typeof chatInfos !== 'object') { return false }
|
||||
|
||||
if (chatInfos.type !== 'xmpp') { return false }
|
||||
if ((typeof chatInfos.jid) !== 'string') { return false }
|
||||
if (!Array.isArray(chatInfos.links)) { return false }
|
||||
|
||||
const r: LiveChatJSONLDInfos = {
|
||||
if (!('xmppserver' in chatInfos)) {
|
||||
// V0 format, migrating on the fly to v1.
|
||||
return _sanitizePeertubeLiveChatInfosV0(options, chatInfos)
|
||||
}
|
||||
|
||||
if (!chatInfos.xmppserver || (typeof chatInfos.xmppserver !== 'object')) {
|
||||
return false
|
||||
}
|
||||
const xmppserver = chatInfos.xmppserver
|
||||
|
||||
if ((typeof xmppserver.host) !== 'string') { return false }
|
||||
const host = _validateHost(xmppserver.host)
|
||||
if (!host) { return false }
|
||||
if ((typeof xmppserver.muc) !== 'string') { return false }
|
||||
const muc = _validateHost(xmppserver.muc)
|
||||
if (!muc) { return false }
|
||||
|
||||
const r: LiveChatJSONLDAttributeV1 = {
|
||||
type: chatInfos.type,
|
||||
jid: chatInfos.jid,
|
||||
links: []
|
||||
}
|
||||
|
||||
for (const link of chatInfos.links) {
|
||||
if ((typeof link) !== 'object') { continue }
|
||||
if (['xmpp-bosh-anonymous', 'xmpp-websocket-anonymous'].includes(link.type)) {
|
||||
if ((typeof link.jid) !== 'string') { continue }
|
||||
if ((typeof link.url) !== 'string') { continue }
|
||||
|
||||
if (
|
||||
!_validUrl(link.url, {
|
||||
noSearchParams: true,
|
||||
protocol: link.type === 'xmpp-websocket-anonymous' ? 'ws.' : 'http.'
|
||||
})
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
r.links.push({
|
||||
type: link.type,
|
||||
jid: link.jid,
|
||||
url: link.url
|
||||
})
|
||||
}
|
||||
if (link.type === 'xmpp-s2s') {
|
||||
if (!/^\d+$/.test(link.port)) {
|
||||
continue
|
||||
}
|
||||
const host = _validateHost(link.host)
|
||||
if (!host) {
|
||||
continue
|
||||
}
|
||||
r.links.push({
|
||||
type: link.type,
|
||||
host,
|
||||
port: link.port
|
||||
})
|
||||
}
|
||||
if (link.type === 'xmpp-peertube-livechat-ws-s2s') {
|
||||
if ((typeof link.url) !== 'string') { continue }
|
||||
|
||||
if (
|
||||
!_validUrl(link.url, {
|
||||
noSearchParams: true,
|
||||
protocol: 'ws.'
|
||||
})
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
r.links.push({
|
||||
type: link.type,
|
||||
url: link.url
|
||||
})
|
||||
xmppserver: {
|
||||
host,
|
||||
muc
|
||||
}
|
||||
}
|
||||
|
||||
if (xmppserver.directs2s) {
|
||||
if ((typeof xmppserver.directs2s) === 'object') {
|
||||
const port = xmppserver.directs2s.port
|
||||
if ((typeof port === 'string') && /^\d+$/.test(port)) {
|
||||
r.xmppserver.directs2s = {
|
||||
port
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (xmppserver.websockets2s) {
|
||||
if ((typeof xmppserver.websockets2s) === 'object') {
|
||||
const url = xmppserver.websockets2s.url
|
||||
if ((typeof url === 'string') && _validUrl(url, {
|
||||
noSearchParams: true,
|
||||
protocol: 'ws.'
|
||||
})) {
|
||||
r.xmppserver.websockets2s = {
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (xmppserver.anonymous) {
|
||||
const virtualhost = _validateHost(xmppserver.anonymous.virtualhost)
|
||||
if (virtualhost) {
|
||||
r.xmppserver.anonymous = {
|
||||
virtualhost
|
||||
}
|
||||
|
||||
const bosh = xmppserver.anonymous.bosh
|
||||
if ((typeof bosh === 'string') && _validUrl(bosh, {
|
||||
noSearchParams: true,
|
||||
protocol: 'http.'
|
||||
})) {
|
||||
r.xmppserver.anonymous.bosh = bosh
|
||||
}
|
||||
|
||||
const websocket = xmppserver.anonymous.websocket
|
||||
if ((typeof websocket === 'string') && _validUrl(websocket, {
|
||||
noSearchParams: true,
|
||||
protocol: 'ws.'
|
||||
})) {
|
||||
r.xmppserver.anonymous.websocket = websocket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
@ -107,8 +133,9 @@ function _validUrl (s: string, constraints: URLConstraints): boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
function _validateHost (s: string): false | string {
|
||||
function _validateHost (s: any): false | string {
|
||||
try {
|
||||
if (typeof s !== 'string') { return false }
|
||||
if (s.includes('/')) { return false }
|
||||
const url = new URL('http://' + s)
|
||||
return url.hostname
|
||||
@ -117,6 +144,68 @@ function _validateHost (s: string): false | string {
|
||||
}
|
||||
}
|
||||
|
||||
function _sanitizePeertubeLiveChatInfosV0 (options: RegisterServerOptions, chatInfos: any): LiveChatJSONLDAttributeV1 {
|
||||
const logger = options.peertubeHelpers.logger
|
||||
logger.debug('We are have to migrate data from the old JSONLD format')
|
||||
|
||||
if (chatInfos === false) { return false }
|
||||
if (typeof chatInfos !== 'object') { return false }
|
||||
|
||||
if (chatInfos.type !== 'xmpp') { return false }
|
||||
if ((typeof chatInfos.jid) !== 'string') { return false }
|
||||
|
||||
// no link? invalid! dropping all.
|
||||
if (!Array.isArray(chatInfos.links)) { return false }
|
||||
|
||||
const muc = _validateHost(chatInfos.jid.split('@')[1])
|
||||
if (!muc) { return false }
|
||||
if (!muc.startsWith('room.')) {
|
||||
logger.error('We expected old format host to begin with "room.". Discarding.')
|
||||
return false
|
||||
}
|
||||
const host = _validateHost(muc.replace(/^room\./, ''))
|
||||
if (!host) { return false }
|
||||
|
||||
const r: LiveChatJSONLDAttributeV1 = {
|
||||
type: chatInfos.type,
|
||||
jid: chatInfos.jid,
|
||||
xmppserver: {
|
||||
host,
|
||||
muc
|
||||
}
|
||||
}
|
||||
|
||||
for (const link of chatInfos.links) {
|
||||
if ((typeof link) !== 'object') { continue }
|
||||
if (['xmpp-bosh-anonymous', 'xmpp-websocket-anonymous'].includes(link.type)) {
|
||||
if ((typeof link.jid) !== 'string') { continue }
|
||||
if ((typeof link.url) !== 'string') { continue }
|
||||
|
||||
if (
|
||||
!_validUrl(link.url, {
|
||||
noSearchParams: true,
|
||||
protocol: link.type === 'xmpp-websocket-anonymous' ? 'ws.' : 'http.'
|
||||
})
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (!r.xmppserver.anonymous) {
|
||||
r.xmppserver.anonymous = {
|
||||
virtualhost: link.jid
|
||||
}
|
||||
}
|
||||
if (link.type === 'xmpp-bosh-anonymous') {
|
||||
r.xmppserver.anonymous.bosh = link.url
|
||||
} else if (link.type === 'xmpp-websocket-anonymous') {
|
||||
r.xmppserver.anonymous.websocket = link.url
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
export {
|
||||
sanitizePeertubeLiveChatInfos
|
||||
}
|
||||
|
Reference in New Issue
Block a user