@ -1,5 +1,5 @@
/ * g l o b a l l o g , t e x t s e c u r e , l i b l o k i , S i g n a l , W h i s p e r , H e a d e r s , C o n v e r s a t i o n C o n t r o l l e r ,
clearTimeout , MessageController , libsignal , StringView , window , _ , lokiFileServerAPI ,
clearTimeout , MessageController , libsignal , StringView , window , _ ,
dcodeIO , Buffer * /
const EventEmitter = require ( 'events' ) ;
const nodeFetch = require ( 'node-fetch' ) ;
@ -13,7 +13,8 @@ const PUBLICCHAT_DELETION_POLL_EVERY = 5 * 1000; // 5s
const PUBLICCHAT _MOD _POLL _EVERY = 30 * 1000 ; // 30s
const PUBLICCHAT _MIN _TIME _BETWEEN _DUPLICATE _MESSAGES = 10 * 1000 ; // 10s
const ATTACHMENT _TYPE = 'net.app.core.oembed' ;
const HOMESERVER _USER _ANNOTATION _TYPE = 'network.loki.messenger.homeserver' ;
const MESSAGE _ATTACHMENT _TYPE = 'net.app.core.oembed' ;
const LOKI _ATTACHMENT _TYPE = 'attachment' ;
const LOKI _PREVIEW _TYPE = 'preview' ;
@ -26,7 +27,6 @@ class LokiAppDotNetAPI extends EventEmitter {
this . myPrivateKey = false ;
this . allMembers = [ ] ;
// Multidevice states
this . slavePrimaryMap = { } ;
this . primaryUserProfileName = { } ;
}
@ -843,6 +843,7 @@ class LokiPublicChannelAPI {
async pollOnceForMessages ( ) {
const params = {
include _annotations : 1 ,
include _user _annotations : 1 , // to get the home server
include _deleted : false ,
} ;
if ( ! this . conversation ) {
@ -863,7 +864,7 @@ class LokiPublicChannelAPI {
}
let receivedAt = new Date ( ) . getTime ( ) ;
const pubKeys = [ ] ;
const homeServerPubKeys = { } ;
let pendingMessages = [ ] ;
// the signature forces this to be async
@ -926,8 +927,27 @@ class LokiPublicChannelAPI {
const from = adnMessage . user . name || 'Anonymous' ; // profileName
// track sources for multidevice support
if ( pubKeys . indexOf ( ` @ ${ adnMessage . user . username } ` ) === - 1 ) {
pubKeys . push ( ` @ ${ adnMessage . user . username } ` ) ;
// sort it by home server
let homeServer = window . getDefaultFileServer ( ) ;
if ( adnMessage . user && adnMessage . user . annotations . length ) {
const homeNotes = adnMessage . user . annotations . filter (
note => note . type === HOMESERVER _USER _ANNOTATION _TYPE
) ;
// FIXME: this annotation should probably be signed and verified...
homeServer = homeNotes . reduce (
( curVal , note ) => ( note . value ? note . value : curVal ) ,
homeServer
) ;
}
if ( homeServerPubKeys [ homeServer ] === undefined ) {
homeServerPubKeys [ homeServer ] = [ ] ;
}
if (
homeServerPubKeys [ homeServer ] . indexOf (
` @ ${ adnMessage . user . username } `
) === - 1
) {
homeServerPubKeys [ homeServer ] . push ( ` @ ${ adnMessage . user . username } ` ) ;
}
// generate signal message object
@ -978,20 +998,50 @@ class LokiPublicChannelAPI {
return ;
}
// get list of verified primary PKs
const verifiedPrimaryPKs = await lokiFileServerAPI . verifyPrimaryPubKeys (
pubKeys
// slave to primary map for this group of messages
let slavePrimaryMap = { } ;
// pubKey to avatar
let avatarMap = { } ;
// reduce list of servers into verified maps and keys
const verifiedPrimaryPKs = await Object . keys ( homeServerPubKeys ) . reduce (
async ( curVal , serverUrl ) => {
// get an API to this server
const serverAPI = await window . lokiFileServerAPIFactory . establishConnection (
serverUrl
) ;
// get list of verified primary PKs
const result = await serverAPI . verifyPrimaryPubKeys (
homeServerPubKeys [ serverUrl ]
) ;
// merged these device mappings into our slavePrimaryMap
// should not be any collisions, since each pubKey can only have one home server
slavePrimaryMap = { ... slavePrimaryMap , ... result . slaveMap } ;
// merge this servers avatarMap into our map
// again shouldn't be any collisions
avatarMap = { ... avatarMap , ... serverAPI . avatarMap } ;
// copy verified pub keys into result
return curVal . concat ( result . verifiedPrimaryPKs ) ;
} ,
[ ]
) ;
// access slavePrimaryMap set by verifyPrimaryPubKeys
const { slavePrimaryMap } = this . serverAPI . chatAPI ;
// sort pending messages by if slave device or not
/* eslint-disable no-param-reassign */
const slaveMessages = pendingMessages
. filter ( messageData => ! ! messageData )
. filter ( messageData => ! ! messageData ) // filter out false messages
. reduce ( ( retval , messageData ) => {
// if a known slave, queue
// if a known slave
if ( slavePrimaryMap [ messageData . source ] ) {
// pop primary device avatars in
if ( avatarMap [ slavePrimaryMap [ messageData . source ] ] ) {
// modify messageData for user's avatar?
}
// delay sending the message
if ( retval [ messageData . source ] === undefined ) {
retval [ messageData . source ] = [ messageData ] ;
@ -999,7 +1049,14 @@ class LokiPublicChannelAPI {
retval [ messageData . source ] . push ( messageData ) ;
}
} else {
// no user or isPrimary means not multidevice, send event now
// not from a paired/slave/unregistered device
// pop current device avatars in
if ( avatarMap [ messageData . source ] ) {
// modify messageData for user's avatar?
}
// send event now
this . serverAPI . chatAPI . emit ( 'publicMessage' , {
message : messageData ,
} ) ;
@ -1080,7 +1137,7 @@ class LokiPublicChannelAPI {
static getAnnotationFromPreview ( preview ) {
const annotation = {
type : ATTACHMENT_TYPE ,
type : MESSAGE_ ATTACHMENT_TYPE ,
value : {
// Mandatory ADN fields
version : '1.0' ,
@ -1118,7 +1175,7 @@ class LokiPublicChannelAPI {
type = 'other' ;
}
const annotation = {
type : ATTACHMENT_TYPE ,
type : MESSAGE_ ATTACHMENT_TYPE ,
value : {
// Mandatory ADN fields
version : '1.0' ,