You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/libloki/api.js

232 lines
7.0 KiB
JavaScript

/* global window, textsecure, dcodeIO, StringView, ConversationController, _, libsession */
/* eslint-disable no-bitwise */
// eslint-disable-next-line func-names
(function() {
window.libloki = window.libloki || {};
const DebugFlagsEnum = {
GROUP_SYNC_MESSAGES: 1,
CONTACT_SYNC_MESSAGES: 2,
SESSION_REQUEST_MESSAGES: 8,
SESSION_MESSAGE_SENDING: 16,
SESSION_BACKGROUND_MESSAGE: 32,
GROUP_REQUEST_INFO: 64,
// If you add any new flag, be sure it is bitwise safe! (unique and 2 multiples)
ALL: 65535,
};
const debugFlags = DebugFlagsEnum.ALL;
const debugLogFn = (...args) => {
if (window.lokiFeatureFlags.debugMessageLogs) {
window.console.warn(...args);
}
};
function logSessionMessageSending(...args) {
if (debugFlags & DebugFlagsEnum.SESSION_MESSAGE_SENDING) {
debugLogFn(...args);
}
}
function logGroupSync(...args) {
if (debugFlags & DebugFlagsEnum.GROUP_SYNC_MESSAGES) {
debugLogFn(...args);
}
}
function logGroupRequestInfo(...args) {
if (debugFlags & DebugFlagsEnum.GROUP_REQUEST_INFO) {
debugLogFn(...args);
}
}
function logContactSync(...args) {
if (debugFlags & DebugFlagsEnum.CONTACT_SYNC_MESSAGES) {
debugLogFn(...args);
}
}
function logBackgroundMessage(...args) {
if (debugFlags & DebugFlagsEnum.SESSION_BACKGROUND_MESSAGE) {
debugLogFn(...args);
}
}
async function sendSessionEstablishedMessage(pubKey) {
const user = libsession.Types.PubKey.from(pubKey);
const sessionEstablished = new window.libsession.Messages.Outgoing.SessionEstablished(
{ timestamp: Date.now() }
);
await libsession.messageQueue.send(user, sessionEstablished);
}
async function sendBackgroundMessage(pubKey, debugMessageType) {
const backgroundMessage = textsecure.OutgoingMessage.buildBackgroundMessage(
pubKey,
debugMessageType
);
await backgroundMessage.sendToNumber(pubKey, false);
}
function sendUnpairingMessageToSecondary(pubKey) {
const unpairingMessage = textsecure.OutgoingMessage.buildUnpairingMessage(
pubKey
);
return unpairingMessage.sendToNumber(pubKey, false);
}
// Serialise as <Element0.length><Element0><Element1.length><Element1>...
// This is an implementation of the reciprocal of contacts_parser.js
function serialiseByteBuffers(buffers) {
const result = new dcodeIO.ByteBuffer();
buffers.forEach(buffer => {
// bytebuffer container expands and increments
// offset automatically
result.writeInt32(buffer.limit);
result.append(buffer);
});
result.limit = result.offset;
result.reset();
return result;
}
async function createContactSyncProtoMessage(sessionContacts) {
if (sessionContacts.length === 0) {
return null;
}
const rawContacts = await Promise.all(
sessionContacts.map(async conversation => {
const profile = conversation.getLokiProfile();
const number = conversation.getNumber();
const name = profile
? profile.displayName
: conversation.getProfileName();
const status = await conversation.safeGetVerified();
const protoState = textsecure.storage.protocol.convertVerifiedStatusToProtoState(
status
);
const verified = new textsecure.protobuf.Verified({
state: protoState,
destination: number,
identityKey: StringView.hexToArrayBuffer(number),
});
return {
name,
verified,
number,
nickname: conversation.getNickname(),
blocked: conversation.isBlocked(),
expireTimer: conversation.get('expireTimer'),
};
})
);
// Convert raw contacts to an array of buffers
const contactDetails = rawContacts
.filter(x => x.number !== textsecure.storage.user.getNumber())
.map(x => new textsecure.protobuf.ContactDetails(x))
.map(x => x.encode());
// Serialise array of byteBuffers into 1 byteBuffer
const byteBuffer = serialiseByteBuffers(contactDetails);
const data = new Uint8Array(byteBuffer.toArrayBuffer());
const contacts = new textsecure.protobuf.SyncMessage.Contacts({
data,
});
const syncMessage = new textsecure.protobuf.SyncMessage({
contacts,
});
return syncMessage;
}
function createGroupSyncProtoMessage(sessionGroup) {
// We are getting a single open group here
const rawGroup = {
id: window.Signal.Crypto.bytesFromString(sessionGroup.id),
name: sessionGroup.get('name'),
members: sessionGroup.get('members') || [],
blocked: sessionGroup.isBlocked(),
expireTimer: sessionGroup.get('expireTimer'),
admins: sessionGroup.get('groupAdmins') || [],
};
// Convert raw group to a buffer
const groupDetail = new textsecure.protobuf.GroupDetails(rawGroup).encode();
// Serialise array of byteBuffers into 1 byteBuffer
const byteBuffer = serialiseByteBuffers([groupDetail]);
const data = new Uint8Array(byteBuffer.toArrayBuffer());
const groups = new textsecure.protobuf.SyncMessage.Groups({
data,
});
const syncMessage = new textsecure.protobuf.SyncMessage({
groups,
});
return syncMessage;
}
function createOpenGroupsSyncProtoMessage(conversations) {
// We only want to sync across open groups that we haven't left
const sessionOpenGroups = conversations.filter(
c => c.isPublic() && !c.isRss() && !c.get('left')
);
if (sessionOpenGroups.length === 0) {
return null;
}
const openGroups = sessionOpenGroups.map(
conversation =>
new textsecure.protobuf.SyncMessage.OpenGroupDetails({
url: conversation.id.split('@').pop(),
channelId: conversation.get('channelId'),
})
);
const syncMessage = new textsecure.protobuf.SyncMessage({
openGroups,
});
return syncMessage;
}
function sendSessionRequestsToMembers(members = []) {
// For every member, see if we need to establish a session:
members.forEach(memberPubKey => {
const haveSession = _.some(
textsecure.storage.protocol.sessions,
s => s.number === memberPubKey
);
const ourPubKey = textsecure.storage.user.getNumber();
if (!haveSession && memberPubKey !== ourPubKey) {
// eslint-disable-next-line more/no-then
ConversationController.getOrCreateAndWait(memberPubKey, 'private').then(
() => {
const sessionRequestMessage = textsecure.OutgoingMessage.buildSessionRequestMessage(
memberPubKey
);
sessionRequestMessage.sendToNumber(memberPubKey);
}
);
}
});
}
const debug = {
logContactSync,
logGroupSync,
logSessionMessageSending,
logBackgroundMessage,
logGroupRequestInfo,
};
window.libloki.api = {
sendSessionEstablishedMessage,
sendBackgroundMessage,
sendSessionRequestsToMembers,
sendUnpairingMessageToSecondary,
createContactSyncProtoMessage,
createGroupSyncProtoMessage,
createOpenGroupsSyncProtoMessage,
debug,
};
})();