2020-10-30 20:34:04 +00:00
|
|
|
// Copyright 2020 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2020-09-24 21:53:21 +00:00
|
|
|
/* eslint-disable max-classes-per-file */
|
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
import { Reader } from 'protobufjs';
|
2020-04-13 17:37:29 +00:00
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
import { SignalService as Proto } from '../protobuf';
|
|
|
|
import { normalizeUuid } from '../util/normalizeUuid';
|
|
|
|
import { typedArrayToArrayBuffer } from '../Crypto';
|
|
|
|
|
|
|
|
import Avatar = Proto.ContactDetails.IAvatar;
|
2020-04-13 17:37:29 +00:00
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
type OptionalAvatar = { avatar?: Avatar | null };
|
|
|
|
|
|
|
|
type DecoderBase<Message extends OptionalAvatar> = {
|
|
|
|
decodeDelimited(reader: Reader): Message | undefined;
|
2020-04-13 17:37:29 +00:00
|
|
|
};
|
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
export type MessageWithAvatar<Message extends OptionalAvatar> = Omit<
|
|
|
|
Message,
|
|
|
|
'avatar'
|
|
|
|
> & {
|
|
|
|
avatar?: (Avatar & { data: ArrayBuffer }) | null;
|
2020-04-13 17:37:29 +00:00
|
|
|
};
|
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
export type ModifiedGroupDetails = MessageWithAvatar<Proto.GroupDetails>;
|
|
|
|
|
|
|
|
export type ModifiedContactDetails = MessageWithAvatar<Proto.ContactDetails>;
|
2020-09-24 21:53:21 +00:00
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
// TODO: remove once we move away from ArrayBuffers
|
|
|
|
const FIXMEU8 = Uint8Array;
|
2020-04-13 17:37:29 +00:00
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
class ParserBase<
|
|
|
|
Message extends OptionalAvatar,
|
|
|
|
Decoder extends DecoderBase<Message>
|
|
|
|
> {
|
|
|
|
protected readonly reader: Reader;
|
|
|
|
|
|
|
|
constructor(arrayBuffer: ArrayBuffer, private readonly decoder: Decoder) {
|
|
|
|
this.reader = new Reader(new FIXMEU8(arrayBuffer));
|
2020-04-13 17:37:29 +00:00
|
|
|
}
|
2018-07-21 21:51:20 +00:00
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
protected decodeDelimited(): MessageWithAvatar<Message> | undefined {
|
|
|
|
if (this.reader.pos === this.reader.len) {
|
|
|
|
return undefined; // eof
|
|
|
|
}
|
|
|
|
|
2018-05-02 01:54:43 +00:00
|
|
|
try {
|
2021-07-09 19:36:10 +00:00
|
|
|
const proto = this.decoder.decodeDelimited(this.reader);
|
2015-06-01 21:08:21 +00:00
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
if (!proto) {
|
|
|
|
return undefined;
|
2018-05-02 01:54:43 +00:00
|
|
|
}
|
2017-09-11 16:50:35 +00:00
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
if (!proto.avatar) {
|
|
|
|
return {
|
|
|
|
...proto,
|
|
|
|
avatar: null,
|
|
|
|
};
|
2020-03-05 21:14:58 +00:00
|
|
|
}
|
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
const attachmentLen = proto.avatar.length ?? 0;
|
|
|
|
const avatarData = this.reader.buf.slice(
|
|
|
|
this.reader.pos,
|
|
|
|
this.reader.pos + attachmentLen
|
|
|
|
);
|
|
|
|
this.reader.skip(attachmentLen);
|
2020-03-05 21:14:58 +00:00
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
return {
|
|
|
|
...proto,
|
|
|
|
|
|
|
|
avatar: {
|
|
|
|
...proto.avatar,
|
|
|
|
|
|
|
|
data: typedArrayToArrayBuffer(avatarData),
|
|
|
|
},
|
|
|
|
};
|
2018-07-21 19:00:08 +00:00
|
|
|
} catch (error) {
|
|
|
|
window.log.error(
|
|
|
|
'ProtoParser.next error:',
|
|
|
|
error && error.stack ? error.stack : error
|
|
|
|
);
|
2021-07-09 19:36:10 +00:00
|
|
|
return undefined;
|
2015-06-01 21:08:21 +00:00
|
|
|
}
|
2020-04-13 17:37:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
export class GroupBuffer extends ParserBase<
|
|
|
|
Proto.GroupDetails,
|
|
|
|
typeof Proto.GroupDetails
|
|
|
|
> {
|
2020-04-13 17:37:29 +00:00
|
|
|
constructor(arrayBuffer: ArrayBuffer) {
|
2021-07-09 19:36:10 +00:00
|
|
|
super(arrayBuffer, Proto.GroupDetails);
|
|
|
|
}
|
|
|
|
|
|
|
|
public next(): ModifiedGroupDetails | undefined {
|
|
|
|
const proto = this.decodeDelimited();
|
|
|
|
if (!proto) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!proto.members) {
|
|
|
|
return proto;
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...proto,
|
|
|
|
members: proto.members.map((member, i) => {
|
|
|
|
if (!member.uuid) {
|
|
|
|
return member;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
...member,
|
|
|
|
uuid: normalizeUuid(member.uuid, `GroupBuffer.member[${i}].uuid`),
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
};
|
2020-04-13 17:37:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-09 19:36:10 +00:00
|
|
|
export class ContactBuffer extends ParserBase<
|
|
|
|
Proto.ContactDetails,
|
|
|
|
typeof Proto.ContactDetails
|
|
|
|
> {
|
2020-04-13 17:37:29 +00:00
|
|
|
constructor(arrayBuffer: ArrayBuffer) {
|
2021-07-09 19:36:10 +00:00
|
|
|
super(arrayBuffer, Proto.ContactDetails);
|
|
|
|
}
|
|
|
|
|
|
|
|
public next(): ModifiedContactDetails | undefined {
|
|
|
|
const proto = this.decodeDelimited();
|
|
|
|
if (!proto) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!proto.uuid) {
|
|
|
|
return proto;
|
|
|
|
}
|
|
|
|
|
|
|
|
const { verified } = proto;
|
|
|
|
|
|
|
|
return {
|
|
|
|
...proto,
|
|
|
|
|
|
|
|
verified:
|
|
|
|
verified && verified.destinationUuid
|
|
|
|
? {
|
|
|
|
...verified,
|
|
|
|
|
|
|
|
destinationUuid: normalizeUuid(
|
|
|
|
verified.destinationUuid,
|
|
|
|
'ContactBuffer.verified.destinationUuid'
|
|
|
|
),
|
|
|
|
}
|
|
|
|
: verified,
|
|
|
|
|
|
|
|
uuid: normalizeUuid(proto.uuid, 'ContactBuffer.uuid'),
|
|
|
|
};
|
2020-04-13 17:37:29 +00:00
|
|
|
}
|
|
|
|
}
|