diff --git a/js/background.js b/js/background.js index 141218b5f..a791a9ccc 100644 --- a/js/background.js +++ b/js/background.js @@ -224,18 +224,11 @@ ); publicConversations.forEach(conversation => { const settings = conversation.getPublicSource(); - window.log.info(`Setting up public conversation for ${conversation.id}`); - const publicChatServer = window.lokiPublicChatAPI.findOrCreateServer( - settings.server + window.lokiPublicChatAPI.registerChannel( + settings.server, + settings.channelId, + conversation.id ); - if (publicChatServer) { - publicChatServer.findOrCreateChannel( - settings.channelId, - conversation.id - ); - } else { - window.log.warn(`Could not set up channel for ${conversation.id}`); - } }); window.lokiP2pAPI = new window.LokiP2pAPI(ourKey); window.lokiP2pAPI.on('pingContact', pubKey => { diff --git a/js/modules/loki_public_chat_api.js b/js/modules/loki_public_chat_api.js index 1acc1e884..6f2e1b916 100644 --- a/js/modules/loki_public_chat_api.js +++ b/js/modules/loki_public_chat_api.js @@ -1,4 +1,4 @@ -/* global log, textsecure */ +/* global log, textsecure, libloki, Signal */ const EventEmitter = require('events'); const nodeFetch = require('node-fetch'); const { URL, URLSearchParams } = require('url'); @@ -22,6 +22,10 @@ class LokiPublicChatAPI extends EventEmitter { } return thisServer; } + registerChannel(hostport, channelId, conversationId) { + const server = this.findOrCreateServer(hostport); + server.findOrCreateChannel(channelId, conversationId); + } unregisterChannel(hostport, channelId) { let thisServer; let i = 0; @@ -46,6 +50,8 @@ class LokiPublicServerAPI { this.chatAPI = chatAPI; this.server = hostport; this.channels = []; + this.tokenPromise = null; + this.baseServerUrl = `https://${this.server}`; } findOrCreateChannel(channelId, conversationId) { let thisChannel = this.channels.find( @@ -72,13 +78,94 @@ class LokiPublicServerAPI { this.channels.splice(i, 1); thisChannel.stopPolling = true; } + + async getOrRefreshServerToken() { + let token = await Signal.Data.getPublicServerTokenByServerName(this.server); + if (!token) { + token = await this.refreshServerToken(); + if (token) { + await Signal.Data.savePublicServerToken({ + server: this.server, + token, + }); + } + } + return token; + } + + async refreshServerToken() { + if (this.tokenPromise === null) { + this.tokenPromise = new Promise(async res => { + const token = await this.requestToken(); + if (!token) { + res(null); + return; + } + const registered = await this.submitToken(token); + if (!registered) { + res(null); + return; + } + res(token); + }); + } + const token = await this.tokenPromise; + this.tokenPromise = null; + return token; + } + + async requestToken() { + const url = new URL(`${this.baseServerUrl}/loki/v1/get_challenge`); + const params = { + pubKey: this.chatAPI.ourKey, + }; + url.search = new URLSearchParams(params); + + let res; + try { + res = await nodeFetch(url); + } catch (e) { + return null; + } + if (!res.ok) { + return null; + } + const body = await res.json(); + const token = await libloki.crypto.decryptToken(body); + return token; + } + + async submitToken(token) { + const options = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + pubKey: this.chatAPI.ourKey, + token, + }), + }; + + try { + const res = await nodeFetch( + `${this.baseServerUrl}/loki/v1/submit_challenge`, + options + ); + return res.ok; + } catch (e) { + return false; + } + } } class LokiPublicChannelAPI { constructor(serverAPI, channelId, conversationId) { this.serverAPI = serverAPI; this.channelId = channelId; - this.baseChannelUrl = `${serverAPI.server}/channels/${this.channelId}`; + this.baseChannelUrl = `${serverAPI.baseServerUrl}/channels/${ + this.channelId + }`; this.groupName = 'unknown'; this.conversationId = conversationId; this.lastGot = 0; @@ -88,6 +175,13 @@ class LokiPublicChannelAPI { this.pollForMessages(); } + getEndpoint() { + const endpoint = `https://${this.serverAPI.server}/channels/${ + this.channelId + }/messages`; + return endpoint; + } + async pollForChannel(source, endpoint) { // groupName will be loaded from server const url = new URL(this.baseChannelUrl); diff --git a/libloki/crypto.js b/libloki/crypto.js index dedda4016..df0b60a7f 100644 --- a/libloki/crypto.js +++ b/libloki/crypto.js @@ -158,7 +158,7 @@ } } - async function decryptToken(ivAndCipherText64, serverPubKey64) { + async function decryptToken({ ivAndCipherText64, serverPubKey64 }) { const ivAndCipherText = new Uint8Array( dcodeIO.ByteBuffer.fromBase64(ivAndCipherText64).toArrayBuffer() );