import _ from 'underscore'; import { getSodium } from '../../session/crypto'; import { PubKey } from '../../session/types'; import { fromBase64ToArray, fromBase64ToArrayBuffer, fromHex, fromHexToArray, } from '../../session/utils/String'; import { OpenGroupMessageV2 } from './OpenGroupMessageV2'; export const defaultServer = 'https://sessionopengroup.com'; export const defaultServerPublicKey = '658d29b91892a2389505596b135e76a53db6e11d613a51dbd3d0816adffb231b'; export type OpenGroupRequestCommonType = { serverUrl: string; roomId: string; }; export type OpenGroupV2Request = { method: 'GET' | 'POST' | 'DELETE' | 'PUT'; room: string; server: string; endpoint: string; // queryParams are used for post or get, but not the same way queryParams?: Record; headers?: Record; isAuthRequired: boolean; // Always `true` under normal circumstances. You might want to disable this when running over Lokinet. useOnionRouting?: boolean; }; export type OpenGroupV2Info = { id: string; name: string; imageId?: string; }; /** * Try to build an full url and check it for validity. * @returns null if the check failed. the built URL otherwise */ export const buildUrl = (request: OpenGroupV2Request): URL | null => { let rawURL = `${request.server}/${request.endpoint}`; if (request.method === 'GET') { const entries = Object.entries(request.queryParams || {}); if (entries.length) { const queryString = entries .map(([key, value]) => `${key}=${value}`) .join('&'); rawURL += `?${queryString}`; } } // this just check that the URL is valid try { return new URL(`${rawURL}`); } catch (error) { return null; } }; /** * Map of serverUrl to roomId to list of moderators as a Set */ export const cachedModerators: Map< string, Map> > = new Map(); export const setCachedModerators = ( serverUrl: string, roomId: string, newModerators: Array ) => { const allRoomsMods = cachedModerators.get(serverUrl); if (!allRoomsMods) { cachedModerators.set(serverUrl, new Map()); } // tslint:disable: no-non-null-assertion if (!allRoomsMods!.get(roomId)) { allRoomsMods!.set(roomId, new Set()); } newModerators.forEach(m => { allRoomsMods!.get(roomId)?.add(m); }); }; export const parseMessages = async ( onionResult: any ): Promise> => { const rawMessages = onionResult?.result?.messages as Array< Record >; if (!rawMessages) { window.log.info('no new messages'); return []; } const messages = await Promise.all( rawMessages.map(async r => { try { const opengroupMessage = OpenGroupMessageV2.fromJson(r); console.warn('opengroupMessage', opengroupMessage); if ( !opengroupMessage?.serverId || !opengroupMessage.sentTimestamp || !opengroupMessage.base64EncodedData || !opengroupMessage.base64EncodedSignature ) { window.log.warn('invalid open group message received'); return null; } // Validate the message signature const senderPubKey = PubKey.cast( opengroupMessage.sender ).withoutPrefix(); const signature = fromBase64ToArrayBuffer( opengroupMessage.base64EncodedSignature ); const messageData = fromBase64ToArrayBuffer( opengroupMessage.base64EncodedData ); // throws if signature failed await window.libsignal.Curve.async.verifySignature( fromHex(senderPubKey), messageData, signature ); return opengroupMessage; } catch (e) { window.log.error( 'An error happened while fetching getMessages output:', e ); return null; } }) ); return _.compact(messages); };