Merge pull request #2488 from Bilb/message-request-include-profile

Profile in messageRequest
pull/2524/head
Audric Ackermann 3 years ago committed by GitHub
commit 3788f5a4d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -37,7 +37,9 @@ message Unsend {
message MessageRequestResponse { message MessageRequestResponse {
// @required // @required
required bool isApproved = 1; required bool isApproved = 1;
optional bytes profileKey = 2;
optional DataMessage.LokiProfile profile = 3;
} }
message Content { message Content {

@ -926,7 +926,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
const messageRequestResponseParams: MessageRequestResponseParams = { const messageRequestResponseParams: MessageRequestResponseParams = {
timestamp, timestamp,
// lokiProfile: UserUtils.getOurProfile(), // we can't curently include our profile in that response lokiProfile: UserUtils.getOurProfile(),
}; };
const messageRequestResponse = new MessageRequestResponse(messageRequestResponseParams); const messageRequestResponse = new MessageRequestResponse(messageRequestResponseParams);

@ -27,6 +27,7 @@ import {
} from '../interactions/conversations/unsendingInteractions'; } from '../interactions/conversations/unsendingInteractions';
import { ConversationTypeEnum } from '../models/conversationAttributes'; import { ConversationTypeEnum } from '../models/conversationAttributes';
import { findCachedBlindedMatchOrLookupOnAllServers } from '../session/apis/open_group_api/sogsv3/knownBlindedkeys'; import { findCachedBlindedMatchOrLookupOnAllServers } from '../session/apis/open_group_api/sogsv3/knownBlindedkeys';
import { appendFetchAvatarAndProfileJob } from './userProfileImageUpdates';
export async function handleSwarmContentMessage(envelope: EnvelopePlus, messageHash: string) { export async function handleSwarmContentMessage(envelope: EnvelopePlus, messageHash: string) {
try { try {
@ -605,6 +606,11 @@ async function handleMessageRequestResponse(
messageRequestResponse: SignalService.MessageRequestResponse messageRequestResponse: SignalService.MessageRequestResponse
) { ) {
const { isApproved } = messageRequestResponse; const { isApproved } = messageRequestResponse;
if (!isApproved) {
window?.log?.error('handleMessageRequestResponse: isApproved is false -- dropping message.');
await removeFromCache(envelope);
return;
}
if (!messageRequestResponse) { if (!messageRequestResponse) {
window?.log?.error('handleMessageRequestResponse: Invalid parameters -- dropping message.'); window?.log?.error('handleMessageRequestResponse: Invalid parameters -- dropping message.');
await removeFromCache(envelope); await removeFromCache(envelope);
@ -675,6 +681,14 @@ async function handleMessageRequestResponse(
} }
} }
if (messageRequestResponse.profile && !isEmpty(messageRequestResponse.profile)) {
void appendFetchAvatarAndProfileJob(
conversationToApprove,
messageRequestResponse.profile,
messageRequestResponse.profileKey
);
}
if (!conversationToApprove || conversationToApprove.didApproveMe() === isApproved) { if (!conversationToApprove || conversationToApprove.didApproveMe() === isApproved) {
if (conversationToApprove) { if (conversationToApprove) {
await conversationToApprove.commit(); await conversationToApprove.commit();

@ -1,18 +1,28 @@
import { SignalService } from '../../../../protobuf'; import { SignalService } from '../../../../protobuf';
import { LokiProfile } from '../../../../types/Message';
import { ContentMessage } from '../ContentMessage'; import { ContentMessage } from '../ContentMessage';
import { MessageParams } from '../Message'; import { MessageParams } from '../Message';
import { buildProfileForOutgoingMessage } from '../visibleMessage/VisibleMessage';
// tslint:disable-next-line: no-empty-interface // tslint:disable-next-line: no-empty-interface
export interface MessageRequestResponseParams extends MessageParams {} export interface MessageRequestResponseParams extends MessageParams {
lokiProfile?: LokiProfile;
}
export class MessageRequestResponse extends ContentMessage { export class MessageRequestResponse extends ContentMessage {
// we actually send a response only if it is an accept // we actually send a response only if it is an accept
// private readonly isApproved: boolean; // private readonly isApproved: boolean;
private readonly profileKey?: Uint8Array;
private readonly profile?: SignalService.DataMessage.ILokiProfile;
constructor(params: MessageRequestResponseParams) { constructor(params: MessageRequestResponseParams) {
super({ super({
timestamp: params.timestamp, timestamp: params.timestamp,
} as MessageRequestResponseParams); } as MessageRequestResponseParams);
const profile = buildProfileForOutgoingMessage(params);
this.profile = profile.lokiProfile;
this.profileKey = profile.profileKey;
} }
public contentProto(): SignalService.Content { public contentProto(): SignalService.Content {
@ -24,6 +34,8 @@ export class MessageRequestResponse extends ContentMessage {
public messageRequestResponseProto(): SignalService.MessageRequestResponse { public messageRequestResponseProto(): SignalService.MessageRequestResponse {
return new SignalService.MessageRequestResponse({ return new SignalService.MessageRequestResponse({
isApproved: true, isApproved: true,
profileKey: this.profileKey?.length ? this.profileKey : undefined,
profile: this.profile,
}); });
} }
} }

@ -1,4 +1,5 @@
import ByteBuffer from 'bytebuffer'; import ByteBuffer from 'bytebuffer';
import { isEmpty } from 'lodash';
import { DataMessage } from '..'; import { DataMessage } from '..';
import { SignalService } from '../../../../protobuf'; import { SignalService } from '../../../../protobuf';
import { LokiProfile } from '../../../../types/Message'; import { LokiProfile } from '../../../../types/Message';
@ -80,8 +81,7 @@ export class VisibleMessage extends DataMessage {
private readonly body?: string; private readonly body?: string;
private readonly quote?: Quote; private readonly quote?: Quote;
private readonly profileKey?: Uint8Array; private readonly profileKey?: Uint8Array;
private readonly displayName?: string; private readonly profile?: SignalService.DataMessage.ILokiProfile;
private readonly avatarPointer?: string;
private readonly preview?: Array<PreviewWithAttachmentUrl>; private readonly preview?: Array<PreviewWithAttachmentUrl>;
/// In the case of a sync message, the public key of the person the message was targeted at. /// In the case of a sync message, the public key of the person the message was targeted at.
@ -94,21 +94,12 @@ export class VisibleMessage extends DataMessage {
this.body = params.body; this.body = params.body;
this.quote = params.quote; this.quote = params.quote;
this.expireTimer = params.expireTimer; this.expireTimer = params.expireTimer;
if (params.lokiProfile && params.lokiProfile.profileKey) {
if (
params.lokiProfile.profileKey instanceof Uint8Array ||
(params.lokiProfile.profileKey as any) instanceof ByteBuffer
) {
this.profileKey = new Uint8Array(params.lokiProfile.profileKey);
} else {
this.profileKey = new Uint8Array(
ByteBuffer.wrap(params.lokiProfile.profileKey).toArrayBuffer()
);
}
}
this.displayName = params.lokiProfile && params.lokiProfile.displayName; const profile = buildProfileForOutgoingMessage(params);
this.avatarPointer = params.lokiProfile && params.lokiProfile.avatarPointer;
this.profile = profile.lokiProfile;
this.profileKey = profile.profileKey;
this.preview = params.preview; this.preview = params.preview;
this.reaction = params.reaction; this.reaction = params.reaction;
this.syncTarget = params.syncTarget; this.syncTarget = params.syncTarget;
@ -137,18 +128,10 @@ export class VisibleMessage extends DataMessage {
dataMessage.syncTarget = this.syncTarget; dataMessage.syncTarget = this.syncTarget;
} }
if (this.avatarPointer || this.displayName) { if (this.profile) {
const profile = new SignalService.DataMessage.LokiProfile(); dataMessage.profile = this.profile;
if (this.avatarPointer) {
profile.profilePicture = this.avatarPointer;
}
if (this.displayName) {
profile.displayName = this.displayName;
}
dataMessage.profile = profile;
} }
if (this.profileKey && this.profileKey.length) { if (this.profileKey && this.profileKey.length) {
dataMessage.profileKey = this.profileKey; dataMessage.profileKey = this.profileKey;
} }
@ -201,3 +184,47 @@ export class VisibleMessage extends DataMessage {
return this.identifier === comparator.identifier && this.timestamp === comparator.timestamp; return this.identifier === comparator.identifier && this.timestamp === comparator.timestamp;
} }
} }
export function buildProfileForOutgoingMessage(params: { lokiProfile?: LokiProfile }) {
let profileKey: Uint8Array | undefined;
if (params.lokiProfile && params.lokiProfile.profileKey) {
if (
params.lokiProfile.profileKey instanceof Uint8Array ||
(params.lokiProfile.profileKey as any) instanceof ByteBuffer
) {
profileKey = new Uint8Array(params.lokiProfile.profileKey);
} else {
profileKey = new Uint8Array(ByteBuffer.wrap(params.lokiProfile.profileKey).toArrayBuffer());
}
}
const displayName = params.lokiProfile?.displayName;
// no need to iclude the avatarPointer if there is no profileKey associated with it.
const avatarPointer =
params.lokiProfile?.avatarPointer &&
!isEmpty(profileKey) &&
params.lokiProfile.avatarPointer &&
!isEmpty(params.lokiProfile.avatarPointer)
? params.lokiProfile.avatarPointer
: undefined;
let lokiProfile: SignalService.DataMessage.ILokiProfile | undefined;
if (avatarPointer || displayName) {
lokiProfile = new SignalService.DataMessage.LokiProfile();
// we always need a profileKey tom decode an avatar pointer
if (avatarPointer && avatarPointer.length && profileKey) {
lokiProfile.profilePicture = avatarPointer;
}
if (displayName) {
lokiProfile.displayName = displayName;
}
}
return {
lokiProfile,
profileKey: lokiProfile?.profilePicture ? profileKey : undefined,
};
}

@ -0,0 +1,160 @@
import { expect } from 'chai';
import { v4 } from 'uuid';
import { SignalService } from '../../../../protobuf';
import { Constants } from '../../../../session';
import { MessageRequestResponse } from '../../../../session/messages/outgoing/controlMessage/MessageRequestResponse';
// tslint:disable: no-unused-expression
// tslint:disable-next-line: max-func-body-length
describe('MessageRequestResponse', () => {
let message: MessageRequestResponse | undefined;
it('correct ttl', () => {
message = new MessageRequestResponse({
timestamp: Date.now(),
});
expect(message.ttl()).to.equal(Constants.TTL_DEFAULT.TTL_MAX);
});
it('has an identifier', () => {
message = new MessageRequestResponse({
timestamp: Date.now(),
});
expect(message.identifier).to.not.equal(null, 'identifier cannot be null');
expect(message.identifier).to.not.equal(undefined, 'identifier cannot be undefined');
});
it('has an identifier matching if given', () => {
const identifier = v4();
message = new MessageRequestResponse({
timestamp: Date.now(),
identifier,
});
expect(message.identifier).to.not.equal(identifier, 'identifier should match');
});
it('isApproved is always true', () => {
message = new MessageRequestResponse({
timestamp: Date.now(),
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
expect(decoded.messageRequestResponse)
.to.have.property('isApproved')
.to.be.eq(true, 'isApproved is true');
});
it('can create response without lokiProfile', () => {
message = new MessageRequestResponse({
timestamp: Date.now(),
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
expect(decoded.messageRequestResponse)
.to.have.property('profile')
.to.be.eq(null, 'no profile field if no profile given');
});
it('can create response with display name only', () => {
message = new MessageRequestResponse({
timestamp: Date.now(),
lokiProfile: { displayName: 'Jane', profileKey: null },
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
expect(decoded.messageRequestResponse?.profile?.displayName).to.be.deep.eq('Jane');
expect(decoded.messageRequestResponse?.profile?.profilePicture).to.be.empty;
expect(decoded.messageRequestResponse?.profileKey).to.be.empty;
});
it('empty profileKey does not get included', () => {
message = new MessageRequestResponse({
timestamp: Date.now(),
lokiProfile: { displayName: 'Jane', profileKey: new Uint8Array(0) },
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
expect(decoded.messageRequestResponse?.profile?.displayName).to.be.eq('Jane');
expect(decoded.messageRequestResponse?.profile?.profilePicture).to.be.empty;
expect(decoded.messageRequestResponse?.profileKey).to.be.empty;
});
it('can create response with display name and profileKey and profileImage', () => {
message = new MessageRequestResponse({
timestamp: Date.now(),
lokiProfile: {
displayName: 'Jane',
profileKey: new Uint8Array([1, 2, 3, 4, 5, 6]),
avatarPointer: 'https://somevalidurl.com',
},
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
expect(decoded.messageRequestResponse?.profile?.displayName).to.be.deep.eq('Jane');
expect(decoded.messageRequestResponse?.profileKey).to.be.not.empty;
if (!decoded.messageRequestResponse?.profileKey?.buffer) {
throw new Error('decoded.messageRequestResponse?.profileKey?.buffer should be set');
}
expect(decoded.messageRequestResponse?.profile?.profilePicture).to.be.eq(
'https://somevalidurl.com'
);
// don't ask me why deep.eq ([1,2,3, ...]) gives nothing interesting but a 8192 buffer not matching
expect(decoded.messageRequestResponse?.profileKey.length).to.be.eq(6);
expect(decoded.messageRequestResponse?.profileKey[0]).to.be.eq(1);
expect(decoded.messageRequestResponse?.profileKey[1]).to.be.eq(2);
expect(decoded.messageRequestResponse?.profileKey[2]).to.be.eq(3);
expect(decoded.messageRequestResponse?.profileKey[3]).to.be.eq(4);
expect(decoded.messageRequestResponse?.profileKey[4]).to.be.eq(5);
expect(decoded.messageRequestResponse?.profileKey[5]).to.be.eq(6);
});
it('profileKey not included if profileUrl not set', () => {
message = new MessageRequestResponse({
timestamp: Date.now(),
lokiProfile: { displayName: 'Jane', profileKey: new Uint8Array([1, 2, 3, 4, 5, 6]) },
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
expect(decoded.messageRequestResponse?.profile?.displayName).to.be.deep.eq('Jane');
if (!decoded.messageRequestResponse?.profileKey?.buffer) {
throw new Error('decoded.messageRequestResponse?.profileKey?.buffer should be set');
}
expect(decoded.messageRequestResponse?.profile?.profilePicture).to.be.empty;
expect(decoded.messageRequestResponse?.profileKey).to.be.empty;
});
it('url not included if profileKey not set', () => {
message = new MessageRequestResponse({
timestamp: Date.now(),
lokiProfile: {
displayName: 'Jane',
profileKey: null,
avatarPointer: 'https://somevalidurl.com',
},
});
const plainText = message.plainTextBuffer();
const decoded = SignalService.Content.decode(plainText);
expect(decoded.messageRequestResponse?.profile?.displayName).to.be.deep.eq('Jane');
if (!decoded.messageRequestResponse?.profileKey?.buffer) {
throw new Error('decoded.messageRequestResponse?.profileKey?.buffer should be set');
}
expect(decoded.messageRequestResponse?.profile?.displayName).to.be.eq('Jane');
expect(decoded.messageRequestResponse?.profile?.profilePicture).to.be.empty;
expect(decoded.messageRequestResponse?.profileKey).to.be.empty;
});
});
Loading…
Cancel
Save