Merge remote-tracking branch 'origin/clearnet' into unstable

pull/3122/head
Audric Ackermann 9 months ago
commit 8ce2bf6d2a

@ -1,3 +1,4 @@
name: 'Setup and build'
description: 'Setup and build Session Desktop'
runs:

@ -2,7 +2,7 @@
"name": "session-desktop",
"productName": "Session",
"description": "Private messaging from your desktop",
"version": "1.12.3",
"version": "1.12.4",
"license": "GPL-3.0",
"author": {
"name": "Oxen Labs",
@ -96,7 +96,7 @@
"fs-extra": "9.0.0",
"glob": "7.1.2",
"image-type": "^4.1.0",
"libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.4/libsession_util_nodejs-v0.3.4.tar.gz",
"libsession_util_nodejs": "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.19/libsession_util_nodejs-v0.3.19.tar.gz",
"libsodium-wrappers-sumo": "^0.7.9",
"linkify-it": "^4.0.1",
"lodash": "^4.17.21",

@ -18,7 +18,6 @@ import { OpenGroupUtils } from '../session/apis/open_group_api/utils';
import { getOpenGroupV2ConversationId } from '../session/apis/open_group_api/utils/OpenGroupUtils';
import { getSwarmPollingInstance } from '../session/apis/snode_api';
import { getConversationController } from '../session/conversations';
import { IncomingMessage } from '../session/messages/incoming/IncomingMessage';
import { Profile, ProfileManager } from '../session/profile_manager/ProfileManager';
import { PubKey } from '../session/types';
import { StringUtils, UserUtils } from '../session/utils';
@ -37,6 +36,8 @@ import { Registration } from '../util/registration';
import { ReleasedFeatures } from '../util/releaseFeature';
import { Storage, isSignInByLinking, setLastProfileUpdateTimestamp } from '../util/storage';
import { SnodeNamespaces } from '../session/apis/snode_api/namespaces';
import { RetrieveMessageItemWithNamespace } from '../session/apis/snode_api/types';
// eslint-disable-next-line import/no-unresolved
import { ConfigWrapperObjectTypes } from '../webworker/workers/browser/libsession_worker_functions';
import {
@ -52,18 +53,29 @@ import { HexKeyPair } from './keypairs';
import { queueAllCachedFromSource } from './receiver';
import { EnvelopePlus } from './types';
function groupByVariant(
incomingConfigs: Array<IncomingMessage<SignalService.ISharedConfigMessage>>
) {
function groupByNamespace(incomingConfigs: Array<RetrieveMessageItemWithNamespace>) {
const groupedByVariant: Map<
ConfigWrapperObjectTypes,
Array<IncomingMessage<SignalService.ISharedConfigMessage>>
Array<RetrieveMessageItemWithNamespace>
> = new Map();
incomingConfigs.forEach(incomingConfig => {
const { kind } = incomingConfig.message;
const wrapperId = LibSessionUtil.kindToVariant(kind);
const { namespace } = incomingConfig;
const wrapperId: ConfigWrapperObjectTypes | null =
namespace === SnodeNamespaces.UserProfile
? 'UserConfig'
: namespace === SnodeNamespaces.UserContacts
? 'ContactsConfig'
: namespace === SnodeNamespaces.UserGroups
? 'UserGroupsConfig'
: namespace === SnodeNamespaces.ConvoInfoVolatile
? 'ConvoInfoVolatileConfig'
: null;
if (!wrapperId) {
throw new Error('Unexpected wrapperId');
}
if (!groupedByVariant.has(wrapperId)) {
groupedByVariant.set(wrapperId, []);
@ -75,10 +87,10 @@ function groupByVariant(
}
async function mergeConfigsWithIncomingUpdates(
incomingConfigs: Array<IncomingMessage<SignalService.ISharedConfigMessage>>
incomingConfigs: Array<RetrieveMessageItemWithNamespace>
): Promise<Map<ConfigWrapperObjectTypes, IncomingConfResult>> {
// first, group by variant so we do a single merge call
const groupedByVariant = groupByVariant(incomingConfigs);
const groupedByNamespace = groupByNamespace(incomingConfigs);
const groupedResults: Map<ConfigWrapperObjectTypes, IncomingConfResult> = new Map();
@ -86,15 +98,15 @@ async function mergeConfigsWithIncomingUpdates(
const publicKey = UserUtils.getOurPubKeyStrFromCache();
try {
for (let index = 0; index < groupedByVariant.size; index++) {
const variant = [...groupedByVariant.keys()][index];
const sameVariant = groupedByVariant.get(variant);
for (let index = 0; index < groupedByNamespace.size; index++) {
const variant = [...groupedByNamespace.keys()][index];
const sameVariant = groupedByNamespace.get(variant);
if (!sameVariant?.length) {
continue;
}
const toMerge = sameVariant.map(msg => ({
data: msg.message.data,
hash: msg.messageHash,
data: StringUtils.fromBase64ToArray(msg.data),
hash: msg.hash,
}));
if (window.sessionFeatureFlags.debug.debugLibsessionDumps) {
window.log.info(
@ -105,9 +117,7 @@ async function mergeConfigsWithIncomingUpdates(
for (let dumpIndex = 0; dumpIndex < toMerge.length; dumpIndex++) {
const element = toMerge[dumpIndex];
window.log.info(
`printDumpsForDebugging: toMerge of ${dumpIndex}:${element.hash}: ${StringUtils.toHex(
element.data
)} `,
`printDumpsForDebugging: toMerge of ${dumpIndex}:${element.hash}: ${element.data} `,
StringUtils.toHex(await GenericWrapperActions.dump(variant))
);
}
@ -117,8 +127,8 @@ async function mergeConfigsWithIncomingUpdates(
const needsPush = await GenericWrapperActions.needsPush(variant);
const needsDump = await GenericWrapperActions.needsDump(variant);
const mergedTimestamps = sameVariant
.filter(m => hashesMerged.includes(m.messageHash))
.map(m => m.envelopeTimestamp);
.filter(m => hashesMerged.includes(m.hash))
.map(m => m.timestamp);
const latestEnvelopeTimestamp = Math.max(...mergedTimestamps);
window.log.debug(
@ -891,7 +901,7 @@ async function processMergingResults(results: Map<ConfigWrapperObjectTypes, Inco
}
async function handleConfigMessagesViaLibSession(
configMessages: Array<IncomingMessage<SignalService.ISharedConfigMessage>>
configMessages: Array<RetrieveMessageItemWithNamespace>
) {
const userConfigLibsession = await ReleasedFeatures.checkIsUserConfigFeatureReleased();
@ -906,9 +916,8 @@ async function handleConfigMessagesViaLibSession(
window?.log?.debug(
`Handling our sharedConfig message via libsession_util ${JSON.stringify(
configMessages.map(m => ({
variant: LibSessionUtil.kindToVariant(m.message.kind),
hash: m.messageHash,
seqno: (m.message.seqno as Long).toNumber(),
namespace: m.namespace,
hash: m.hash,
}))
)}`
);

@ -11,8 +11,6 @@ import * as snodePool from './snodePool';
import { ConversationModel } from '../../../models/conversation';
import { ConfigMessageHandler } from '../../../receiver/configMessage';
import { decryptEnvelopeWithOurKey } from '../../../receiver/contentMessage';
import { EnvelopePlus } from '../../../receiver/types';
import { updateIsOnline } from '../../../state/ducks/onion';
import { ReleasedFeatures } from '../../../util/releaseFeature';
import {
@ -22,16 +20,19 @@ import {
} from '../../../webworker/workers/browser/libsession_worker_interface';
import { DURATION, SWARM_POLLING_TIMEOUT } from '../../constants';
import { getConversationController } from '../../conversations';
import { IncomingMessage } from '../../messages/incoming/IncomingMessage';
import { StringUtils, UserUtils } from '../../utils';
import { ed25519Str } from '../../utils/String';
import { NotFoundError } from '../../utils/errors';
import { LibSessionUtil } from '../../utils/libsession/libsession_utils';
import { SnodeNamespace, SnodeNamespaces } from './namespaces';
import { SnodeAPIRetrieve } from './retrieveRequest';
import { RetrieveMessageItem, RetrieveMessagesResultsBatched } from './types';
import {
RetrieveMessageItem,
RetrieveMessageItemWithNamespace,
RetrieveMessagesResultsBatched,
} from './types';
export function extractWebSocketContent(
function extractWebSocketContent(
message: string,
messageHash: string
): null | {
@ -265,9 +266,16 @@ export class SwarmPolling {
// check if we just fetched the details from the config namespaces.
// If yes, merge them together and exclude them from the rest of the messages.
if (userConfigLibsession && resultsFromAllNamespaces) {
const userConfigMessages = resultsFromAllNamespaces
.filter(m => SnodeNamespace.isUserConfigNamespace(m.namespace))
.map(r => r.messages.messages);
const userConfigMessages = resultsFromAllNamespaces.filter(m =>
SnodeNamespace.isUserConfigNamespace(m.namespace)
);
const userConfigMessagesWithNamespace: Array<Array<RetrieveMessageItemWithNamespace>> =
userConfigMessages.map(r => {
return (r.messages.messages || []).map(m => {
return { ...m, namespace: r.namespace };
});
});
allNamespacesWithoutUserConfigIfNeeded = flatten(
compact(
@ -283,7 +291,7 @@ export class SwarmPolling {
`received userConfigMessages count: ${userConfigMessagesMerged.length} for key ${pubkey.key}`
);
try {
await this.handleSharedConfigMessages(userConfigMessagesMerged);
await this.handleSharedConfigMessages(flatten(userConfigMessagesWithNamespace));
} catch (e) {
window.log.warn(
`handleSharedConfigMessages of ${userConfigMessagesMerged.length} failed with ${e.message}`
@ -366,52 +374,16 @@ export class SwarmPolling {
}
private async handleSharedConfigMessages(
userConfigMessagesMerged: Array<RetrieveMessageItem>,
userConfigMessagesMerged: Array<RetrieveMessageItemWithNamespace>,
returnDisplayNameOnly?: boolean
): Promise<string> {
const extractedUserConfigMessage = compact(
userConfigMessagesMerged.map((m: RetrieveMessageItem) => {
return extractWebSocketContent(m.data, m.hash);
})
);
const allDecryptedConfigMessages: Array<IncomingMessage<SignalService.ISharedConfigMessage>> =
[];
for (let index = 0; index < extractedUserConfigMessage.length; index++) {
const userConfigMessage = extractedUserConfigMessage[index];
try {
const envelope: EnvelopePlus = SignalService.Envelope.decode(userConfigMessage.body) as any;
const decryptedEnvelope = await decryptEnvelopeWithOurKey(envelope);
if (!decryptedEnvelope?.byteLength) {
continue;
}
const content = SignalService.Content.decode(new Uint8Array(decryptedEnvelope));
if (content.sharedConfigMessage) {
const asIncomingMsg: IncomingMessage<SignalService.ISharedConfigMessage> = {
envelopeTimestamp: toNumber(envelope.timestamp),
message: content.sharedConfigMessage,
messageHash: userConfigMessage.messageHash,
authorOrGroupPubkey: envelope.source,
authorInGroup: envelope.senderIdentity,
};
allDecryptedConfigMessages.push(asIncomingMsg);
} else {
throw new Error(
'received a message from the namespace reserved for user config but it did not contain a sharedConfigMessage'
);
}
} catch (e) {
window.log.warn(
`failed to decrypt message with hash "${userConfigMessage.messageHash}": ${e.message}`
);
}
if (!userConfigMessagesMerged.length) {
return '';
}
if (allDecryptedConfigMessages.length) {
try {
window.log.info(
`handleConfigMessagesViaLibSession of "${allDecryptedConfigMessages.length}" messages with libsession`
`handleConfigMessagesViaLibSession of "${userConfigMessagesMerged.length}" messages with libsession`
);
if (returnDisplayNameOnly) {
@ -424,9 +396,9 @@ export class SwarmPolling {
const privateKeyEd25519 = keypair.privKeyBytes;
// we take the lastest config message to create the wrapper in memory
const incomingConfigMessages = allDecryptedConfigMessages.map(m => ({
data: m.message.data,
hash: m.messageHash,
const incomingConfigMessages = userConfigMessagesMerged.map(m => ({
data: StringUtils.fromBase64ToArray( m.data),
hash: m.hash,
}));
await GenericWrapperActions.init('UserConfig', privateKeyEd25519, null);
@ -449,15 +421,15 @@ export class SwarmPolling {
return '';
}
await ConfigMessageHandler.handleConfigMessagesViaLibSession(allDecryptedConfigMessages);
await ConfigMessageHandler.handleConfigMessagesViaLibSession(userConfigMessagesMerged);
} catch (e) {
const allMessageHases = allDecryptedConfigMessages.map(m => m.messageHash).join(',');
const allMessageHases = userConfigMessagesMerged.map(m => m.hash).join(',');
window.log.warn(
`failed to handle messages hashes "${allMessageHases}" with libsession. Error: "${e.message}"`
);
}
}
return '';
return ''
}
// Fetches messages for `pubkey` from `node` potentially updating

@ -7,6 +7,8 @@ export type RetrieveMessageItem = {
timestamp: number;
};
export type RetrieveMessageItemWithNamespace = RetrieveMessageItem & { namespace: number };
export type RetrieveMessagesResultsContent = {
hf?: Array<number>;
messages?: Array<RetrieveMessageItem>;

@ -9,36 +9,26 @@ import { MessageParams } from '../Message';
interface SharedConfigParams extends MessageParams {
seqno: Long;
kind: SignalService.SharedConfigMessage.Kind;
data: Uint8Array;
readyToSendData: Uint8Array;
}
export class SharedConfigMessage extends ContentMessage {
public readonly seqno: Long;
public readonly kind: SignalService.SharedConfigMessage.Kind;
public readonly data: Uint8Array;
public readonly readyToSendData: Uint8Array;
constructor(params: SharedConfigParams) {
super({ timestamp: params.timestamp, identifier: params.identifier });
this.data = params.data;
this.readyToSendData = params.readyToSendData;
this.kind = params.kind;
this.seqno = params.seqno;
}
public contentProto(): SignalService.Content {
return new SignalService.Content({
sharedConfigMessage: this.sharedConfigProto(),
});
throw new Error('SharedConfigMessage must not be sent wrapped anymore');
}
public ttl(): number {
return TTL_DEFAULT.CONFIG_MESSAGE;
}
protected sharedConfigProto(): SignalService.SharedConfigMessage {
return new SignalService.SharedConfigMessage({
data: this.data,
kind: this.kind,
seqno: this.seqno,
});
}
}

@ -379,32 +379,20 @@ async function sendMessagesToSnode(
try {
const recipient = PubKey.cast(destination);
const encryptedAndWrapped = await encryptMessagesAndWrap(
params.map(m => ({
destination: m.pubkey,
plainTextBuffer: m.message.plainTextBuffer(),
namespace: m.namespace,
ttl: m.message.ttl(),
const encryptedAndWrapped: Array<Omit<EncryptAndWrapMessageResults, 'data' | 'isSyncMessage'>> =
[];
params.forEach(m => {
const wrapped = {
identifier: m.message.identifier,
isSyncMessage: MessageSender.isContentSyncMessage(m.message),
}))
);
// first update all the associated timestamps of our messages in DB, if the outgoing messages are associated with one.
await Promise.all(
encryptedAndWrapped.map(async (m, index) => {
// make sure to update the local sent_at timestamp, because sometimes, we will get the just pushed message in the receiver side
// before we return from the await below.
// and the isDuplicate messages relies on sent_at timestamp to be valid.
const found = await Data.getMessageById(m.identifier);
// make sure to not update the sent timestamp if this a currently syncing message
if (found && !found.get('sentSync')) {
found.set({ sent_at: encryptedAndWrapped[index].networkTimestamp });
await found.commit();
}
})
);
namespace: m.namespace,
ttl: m.message.ttl(),
networkTimestamp: GetNetworkTime.getNowWithNetworkOffset(),
data64: ByteBuffer.wrap(m.message.readyToSendData).toString('base64'),
};
encryptedAndWrapped.push(wrapped);
});
const batchResults = await pRetry(
async () => {
@ -432,35 +420,6 @@ async function sendMessagesToSnode(
throw new Error('result is empty for sendMessagesToSnode');
}
const isDestinationClosedGroup = getConversationController()
.get(recipient.key)
?.isClosedGroup();
await Promise.all(
encryptedAndWrapped.map(async (message, index) => {
// If message also has a sync message, save that hash. Otherwise save the hash from the regular message send i.e. only closed groups in this case.
if (
message.identifier &&
(message.isSyncMessage || isDestinationClosedGroup) &&
batchResults[index] &&
!isEmpty(batchResults[index]) &&
isString(batchResults[index].body.hash)
) {
const hashFoundInResponse = batchResults[index].body.hash;
const foundMessage = await Data.getMessageById(message.identifier);
if (foundMessage) {
await foundMessage.updateMessageHash(hashFoundInResponse);
await foundMessage.commit();
window?.log?.info(
`updated message ${foundMessage.get('id')} with hash: ${foundMessage.get(
'messageHash'
)}`
);
}
}
})
);
return batchResults;
} catch (e) {
window.log.warn(`sendMessagesToSnode failed with ${e.message}`);

@ -225,7 +225,7 @@ class ConfigurationSyncJob extends PersistedJob<ConfigurationSyncPersistedData>
...m,
message: {
...m.message,
data: to_hex(m.message.data),
readyToSendData: to_hex(m.message.readyToSendData),
},
};
})

@ -130,14 +130,14 @@ async function pendingChangesForPubkey(pubkey: string): Promise<Array<OutgoingCo
}
variantsNeedingPush.add(variant);
const { data, seqno, hashes } = await GenericWrapperActions.push(variant);
const { data: readyToSendData, seqno, hashes } = await GenericWrapperActions.push(variant);
const kind = variantToKind(variant);
const namespace = await GenericWrapperActions.storageNamespace(variant);
results.push({
message: new SharedConfigMessage({
data,
readyToSendData,
kind,
seqno: Long.fromNumber(seqno),
timestamp: GetNetworkTime.getNowWithNetworkOffset(),

@ -2868,9 +2868,9 @@ available-typed-arrays@^1.0.7:
possible-typed-array-names "^1.0.0"
axios@^1.6.5:
version "1.6.8"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66"
integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==
version "1.7.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621"
integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==
dependencies:
follow-redirects "^1.15.6"
form-data "^4.0.0"
@ -3939,7 +3939,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.6.8, debug@^2.6.9:
dependencies:
ms "2.0.0"
debug@4, debug@4.3.4, debug@^4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@ -3953,6 +3953,13 @@ debug@^3.2.7:
dependencies:
ms "^2.1.1"
debug@^4:
version "4.3.5"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e"
integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==
dependencies:
ms "2.1.2"
decamelize-keys@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8"
@ -5170,6 +5177,15 @@ fs-extra@^11.0.0, fs-extra@^11.2.0:
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-extra@^11.2.0:
version "11.2.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b"
integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-extra@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
@ -6574,9 +6590,9 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
"libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.4/libsession_util_nodejs-v0.3.4.tar.gz":
version "0.3.4"
resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.4/libsession_util_nodejs-v0.3.4.tar.gz#e8ef383ae0dbb2e54060c23fe930e8649f189870"
"libsession_util_nodejs@https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.19/libsession_util_nodejs-v0.3.19.tar.gz":
version "0.3.19"
resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.19/libsession_util_nodejs-v0.3.19.tar.gz#221c1fc34fcc18601aea4ce1b733ebfa55af66ea"
dependencies:
cmake-js "^7.2.1"
node-addon-api "^6.1.0"
@ -9632,6 +9648,18 @@ tar@^6.1.0, tar@^6.1.11, tar@^6.2.0:
mkdirp "^1.0.3"
yallist "^4.0.0"
tar@^6.2.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a"
integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
minipass "^5.0.0"
minizlib "^2.1.1"
mkdirp "^1.0.3"
yallist "^4.0.0"
temp-dir@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e"

Loading…
Cancel
Save