encrypted pictures for public chats

pull/661/head
sachaaaaa 5 years ago
parent 0122fb8603
commit a29f0a4120

@ -985,6 +985,12 @@
// so we could disable this here // so we could disable this here
// or least it enable for the quickest response // or least it enable for the quickest response
window.lokiPublicChatAPI.setProfileName(newName); window.lokiPublicChatAPI.setProfileName(newName);
window
.getConversations()
.filter(convo => convo.isPublic() && !convo.isRss())
.forEach(convo =>
convo.trigger('ourAvatarChanged', { url, profileKey })
);
}, },
}); });
} }

@ -157,6 +157,15 @@
this.on('expiration-change', this.updateAndMerge); this.on('expiration-change', this.updateAndMerge);
this.on('expired', this.onExpired); this.on('expired', this.onExpired);
this.on('ourAvatarChanged', avatar =>
this.updateAvatarOnPublicChat(avatar)
);
// Always share profile pics with public chats
if (this.isPublic) {
this.set('profileSharing', true);
}
const sealedSender = this.get('sealedSender'); const sealedSender = this.get('sealedSender');
if (sealedSender === undefined) { if (sealedSender === undefined) {
this.set({ sealedSender: SEALED_SENDER.UNKNOWN }); this.set({ sealedSender: SEALED_SENDER.UNKNOWN });
@ -1677,6 +1686,27 @@
); );
}, },
async updateAvatarOnPublicChat({ url, profileKey }) {
if (!this.isPublic()) {
return;
}
if (this.isRss()) {
return;
}
if (!this.get('profileSharing')) {
return;
}
if (profileKey && typeof profileKey !== 'string') {
// eslint-disable-next-line no-param-reassign
profileKey = window.Signal.Crypto.arrayBufferToBase64(profileKey);
}
const serverAPI = await lokiPublicChatAPI.findOrCreateServer(
this.get('server')
);
await serverAPI.setAvatar(url, profileKey);
},
async handleMessageSendResult({ async handleMessageSendResult({
failoverNumbers, failoverNumbers,
unidentifiedDeliveries, unidentifiedDeliveries,

@ -14,6 +14,7 @@ const PUBLICCHAT_MOD_POLL_EVERY = 30 * 1000; // 30s
const PUBLICCHAT_MIN_TIME_BETWEEN_DUPLICATE_MESSAGES = 10 * 1000; // 10s const PUBLICCHAT_MIN_TIME_BETWEEN_DUPLICATE_MESSAGES = 10 * 1000; // 10s
const HOMESERVER_USER_ANNOTATION_TYPE = 'network.loki.messenger.homeserver'; const HOMESERVER_USER_ANNOTATION_TYPE = 'network.loki.messenger.homeserver';
const AVATAR_USER_ANNOTATION_TYPE = 'network.loki.messenger.avatar';
const MESSAGE_ATTACHMENT_TYPE = 'net.app.core.oembed'; const MESSAGE_ATTACHMENT_TYPE = 'net.app.core.oembed';
const LOKI_ATTACHMENT_TYPE = 'attachment'; const LOKI_ATTACHMENT_TYPE = 'attachment';
const LOKI_PREVIEW_TYPE = 'preview'; const LOKI_PREVIEW_TYPE = 'preview';
@ -133,6 +134,17 @@ class LokiAppDotNetAPI extends EventEmitter {
}) })
); );
} }
async setAvatar(url, profileKey) {
await Promise.all(
this.servers.map(async server => {
// this may fail
// but we can't create a sql table to remember to retry forever
// I think we just silently fail for now
await server.setAvatar(url, profileKey);
})
);
}
} }
class LokiAppDotNetServerAPI { class LokiAppDotNetServerAPI {
@ -254,6 +266,14 @@ class LokiAppDotNetServerAPI {
return res.response.data.annotations || []; return res.response.data.annotations || [];
} }
async setAvatar(url, profileKey) {
let value = null;
if (url && profileKey) {
value = { url, profileKey };
}
return this.setSelfAnnotation(AVATAR_USER_ANNOTATION_TYPE, value);
}
// get active token for this server // get active token for this server
async getOrRefreshServerToken(forceRefresh = false) { async getOrRefreshServerToken(forceRefresh = false) {
let token; let token;
@ -938,7 +958,16 @@ class LokiPublicChannelAPI {
} }
// timestamp is the only required field we've had since the first deployed version // timestamp is the only required field we've had since the first deployed version
const { timestamp, quote, avatar } = noteValue; const { timestamp, quote } = noteValue;
let profileKey = null;
let avatar = null;
const avatarNote = adnMessage.user.annotations.find(
note => note.type === AVATAR_USER_ANNOTATION_TYPE
);
if (avatarNote) {
({ profileKey, url: avatar } = avatarNote.value);
}
if (quote) { if (quote) {
// TODO: Enable quote attachments again using proper ADN style // TODO: Enable quote attachments again using proper ADN style
@ -1002,6 +1031,7 @@ class LokiPublicChannelAPI {
preview, preview,
quote, quote,
avatar, avatar,
profileKey,
}; };
} }
@ -1070,12 +1100,21 @@ class LokiPublicChannelAPI {
return false; // Invalid or delete message return false; // Invalid or delete message
} }
const pubKey = adnMessage.user.username;
const messengerData = await this.getMessengerData(adnMessage); const messengerData = await this.getMessengerData(adnMessage);
if (messengerData === false) { if (messengerData === false) {
return false; return false;
} }
const { timestamp, quote, attachments, preview } = messengerData; const {
timestamp,
quote,
attachments,
preview,
avatar,
profileKey,
} = messengerData;
if (!timestamp) { if (!timestamp) {
return false; // Invalid message return false; // Invalid message
} }
@ -1083,7 +1122,7 @@ class LokiPublicChannelAPI {
// Duplicate check // Duplicate check
const isDuplicate = message => { const isDuplicate = message => {
// The username in this case is the users pubKey // The username in this case is the users pubKey
const sameUsername = message.username === adnMessage.user.username; const sameUsername = message.username === pubKey;
const sameText = message.text === adnMessage.text; const sameText = message.text === adnMessage.text;
// Don't filter out messages that are too far apart from each other // Don't filter out messages that are too far apart from each other
const timestampsSimilar = const timestampsSimilar =
@ -1103,7 +1142,7 @@ class LokiPublicChannelAPI {
this.lastMessagesCache = [ this.lastMessagesCache = [
...this.lastMessagesCache, ...this.lastMessagesCache,
{ {
username: adnMessage.user.username, username: pubKey,
text: adnMessage.text, text: adnMessage.text,
timestamp, timestamp,
}, },
@ -1112,7 +1151,7 @@ class LokiPublicChannelAPI {
const from = adnMessage.user.name || 'Anonymous'; // profileName const from = adnMessage.user.name || 'Anonymous'; // profileName
// if us // if us
if (adnMessage.user.username === ourNumber) { if (pubKey === ourNumber) {
// update the last name we saw from ourself // update the last name we saw from ourself
lastProfileName = from; lastProfileName = from;
} }
@ -1133,12 +1172,8 @@ class LokiPublicChannelAPI {
if (homeServerPubKeys[homeServer] === undefined) { if (homeServerPubKeys[homeServer] === undefined) {
homeServerPubKeys[homeServer] = []; homeServerPubKeys[homeServer] = [];
} }
if ( if (homeServerPubKeys[homeServer].indexOf(`@${pubKey}`) === -1) {
homeServerPubKeys[homeServer].indexOf( homeServerPubKeys[homeServer].push(`@${pubKey}`);
`@${adnMessage.user.username}`
) === -1
) {
homeServerPubKeys[homeServer].push(`@${adnMessage.user.username}`);
} }
// generate signal message object // generate signal message object
@ -1146,7 +1181,7 @@ class LokiPublicChannelAPI {
serverId: adnMessage.id, serverId: adnMessage.id,
clientVerified: true, clientVerified: true,
friendRequest: false, friendRequest: false,
source: adnMessage.user.username, source: pubKey,
sourceDevice: 1, sourceDevice: 1,
timestamp, timestamp,
@ -1163,7 +1198,7 @@ class LokiPublicChannelAPI {
}, },
flags: 0, flags: 0,
expireTimer: 0, expireTimer: 0,
profileKey: null, profileKey,
timestamp, timestamp,
received_at: receivedAt, received_at: receivedAt,
sent_at: timestamp, sent_at: timestamp,
@ -1172,6 +1207,7 @@ class LokiPublicChannelAPI {
preview, preview,
profile: { profile: {
displayName: from, displayName: from,
avatar,
}, },
}, },
}; };
@ -1191,8 +1227,6 @@ class LokiPublicChannelAPI {
// slave to primary map for this group of messages // slave to primary map for this group of messages
let slavePrimaryMap = {}; let slavePrimaryMap = {};
// pubKey to avatar
let avatarMap = {};
// reduce list of servers into verified maps and keys // reduce list of servers into verified maps and keys
const verifiedPrimaryPKs = await Object.keys(homeServerPubKeys).reduce( const verifiedPrimaryPKs = await Object.keys(homeServerPubKeys).reduce(
@ -1211,10 +1245,6 @@ class LokiPublicChannelAPI {
// should not be any collisions, since each pubKey can only have one home server // should not be any collisions, since each pubKey can only have one home server
slavePrimaryMap = { ...slavePrimaryMap, ...result.slaveMap }; 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 // copy verified pub keys into result
return curVal.concat(result.verifiedPrimaryPKs); return curVal.concat(result.verifiedPrimaryPKs);
}, },
@ -1228,13 +1258,6 @@ class LokiPublicChannelAPI {
.reduce((retval, messageData) => { .reduce((retval, messageData) => {
// if a known slave // if a known slave
if (slavePrimaryMap[messageData.source]) { if (slavePrimaryMap[messageData.source]) {
// pop primary device avatars in
if (avatarMap[slavePrimaryMap[messageData.source]]) {
// modify messageData for user's avatar
messageData.message.profile.avatar =
avatarMap[slavePrimaryMap[messageData.source]];
}
// delay sending the message // delay sending the message
if (retval[messageData.source] === undefined) { if (retval[messageData.source] === undefined) {
retval[messageData.source] = [messageData]; retval[messageData.source] = [messageData];
@ -1243,13 +1266,6 @@ class LokiPublicChannelAPI {
} }
} else { } else {
// not from a paired/slave/unregistered device // not from a paired/slave/unregistered device
// pop current device avatars in
if (avatarMap[messageData.source]) {
// modify messageData for user's avatar
messageData.message.profile.avatar = avatarMap[messageData.source];
}
// send event now // send event now
this.serverAPI.chatAPI.emit('publicMessage', { this.serverAPI.chatAPI.emit('publicMessage', {
message: messageData, message: messageData,
@ -1270,7 +1286,19 @@ class LokiPublicChannelAPI {
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
this.primaryUserProfileName = verifiedDeviceResults.reduce( this.primaryUserProfileName = verifiedDeviceResults.reduce(
(mapOut, user) => { (mapOut, user) => {
mapOut[user.username] = user.name; let avatar = null;
let profileKey = null;
const avatarNote = user.annotations.find(
note => note.type === AVATAR_USER_ANNOTATION_TYPE
);
if (avatarNote) {
({ profileKey, url: avatar } = avatarNote.value);
}
mapOut[user.username] = {
name: user.name,
avatar,
profileKey,
};
return mapOut; return mapOut;
}, },
{} {}
@ -1294,9 +1322,12 @@ class LokiPublicChannelAPI {
if (slavePrimaryMap[messageData.source]) { if (slavePrimaryMap[messageData.source]) {
// rewrite source, profile // rewrite source, profile
messageData.source = primaryPubKey; messageData.source = primaryPubKey;
messageData.message.profile.displayName = this.primaryUserProfileName[ const { name, avatar, profileKey } = this.primaryUserProfileName[
primaryPubKey primaryPubKey
]; ];
messageData.message.profile.displayName = name;
messageData.message.profile.avatar = avatar;
messageData.message.profileKey = profileKey;
} }
this.serverAPI.chatAPI.emit('publicMessage', { this.serverAPI.chatAPI.emit('publicMessage', {
message: messageData, message: messageData,
@ -1408,8 +1439,6 @@ class LokiPublicChannelAPI {
LokiPublicChannelAPI.getAnnotationFromPreview LokiPublicChannelAPI.getAnnotationFromPreview
); );
const avatarAnnotation = data.profile.avatar || null;
const payload = { const payload = {
text, text,
annotations: [ annotations: [
@ -1417,8 +1446,6 @@ class LokiPublicChannelAPI {
type: 'network.loki.messenger.publicChat', type: 'network.loki.messenger.publicChat',
value: { value: {
timestamp: messageTimeStamp, timestamp: messageTimeStamp,
// can remove after this release
avatar: avatarAnnotation,
}, },
}, },
...attachmentAnnotations, ...attachmentAnnotations,

@ -17,6 +17,10 @@ class LokiFileServerInstance {
this._adnApi = new LokiAppDotNetAPI(ourKey); this._adnApi = new LokiAppDotNetAPI(ourKey);
this.avatarMap = {}; this.avatarMap = {};
} }
// FIXME: this is not file-server specific
// and is currently called by LokiAppDotNetAPI.
// LokiAppDotNetAPI (base) should not know about LokiFileServer.
async establishConnection(serverUrl) { async establishConnection(serverUrl) {
// FIXME: we don't always need a token... // FIXME: we don't always need a token...
this._server = await this._adnApi.findOrCreateServer(serverUrl); this._server = await this._adnApi.findOrCreateServer(serverUrl);

@ -172,7 +172,11 @@ MessageReceiver.prototype.extend({
message.source, message.source,
'private' 'private'
); );
await this.updateProfile(conversation, message.message.profile); await this.updateProfile(
conversation,
message.message.profile,
message.message.profileKey
);
} }
const ev = new Event('message'); const ev = new Event('message');
@ -1295,7 +1299,7 @@ MessageReceiver.prototype.extend({
} catch (e) { } catch (e) {
window.log.error(`Could not decrypt profile image: ${e}`); window.log.error(`Could not decrypt profile image: ${e}`);
} }
} }
newProfile.avatar = path; newProfile.avatar = path;
} }
} else { } else {

Loading…
Cancel
Save