Merge branch 'clearnet' into drop-auto-fr-messages

pull/1252/head
Audric Ackermann 5 years ago
commit e6e179b45b
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -878,7 +878,7 @@
"message": "Waiting for device to register..."
},
"pairNewDevicePrompt": {
"message": "Scan the QR Code on your secondary device"
"message": "Scan the QR Code on your other device"
},
"pairedDevices": {
"message": "Linked Devices"
@ -2240,7 +2240,7 @@
"message": "Remove"
},
"invalidHexId": {
"message": "Invalid Session ID or LNS Name",
"message": "Invalid Session ID",
"description": "Error string shown when user types an invalid pubkey hex string"
},
"invalidLnsFormat": {
@ -2360,7 +2360,7 @@
"message": "Say hello to your Session ID"
},
"allUsersAreRandomly...": {
"message": "Your Session ID is the unique address people can use to contact you on Session. Your Session ID is totally private, anonymous, and has no connection to your real identity."
"message": "Your Session ID is the unique address people can use to contact you on Session. With no connection to your real identity, your Session ID is totally anonymous and private by design."
},
"getStarted": {
"message": "Get started"
@ -2409,7 +2409,7 @@
"message": "Enter your <strong>Session ID</strong> below to link this device to your <strong>Session ID</strong>."
},
"enterSessionIDHere": {
"message": "Enter your Session ID here"
"message": "Enter your Session ID"
},
"continueYourSession": {
"message": "Continue Your Session"
@ -2460,7 +2460,7 @@
"message": "Enter Session ID"
},
"pasteSessionIDRecipient": {
"message": "Enter a Session ID or LNS name"
"message": "Enter a Session ID"
},
"usersCanShareTheir...": {
"message": "Users can share their Session ID from their account settings, or by sharing their QR code."
@ -2604,10 +2604,10 @@
"message": "Secret words"
},
"pairingDevice": {
"message": "Pairing Device"
"message": "Linking Device"
},
"gotPairingRequest": {
"message": "Pairing request received"
"message": "Linking request received"
},
"devicePairedSuccessfully": {
"message": "Device linked successfully"

@ -1391,10 +1391,11 @@
pubKey
);
await window.lokiFileServerAPI.updateOurDeviceMapping();
// TODO: we should ensure the message was sent and retry automatically if not
const device = new libsession.Types.PubKey(pubKey);
const unlinkMessage = new libsession.Messages.Outgoing.DeviceUnlinkMessage(
pubKey
{
timestamp: Date.now(),
}
);
await libsession.getMessageQueue().send(device, unlinkMessage);

@ -47,15 +47,6 @@
return _.include(groupIds, groupId);
};
storage.addBlockedGroup = groupId => {
const groupIds = storage.get(BLOCKED_GROUPS_ID, []);
if (_.include(groupIds, groupId)) {
return;
}
window.log.info(`adding groupId(${groupId}) to blocked list`);
storage.put(BLOCKED_GROUPS_ID, groupIds.concat(groupId));
};
storage.removeBlockedGroup = groupId => {
const groupIds = storage.get(BLOCKED_GROUPS_ID, []);
if (!_.include(groupIds, groupId)) {

@ -250,9 +250,14 @@
? BlockedNumberController.block(this.id)
: BlockedNumberController.blockGroup(this.id);
await promise;
this.trigger('change');
this.trigger('change', this);
this.messageCollection.forEach(m => m.trigger('change'));
this.updateTextInputState();
if (this.isPrivate()) {
await textsecure.messaging.sendContactSyncMessage([this]);
} else {
await textsecure.messaging.sendGroupSyncMessage([this]);
}
},
async unblock() {
if (!this.id || this.isPublic() || this.isRss()) {
@ -262,9 +267,14 @@
? BlockedNumberController.unblock(this.id)
: BlockedNumberController.unblockGroup(this.id);
await promise;
this.trigger('change');
this.trigger('change', this);
this.messageCollection.forEach(m => m.trigger('change'));
this.updateTextInputState();
if (this.isPrivate()) {
await textsecure.messaging.sendContactSyncMessage([this]);
} else {
await textsecure.messaging.sendGroupSyncMessage([this]);
}
},
setMessageSelectionBackdrop() {
const messageSelected = this.selectedMessages.size > 0;
@ -1382,7 +1392,7 @@
const groupInvitMessage = new libsession.Messages.Outgoing.GroupInvitationMessage(
{
identifier: id,
timestamp: Date.now(),
serverName: groupInvitation.name,
channelId: groupInvitation.channelId,
serverAddress: groupInvitation.address,
@ -2720,7 +2730,7 @@
const ourConversation = window.ConversationController.get(ourNumber);
let profileKey = null;
if (this.get('profileSharing')) {
profileKey = storage.get('profileKey');
profileKey = new Uint8Array(storage.get('profileKey'));
}
const avatarPointer = ourConversation.get('avatarPointer');
const { displayName } = ourConversation.getLokiProfile();

@ -1087,8 +1087,10 @@
}
const { body, attachments, preview, quote } = await this.uploadData();
const ourNumber = window.storage.get('primaryDevicePubKey');
const ourConversation = window.ConversationController.get(ourNumber);
const chatMessage = new libsession.Messages.Outgoing.ChatMessage({
const chatParams = {
identifier: this.id,
body,
timestamp: this.get('sent_at'),
@ -1096,8 +1098,14 @@
attachments,
preview,
quote,
lokiProfile: this.conversation.getOurProfile(),
});
};
if (ourConversation) {
chatParams.lokiProfile = ourConversation.getOurProfile();
}
const chatMessage = new libsession.Messages.Outgoing.ChatMessage(
chatParams
);
// Special-case the self-send case - we send only a sync message
if (recipients.length === 1) {

@ -96,8 +96,9 @@ class LokiMessageAPI {
// eslint-disable-next-line more/no-then
snode = await primitives.firstTrue(promises);
} catch (e) {
const snodeStr = snode ? `${snode.ip}:${snode.port}` : 'null';
log.warn(
`loki_message:::sendMessage - ${e.code} ${e.message} to ${pubKey} via ${snode.ip}:${snode.port}`
`loki_message:::sendMessage - ${e.code} ${e.message} to ${pubKey} via snode:${snodeStr}`
);
if (e instanceof textsecure.WrongDifficultyError) {
// Force nonce recalculation

@ -289,6 +289,7 @@
window.storage.get('primaryDevicePubKey')
),
isKickedFromGroup: this.model.get('isKickedFromGroup'),
isBlocked: this.model.isBlocked(),
timerOptions: Whisper.ExpirationTimerOptions.map(item => ({
name: item.getName(),

@ -553,7 +553,7 @@
timestamp: Date.now(),
primaryDevicePubKey,
secondaryDevicePubKey: ourPubKey,
requestSignature,
requestSignature: new Uint8Array(requestSignature),
}
);
await window.libsession
@ -616,14 +616,7 @@
);
// We need to send the our profile to the secondary device
const { displayName } = ourConversation.getLokiProfile();
const avatarPointer = ourConversation.get('avatarPointer');
const profileKey = window.storage.get('profileKey');
const lokiProfile = {
displayName,
profileKey,
avatarPointer,
};
const lokiProfile = ourConversation.getOurProfile();
// Try to upload to the file server and then send a message
try {
@ -631,7 +624,10 @@
const requestPairingMessage = new libsession.Messages.Outgoing.DeviceLinkGrantMessage(
{
timestamp: Date.now(),
...authorisation,
primaryDevicePubKey: ourPubKey,
secondaryDevicePubKey: secondaryDeviceStr,
requestSignature: new Uint8Array(requestSignature),
grantSignature: new Uint8Array(grantSignature),
lokiProfile,
}
);
@ -657,7 +653,7 @@
const conversations = window.getConversations().models;
await textsecure.messaging.sendGroupSyncMessage(conversations);
await textsecure.messaging.sendOpenGroupsSyncMessage(conversations);
await textsecure.messaging.sendContactSyncMessage(conversations);
await textsecure.messaging.sendContactSyncMessage();
}, 5000);
},
validatePubKeyHex(pubKey) {

@ -1,535 +0,0 @@
/* global
textsecure,
libsignal,
window,
libloki,
StringView,
lokiMessageAPI,
*/
/* eslint-disable more/no-then */
/* eslint-disable no-unreachable */
const NUM_SEND_CONNECTIONS = 3;
const getTTLForType = type => {
switch (type) {
case 'device-unpairing':
return 4 * 24 * 60 * 60 * 1000; // 4 days for device unpairing
case 'onlineBroadcast':
return 60 * 1000; // 1 minute for online broadcast message
case 'pairing-request':
return 2 * 60 * 1000; // 2 minutes for pairing requests
default:
return 24 * 60 * 60 * 1000; // 1 day default for any other message
}
};
function _getPaddedMessageLength(messageLength) {
const messageLengthWithTerminator = messageLength + 1;
let messagePartCount = Math.floor(messageLengthWithTerminator / 160);
if (messageLengthWithTerminator % 160 !== 0) {
messagePartCount += 1;
}
return messagePartCount * 160;
}
function _convertMessageToText(messageBuffer) {
const plaintext = new Uint8Array(
_getPaddedMessageLength(messageBuffer.byteLength + 1) - 1
);
plaintext.set(new Uint8Array(messageBuffer));
plaintext[messageBuffer.byteLength] = 0x80;
return plaintext;
}
function _getPlaintext(messageBuffer) {
return _convertMessageToText(messageBuffer);
}
function wrapInWebsocketMessage(outgoingObject, timestamp) {
const source =
outgoingObject.type ===
textsecure.protobuf.Envelope.Type.UNIDENTIFIED_SENDER
? null
: outgoingObject.ourKey;
const messageEnvelope = new textsecure.protobuf.Envelope({
type: outgoingObject.type,
source,
sourceDevice: outgoingObject.sourceDevice,
timestamp,
content: outgoingObject.content,
});
const requestMessage = new textsecure.protobuf.WebSocketRequestMessage({
id: new Uint8Array(libsignal.crypto.getRandomBytes(1))[0], // random ID for now
verb: 'PUT',
path: '/api/v1/message',
body: messageEnvelope.encode().toArrayBuffer(),
});
const websocketMessage = new textsecure.protobuf.WebSocketMessage({
type: textsecure.protobuf.WebSocketMessage.Type.REQUEST,
request: requestMessage,
});
const bytes = new Uint8Array(websocketMessage.encode().toArrayBuffer());
return bytes;
}
function getStaleDeviceIdsForNumber(number) {
return textsecure.storage.protocol.getDeviceIds(number).then(deviceIds => {
if (deviceIds.length === 0) {
return [1];
}
const updateDevices = [];
return Promise.all(
deviceIds.map(deviceId => {
const address = new libsignal.SignalProtocolAddress(number, deviceId);
const sessionCipher = new libsignal.SessionCipher(
textsecure.storage.protocol,
address
);
return sessionCipher.hasOpenSession().then(hasSession => {
if (!hasSession) {
updateDevices.push(deviceId);
}
});
})
).then(() => updateDevices);
});
}
function OutgoingMessage(
server,
timestamp,
numbers,
message,
silent,
callback,
options = {}
) {
if (message instanceof textsecure.protobuf.DataMessage) {
const content = new textsecure.protobuf.Content();
content.dataMessage = message;
// eslint-disable-next-line no-param-reassign
message = content;
}
this.server = server;
this.timestamp = timestamp;
this.numbers = numbers;
this.message = message; // ContentMessage proto
this.callback = callback;
this.silent = silent;
this.numbersCompleted = 0;
this.errors = [];
this.successfulNumbers = [];
this.fallBackEncryption = false;
this.failoverNumbers = [];
this.unidentifiedDeliveries = [];
const {
numberInfo,
senderCertificate,
online,
messageType,
isPublic,
isMediumGroup,
publicSendData,
autoSession,
} = options || {};
this.numberInfo = numberInfo;
this.isPublic = isPublic;
this.isMediumGroup = !!isMediumGroup;
this.isGroup = !!(
this.message &&
this.message.dataMessage &&
this.message.dataMessage.group
);
this.publicSendData = publicSendData;
this.senderCertificate = senderCertificate;
this.online = online;
this.messageType = messageType || 'outgoing';
this.autoSession = autoSession || false;
}
OutgoingMessage.prototype = {
constructor: OutgoingMessage,
numberCompleted() {
this.numbersCompleted += 1;
if (this.numbersCompleted >= this.numbers.length) {
this.callback({
successfulNumbers: this.successfulNumbers,
failoverNumbers: this.failoverNumbers,
errors: this.errors,
unidentifiedDeliveries: this.unidentifiedDeliveries,
messageType: this.messageType,
});
}
},
registerError(number, reason, error) {
if (!error || (error.name === 'HTTPError' && error.code !== 404)) {
// eslint-disable-next-line no-param-reassign
error = new textsecure.OutgoingMessageError(
number,
this.message.toArrayBuffer(),
this.timestamp,
error
);
}
// eslint-disable-next-line no-param-reassign
error.number = number;
// eslint-disable-next-line no-param-reassign
error.reason = reason;
this.errors[this.errors.length] = error;
this.numberCompleted();
},
reloadDevicesAndSend(primaryPubKey, multiDevice = true) {
const ourNumber = textsecure.storage.user.getNumber();
if (!multiDevice) {
if (primaryPubKey === ourNumber) {
return Promise.resolve();
}
return this.doSendMessage(primaryPubKey, [primaryPubKey]);
}
return (
window.libsession.Protocols.MultiDeviceProtocol.getAllDevices(
primaryPubKey
)
// Don't send to ourselves
.then(devicesPubKeys =>
devicesPubKeys.filter(pubKey => pubKey.key !== ourNumber)
)
.then(devicesPubKeys => {
if (devicesPubKeys.length === 0) {
// No need to start the sending of message without a recipient
return Promise.resolve();
}
return this.doSendMessage(primaryPubKey, devicesPubKeys);
})
);
},
getKeysForNumber(number, updateDevices) {
const handleResult = response =>
Promise.all(
response.devices.map(device => {
// eslint-disable-next-line no-param-reassign
device.identityKey = response.identityKey;
if (
updateDevices === undefined ||
updateDevices.indexOf(device.deviceId) > -1
) {
const address = new libsignal.SignalProtocolAddress(
number,
device.deviceId
);
const builder = new libsignal.SessionBuilder(
textsecure.storage.protocol,
address
);
if (device.registrationId === 0) {
window.log.info('device registrationId 0!');
}
return builder
.processPreKey(device)
.then(async () => {
// TODO: only remove the keys that were used above!
await libloki.storage.removeContactPreKeyBundle(number);
return true;
})
.catch(error => {
if (error.message === 'Identity key changed') {
// eslint-disable-next-line no-param-reassign
error.timestamp = this.timestamp;
// eslint-disable-next-line no-param-reassign
error.originalMessage = this.message.toArrayBuffer();
// eslint-disable-next-line no-param-reassign
error.identityKey = device.identityKey;
}
throw error;
});
}
return false;
})
);
let promise = Promise.resolve(true);
updateDevices.forEach(device => {
promise = promise.then(() =>
Promise.all([
textsecure.storage.protocol.loadContactPreKey(number),
textsecure.storage.protocol.loadContactSignedPreKey(number),
])
.then(keys => {
const [preKey, signedPreKey] = keys;
if (preKey === undefined || signedPreKey === undefined) {
return false;
}
const identityKey = StringView.hexToArrayBuffer(number);
return handleResult({
identityKey,
devices: [
{ deviceId: device, preKey, signedPreKey, registrationId: 0 },
],
}).then(results => results.every(value => value === true));
})
.catch(e => {
throw e;
})
);
});
return promise;
},
// Default ttl to 24 hours if no value provided
async transmitMessage(number, data, timestamp, ttl = 24 * 60 * 60 * 1000) {
const pubKey = number;
try {
// TODO: Make NUM_CONCURRENT_CONNECTIONS a global constant
const options = {
numConnections: NUM_SEND_CONNECTIONS,
};
options.isPublic = this.isPublic;
if (this.isPublic) {
options.publicSendData = this.publicSendData;
}
await lokiMessageAPI.sendMessage(pubKey, data, timestamp, ttl, options);
} catch (e) {
if (e.name === 'HTTPError' && e.code !== 409 && e.code !== 410) {
throw new textsecure.SendMessageNetworkError(number, '', e, timestamp);
} else if (e.name === 'TimedOutError') {
throw new textsecure.PoWError(number, e);
}
throw e;
}
},
async buildMessage(devicePubKey) {
const updatedDevices = await getStaleDeviceIdsForNumber(devicePubKey);
const keysFound = await this.getKeysForNumber(devicePubKey, updatedDevices);
// Check if we need to attach the preKeys
const enableFallBackEncryption = !keysFound;
const flags = this.message.dataMessage
? this.message.dataMessage.get_flags()
: null;
// END_SESSION means Session reset message
const isEndSession =
flags === textsecure.protobuf.DataMessage.Flags.END_SESSION;
const isSessionRequest = false;
if (enableFallBackEncryption || isEndSession) {
// Encrypt them with the fallback
const pkb = await libloki.storage.getPreKeyBundleForContact(devicePubKey);
this.message.preKeyBundleMessage = new textsecure.protobuf.PreKeyBundleMessage(
pkb
);
window.log.info('attaching prekeys to outgoing message');
}
const messageBuffer = this.message.toArrayBuffer();
const logDetails = {
message: this.message,
};
const ourPubKey = textsecure.storage.user.getNumber();
const ourPrimaryPubkey = window.storage.get('primaryDevicePubKey');
const secondaryPubKeys =
(await window.libsession.Protocols.MultiDeviceProtocol.getSecondaryDevices(
ourPubKey
)) || [];
let aliasedPubkey = devicePubKey;
if (devicePubKey === ourPubKey) {
aliasedPubkey = 'OUR_PUBKEY'; // should not happen
} else if (devicePubKey === ourPrimaryPubkey) {
aliasedPubkey = 'OUR_PRIMARY_PUBKEY';
} else if (secondaryPubKeys.some(device => device.key === devicePubKey)) {
aliasedPubkey = 'OUR SECONDARY PUBKEY';
}
libloki.api.debug.logSessionMessageSending(
`Sending :${this.messageType} message to ${aliasedPubkey} details:`,
logDetails
);
const plaintext = _getPlaintext(messageBuffer);
// No limit on message keys if we're communicating with our other devices
// FIXME options not used at all; if (ourPubkey === number) {
// options.messageKeysLimit = false;
// }
const ttl = getTTLForType(this.messageType);
const ourKey = textsecure.storage.user.getNumber();
return {
ttl,
ourKey,
sourceDevice: 1,
plaintext,
pubKey: devicePubKey,
isSessionRequest,
enableFallBackEncryption,
};
},
async encryptMessage(clearMessage) {
if (clearMessage === null) {
window.log.warn(
'clearMessage is null on encryptMessage... Returning null'
);
return null;
}
const {
ttl,
ourKey,
sourceDevice,
plaintext,
pubKey,
isSessionRequest,
enableFallBackEncryption,
} = clearMessage;
// Session doesn't use the deviceId scheme, it's always 1.
// Instead, there are multiple device public keys.
const deviceId = 1;
const address = new libsignal.SignalProtocolAddress(pubKey, deviceId);
let sessionCipher;
if (enableFallBackEncryption) {
sessionCipher = new libloki.crypto.FallBackSessionCipher(address);
} else {
sessionCipher = new libsignal.SessionCipher(
textsecure.storage.protocol,
address
);
}
const innerCiphertext = await sessionCipher.encrypt(plaintext);
const secretSessionCipher = new window.Signal.Metadata.SecretSessionCipher(
textsecure.storage.protocol
);
const senderCert = new textsecure.protobuf.SenderCertificate();
senderCert.sender = ourKey;
senderCert.senderDevice = deviceId;
const ciphertext = await secretSessionCipher.encrypt(
address.getName(),
senderCert,
innerCiphertext
);
const type = textsecure.protobuf.Envelope.Type.UNIDENTIFIED_SENDER;
const content = window.Signal.Crypto.arrayBufferToBase64(ciphertext);
return {
type,
ttl,
ourKey,
sourceDevice,
content,
pubKey,
isSessionRequest,
};
},
// Send a message to a public group
async sendPublicMessage(number) {
await this.transmitMessage(
number,
this.message.dataMessage,
this.timestamp,
0 // ttl
);
this.successfulNumbers[this.successfulNumbers.length] = number;
this.numberCompleted();
},
// Send a message to a private group member or a session chat (one to one)
async sendSessionMessage(outgoingObjects) {
// TODO: handle multiple devices/messages per transmit
const promises = outgoingObjects.map(async outgoingObject => {
if (!outgoingObject) {
return;
}
const { pubKey: destination, ttl } = outgoingObject;
try {
const socketMessage = wrapInWebsocketMessage(
outgoingObject,
this.timestamp
);
await this.transmitMessage(
destination,
socketMessage,
this.timestamp,
ttl
);
this.successfulNumbers.push(destination);
} catch (e) {
e.number = destination;
this.errors.push(e);
}
});
await Promise.all(promises);
this.numbersCompleted += this.successfulNumbers.length;
this.numberCompleted();
},
async buildAndEncrypt(devicePubKey) {
const clearMessage = await this.buildMessage(devicePubKey);
return this.encryptMessage(clearMessage);
},
// eslint-disable-next-line no-unused-vars
async doSendMessage(primaryPubKey, devicesPubKeys) {
if (this.isPublic) {
await this.sendPublicMessage(primaryPubKey);
return;
}
this.numbers = devicesPubKeys;
if (this.isMediumGroup) {
await this.sendMediumGroupMessage(primaryPubKey);
return;
}
const outgoingObjects = await Promise.all(
devicesPubKeys.map(pk => this.buildAndEncrypt(pk, primaryPubKey))
);
this.sendSessionMessage(outgoingObjects);
},
sendToNumber(number, multiDevice = true) {
return this.reloadDevicesAndSend(number, multiDevice).catch(error => {
if (error.message === 'Identity key changed') {
// eslint-disable-next-line no-param-reassign
error = new textsecure.OutgoingIdentityKeyError(
number,
error.originalMessage,
error.timestamp,
error.identityKey
);
this.registerError(number, 'Identity key changed', error);
} else {
this.registerError(
number,
`Failed to retrieve new device keys for number ${number}`,
error
);
}
});
},
};
window.textsecure = window.textsecure || {};
window.textsecure.OutgoingMessage = OutgoingMessage;

@ -360,8 +360,13 @@ MessageSender.prototype = {
});
},
async sendContactSyncMessage() {
const convosToSync = await libsession.Utils.SyncMessageUtils.getSyncContacts();
async sendContactSyncMessage(convos) {
let convosToSync;
if (!convos) {
convosToSync = await libsession.Utils.SyncMessageUtils.getSyncContacts();
} else {
convosToSync = convos;
}
if (convosToSync.size === 0) {
window.console.info('No contacts to sync.');
@ -397,11 +402,7 @@ MessageSender.prototype = {
}
// We only want to sync across closed groups that we haven't left
const sessionGroups = conversations.filter(
c =>
c.isClosedGroup() &&
!c.get('left') &&
!c.isBlocked() &&
!c.isMediumGroup()
c => c.isClosedGroup() && !c.get('left') && !c.isMediumGroup()
);
if (sessionGroups.length === 0) {
window.console.info('No closed group to sync.');
@ -458,6 +459,7 @@ MessageSender.prototype = {
if (myDevice !== 1 && myDevice !== '1') {
const syncReadMessages = new libsession.Messages.Outgoing.SyncReadMessage(
{
timestamp: Date.now(),
readMessages: reads,
}
);

@ -9,11 +9,11 @@ message Envelope {
UNKNOWN = 0;
CIPHERTEXT = 1;
KEY_EXCHANGE = 2;
PREKEY_BUNDLE = 3; //Used By Signal. DO NOT TOUCH! we don't use this at all.
PREKEY_BUNDLE = 3; //This field is used by Signal. DO NOT TOUCH!.
RECEIPT = 5;
UNIDENTIFIED_SENDER = 6;
MEDIUM_GROUP_CIPHERTEXT = 7;
FALLBACK_MESSAGE = 101; // contains prekeys and is using simple encryption
FALLBACK_MESSAGE = 101; // Custom Encryption for when we don't have a session or we need to establish a session
}
optional Type type = 1;
@ -35,7 +35,7 @@ message Content {
optional NullMessage nullMessage = 4;
optional ReceiptMessage receiptMessage = 5;
optional TypingMessage typingMessage = 6;
optional PreKeyBundleMessage preKeyBundleMessage = 101;
optional PreKeyBundleMessage preKeyBundleMessage = 101; // The presence of this indicated that we want to establish a new session (Session Request)
optional LokiAddressMessage lokiAddressMessage = 102;
optional PairingAuthorisationMessage pairingAuthorisation = 103;
}

@ -5,7 +5,6 @@
.panel,
.panel-wrapper {
height: calc(100% - #{$header-height});
overflow-y: scroll;
}
@ -22,9 +21,16 @@
.panel-wrapper {
display: flex;
flex-direction: column;
flex-grow: 1;
overflow: initial;
}
.conversation-content-left {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.main.panel {
.discussion-container {
flex-grow: 1;

@ -1350,6 +1350,16 @@ label {
overflow: hidden;
user-select: all;
overflow-y: auto;
padding: 0px 5px 20px 5px;
&.session-id-editable-textarea:placeholder-shown {
padding: 20px 5px 0px 5px;
}
&.group-id-editable-textarea {
margin-top: 15px;
white-space: nowrap;
}
}
input {
@ -1594,6 +1604,10 @@ input {
}
}
.create-group-name-input {
display: flex;
justify-content: center;
width: 100%;
.session-id-editable {
height: 60px !important;

@ -238,9 +238,11 @@ $session-compose-margin: 20px;
box-shadow: 0 0 100px 0 rgba(0, 0, 0, 0.5);
display: flex;
flex-direction: column;
flex-grow: 1;
align-items: center;
overflow-y: auto;
overflow-x: hidden;
.session-icon .exit {
padding: 13px;
}
@ -307,6 +309,8 @@ $session-compose-margin: 20px;
}
.session-id-editable {
width: 90%;
textarea::-webkit-inner-spin-button {
margin: 0px 20px;
width: -webkit-fill-available;

@ -11,7 +11,6 @@ import { ContactName } from './conversation/ContactName';
import { TypingAnimation } from './conversation/TypingAnimation';
import { Colors, LocalizerType } from '../types/Util';
import { SessionButton, SessionButtonColor } from './session/SessionButton';
export type PropsData = {
id: string;
@ -181,11 +180,11 @@ export class ConversationListItem extends React.PureComponent<Props> {
{!isPublic && !isRss && !isMe ? (
<MenuItem onClick={blockHandler}>{blockTitle}</MenuItem>
) : null}
{!isPublic && !isRss && !isMe ? (
{/* {!isPublic && !isRss && !isMe ? (
<MenuItem onClick={onChangeNickname}>
{i18n('changeNickname')}
</MenuItem>
) : null}
) : null} */}
{!isPublic && !isRss && !isMe && hasNickname ? (
<MenuItem onClick={onClearNickname}>{i18n('clearNickname')}</MenuItem>
) : null}

@ -262,14 +262,6 @@ export class DevicePairingDialog extends React.Component<Props, State> {
private requestReceived(secondaryDevicePubKey: string | EventHandlerNonNull) {
// FIFO: push at the front of the array with unshift()
this.state.pubKeyRequests.unshift(secondaryDevicePubKey);
window.pushToast({
title: window.i18n('gotPairingRequest'),
description: `${window.shortenPubkey(
secondaryDevicePubKey
)} ${window.i18n(
'showPairingWordsTitle'
)}: ${window.mnemonic.pubkey_to_secret_words(secondaryDevicePubKey)}`,
});
if (!this.state.currentPubKey) {
this.nextPubKey();
this.stopReceivingRequests();

@ -337,7 +337,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
setProfileName: this.state.profileName,
},
() => {
// Update settinngs in dialog complete;
// Update settings in dialog complete;
// now callback to reloadactions panel avatar
this.props.callback(this.state.avatar);
}

@ -454,7 +454,7 @@ export class ConversationHeader extends React.Component<Props> {
const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser');
const blockHandler = isBlocked ? onUnblockUser : onBlockUser;
const disappearingMessagesMenuItem = !isKickedFromGroup && (
const disappearingMessagesMenuItem = !isKickedFromGroup && !isBlocked && (
<SubMenu title={disappearingTitle}>
{(timerOptions || []).map(item => (
<MenuItem
@ -480,7 +480,7 @@ export class ConversationHeader extends React.Component<Props> {
const resetSessionMenuItem = !isGroup && (
<MenuItem onClick={onResetSession}>{i18n('resetSession')}</MenuItem>
);
const blockHandlerMenuItem = !isMe && !isGroup && !isRss && (
const blockHandlerMenuItem = !isMe && !isRss && (
<MenuItem onClick={blockHandler}>{blockTitle}</MenuItem>
);

@ -24,6 +24,7 @@ interface Props {
}
export class ActionsPanel extends React.Component<Props, State> {
private ourConversation: any;
constructor(props: Props) {
super(props);
this.state = {
@ -31,6 +32,7 @@ export class ActionsPanel extends React.Component<Props, State> {
};
this.editProfileHandle = this.editProfileHandle.bind(this);
this.refreshAvatarCallback = this.refreshAvatarCallback.bind(this);
}
public componentDidMount() {
@ -42,10 +44,36 @@ export class ActionsPanel extends React.Component<Props, State> {
this.setState({
avatarPath: conversation.getAvatarPath(),
});
// When our primary device updates its avatar, we will need for a message sync to know about that.
// Once we get the avatar update, we need to refresh this react component.
// So we listen to changes on our profile avatar and use the updated avatarPath (done on message received).
this.ourConversation = conversation;
this.ourConversation.on(
'change',
() => {
this.refreshAvatarCallback(this.ourConversation);
},
'refreshAvatarCallback'
);
}
);
}
public refreshAvatarCallback(conversation: any) {
if (conversation.changed?.profileAvatar) {
this.setState({
avatarPath: conversation.getAvatarPath(),
});
}
}
public componentWillUnmount() {
if (this.ourConversation) {
this.ourConversation.off('change', null, 'refreshAvatarCallback');
}
}
public Section = ({
isSelected,
onSelect,

@ -196,6 +196,7 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
editable={!noContactsForClosedGroup}
placeholder={placeholder}
value={groupName}
isGroup={true}
maxLength={window.CONSTANTS.MAX_GROUPNAME_LENGTH}
onChange={this.onGroupNameChanged}
onPressEnter={() => onButtonClick(groupName, selectedMembers)}

@ -22,6 +22,7 @@ interface Props {
isAdmin: boolean;
amMod: boolean;
isKickedFromGroup: boolean;
isBlocked: boolean;
onGoBack: () => void;
onInviteContacts: () => void;
@ -215,10 +216,12 @@ export class SessionGroupSettings extends React.Component<Props, any> {
isAdmin,
isKickedFromGroup,
amMod,
isBlocked,
} = this.props;
const { documents, media, onItemClick } = this.state;
const showMemberCount = !!(memberCount && memberCount > 0);
const hasDisappearingMessages = !isPublic && !isKickedFromGroup;
const hasDisappearingMessages =
!isPublic && !isKickedFromGroup && !isBlocked;
const leaveGroupString = isPublic
? window.i18n('leaveOpenGroup')
: isKickedFromGroup
@ -235,9 +238,11 @@ export class SessionGroupSettings extends React.Component<Props, any> {
});
const showUpdateGroupNameButton =
isPublic && !isKickedFromGroup ? amMod : isAdmin;
isPublic && !isKickedFromGroup
? amMod && !isBlocked
: isAdmin && !isBlocked;
const showUpdateGroupMembersButton =
!isPublic && !isKickedFromGroup && isAdmin;
!isPublic && !isKickedFromGroup && !isBlocked && isAdmin;
return (
<div className="group-settings">
@ -313,16 +318,18 @@ export class SessionGroupSettings extends React.Component<Props, any> {
isAdmin,
isPublic,
isKickedFromGroup,
isBlocked,
} = this.props;
const showInviteContacts = (isPublic || isAdmin) && !isKickedFromGroup;
const showInviteContacts =
(isPublic || isAdmin) && !isKickedFromGroup && !isBlocked;
return (
<div className="group-settings-header">
<SessionIconButton
iconType={SessionIconType.Chevron}
iconSize={SessionIconSize.Medium}
iconRotation={90}
iconRotation={270}
onClick={onGoBack}
/>
<Avatar

@ -9,6 +9,7 @@ interface Props {
onChange?: any;
onPressEnter?: any;
maxLength?: number;
isGroup?: boolean;
}
export class SessionIdEditable extends React.PureComponent<Props> {
@ -28,7 +29,14 @@ export class SessionIdEditable extends React.PureComponent<Props> {
}
public render() {
const { placeholder, editable, text, value, maxLength } = this.props;
const {
placeholder,
editable,
text,
value,
maxLength,
isGroup,
} = this.props;
return (
<div
@ -38,7 +46,11 @@ export class SessionIdEditable extends React.PureComponent<Props> {
)}
>
<textarea
className="session-id-editable-textarea"
className={classNames(
isGroup
? 'group-id-editable-textarea'
: 'session-id-editable-textarea'
)}
ref={this.inputRef}
placeholder={placeholder}
disabled={!editable}

@ -10,6 +10,7 @@ import {
import { UserUtil } from '../../../util';
import { MultiDeviceProtocol } from '../../../session/protocols';
import { PubKey } from '../../../session/types';
import { NumberUtils } from '../../../session/utils';
export enum SessionSettingCategory {
Appearance = 'appearance',
@ -441,7 +442,13 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
id: 'message-ttl',
title: window.i18n('messageTTL'),
description: window.i18n('messageTTLSettingDescription'),
hidden: false,
// TODO: Revert
// TTL set to 2 days for mobile push notification compabability
// temporary fix .t 13/07/2020
//
// TODO: Hook up this TTL to message sending when re-enabling.
// This setting is not used in any libsession sending code
hidden: true,
type: SessionSettingType.Slider,
category: SessionSettingCategory.Privacy,
setFn: undefined,
@ -452,7 +459,10 @@ export class SettingsView extends React.Component<SettingsViewProps, State> {
step: 6,
min: 12,
max: 96,
defaultValue: 24,
defaultValue: NumberUtils.msAsUnit(
window.CONSTANTS.TTL_DEFAULT_REGULAR_MESSAGE,
'hour'
),
info: (value: number) => `${value} Hours`,
},
confirmationDialogParams: undefined,

@ -146,13 +146,7 @@ interface GroupInfo {
}
export async function onGroupReceived(details: GroupInfo) {
const {
ConversationController,
libloki,
storage,
textsecure,
Whisper,
} = window;
const { ConversationController, libloki, textsecure, Whisper } = window;
const { id } = details;
@ -189,12 +183,6 @@ export async function onGroupReceived(details: GroupInfo) {
updates.left = true;
}
if (details.blocked) {
storage.addBlockedGroup(id);
} else {
storage.removeBlockedGroup(id);
}
conversation.set(updates);
// Update the conversation avatar only if new avatar exists and hash differs
@ -210,6 +198,13 @@ export async function onGroupReceived(details: GroupInfo) {
);
conversation.set(newAttributes);
}
const isBlocked = details.blocked || false;
if (conversation.isClosedGroup()) {
await BlockedNumberController.setGroupBlocked(conversation.id, isBlocked);
}
conversation.trigger('change', conversation);
conversation.updateTextInputState();
await window.Signal.Data.updateConversation(id, conversation.attributes, {
Conversation: Whisper.Conversation,

@ -12,6 +12,7 @@ import { MultiDeviceProtocol, SessionProtocol } from '../session/protocols';
import { PubKey } from '../session/types';
import ByteBuffer from 'bytebuffer';
import { BlockedNumberController } from '../util';
async function unpairingRequestIsLegit(source: string, ourPubKey: string) {
const { textsecure, storage, lokiFileServerAPI } = window;
@ -287,6 +288,7 @@ export async function handleContacts(
await removeFromCache(envelope);
}
// tslint:disable-next-line: max-func-body-length
async function onContactReceived(details: any) {
const {
ConversationController,
@ -427,7 +429,15 @@ async function onContactReceived(details: any) {
verifiedEvent.viaContactSync = true;
await onVerified(verifiedEvent);
}
await conversation.trigger('change');
const isBlocked = details.blocked || false;
if (conversation.isPrivate()) {
await BlockedNumberController.setBlocked(conversation.id, isBlocked);
}
conversation.updateTextInputState();
await conversation.trigger('change', conversation);
} catch (error) {
window.log.error('onContactReceived error:', Errors.toLogFormat(error));
}

@ -0,0 +1,13 @@
import { NumberUtils } from './utils';
// Default TTL
export const TTL_DEFAULT = {
PAIRING_REQUEST: NumberUtils.timeAsMs(2, 'minutes'),
DEVICE_UNPAIRING: NumberUtils.timeAsMs(4, 'days'),
SESSION_REQUEST: NumberUtils.timeAsMs(4, 'days'),
SESSION_ESTABLISHED: NumberUtils.timeAsMs(2, 'days'),
END_SESSION_MESSAGE: NumberUtils.timeAsMs(4, 'days'),
TYPING_MESSAGE: NumberUtils.timeAsMs(1, 'minute'),
ONLINE_BROADCAST: NumberUtils.timeAsMs(1, 'minute'),
REGULAR_MESSAGE: NumberUtils.timeAsMs(2, 'days'),
};

@ -3,7 +3,8 @@ import * as Protocols from './protocols';
import * as Types from './types';
import * as Utils from './utils';
import * as Sending from './sending';
import * as Constants from './constants';
export * from './instance';
export { Messages, Utils, Protocols, Types, Sending };
export { Messages, Utils, Protocols, Types, Sending, Constants };

@ -1,5 +1,6 @@
import { Message } from '../Message';
import { SignalService } from '../../../../protobuf';
import { Constants } from '../../..';
export abstract class ContentMessage extends Message {
public plainTextBuffer(): Uint8Array {
@ -14,7 +15,6 @@ export abstract class ContentMessage extends Message {
* this value can be used in all child classes
*/
protected getDefaultTTL(): number {
// 1 day default for any other message
return 24 * 60 * 60 * 1000;
return Constants.TTL_DEFAULT.REGULAR_MESSAGE;
}
}

@ -1,9 +1,10 @@
import { SessionRequestMessage } from './SessionRequestMessage';
import { SignalService } from '../../../../protobuf';
import { Constants } from '../../..';
export class EndSessionMessage extends SessionRequestMessage {
public ttl(): number {
return 4 * 24 * 60 * 60 * 1000; // 4 days
return Constants.TTL_DEFAULT.END_SESSION_MESSAGE;
}
protected contentProto(): SignalService.Content {

@ -2,6 +2,7 @@ import { ContentMessage } from './ContentMessage';
import { SignalService } from '../../../../protobuf';
import * as crypto from 'crypto';
import { MessageParams } from '../Message';
import { Constants } from '../../..';
export class SessionEstablishedMessage extends ContentMessage {
public readonly padding: Buffer;
@ -18,7 +19,7 @@ export class SessionEstablishedMessage extends ContentMessage {
this.padding = crypto.randomBytes(paddingLength);
}
public ttl(): number {
return 5 * 60 * 1000;
return Constants.TTL_DEFAULT.SESSION_ESTABLISHED;
}
protected contentProto(): SignalService.Content {

@ -1,6 +1,7 @@
import { ContentMessage } from './ContentMessage';
import { SignalService } from '../../../../protobuf';
import { MessageParams } from '../Message';
import { Constants } from '../../..';
export interface PreKeyBundleType {
identityKey: Uint8Array;
@ -17,7 +18,6 @@ interface SessionRequestParams extends MessageParams {
}
export class SessionRequestMessage extends ContentMessage {
public static readonly ttl = 4 * 24 * 60 * 60 * 1000; // 4 days
private readonly preKeyBundle: PreKeyBundleType;
constructor(params: SessionRequestParams) {
@ -25,8 +25,14 @@ export class SessionRequestMessage extends ContentMessage {
this.preKeyBundle = params.preKeyBundle;
}
public static defaultTTL(): number {
// Gets the default TTL for Session Request, as
// public static readonly ttl: number <-- cannot be assigned a non-literal
return Constants.TTL_DEFAULT.SESSION_REQUEST;
}
public ttl(): number {
return SessionRequestMessage.ttl;
return SessionRequestMessage.defaultTTL();
}
protected getPreKeyBundleMessage(): SignalService.PreKeyBundleMessage {

@ -1,9 +1,9 @@
import { ContentMessage } from './ContentMessage';
import { SignalService } from '../../../../protobuf';
import { TextEncoder } from 'util';
import { MessageParams } from '../Message';
import { StringUtils } from '../../../utils';
import { PubKey } from '../../../types';
import { Constants } from '../../..';
interface TypingMessageParams extends MessageParams {
isTyping: boolean;
@ -26,7 +26,7 @@ export class TypingMessage extends ContentMessage {
}
public ttl(): number {
return 60 * 1000; // 1 minute for typing indicators
return Constants.TTL_DEFAULT.TYPING_MESSAGE;
}
protected contentProto(): SignalService.Content {

@ -1,9 +1,10 @@
import { DataMessage } from './DataMessage';
import { SignalService } from '../../../../../protobuf';
import { Constants } from '../../../..';
export class DeviceUnlinkMessage extends DataMessage {
public ttl(): number {
return 4 * 24 * 60 * 60 * 1000; // 4 days for device unlinking
return Constants.TTL_DEFAULT.DEVICE_UNPAIRING;
}
public dataProto(): SignalService.DataMessage {

@ -25,6 +25,13 @@ export class DeviceLinkGrantMessage extends DeviceLinkRequestMessage {
requestSignature: params.requestSignature,
});
if (!(params.lokiProfile.profileKey instanceof Uint8Array)) {
throw new TypeError('profileKey must be of type Uint8Array');
}
if (!(params.grantSignature instanceof Uint8Array)) {
throw new TypeError('grantSignature must be of type Uint8Array');
}
this.displayName = params.lokiProfile.displayName;
this.avatarPointer = params.lokiProfile.avatarPointer;
this.profileKey = params.lokiProfile.profileKey;

@ -1,6 +1,7 @@
import { ContentMessage } from '../ContentMessage';
import { SignalService } from '../../../../../protobuf';
import { MessageParams } from '../../Message';
import { Constants } from '../../../..';
export interface DeviceLinkMessageParams extends MessageParams {
primaryDevicePubKey: string;
secondaryDevicePubKey: string;
@ -14,13 +15,23 @@ export class DeviceLinkRequestMessage extends ContentMessage {
constructor(params: DeviceLinkMessageParams) {
super({ timestamp: params.timestamp, identifier: params.identifier });
if (!(params.requestSignature instanceof Uint8Array)) {
throw new TypeError('requestSignature must be of type Uint8Array');
}
if (typeof params.primaryDevicePubKey !== 'string') {
throw new TypeError('primaryDevicePubKey must be of type string');
}
if (typeof params.secondaryDevicePubKey !== 'string') {
throw new TypeError('secondaryDevicePubKey must be of type string');
}
this.primaryDevicePubKey = params.primaryDevicePubKey;
this.secondaryDevicePubKey = params.secondaryDevicePubKey;
this.requestSignature = params.requestSignature;
}
public ttl(): number {
return 2 * 60 * 1000; // 2 minutes for pairing requests
return Constants.TTL_DEFAULT.PAIRING_REQUEST;
}
protected getDataMessage(): SignalService.DataMessage | undefined {

@ -81,7 +81,7 @@ export class SessionProtocol {
const now = Date.now();
const sentTimestamps = Object.entries(this.sentSessionsTimestamp);
const promises = sentTimestamps.map(async ([device, sent]) => {
const expireTime = sent + SessionRequestMessage.ttl;
const expireTime = sent + SessionRequestMessage.defaultTTL();
// Check if we need to send a session request
if (now < expireTime) {
return;

@ -298,8 +298,6 @@ export async function storeOnNode(
return false;
}
const res = snodeRes as any;
const json = JSON.parse(snodeRes.body);
// Make sure we aren't doing too much PoW
const currentDifficulty = window.storage.get('PoWDifficulty', null);

@ -0,0 +1,47 @@
type TimeUnit =
| 'second'
| 'seconds'
| 'minute'
| 'minutes'
| 'hour'
| 'hours'
| 'day'
| 'days';
export const timeAsMs = (value: number, unit: TimeUnit) => {
// Converts a time to milliseconds
// Valid units: second, minute, hour, day
const unitAsSingular = unit.replace(new RegExp('s?$'), '');
switch (unitAsSingular) {
case 'second':
return value * 1000;
case 'minute':
return value * 60 * 1000;
case 'hour':
return value * 60 * 60 * 1000;
case 'day':
return value * 24 * 60 * 60 * 1000;
default:
return value;
}
};
export const msAsUnit = (value: number, unit: TimeUnit) => {
// Converts milliseconds to your preferred unit
// Valid units: second(s), minute(s), hour(s), day(s)
const unitAsSingular = unit.replace(new RegExp('s?$'), '');
switch (unitAsSingular) {
case 'second':
return value / 1000;
case 'minute':
return value / 60 / 1000;
case 'hour':
return value / 60 / 60 / 1000;
case 'day':
return value / 24 / 60 / 60 / 1000;
default:
return value;
}
};

@ -2,6 +2,7 @@ import * as MessageUtils from './Messages';
import * as GroupUtils from './Groups';
import * as SyncMessageUtils from './SyncMessage';
import * as StringUtils from './String';
import * as NumberUtils from './Number';
import * as PromiseUtils from './Promise';
import * as ProtobufUtils from './Protobuf';
@ -14,6 +15,7 @@ export {
SyncMessageUtils,
GroupUtils,
StringUtils,
NumberUtils,
PromiseUtils,
ProtobufUtils,
};

@ -54,6 +54,7 @@ export type ConversationType = {
isTyping: boolean;
isSecondary?: boolean;
primaryDevice: string;
isBlocked: boolean;
};
export type ConversationLookupType = {
[key: string]: ConversationType;

@ -11,6 +11,7 @@ import {
import { getIntl, getRegionCode, getUserNumber } from './user';
import { PropsData as ConversationListItemPropsType } from '../../components/ConversationListItem';
import { BlockedNumberController } from '../../util';
export const getConversations = (state: StateType): ConversationsStateType =>
state.conversations;
@ -119,6 +120,16 @@ export const _getLeftPaneLists = (
isSelected: true,
};
}
const isBlocked =
BlockedNumberController.isBlocked(conversation.primaryDevice) ||
BlockedNumberController.isGroupBlocked(conversation.id);
if (isBlocked) {
conversation = {
...conversation,
isBlocked: true,
};
}
// Add Open Group to list as soon as the name has been set
if (

@ -9,6 +9,7 @@ import {
import { SignalService } from '../../../protobuf';
import { TextEncoder } from 'util';
import { toNumber } from 'lodash';
import { Constants } from '../../../session';
describe('ChatMessage', () => {
it('can create empty message with just a timestamp', () => {
@ -128,11 +129,11 @@ describe('ChatMessage', () => {
expect(firstAttachment?.url).to.be.deep.equal('url');
});
it('ttl of 1 day', () => {
it('correct ttl', () => {
const message = new ChatMessage({
timestamp: Date.now(),
});
expect(message.ttl()).to.equal(24 * 60 * 60 * 1000);
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.REGULAR_MESSAGE);
});
it('has an identifier', () => {

@ -9,6 +9,7 @@ import { TextEncoder } from 'util';
import { TestUtils } from '../../test-utils';
import { StringUtils } from '../../../session/utils';
import { PubKey } from '../../../session/types';
import { Constants } from '../../../session';
describe('ClosedGroupChatMessage', () => {
let groupId: PubKey;
@ -44,7 +45,7 @@ describe('ClosedGroupChatMessage', () => {
.to.be.equal(chatMessage.timestamp);
});
it('ttl of 1 day', () => {
it('correct ttl', () => {
const chatMessage = new ChatMessage({
timestamp: Date.now(),
});
@ -52,7 +53,7 @@ describe('ClosedGroupChatMessage', () => {
groupId,
chatMessage,
});
expect(message.ttl()).to.equal(24 * 60 * 60 * 1000);
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.REGULAR_MESSAGE);
});
it('has an identifier', () => {

@ -7,6 +7,7 @@ import {
} from '../../../session/messages/outgoing';
import { SignalService } from '../../../protobuf';
import { LokiProfile } from '../../../types/Message';
import { Constants } from '../../../session';
describe('DeviceLinkMessage', () => {
let linkRequestMessage: DeviceLinkRequestMessage;
@ -115,9 +116,13 @@ describe('DeviceLinkMessage', () => {
});
});
it('ttl of 2 minutes', () => {
expect(linkRequestMessage.ttl()).to.equal(2 * 60 * 1000);
expect(linkGrantMessage.ttl()).to.equal(2 * 60 * 1000);
it('correct ttl', () => {
expect(linkRequestMessage.ttl()).to.equal(
Constants.TTL_DEFAULT.PAIRING_REQUEST
);
expect(linkGrantMessage.ttl()).to.equal(
Constants.TTL_DEFAULT.PAIRING_REQUEST
);
});
it('has an identifier', () => {

@ -3,6 +3,7 @@ import { beforeEach } from 'mocha';
import { DeviceUnlinkMessage } from '../../../session/messages/outgoing';
import { SignalService } from '../../../protobuf';
import { Constants } from '../../../session';
describe('DeviceUnlinkMessage', () => {
let message: DeviceUnlinkMessage;
@ -21,8 +22,8 @@ describe('DeviceUnlinkMessage', () => {
);
});
it('ttl of 4 days', () => {
expect(message.ttl()).to.equal(4 * 24 * 60 * 60 * 1000);
it('correct ttl', () => {
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.DEVICE_UNPAIRING);
});
it('has an identifier', () => {

@ -4,6 +4,7 @@ import { beforeEach } from 'mocha';
import { EndSessionMessage } from '../../../session/messages/outgoing';
import { SignalService } from '../../../protobuf';
import { TextEncoder } from 'util';
import { Constants } from '../../../session';
describe('EndSessionMessage', () => {
let message: EndSessionMessage;
@ -64,8 +65,8 @@ describe('EndSessionMessage', () => {
expect(decoded.dataMessage).to.have.deep.property('body', 'TERMINATE');
});
it('ttl of 4 days', () => {
expect(message.ttl()).to.equal(4 * 24 * 60 * 60 * 1000);
it('correct ttl', () => {
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.END_SESSION_MESSAGE);
});
it('has an identifier', () => {

@ -3,6 +3,7 @@ import { beforeEach } from 'mocha';
import { GroupInvitationMessage } from '../../../session/messages/outgoing';
import { SignalService } from '../../../protobuf';
import { Constants } from '../../../session';
describe('GroupInvitationMessage', () => {
let message: GroupInvitationMessage;
@ -38,8 +39,8 @@ describe('GroupInvitationMessage', () => {
);
});
it('ttl of 1 day', () => {
expect(message.ttl()).to.equal(24 * 60 * 60 * 1000);
it('correct ttl', () => {
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.REGULAR_MESSAGE);
});
it('has an identifier', () => {

@ -7,6 +7,7 @@ import {
} from '../../../session/messages/outgoing';
import { SignalService } from '../../../protobuf';
import { toNumber } from 'lodash';
import { Constants } from '../../../session';
describe('ReceiptMessage', () => {
let readMessage: ReadReceiptMessage;
@ -42,9 +43,11 @@ describe('ReceiptMessage', () => {
expect(decodedTimestamps).to.deep.equal(timestamps);
});
it('ttl of 1 day', () => {
expect(readMessage.ttl()).to.equal(24 * 60 * 60 * 1000);
expect(deliveryMessage.ttl()).to.equal(24 * 60 * 60 * 1000);
it('correct ttl', () => {
expect(readMessage.ttl()).to.equal(Constants.TTL_DEFAULT.REGULAR_MESSAGE);
expect(deliveryMessage.ttl()).to.equal(
Constants.TTL_DEFAULT.REGULAR_MESSAGE
);
});
it('has an identifier', () => {

@ -3,6 +3,7 @@ import { beforeEach } from 'mocha';
import { SessionEstablishedMessage } from '../../../session/messages/outgoing';
import { SignalService } from '../../../protobuf';
import { Constants } from '../../../session';
describe('SessionEstablishedMessage', () => {
let message: SessionEstablishedMessage;
@ -21,8 +22,8 @@ describe('SessionEstablishedMessage', () => {
);
});
it('ttl of 5 minutes', () => {
expect(message.ttl()).to.equal(5 * 60 * 1000);
it('correct ttl', () => {
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.SESSION_ESTABLISHED);
});
it('has an identifier', () => {

@ -4,6 +4,7 @@ import { beforeEach } from 'mocha';
import { SessionRequestMessage } from '../../../session/messages/outgoing';
import { SignalService } from '../../../protobuf';
import { TextDecoder, TextEncoder } from 'util';
import { Constants } from '../../../session';
describe('SessionRequestMessage', () => {
let message: SessionRequestMessage;
@ -64,8 +65,8 @@ describe('SessionRequestMessage', () => {
);
});
it('ttl of 4 days', () => {
expect(message.ttl()).to.equal(4 * 24 * 60 * 60 * 1000);
it('correct ttl', () => {
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.SESSION_REQUEST);
});
it('has an identifier', () => {

@ -7,6 +7,7 @@ import Long from 'long';
import { toNumber } from 'lodash';
import { StringUtils } from '../../../session/utils';
import { TestUtils } from '../../test-utils';
import { Constants } from '../../../session';
describe('TypingMessage', () => {
it('has Action.STARTED if isTyping = true', () => {
@ -79,12 +80,12 @@ describe('TypingMessage', () => {
);
});
it('ttl of 1 minute', () => {
it('correct ttl', () => {
const message = new TypingMessage({
timestamp: Date.now(),
isTyping: true,
});
expect(message.ttl()).to.equal(60 * 1000);
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.TYPING_MESSAGE);
});
it('has an identifier', () => {

@ -124,7 +124,7 @@ describe('SessionProtocol', () => {
});
// Set the time just before expiry
clock.tick(SessionRequestMessage.ttl - 100);
clock.tick(SessionRequestMessage.defaultTTL() - 100);
await SessionProtocol.checkSessionRequestExpiry();
expect(getItemById.calledWith('sentSessionsTimestamp'));
@ -140,7 +140,7 @@ describe('SessionProtocol', () => {
});
// Expire the request
clock.tick(SessionRequestMessage.ttl + 100);
clock.tick(SessionRequestMessage.defaultTTL() + 100);
await SessionProtocol.checkSessionRequestExpiry();
expect(getItemById.calledWith('sentSessionsTimestamp'));
@ -159,7 +159,7 @@ describe('SessionProtocol', () => {
sandbox.stub(SessionProtocol, 'sendSessionRequestIfNeeded').resolves();
// Expire the request
clock.tick(SessionRequestMessage.ttl + 100);
clock.tick(SessionRequestMessage.defaultTTL() + 100);
await SessionProtocol.checkSessionRequestExpiry();
expect(getItemById.calledWith('sentSessionsTimestamp'));

@ -29,6 +29,7 @@ describe('state/selectors/conversations', () => {
mentionedUs: false,
isSelected: false,
isTyping: false,
isBlocked: false,
},
id2: {
id: 'id2',
@ -47,6 +48,7 @@ describe('state/selectors/conversations', () => {
mentionedUs: false,
isSelected: false,
isTyping: false,
isBlocked: false,
},
id3: {
id: 'id3',
@ -65,6 +67,7 @@ describe('state/selectors/conversations', () => {
mentionedUs: false,
isSelected: false,
isTyping: false,
isBlocked: false,
},
id4: {
id: 'id4',
@ -83,6 +86,7 @@ describe('state/selectors/conversations', () => {
mentionedUs: false,
isSelected: false,
isTyping: false,
isBlocked: false,
},
id5: {
id: 'id5',
@ -101,6 +105,7 @@ describe('state/selectors/conversations', () => {
mentionedUs: false,
isSelected: false,
isTyping: false,
isBlocked: false,
},
};
const comparator = _getConversationComparator(i18n, regionCode);

@ -89,6 +89,26 @@ export class BlockedNumberController {
}
}
public static async setBlocked(
user: string | PubKey,
blocked: boolean
): Promise<void> {
if (blocked) {
return BlockedNumberController.block(user);
}
return BlockedNumberController.unblock(user);
}
public static async setGroupBlocked(
groupId: string | PubKey,
blocked: boolean
): Promise<void> {
if (blocked) {
return BlockedNumberController.blockGroup(groupId);
}
return BlockedNumberController.unblockGroup(groupId);
}
public static async blockGroup(groupId: string | PubKey): Promise<void> {
await this.load();
const id = PubKey.cast(groupId);

Loading…
Cancel
Save