You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
12 KiB
TypeScript
363 lines
12 KiB
TypeScript
/* eslint-disable consistent-return */
|
|
/* eslint-disable no-case-declarations */
|
|
import {
|
|
BaseConfigWrapperNode,
|
|
BlindingWrapperNode,
|
|
ContactsConfigWrapperNode,
|
|
ConvoInfoVolatileWrapperNode,
|
|
GroupPubkeyType,
|
|
MetaGroupWrapperNode,
|
|
MultiEncryptWrapperNode,
|
|
UserConfigWrapperNode,
|
|
UserGroupsWrapperNode,
|
|
} from 'libsession_util_nodejs';
|
|
import { isEmpty, isNull, isObject } from 'lodash';
|
|
|
|
import {
|
|
BlindingConfig,
|
|
ConfigWrapperGroup,
|
|
ConfigWrapperObjectTypesMeta,
|
|
ConfigWrapperUser,
|
|
MetaGroupConfig,
|
|
MultiEncryptConfig,
|
|
isBlindingWrapperType,
|
|
isMetaGroupWrapperType,
|
|
isMultiEncryptWrapperType,
|
|
isUserConfigWrapperType,
|
|
} from '../../browser/libsession_worker_functions';
|
|
|
|
/* eslint-disable no-console */
|
|
/* eslint-disable strict */
|
|
|
|
/**
|
|
*
|
|
* @param _x Looks like we need to duplicate this function here as we cannot import the existing one from a webworker context
|
|
*/
|
|
function assertUnreachable(_x: never, message: string): never {
|
|
console.info(`assertUnreachable: Didn't expect to get here with "${message}"`);
|
|
throw new Error("Didn't expect to get here");
|
|
}
|
|
|
|
// we can only have one of those so don't worry about storing them in a map for now
|
|
let userProfileWrapper: UserConfigWrapperNode | undefined;
|
|
let contactsConfigWrapper: ContactsConfigWrapperNode | undefined;
|
|
let userGroupsConfigWrapper: UserGroupsWrapperNode | undefined;
|
|
let convoInfoVolatileConfigWrapper: ConvoInfoVolatileWrapperNode | undefined;
|
|
|
|
const metaGroupWrappers: Map<GroupPubkeyType, MetaGroupWrapperNode> = new Map();
|
|
|
|
function getUserWrapper(type: ConfigWrapperUser): BaseConfigWrapperNode | undefined {
|
|
switch (type) {
|
|
case 'UserConfig':
|
|
return userProfileWrapper;
|
|
case 'ContactsConfig':
|
|
return contactsConfigWrapper;
|
|
case 'UserGroupsConfig':
|
|
return userGroupsConfigWrapper;
|
|
case 'ConvoInfoVolatileConfig':
|
|
return convoInfoVolatileConfigWrapper;
|
|
default:
|
|
assertUnreachable(type, `getUserWrapper: Missing case error "${type}"`);
|
|
}
|
|
}
|
|
|
|
function getGroupPubkeyFromWrapperType(type: ConfigWrapperGroup): GroupPubkeyType {
|
|
assertGroupWrapperType(type);
|
|
return type.substring(type.indexOf('-03') + 1) as GroupPubkeyType; // typescript is not yet smart enough
|
|
}
|
|
|
|
function getGroupWrapper(type: ConfigWrapperGroup): MetaGroupWrapperNode | undefined {
|
|
assertGroupWrapperType(type);
|
|
|
|
if (isMetaGroupWrapperType(type)) {
|
|
const pk = getGroupPubkeyFromWrapperType(type);
|
|
|
|
return metaGroupWrappers.get(pk);
|
|
}
|
|
assertUnreachable(type, `getGroupWrapper: Missing case error "${type}"`);
|
|
}
|
|
|
|
function getCorrespondingUserWrapper(wrapperType: ConfigWrapperUser): BaseConfigWrapperNode {
|
|
if (isUserConfigWrapperType(wrapperType)) {
|
|
switch (wrapperType) {
|
|
case 'UserConfig':
|
|
case 'ContactsConfig':
|
|
case 'UserGroupsConfig':
|
|
case 'ConvoInfoVolatileConfig':
|
|
const wrapper = getUserWrapper(wrapperType);
|
|
if (!wrapper) {
|
|
throw new Error(`UserWrapper: ${wrapperType} is not init yet`);
|
|
}
|
|
return wrapper;
|
|
default:
|
|
assertUnreachable(
|
|
wrapperType,
|
|
`getCorrespondingUserWrapper: Missing case error "${wrapperType}"`
|
|
);
|
|
}
|
|
}
|
|
|
|
assertUnreachable(
|
|
wrapperType,
|
|
`getCorrespondingUserWrapper missing global handling for "${wrapperType}"`
|
|
);
|
|
}
|
|
|
|
function getCorrespondingGroupWrapper(wrapperType: MetaGroupConfig): MetaGroupWrapperNode {
|
|
if (isMetaGroupWrapperType(wrapperType)) {
|
|
const wrapper = getGroupWrapper(wrapperType);
|
|
if (!wrapper) {
|
|
throw new Error(`GroupWrapper: ${wrapperType} is not init yet`);
|
|
}
|
|
return wrapper;
|
|
}
|
|
assertUnreachable(
|
|
wrapperType,
|
|
`getCorrespondingGroupWrapper missing global handling for "${wrapperType}"`
|
|
);
|
|
}
|
|
|
|
function getMultiEncryptWrapper(wrapperType: MultiEncryptConfig): MultiEncryptWrapperNode {
|
|
if (isMultiEncryptWrapperType(wrapperType)) {
|
|
return MultiEncryptWrapperNode;
|
|
}
|
|
assertUnreachable(wrapperType, `getMultiEncrypt missing global handling for "${wrapperType}"`);
|
|
}
|
|
|
|
function getBlindingWrapper(wrapperType: BlindingConfig): BlindingWrapperNode {
|
|
if (isBlindingWrapperType(wrapperType)) {
|
|
return BlindingWrapperNode;
|
|
}
|
|
assertUnreachable(wrapperType, `getBlindingWrapper missing global handling for "${wrapperType}"`);
|
|
}
|
|
|
|
function isUInt8Array(value: unknown): value is Uint8Array {
|
|
return isObject(value) && value.constructor === Uint8Array;
|
|
}
|
|
|
|
function assertUserWrapperType(wrapperType: ConfigWrapperObjectTypesMeta): ConfigWrapperUser {
|
|
if (!isUserConfigWrapperType(wrapperType)) {
|
|
throw new Error(`wrapperType "${wrapperType} is not of type User"`);
|
|
}
|
|
return wrapperType;
|
|
}
|
|
|
|
function assertGroupWrapperType(wrapperType: ConfigWrapperObjectTypesMeta): ConfigWrapperGroup {
|
|
if (!isMetaGroupWrapperType(wrapperType)) {
|
|
throw new Error(`wrapperType "${wrapperType} is not of type Group"`);
|
|
}
|
|
return wrapperType;
|
|
}
|
|
|
|
/**
|
|
* This function can be used to initialize a wrapper which takes the private ed25519 key of the user and a dump as argument.
|
|
*/
|
|
function initUserWrapper(options: Array<unknown>, wrapperType: ConfigWrapperUser) {
|
|
const userType = assertUserWrapperType(wrapperType);
|
|
|
|
const wrapper = getUserWrapper(wrapperType);
|
|
if (wrapper) {
|
|
throw new Error(`${wrapperType} already init`);
|
|
}
|
|
if (options.length !== 2) {
|
|
throw new Error(`${wrapperType} init needs two arguments`);
|
|
}
|
|
const [edSecretKey, dump] = options;
|
|
|
|
if (isEmpty(edSecretKey) || !isUInt8Array(edSecretKey)) {
|
|
throw new Error(`${wrapperType} init needs a valid edSecretKey`);
|
|
}
|
|
|
|
if (!isNull(dump) && !isUInt8Array(dump)) {
|
|
throw new Error(`${wrapperType} init needs a valid dump`);
|
|
}
|
|
switch (userType) {
|
|
case 'UserConfig':
|
|
userProfileWrapper = new UserConfigWrapperNode(edSecretKey, dump);
|
|
break;
|
|
case 'ContactsConfig':
|
|
contactsConfigWrapper = new ContactsConfigWrapperNode(edSecretKey, dump);
|
|
break;
|
|
case 'UserGroupsConfig':
|
|
userGroupsConfigWrapper = new UserGroupsWrapperNode(edSecretKey, dump);
|
|
break;
|
|
case 'ConvoInfoVolatileConfig':
|
|
convoInfoVolatileConfigWrapper = new ConvoInfoVolatileWrapperNode(edSecretKey, dump);
|
|
break;
|
|
default:
|
|
assertUnreachable(userType, `initUserWrapper: Missing case error "${userType}"`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* * This function is used to free wrappers from memory only
|
|
*
|
|
* NOTE only use this function for wrappers that have not been saved to the database.
|
|
*
|
|
* EXAMPLE When restoring an account and fetching the display name of a user. We want to fetch a UserProfile config message and make a temporary wrapper for it in order to look up the display name.
|
|
*/
|
|
function freeUserWrapper(wrapperType: ConfigWrapperObjectTypesMeta) {
|
|
const userWrapperType = assertUserWrapperType(wrapperType);
|
|
|
|
switch (userWrapperType) {
|
|
case 'UserConfig':
|
|
userProfileWrapper = undefined;
|
|
break;
|
|
case 'ContactsConfig':
|
|
contactsConfigWrapper = undefined;
|
|
break;
|
|
case 'UserGroupsConfig':
|
|
userGroupsConfigWrapper = undefined;
|
|
break;
|
|
case 'ConvoInfoVolatileConfig':
|
|
convoInfoVolatileConfigWrapper = undefined;
|
|
break;
|
|
default:
|
|
assertUnreachable(
|
|
userWrapperType,
|
|
`freeUserWrapper: Missing case error "${userWrapperType}"`
|
|
);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function can be used to initialize a group wrapper
|
|
*/
|
|
function initGroupWrapper(options: Array<unknown>, wrapperType: ConfigWrapperGroup) {
|
|
const groupType = assertGroupWrapperType(wrapperType);
|
|
|
|
const wrapper = getGroupWrapper(wrapperType);
|
|
if (wrapper) {
|
|
// console.warn(`group: "${wrapperType}" already init`);
|
|
return;
|
|
}
|
|
|
|
if (options.length !== 1 || isObject(options[0])) {
|
|
throw new Error(`group: "${wrapperType}" init needs 1 arguments`);
|
|
}
|
|
const firstArg = options[0];
|
|
if (
|
|
!isObject(firstArg) ||
|
|
!('groupEd25519Pubkey' in firstArg) ||
|
|
!('groupEd25519Secretkey' in firstArg) ||
|
|
!('metaDumped' in firstArg) ||
|
|
!('userEd25519Secretkey' in firstArg)
|
|
) {
|
|
throw new Error(
|
|
`group: "${wrapperType}" firstArg is not obj type, or missing some keys, or not the correct keys`
|
|
);
|
|
}
|
|
// we need all the fields defined in GroupWrapperConstructor, but the function in the wrapper will throw if we don't forward what's needed
|
|
|
|
const { groupEd25519Pubkey, groupEd25519Secretkey, metaDumped, userEd25519Secretkey } = firstArg;
|
|
|
|
if (
|
|
!isUInt8Array(groupEd25519Pubkey) ||
|
|
(!isUInt8Array(groupEd25519Secretkey) && !isNull(groupEd25519Secretkey)) ||
|
|
(!isUInt8Array(metaDumped) && !isNull(metaDumped)) ||
|
|
!isUInt8Array(userEd25519Secretkey)
|
|
) {
|
|
throw new Error(`group: "${wrapperType}" type of keys is not correct`);
|
|
}
|
|
|
|
if (isMetaGroupWrapperType(groupType)) {
|
|
const pk = getGroupPubkeyFromWrapperType(groupType);
|
|
const justCreated = new MetaGroupWrapperNode({
|
|
groupEd25519Pubkey,
|
|
groupEd25519Secretkey,
|
|
metaDumped,
|
|
userEd25519Secretkey,
|
|
});
|
|
|
|
metaGroupWrappers.set(pk, justCreated);
|
|
return;
|
|
}
|
|
assertUnreachable(groupType, `initGroupWrapper: Missing case error "${groupType}"`);
|
|
}
|
|
|
|
onmessage = async (e: {
|
|
data: [number, ConfigWrapperObjectTypesMeta | 'Blinding', string, ...any];
|
|
}) => {
|
|
const [jobId, config, action, ...args] = e.data;
|
|
|
|
try {
|
|
if (action === 'init') {
|
|
if (config === 'Blinding' || config === 'MultiEncrypt') {
|
|
// nothing to do for the blinding/multiEncrypt wrapper, all functions are static
|
|
postMessage([jobId, null, null]);
|
|
return;
|
|
}
|
|
if (isUserConfigWrapperType(config)) {
|
|
initUserWrapper(args, config);
|
|
postMessage([jobId, null, null]);
|
|
return;
|
|
}
|
|
if (isMetaGroupWrapperType(config)) {
|
|
initGroupWrapper(args, config);
|
|
postMessage([jobId, null, null]);
|
|
return;
|
|
}
|
|
assertUnreachable(config, `Unhandled init wrapper type: ${config}`);
|
|
}
|
|
if (action === 'free') {
|
|
if (config === 'Blinding' || config === 'MultiEncrypt') {
|
|
// nothing to do for the blinding/multiEncrypt wrapper, all functions are static
|
|
postMessage([jobId, null, null]);
|
|
return;
|
|
}
|
|
if (isUserConfigWrapperType(config)) {
|
|
freeUserWrapper(config);
|
|
postMessage([jobId, null, null]);
|
|
return;
|
|
}
|
|
if (isMetaGroupWrapperType(config)) {
|
|
const pk = getGroupPubkeyFromWrapperType(config);
|
|
metaGroupWrappers.delete(pk);
|
|
postMessage([jobId, null, null]);
|
|
return;
|
|
}
|
|
assertUnreachable(config, `Unhandled free wrapper type: ${config}`);
|
|
}
|
|
|
|
const wrapper = isUserConfigWrapperType(config)
|
|
? getCorrespondingUserWrapper(config)
|
|
: isMetaGroupWrapperType(config)
|
|
? getCorrespondingGroupWrapper(config)
|
|
: isMultiEncryptWrapperType(config)
|
|
? getMultiEncryptWrapper(config)
|
|
: isBlindingWrapperType(config)
|
|
? getBlindingWrapper(config)
|
|
: undefined;
|
|
if (!wrapper) {
|
|
throw new Error(`did not find an already built (or static) wrapper for config: "${config}"`);
|
|
}
|
|
const fn = (wrapper as any)[action];
|
|
|
|
if (!fn) {
|
|
throw new Error(
|
|
`Worker: job "${jobId}" did not find function "${action}" on config "${config}"`
|
|
);
|
|
}
|
|
const result = await (wrapper as any)[action](...args);
|
|
|
|
postMessage([jobId, null, result]);
|
|
} catch (error) {
|
|
const errorForDisplay = prepareErrorForPostMessage(error);
|
|
postMessage([jobId, errorForDisplay]);
|
|
}
|
|
};
|
|
|
|
function prepareErrorForPostMessage(error: unknown) {
|
|
if (!error) {
|
|
return null;
|
|
}
|
|
|
|
// if (error.stack) {
|
|
// return error.stack;
|
|
// }
|
|
|
|
return isObject(error) && 'message' in error
|
|
? error.message
|
|
: 'prepareErrorForPostMessage: unknown error';
|
|
}
|