Merge pull request #3119 from oxen-io/clearnet

Session 1.12.4
pull/3124/head v1.12.4
Audric Ackermann 10 months ago committed by GitHub
commit ab86cafb68
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

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

@ -5,6 +5,8 @@
<!-- Mac distribution -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.device.audio-input</key>

@ -5,6 +5,10 @@
<!-- Mac app store -->
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>

@ -29,7 +29,7 @@ exports.default = async function notarizing(context) {
}
const options = {
appBundleId: 'org.getsession.desktop',
appBundleId: 'com.loki-project.messenger-desktop',
appPath: `${appOutDir}/${appName}.app`,
appleId: SIGNING_APPLE_ID,
appleIdPassword: SIGNING_APP_PASSWORD,

@ -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",
@ -95,7 +95,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.1/libsession_util_nodejs-v0.3.1.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",

@ -20,11 +20,6 @@
.module-contact-name__profile-number.italic {
font-style: italic;
}
.module-contact-name.compact {
display: block;
}
// Module: Message
.module-message__error-container {

@ -13,12 +13,11 @@ type Props = {
profileName?: string | null;
module?: string;
boldProfileName?: boolean;
compact?: boolean;
shouldShowPubkey: boolean;
};
export const ContactName = (props: Props) => {
const { pubkey, name, profileName, module, boldProfileName, compact, shouldShowPubkey } = props;
const { pubkey, name, profileName, module, boldProfileName, shouldShowPubkey } = props;
const prefix = module || 'module-contact-name';
const convoName = useNicknameOrProfileNameOrShortenedPubkey(pubkey);
@ -43,7 +42,7 @@ export const ContactName = (props: Props) => {
return (
<span
className={classNames(prefix, compact && 'compact')}
className={classNames(prefix)}
dir="auto"
data-testid={`${prefix}__profile-name`}
style={{

@ -1,6 +1,6 @@
import classNames from 'classnames';
import React, { useState } from 'react';
import { noop } from 'lodash';
import React, { useState } from 'react';
import * as MIME from '../../../../types/MIME';
import * as GoogleChrome from '../../../../util/GoogleChrome';
@ -291,7 +291,6 @@ const QuoteAuthor = (props: QuoteAuthorProps) => {
pubkey={PubKey.shorten(author)}
name={authorName}
profileName={authorProfileName}
compact={true}
shouldShowPubkey={Boolean(props.showPubkeyForAuthor)}
/>
)}

@ -41,7 +41,6 @@ export const QuoteAuthor = (props: QuoteAuthorProps) => {
<ContactName
pubkey={PubKey.shorten(author)}
name={authorName}
compact={true}
shouldShowPubkey={Boolean(authorName && !isMe && isPublic)}
/>
</StyledQuoteAuthor>

@ -14,6 +14,7 @@ import {
} from '../../state/ducks/modalDialog';
import {
useSelectedIsPublic,
useSelectedWeAreAdmin,
useSelectedWeAreModerator,
} from '../../state/selectors/selectedConversation';
import { SortedReactionList } from '../../types/Reaction';
@ -50,6 +51,11 @@ const StyledSendersContainer = styled(Flex)`
padding: 0 16px 16px;
`;
const StyledContactContainer = styled.span`
text-overflow: ellipsis;
overflow: hidden;
`;
const StyledReactionBar = styled(Flex)`
width: 100%;
margin: 12px 0 20px 4px;
@ -132,7 +138,7 @@ const ReactionSenders = (props: ReactionSendersProps) => {
justifyContent={'space-between'}
alignItems={'center'}
>
<Flex container={true} alignItems={'center'}>
<Flex container={true} alignItems={'center'} style={{ overflow: 'hidden' }}>
<Avatar
size={AvatarSize.XS}
pubkey={sender}
@ -143,11 +149,13 @@ const ReactionSenders = (props: ReactionSendersProps) => {
{sender === me ? (
window.i18n('you')
) : (
<ContactName
pubkey={sender}
module="module-conversation__user"
shouldShowPubkey={false}
/>
<StyledContactContainer>
<ContactName
pubkey={sender}
module="module-conversation__user"
shouldShowPubkey={false}
/>
</StyledContactContainer>
)}
</Flex>
{sender === me && (
@ -231,6 +239,7 @@ export const ReactListModal = (props: Props) => {
const msgProps = useMessageReactsPropsById(messageId);
const isPublic = useSelectedIsPublic();
const weAreAdmin = useSelectedWeAreAdmin();
const weAreModerator = useSelectedWeAreModerator();
const me = UserUtils.getOurPubKeyStrFromCache();
@ -362,7 +371,7 @@ export const ReactListModal = (props: Props) => {
</>
)}
</p>
{isPublic && weAreModerator && (
{isPublic && (weAreAdmin || weAreModerator) && (
<SessionButton
text={window.i18n('clearAll')}
buttonColor={SessionButtonColor.Danger}

@ -88,7 +88,19 @@ export type SessionIconType =
export type SessionIconSize = 'tiny' | 'small' | 'medium' | 'large' | 'huge' | 'huge2' | 'max';
export const icons: Record<string, { path: string; viewBox: string; ratio: number }> = {
export type ClipRule = 'nonzero' | 'evenodd' | 'inherit';
export type FillRule = 'nonzero' | 'evenodd';
type IconProps = {
path: string;
viewBox: string;
ratio: number;
fill?: string;
clipRule?: ClipRule;
fillRule?: FillRule;
};
export const icons: Record<SessionIconType, IconProps> = {
addUser: {
path: 'M8.85,2.17c-1.73,0-3.12,1.4-3.12,3.12s1.4,3.12,3.12,3.12c1.73,0,3.13-1.4,3.13-3.12S10.58,2.17,8.85,2.17z M8.85,0.08c2.88,0,5.21,2.33,5.21,5.21s-2.33,5.21-5.21,5.21s-5.2-2.33-5.2-5.21C3.65,2.42,5.98,0.08,8.85,0.08z M20.83,5.29 c0.54,0,0.98,0.41,1.04,0.93l0.01,0.11v2.08h2.08c0.54,0,0.98,0.41,1.04,0.93v0.12c0,0.54-0.41,0.98-0.93,1.04l-0.11,0.01h-2.08 v2.08c0,0.58-0.47,1.04-1.04,1.04c-0.54,0-0.98-0.41-1.04-0.93l-0.01-0.11v-2.08h-2.08c-0.54,0-0.98-0.41-1.04-0.93l-0.01-0.11 c0-0.54,0.41-0.98,0.93-1.04l0.11-0.01h2.08V6.34C19.79,5.76,20.26,5.29,20.83,5.29z M12.5,12.58c2.8,0,5.09,2.21,5.2,4.99v0.22 v2.08c0,0.58-0.47,1.04-1.04,1.04c-0.54,0-0.98-0.41-1.04-0.93l-0.01-0.11v-2.08c0-1.67-1.3-3.03-2.95-3.12h-0.18H5.21 c-1.67,0-3.03,1.3-3.12,2.95v0.18v2.08c0,0.58-0.47,1.04-1.04,1.04c-0.54,0-0.98-0.41-1.04-0.93L0,19.88V17.8 c0-2.8,2.21-5.09,4.99-5.2h0.22h7.29V12.58z',
viewBox: '0 0 25 21',

@ -1,7 +1,8 @@
import React from 'react';
import styled, { css, keyframes } from 'styled-components';
import React, { memo } from 'react';
import styled, { css, CSSProperties, keyframes } from 'styled-components';
import { icons, SessionIconSize, SessionIconType } from '.';
import { ClipRule, FillRule } from './Icons';
export type SessionIconProps = {
iconType: SessionIconType;
@ -15,6 +16,7 @@ export type SessionIconProps = {
glowStartDelay?: number;
noScale?: boolean;
backgroundColor?: string;
style?: CSSProperties;
dataTestId?: string;
unreadCount?: number;
};
@ -55,6 +57,9 @@ type StyledSvgProps = {
noScale?: boolean;
iconColor?: string;
backgroundColor?: string;
fill?: string;
clipRule?: ClipRule;
filleRule?: FillRule;
};
const rotate = keyframes`
@ -119,37 +124,28 @@ const animation = (props: {
return undefined;
};
const Svg = React.memo(styled.svg<StyledSvgProps>`
const Svg = memo(styled.svg<StyledSvgProps>`
width: ${props => props.width};
transform: ${props => `rotate(${props.iconRotation}deg)`};
${props => animation(props)};
border-radius: ${props => props.borderRadius};
background-color: ${props =>
props.backgroundColor ? props.backgroundColor : '--button-icon-background-color'};
border-radius: ${props => (props.borderRadius ? props.borderRadius : '')};
props.backgroundColor ? props.backgroundColor : 'var(--button-icon-background-color)'};
filter: ${props => (props.noScale ? `drop-shadow(0px 0px 4px ${props.iconColor})` : '')};
fill: ${props => (props.iconColor ? props.iconColor : '--button-icon-stroke-color')};
fill: ${props => (props.iconColor ? props.iconColor : 'var(--button-icon-stroke-color)')};
padding: ${props => (props.iconPadding ? props.iconPadding : '')};
transition: inherit;
`);
const SessionSvg = (props: {
viewBox: string;
path: string | Array<string>;
width: string | number;
height: string | number;
iconRotation: number;
iconColor?: string;
rotateDuration?: number;
glowDuration?: number;
glowStartDelay?: number;
noScale?: boolean;
borderRadius?: string;
backgroundColor?: string;
iconPadding?: string;
dataTestId?: string;
}) => {
const colorSvg = props.iconColor ? props.iconColor : '--button-icon-stroke-color';
const SessionSvg = (
props: StyledSvgProps & {
viewBox: string;
path: string | Array<string>;
style?: CSSProperties;
dataTestId?: string;
}
) => {
const colorSvg = props.iconColor ? props.iconColor : 'var(--button-icon-stroke-color)';
const pathArray = props.path instanceof Array ? props.path : [props.path];
const propsToPick = {
width: props.width,
@ -164,6 +160,10 @@ const SessionSvg = (props: {
backgroundColor: props.backgroundColor,
borderRadius: props.borderRadius,
iconPadding: props.iconPadding,
fill: props.fill,
clipRule: props.clipRule,
fillRule: props.filleRule,
style: props.style,
dataTestId: props.dataTestId,
};
@ -187,6 +187,7 @@ export const SessionIcon = (props: SessionIconProps) => {
noScale,
backgroundColor,
iconPadding,
style,
dataTestId,
} = props;
let { iconSize, iconRotation } = props;
@ -196,6 +197,9 @@ export const SessionIcon = (props: SessionIconProps) => {
const iconDimensions = getIconDimensionFromIconSize(iconSize);
const iconDef = icons[iconType];
const ratio = iconDef?.ratio || 1;
const fill = iconDef?.fill || undefined;
const clipRule = iconDef?.clipRule || 'nonzero';
const fillRule = iconDef?.fillRule || 'nonzero';
return (
<SessionSvg
@ -212,6 +216,10 @@ export const SessionIcon = (props: SessionIconProps) => {
iconColor={iconColor}
backgroundColor={backgroundColor}
iconPadding={iconPadding}
fill={fill}
clipRule={clipRule}
filleRule={fillRule}
style={style}
dataTestId={dataTestId}
/>
);

@ -68,13 +68,21 @@ function IconMessageStatus({ status }: { status: LastMessageStatusType }) {
const nonErrorIconColor = 'var(--text-secondary-color';
switch (status) {
case 'error':
return <SessionIcon iconColor={'var(--danger-color'} iconType="error" iconSize="tiny" />;
return (
<SessionIcon
iconColor={'var(--danger-color'}
iconType="error"
iconSize="tiny"
style={{ flexShrink: 0 }}
/>
);
case 'read':
return (
<SessionIcon
iconColor={nonErrorIconColor}
iconType="doubleCheckCircleFilled"
iconSize="tiny"
style={{ flexShrink: 0 }}
/>
);
case 'sending':
@ -84,10 +92,18 @@ function IconMessageStatus({ status }: { status: LastMessageStatusType }) {
iconColor={nonErrorIconColor}
iconType="sending"
iconSize="tiny"
style={{ flexShrink: 0 }}
/>
);
case 'sent':
return <SessionIcon iconColor={nonErrorIconColor} iconType="circleCheck" iconSize="tiny" />;
return (
<SessionIcon
iconColor={nonErrorIconColor}
iconType="circleCheck"
iconSize="tiny"
style={{ flexShrink: 0 }}
/>
);
case undefined:
return null;
default:

@ -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';
@ -42,6 +41,8 @@ import {
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 {
@ -57,18 +58,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, []);
@ -80,10 +92,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();
@ -91,15 +103,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(
@ -110,9 +122,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))
);
}
@ -122,8 +132,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(
@ -895,7 +905,7 @@ async function processMergingResults(results: Map<ConfigWrapperObjectTypes, Inco
}
async function handleConfigMessagesViaLibSession(
configMessages: Array<IncomingMessage<SignalService.ISharedConfigMessage>>
configMessages: Array<RetrieveMessageItemWithNamespace>
) {
const userConfigLibsession = await ReleasedFeatures.checkIsUserConfigFeatureReleased();
@ -910,9 +920,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,
}))
)}`
);

@ -251,7 +251,9 @@ export async function handleSwarmDataMessage({
}
let msgModel =
isSyncedMessage || (envelope.senderIdentity && isUsFromCache(envelope.senderIdentity))
isSyncedMessage ||
(envelope.senderIdentity && isUsFromCache(envelope.senderIdentity)) ||
(envelope.source && isUsFromCache(envelope.source))
? createSwarmMessageSentFromUs({
conversationId: convoIdToAddTheMessageTo,
messageHash,

@ -1,7 +1,7 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable more/no-then */
/* eslint-disable @typescript-eslint/no-misused-promises */
import { compact, concat, flatten, last, sample, toNumber, uniqBy } from 'lodash';
import { compact, concat, flatten, last, sample, uniqBy } from 'lodash';
import { Data, Snode } from '../../../data/data';
import { SignalService } from '../../../protobuf';
import * as Receiver from '../../../receiver/receiver';
@ -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 {
@ -21,15 +19,18 @@ 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 { LibSessionUtil } from '../../utils/libsession/libsession_utils';
import { SnodeNamespace, SnodeNamespaces } from './namespaces';
import { SnodeAPIRetrieve } from './retrieveRequest';
import { RetrieveMessageItem, RetrieveMessagesResultsBatched } from './types';
import { ed25519Str } from '../../utils/String';
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}`
@ -365,58 +373,22 @@ export class SwarmPolling {
}
}
private async handleSharedConfigMessages(userConfigMessagesMerged: Array<RetrieveMessageItem>) {
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}`
);
}
private async handleSharedConfigMessages(
userConfigMessagesMerged: Array<RetrieveMessageItemWithNamespace>
) {
if (!userConfigMessagesMerged.length) {
return;
}
if (allDecryptedConfigMessages.length) {
try {
window.log.info(
`handleConfigMessagesViaLibSession of "${allDecryptedConfigMessages.length}" messages with libsession`
);
await ConfigMessageHandler.handleConfigMessagesViaLibSession(allDecryptedConfigMessages);
} catch (e) {
const allMessageHases = allDecryptedConfigMessages.map(m => m.messageHash).join(',');
window.log.warn(
`failed to handle messages hashes "${allMessageHases}" with libsession. Error: "${e.message}"`
);
}
try {
window.log.info(
`handleConfigMessagesViaLibSession of "${userConfigMessagesMerged.length}" messages with libsession`
);
await ConfigMessageHandler.handleConfigMessagesViaLibSession(userConfigMessagesMerged);
} catch (e) {
const allMessagesHashes = userConfigMessagesMerged.map(m => m.hash).join(',');
window.log.warn(
`failed to handle messages hashes "${allMessagesHashes}" with libsession. Error: "${e.message}"`
);
}
}

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

@ -17,6 +17,15 @@ export const DURATION = {
WEEKS: days * 7,
};
export const FILESIZE = {
/** 1KB */
KB: 1024,
/** 1MB */
MB: 1024 * 1024,
/** 1GB */
GB: 1024 * 1024 * 1024,
};
export const TTL_DEFAULT = {
/** 20 seconds */
TYPING_MESSAGE: 20 * DURATION.SECONDS,

@ -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),
},
};
})

@ -127,14 +127,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(),

@ -3,6 +3,7 @@ import imageType from 'image-type';
import { arrayBufferToBlob } from 'blob-util';
import loadImage from 'blueimp-load-image';
import fileSize from 'filesize';
import { StagedAttachmentType } from '../components/conversation/composition/CompositionBox';
import { SignalService } from '../protobuf';
import { getDecryptedMediaUrl } from '../session/crypto/DecryptedAttachmentsManager';
@ -12,7 +13,7 @@ import { IMAGE_GIF, IMAGE_JPEG, IMAGE_PNG, IMAGE_TIFF, IMAGE_UNKNOWN } from '../
import { getAbsoluteAttachmentPath, processNewAttachment } from '../types/MessageAttachment';
import { THUMBNAIL_SIDE } from '../types/attachments/VisualAttachment';
import { MAX_ATTACHMENT_FILESIZE_BYTES } from '../session/constants';
import { FILESIZE, MAX_ATTACHMENT_FILESIZE_BYTES } from '../session/constants';
import { perfEnd, perfStart } from '../session/utils/Performance';
/**
@ -53,7 +54,7 @@ export async function autoScaleForAvatar<T extends { contentType: string; blob:
) {
const maxMeasurements = {
maxSide: AVATAR_MAX_SIDE,
maxSize: 1000 * 1024,
maxSize: 5 * FILESIZE.MB,
};
// we can only upload jpeg, gif, or png as avatar/opengroup
@ -79,7 +80,7 @@ export async function autoScaleForAvatar<T extends { contentType: string; blob:
export async function autoScaleForIncomingAvatar(incomingAvatar: ArrayBuffer) {
const maxMeasurements = {
maxSide: AVATAR_MAX_SIDE,
maxSize: 1000 * 1024,
maxSize: 5 * FILESIZE.MB,
};
// the avatar url send in a message does not contain anything related to the avatar MIME type, so
@ -186,7 +187,7 @@ export async function autoScale<T extends { contentType: string; blob: Blob }>(
}
if (blob.type === IMAGE_GIF && blob.size > maxSize) {
throw new Error(`GIF is too large, required size is ${maxSize}`);
throw new Error(`GIF is too large. Max size: ${fileSize(maxSize, { base: 10, round: 0 })}`);
}
perfStart(`loadimage-*${blob.size}`);

@ -46,6 +46,11 @@ export const GenericWrapperActions = {
) =>
/** base wrapper generic actions */
callLibSessionWorker([wrapperId, 'init', ed25519Key, dump]) as Promise<void>,
/** This function is used to free wrappers from memory only.
*
* See freeUserWrapper() in libsession.worker.ts */
free: async (wrapperId: ConfigWrapperObjectTypes) =>
callLibSessionWorker([wrapperId, 'free']) as Promise<void>,
confirmPushed: async (wrapperId: ConfigWrapperObjectTypes, seqno: number, hash: string) =>
callLibSessionWorker([wrapperId, 'confirmPushed', seqno, hash]) as ReturnType<
BaseWrapperActionsCalls['confirmPushed']
@ -87,6 +92,7 @@ export const UserConfigWrapperActions: UserConfigWrapperActionsCalls = {
/* Reuse the GenericWrapperActions with the UserConfig argument */
init: async (ed25519Key: Uint8Array, dump: Uint8Array | null) =>
GenericWrapperActions.init('UserConfig', ed25519Key, dump),
free: async () => GenericWrapperActions.free('UserConfig'),
confirmPushed: async (seqno: number, hash: string) =>
GenericWrapperActions.confirmPushed('UserConfig', seqno, hash),
dump: async () => GenericWrapperActions.dump('UserConfig'),
@ -135,6 +141,7 @@ export const ContactsWrapperActions: ContactsWrapperActionsCalls = {
/* Reuse the GenericWrapperActions with the ContactConfig argument */
init: async (ed25519Key: Uint8Array, dump: Uint8Array | null) =>
GenericWrapperActions.init('ContactsConfig', ed25519Key, dump),
free: async () => GenericWrapperActions.free('ContactsConfig'),
confirmPushed: async (seqno: number, hash: string) =>
GenericWrapperActions.confirmPushed('ContactsConfig', seqno, hash),
dump: async () => GenericWrapperActions.dump('ContactsConfig'),
@ -171,6 +178,7 @@ export const UserGroupsWrapperActions: UserGroupsWrapperActionsCalls = {
/* Reuse the GenericWrapperActions with the ContactConfig argument */
init: async (ed25519Key: Uint8Array, dump: Uint8Array | null) =>
GenericWrapperActions.init('UserGroupsConfig', ed25519Key, dump),
free: async () => GenericWrapperActions.free('UserGroupsConfig'),
confirmPushed: async (seqno: number, hash: string) =>
GenericWrapperActions.confirmPushed('UserGroupsConfig', seqno, hash),
dump: async () => GenericWrapperActions.dump('UserGroupsConfig'),
@ -244,6 +252,7 @@ export const ConvoInfoVolatileWrapperActions: ConvoInfoVolatileWrapperActionsCal
/* Reuse the GenericWrapperActions with the ContactConfig argument */
init: async (ed25519Key: Uint8Array, dump: Uint8Array | null) =>
GenericWrapperActions.init('ConvoInfoVolatileConfig', ed25519Key, dump),
free: async () => GenericWrapperActions.free('ConvoInfoVolatileConfig'),
confirmPushed: async (seqno: number, hash: string) =>
GenericWrapperActions.confirmPushed('ConvoInfoVolatileConfig', seqno, hash),
dump: async () => GenericWrapperActions.dump('ConvoInfoVolatileConfig'),

@ -2877,12 +2877,12 @@ available-typed-arrays@^1.0.5, available-typed-arrays@^1.0.6:
dependencies:
possible-typed-array-names "^1.0.0"
axios@^1.3.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.2.tgz#de67d42c755b571d3e698df1b6504cde9b0ee9f2"
integrity sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==
axios@^1.6.5:
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.0"
follow-redirects "^1.15.6"
form-data "^4.0.0"
proxy-from-env "^1.1.0"
@ -3494,23 +3494,23 @@ clsx@^1.0.4, clsx@^1.1.1, clsx@^1.2.1:
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
cmake-js@^7.2.1:
version "7.2.1"
resolved "https://registry.yarnpkg.com/cmake-js/-/cmake-js-7.2.1.tgz#757c0d39994121b084bab96290baf115ee7712cd"
integrity sha512-AdPSz9cSIJWdKvm0aJgVu3X8i0U3mNTswJkSHzZISqmYVjZk7Td4oDFg0mCBA383wO+9pG5Ix7pEP1CZH9x2BA==
version "7.3.0"
resolved "https://registry.yarnpkg.com/cmake-js/-/cmake-js-7.3.0.tgz#6fd6234b7aeec4545c1c806f9e3f7ffacd9798b2"
integrity sha512-dXs2zq9WxrV87bpJ+WbnGKv8WUBXDw8blNiwNHoRe/it+ptscxhQHKB1SJXa1w+kocLMeP28Tk4/eTCezg4o+w==
dependencies:
axios "^1.3.2"
axios "^1.6.5"
debug "^4"
fs-extra "^10.1.0"
fs-extra "^11.2.0"
lodash.isplainobject "^4.0.6"
memory-stream "^1.0.0"
node-api-headers "^0.0.2"
node-api-headers "^1.1.0"
npmlog "^6.0.2"
rc "^1.2.7"
semver "^7.3.8"
tar "^6.1.11"
semver "^7.5.4"
tar "^6.2.0"
url-join "^4.0.1"
which "^2.0.2"
yargs "^17.6.0"
yargs "^17.7.2"
color-convert@^1.9.0:
version "1.9.3"
@ -3905,7 +3905,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==
@ -3919,6 +3919,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"
@ -5067,10 +5074,10 @@ focus-trap@^7.5.4:
dependencies:
tabbable "^6.2.0"
follow-redirects@^1.15.0:
version "1.15.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
follow-redirects@^1.15.6:
version "1.15.6"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b"
integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==
for-each@^0.3.3:
version "0.3.3"
@ -5130,6 +5137,15 @@ fs-extra@^11.0.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"
@ -6526,9 +6542,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.1/libsession_util_nodejs-v0.3.1.tar.gz":
version "0.3.1"
resolved "https://github.com/oxen-io/libsession-util-nodejs/releases/download/v0.3.1/libsession_util_nodejs-v0.3.1.tar.gz#d2c94bfaae6e3ef594609abb08cf8be485fa5d39"
"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"
@ -7393,10 +7409,10 @@ node-addon-api@^6.1.0:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76"
integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==
node-api-headers@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/node-api-headers/-/node-api-headers-0.0.2.tgz#31f4c6c2750b63e598128e76a60aefca6d76ac5d"
integrity sha512-YsjmaKGPDkmhoNKIpkChtCsPVaRE0a274IdERKnuc/E8K1UJdBZ4/mvI006OijlQZHCfpRNOH3dfHQs92se8gg==
node-api-headers@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/node-api-headers/-/node-api-headers-1.1.0.tgz#3f9dd7bb10b29e1c3e3db675979605a308b2373c"
integrity sha512-ucQW+SbYCUPfprvmzBsnjT034IGRB2XK8rRc78BgjNKhTdFKgAwAmgW704bKIBmcYW48it0Gkjpkd39Azrwquw==
node-dir@^0.1.17:
version "0.1.17"
@ -9584,6 +9600,18 @@ tar@^6.1.0, tar@^6.1.11:
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"
@ -10536,7 +10564,7 @@ yargs@^15.1.0:
y18n "^4.0.0"
yargs-parser "^18.1.2"
yargs@^17.0.0, yargs@^17.0.1, yargs@^17.6.0, yargs@^17.6.2:
yargs@^17.0.0, yargs@^17.0.1, yargs@^17.6.2, yargs@^17.7.2:
version "17.7.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==

Loading…
Cancel
Save