|
|
@ -1,5 +1,5 @@
|
|
|
|
/* global log, textsecure, libloki, Signal, Whisper, Headers, ConversationController,
|
|
|
|
/* global log, textsecure, libloki, Signal, Whisper, Headers, ConversationController,
|
|
|
|
clearTimeout, MessageController, libsignal, StringView, window, _ */
|
|
|
|
clearTimeout, MessageController, libsignal, StringView, window, _, lokiFileServerAPI */
|
|
|
|
const EventEmitter = require('events');
|
|
|
|
const EventEmitter = require('events');
|
|
|
|
const nodeFetch = require('node-fetch');
|
|
|
|
const nodeFetch = require('node-fetch');
|
|
|
|
const { URL, URLSearchParams } = require('url');
|
|
|
|
const { URL, URLSearchParams } = require('url');
|
|
|
@ -383,8 +383,7 @@ class LokiAppDotNetServerAPI {
|
|
|
|
if (pubKeys.length > 200) {
|
|
|
|
if (pubKeys.length > 200) {
|
|
|
|
log.warn('Too many pubKeys given to getUsersAnnotations!');
|
|
|
|
log.warn('Too many pubKeys given to getUsersAnnotations!');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
console.log('getUsersAnnotations', pubKeys)
|
|
|
|
const res = await this.serverRequest('users', {
|
|
|
|
const res = await this.serverRequest(`users`, {
|
|
|
|
|
|
|
|
method: 'GET',
|
|
|
|
method: 'GET',
|
|
|
|
params: {
|
|
|
|
params: {
|
|
|
|
ids: pubKeys.join(','),
|
|
|
|
ids: pubKeys.join(','),
|
|
|
@ -445,6 +444,10 @@ class LokiPublicChannelAPI {
|
|
|
|
// Cache for duplicate checking
|
|
|
|
// Cache for duplicate checking
|
|
|
|
this.lastMessagesCache = [];
|
|
|
|
this.lastMessagesCache = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Multidevice states
|
|
|
|
|
|
|
|
this.slavePrimaryMap = {};
|
|
|
|
|
|
|
|
this.primaryUserProfileName = {};
|
|
|
|
|
|
|
|
|
|
|
|
// end properties
|
|
|
|
// end properties
|
|
|
|
|
|
|
|
|
|
|
|
log.info(`registered LokiPublicChannel ${channelId}`);
|
|
|
|
log.info(`registered LokiPublicChannel ${channelId}`);
|
|
|
@ -750,9 +753,10 @@ class LokiPublicChannelAPI {
|
|
|
|
|
|
|
|
|
|
|
|
if (!res.err && res.response) {
|
|
|
|
if (!res.err && res.response) {
|
|
|
|
let receivedAt = new Date().getTime();
|
|
|
|
let receivedAt = new Date().getTime();
|
|
|
|
const pubKeys = []
|
|
|
|
const pubKeys = [];
|
|
|
|
const pendingMessages = []
|
|
|
|
let pendingMessages = [];
|
|
|
|
res.response.data.reverse().forEach(async adnMessage => {
|
|
|
|
pendingMessages = await Promise.all(
|
|
|
|
|
|
|
|
res.response.data.reverse().map(async adnMessage => {
|
|
|
|
// still update our last received if deleted, not signed or not valid
|
|
|
|
// still update our last received if deleted, not signed or not valid
|
|
|
|
this.lastGot = !this.lastGot
|
|
|
|
this.lastGot = !this.lastGot
|
|
|
|
? adnMessage.id
|
|
|
|
? adnMessage.id
|
|
|
@ -809,11 +813,8 @@ class LokiPublicChannelAPI {
|
|
|
|
].splice(-5);
|
|
|
|
].splice(-5);
|
|
|
|
|
|
|
|
|
|
|
|
const from = adnMessage.user.name; // profileName
|
|
|
|
const from = adnMessage.user.name; // profileName
|
|
|
|
console.log('checking', adnMessage.user.username, 'in', pubKeys)
|
|
|
|
if (pubKeys.indexOf(`@${adnMessage.user.username}`) === -1) {
|
|
|
|
if (pubKeys.indexOf(adnMessage.user.username) == -1) {
|
|
|
|
pubKeys.push(`@${adnMessage.user.username}`);
|
|
|
|
console.log('pushing pubkey', adnMessage.user.username)
|
|
|
|
|
|
|
|
pubKeys.push(adnMessage.user.username)
|
|
|
|
|
|
|
|
console.log('pubKeys', pubKeys)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const messageData = {
|
|
|
|
const messageData = {
|
|
|
@ -849,59 +850,193 @@ class LokiPublicChannelAPI {
|
|
|
|
};
|
|
|
|
};
|
|
|
|
receivedAt += 1; // Ensure different arrival times
|
|
|
|
receivedAt += 1; // Ensure different arrival times
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
this.serverAPI.chatAPI.emit('publicMessage', {
|
|
|
|
|
|
|
|
message: messageData,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
console.log('pushing back message from', adnMessage.user.username)
|
|
|
|
|
|
|
|
pendingMessages.push(messageData)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// now process any user meta data updates
|
|
|
|
// now process any user meta data updates
|
|
|
|
// - update their conversation with a potentially new avatar
|
|
|
|
// - update their conversation with a potentially new avatar
|
|
|
|
});
|
|
|
|
return messageData;
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
);
|
|
|
|
this.conversation.setLastRetrievedMessage(this.lastGot);
|
|
|
|
this.conversation.setLastRetrievedMessage(this.lastGot);
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
console.log('pendingMessages', pendingMessages.length)
|
|
|
|
|
|
|
|
if (pendingMessages.length) {
|
|
|
|
if (pendingMessages.length) {
|
|
|
|
const slavePrimaryMap = {};
|
|
|
|
this.slavePrimaryMap = {};
|
|
|
|
|
|
|
|
|
|
|
|
console.log('premultiDeviceResults', pubKeys)
|
|
|
|
// console.log('premultiDeviceResults', pubKeys)
|
|
|
|
if (pubKeys.length) {
|
|
|
|
if (pubKeys.length) {
|
|
|
|
const multiDeviceResults = await lokiFileServerAPI.getDeviceMappingForUsers(pubKeys);
|
|
|
|
const multiDeviceResults = await lokiFileServerAPI.getDeviceMappingForUsers(
|
|
|
|
console.log('multiDeviceResults', multiDeviceResults)
|
|
|
|
pubKeys
|
|
|
|
// no user or isPrimary means not multidevice, send event now
|
|
|
|
);
|
|
|
|
|
|
|
|
// console.log('multiDeviceResults', multiDeviceResults);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
multiDeviceResults.forEach(user => {
|
|
|
|
|
|
|
|
if (user.annotations) {
|
|
|
|
|
|
|
|
user.annotations.forEach(note => {
|
|
|
|
|
|
|
|
if (note.type === 'network.loki.messenger.devicemapping') {
|
|
|
|
|
|
|
|
// console.log('devmap note', note);
|
|
|
|
|
|
|
|
// is slave?
|
|
|
|
|
|
|
|
if (note.value.isPrimary === '0') {
|
|
|
|
|
|
|
|
const { authorisations } = note.value;
|
|
|
|
|
|
|
|
if (Array.isArray(authorisations)) {
|
|
|
|
|
|
|
|
authorisations.forEach(auth => {
|
|
|
|
|
|
|
|
// console.log('auth', auth);
|
|
|
|
|
|
|
|
// FIXME: verify secondary sig
|
|
|
|
|
|
|
|
if (1) {
|
|
|
|
|
|
|
|
// add map to slavePrimaryMap
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
|
|
this.slavePrimaryMap[user.username] &&
|
|
|
|
|
|
|
|
this.slavePrimaryMap[user.username] !==
|
|
|
|
|
|
|
|
auth.primaryDevicePubKey
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
log.warn(
|
|
|
|
|
|
|
|
`file server user annotation primaryKey mismatch, had ${
|
|
|
|
|
|
|
|
this.slavePrimaryMap[user.username]
|
|
|
|
|
|
|
|
} now ${auth.primaryDevicePubKey} for ${
|
|
|
|
|
|
|
|
user.username
|
|
|
|
|
|
|
|
}`
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
this.slavePrimaryMap[user.username] =
|
|
|
|
|
|
|
|
auth.primaryDevicePubKey;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
log.info('updated slavePrimaryMap', this.slavePrimaryMap);
|
|
|
|
|
|
|
|
|
|
|
|
const primaryPubKeys = [];
|
|
|
|
const primaryPubKeys = [];
|
|
|
|
|
|
|
|
const slaveMessages = {};
|
|
|
|
// go through multiDeviceResults and get primary Pubkey
|
|
|
|
// go through multiDeviceResults and get primary Pubkey
|
|
|
|
// verify secondary sig
|
|
|
|
pendingMessages.forEach(messageData => {
|
|
|
|
// add map to slavePrimaryMap
|
|
|
|
// why am I getting these?
|
|
|
|
|
|
|
|
if (messageData === undefined) {
|
|
|
|
|
|
|
|
log.warn('invalid pendingMessages');
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.slavePrimaryMap[messageData.source]) {
|
|
|
|
|
|
|
|
const primaryPubKey = this.slavePrimaryMap[messageData.source];
|
|
|
|
|
|
|
|
// add to lookup (if we don't already have it)
|
|
|
|
|
|
|
|
if (primaryPubKeys.indexOf(`@${primaryPubKey}`) === -1) {
|
|
|
|
|
|
|
|
primaryPubKeys.push(`@${primaryPubKey}`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// delay sending the message
|
|
|
|
|
|
|
|
if (slaveMessages[messageData.source] === undefined) {
|
|
|
|
|
|
|
|
slaveMessages[messageData.source] = [messageData];
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
slaveMessages[messageData.source].push(messageData);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// no user or isPrimary means not multidevice, send event now
|
|
|
|
|
|
|
|
this.serverAPI.chatAPI.emit('publicMessage', {
|
|
|
|
|
|
|
|
message: messageData,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
pendingMessages = []; // free memory
|
|
|
|
|
|
|
|
|
|
|
|
const verifiedPrimaryPKs = [];
|
|
|
|
const verifiedPrimaryPKs = [];
|
|
|
|
// get a list of all of primary pubKeys to verify the secondaryDevice
|
|
|
|
// get a list of all of primary pubKeys to verify the secondaryDevice
|
|
|
|
const primaryDeviceResults = await lokiFileServerAPI.getDeviceMappingForUsers(primaryPubKeys);
|
|
|
|
const primaryDeviceResults = await lokiFileServerAPI.getDeviceMappingForUsers(
|
|
|
|
// go through primaryDeviceResults and verify primary sig
|
|
|
|
primaryPubKeys
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
// go through primaryDeviceResults
|
|
|
|
|
|
|
|
primaryDeviceResults.forEach(user => {
|
|
|
|
|
|
|
|
if (user.annotations) {
|
|
|
|
|
|
|
|
user.annotations.forEach(note => {
|
|
|
|
|
|
|
|
if (note.type === 'network.loki.messenger.devicemapping') {
|
|
|
|
|
|
|
|
if (note.value.isPrimary) {
|
|
|
|
|
|
|
|
const { authorisations } = note.value;
|
|
|
|
|
|
|
|
let found = false;
|
|
|
|
|
|
|
|
if (Array.isArray(authorisations)) {
|
|
|
|
|
|
|
|
authorisations.forEach(auth => {
|
|
|
|
|
|
|
|
// FIXME: verify primary sig
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
|
|
verifiedPrimaryPKs.indexOf(
|
|
|
|
|
|
|
|
`@${auth.primaryDevicePubKey}`
|
|
|
|
|
|
|
|
) === -1
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
verifiedPrimaryPKs.push(
|
|
|
|
|
|
|
|
`@${auth.primaryDevicePubKey}`
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
// if not verified remove from slavePrimaryMap
|
|
|
|
// if not verified remove from slavePrimaryMap
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// not primary or verified
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
Object.keys(slavePrimaryMap).forEach(slaveKey => {
|
|
|
|
|
|
|
|
if (slavePrimaryMap[slaveKey] ==
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// get final list of verified chat server profile names
|
|
|
|
// get final list of verified chat server profile names
|
|
|
|
const verifiedDeviceResults = await this.getDeviceMappingForUsers(verifiedPrimaryPKs);
|
|
|
|
const verifiedDeviceResults = await this.serverAPI.getUsersAnnotations(
|
|
|
|
|
|
|
|
pubKeys
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
// console.log('verifiedDeviceResults', verifiedDeviceResults)
|
|
|
|
|
|
|
|
|
|
|
|
// go through verifiedDeviceResults
|
|
|
|
// go through verifiedDeviceResults
|
|
|
|
// find messages for original slave key using slavePrimaryMap
|
|
|
|
this.primaryUserProfileName = {};
|
|
|
|
|
|
|
|
verifiedDeviceResults.forEach(user => {
|
|
|
|
|
|
|
|
this.primaryUserProfileName[user.username] = user.name;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
Object.keys(slaveMessages).forEach(slaveKey => {
|
|
|
|
|
|
|
|
const primaryPubKey = this.slavePrimaryMap[slaveKey];
|
|
|
|
|
|
|
|
slaveMessages[slaveKey].forEach(messageDataP => {
|
|
|
|
|
|
|
|
const messageData = messageDataP; // for linter
|
|
|
|
|
|
|
|
if (this.slavePrimaryMap[messageData.source]) {
|
|
|
|
|
|
|
|
// rewrite source, profile
|
|
|
|
|
|
|
|
messageData.source = primaryPubKey;
|
|
|
|
|
|
|
|
messageData.message.profile.displayName = this.primaryUserProfileName[
|
|
|
|
|
|
|
|
primaryPubKey
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// console.log('messageData', messageData)
|
|
|
|
|
|
|
|
this.serverAPI.chatAPI.emit('publicMessage', {
|
|
|
|
|
|
|
|
message: messageData,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pendingMessages.forEach(messageData => {
|
|
|
|
// console.log('pendingMessages len', pendingMessages.length);
|
|
|
|
if (slavePrimaryMap[messageData.source]) {
|
|
|
|
// console.log('pendingMessages', pendingMessages);
|
|
|
|
|
|
|
|
// find messages for original slave key using slavePrimaryMap
|
|
|
|
|
|
|
|
if (pendingMessages.length) {
|
|
|
|
|
|
|
|
pendingMessages.forEach(messageDataP => {
|
|
|
|
|
|
|
|
const messageData = messageDataP; // for linter
|
|
|
|
|
|
|
|
// why am I getting these?
|
|
|
|
|
|
|
|
if (messageData === undefined) {
|
|
|
|
|
|
|
|
log.warn(`invalid pendingMessages ${pendingMessages}`);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.slavePrimaryMap[messageData.source]) {
|
|
|
|
// rewrite source, profile
|
|
|
|
// rewrite source, profile
|
|
|
|
messageData.source = slavePrimaryMap.username
|
|
|
|
const primaryPubKey = this.slavePrimaryMap[messageData.source];
|
|
|
|
messageData.message.profile.displayName = slavePrimaryMap.name
|
|
|
|
log.info('Rewriting', messageData.source, 'to', primaryPubKey);
|
|
|
|
|
|
|
|
messageData.source = primaryPubKey;
|
|
|
|
|
|
|
|
messageData.message.profile.displayName = this.primaryUserProfileName[
|
|
|
|
|
|
|
|
primaryPubKey
|
|
|
|
|
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.serverAPI.chatAPI.emit('publicMessage', {
|
|
|
|
this.serverAPI.chatAPI.emit('publicMessage', {
|
|
|
|
message: messageData,
|
|
|
|
message: messageData,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
})
|
|
|
|
});
|
|
|
|
pendingMessages = []
|
|
|
|
}
|
|
|
|
|
|
|
|
pendingMessages = [];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|