|
|
|
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<string, string>;
|
|
|
|
headers?: Record<string, string>;
|
|
|
|
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<string, Set<string>>
|
|
|
|
> = new Map();
|
|
|
|
|
|
|
|
export const setCachedModerators = (
|
|
|
|
serverUrl: string,
|
|
|
|
roomId: string,
|
|
|
|
newModerators: Array<string>
|
|
|
|
) => {
|
|
|
|
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<Array<OpenGroupMessageV2>> => {
|
|
|
|
const rawMessages = onionResult?.result?.messages as Array<
|
|
|
|
Record<string, any>
|
|
|
|
>;
|
|
|
|
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);
|
|
|
|
};
|