@wireapp/core 17.24.0 → 17.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/package.json +2 -2
- package/src/main/broadcast/BroadcastService.js +3 -4
- package/src/main/broadcast/BroadcastService.js.map +1 -1
- package/src/main/broadcast/BroadcastService.ts +3 -5
- package/src/main/conversation/ConversationService.d.ts +16 -9
- package/src/main/conversation/ConversationService.js +52 -65
- package/src/main/conversation/ConversationService.js.map +1 -1
- package/src/main/conversation/ConversationService.test.node.js +17 -38
- package/src/main/conversation/ConversationService.test.node.js.map +1 -1
- package/src/main/conversation/ConversationService.test.node.ts +20 -50
- package/src/main/conversation/ConversationService.ts +72 -124
- package/src/main/conversation/message/MessageService.d.ts +43 -13
- package/src/main/conversation/message/MessageService.js +150 -281
- package/src/main/conversation/message/MessageService.js.map +1 -1
- package/src/main/conversation/message/MessageService.test.node.js +178 -6
- package/src/main/conversation/message/MessageService.test.node.js.map +1 -1
- package/src/main/conversation/message/MessageService.test.node.ts +245 -29
- package/src/main/conversation/message/MessageService.ts +211 -364
- package/src/main/conversation/message/UserClientsUtil.d.ts +22 -0
- package/src/main/conversation/message/UserClientsUtil.js +38 -0
- package/src/main/conversation/message/UserClientsUtil.js.map +1 -0
- package/src/main/conversation/message/UserClientsUtil.ts +44 -0
- package/src/main/conversation/message/UserClientsUtils.test.node.d.ts +1 -0
- package/src/main/conversation/message/UserClientsUtils.test.node.js +42 -0
- package/src/main/conversation/message/UserClientsUtils.test.node.js.map +1 -0
- package/src/main/conversation/message/UserClientsUtils.test.node.ts +44 -0
- package/src/main/cryptography/CryptographyService.js +8 -0
- package/src/main/cryptography/CryptographyService.js.map +1 -1
- package/src/main/cryptography/CryptographyService.test.node.js +2 -2
- package/src/main/cryptography/CryptographyService.test.node.js.map +1 -1
- package/src/main/cryptography/CryptographyService.test.node.ts +5 -4
- package/src/main/cryptography/CryptographyService.ts +10 -2
- package/src/main/util/TypePredicateUtil.js +3 -9
- package/src/main/util/TypePredicateUtil.js.map +1 -1
- package/src/main/util/TypePredicateUtil.ts +3 -9
|
@@ -19,8 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
import {APIClient} from '@wireapp/api-client';
|
|
21
21
|
import {ClientType} from '@wireapp/api-client/src/client';
|
|
22
|
-
import {
|
|
23
|
-
import {GenericMessage, LegalHoldStatus, Text} from '@wireapp/protocol-messaging';
|
|
22
|
+
import {LegalHoldStatus} from '@wireapp/protocol-messaging';
|
|
24
23
|
import {MemoryEngine} from '@wireapp/store-engine';
|
|
25
24
|
import {PayloadBundleSource, PayloadBundleState, PayloadBundleType} from '.';
|
|
26
25
|
|
|
@@ -29,31 +28,6 @@ import * as PayloadHelper from '../test/PayloadHelper';
|
|
|
29
28
|
import {MentionContent, QuoteContent} from './content';
|
|
30
29
|
import {OtrMessage} from './message/OtrMessage';
|
|
31
30
|
|
|
32
|
-
const createMessage = (content: string) => {
|
|
33
|
-
const customTextMessage = GenericMessage.create({
|
|
34
|
-
messageId: PayloadHelper.getUUID(),
|
|
35
|
-
text: Text.create({content}),
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
return GenericMessage.encode(customTextMessage).finish();
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const generatePreKeyBundle = (userCount: number, clientsPerUser: number): UserPreKeyBundleMap => {
|
|
42
|
-
const prekeyBundle: UserPreKeyBundleMap = {};
|
|
43
|
-
for (let userIndex = 0; userIndex < userCount; userIndex++) {
|
|
44
|
-
const userId = PayloadHelper.getUUID();
|
|
45
|
-
prekeyBundle[userId] = {};
|
|
46
|
-
for (let clientIndex = 0; clientIndex < clientsPerUser; clientIndex++) {
|
|
47
|
-
const clientId = PayloadHelper.getUUID();
|
|
48
|
-
prekeyBundle[userId][clientId] = {
|
|
49
|
-
id: -1,
|
|
50
|
-
key: '',
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return prekeyBundle;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
31
|
describe('ConversationService', () => {
|
|
58
32
|
let account: Account;
|
|
59
33
|
|
|
@@ -63,29 +37,6 @@ describe('ConversationService', () => {
|
|
|
63
37
|
await account.initServices(new MemoryEngine());
|
|
64
38
|
});
|
|
65
39
|
|
|
66
|
-
describe("'shouldSendAsExternal'", () => {
|
|
67
|
-
it('returns true for a big payload', () => {
|
|
68
|
-
const preKeyBundles = generatePreKeyBundle(128, 4);
|
|
69
|
-
|
|
70
|
-
const longMessage =
|
|
71
|
-
'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Duis autem';
|
|
72
|
-
const plainText = createMessage(longMessage);
|
|
73
|
-
|
|
74
|
-
const shouldSendAsExternal = account.service!.conversation['shouldSendAsExternal'](plainText, preKeyBundles);
|
|
75
|
-
expect(shouldSendAsExternal).toBe(true);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('returns false for a small payload', async () => {
|
|
79
|
-
const preKeyBundles = generatePreKeyBundle(2, 1);
|
|
80
|
-
|
|
81
|
-
const shortMessage = PayloadHelper.getUUID();
|
|
82
|
-
const plainText = createMessage(shortMessage);
|
|
83
|
-
|
|
84
|
-
const shouldSendAsExternal = account.service!.conversation['shouldSendAsExternal'](plainText, preKeyBundles);
|
|
85
|
-
expect(shouldSendAsExternal).toBe(false);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
40
|
describe('"send"', () => {
|
|
90
41
|
const baseMessage = {
|
|
91
42
|
conversation: PayloadHelper.getUUID(),
|
|
@@ -126,6 +77,25 @@ describe('ConversationService', () => {
|
|
|
126
77
|
expect(callbacks.onSuccess).toHaveBeenCalledWith(jasmine.any(Object), sentTime);
|
|
127
78
|
});
|
|
128
79
|
});
|
|
80
|
+
|
|
81
|
+
it(`cancels message sending if onStart returns false`, async () => {
|
|
82
|
+
account['apiClient'].context = {
|
|
83
|
+
clientType: ClientType.NONE,
|
|
84
|
+
userId: PayloadHelper.getUUID(),
|
|
85
|
+
clientId: PayloadHelper.getUUID(),
|
|
86
|
+
};
|
|
87
|
+
const conversationService = account.service!.conversation;
|
|
88
|
+
spyOn<any>(conversationService, 'sendGenericMessage');
|
|
89
|
+
const message: OtrMessage = {...baseMessage, type: PayloadBundleType.TEXT, content: {text: 'test'}};
|
|
90
|
+
const callbacks = {onStart: () => Promise.resolve(false), onSuccess: jasmine.createSpy()};
|
|
91
|
+
await conversationService.send({
|
|
92
|
+
callbacks,
|
|
93
|
+
payloadBundle: message,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(callbacks.onSuccess).not.toHaveBeenCalled();
|
|
97
|
+
expect(conversationService['sendGenericMessage']).not.toHaveBeenCalled();
|
|
98
|
+
});
|
|
129
99
|
});
|
|
130
100
|
|
|
131
101
|
describe('"createText"', () => {
|
|
@@ -50,7 +50,6 @@ import {
|
|
|
50
50
|
MessageHide,
|
|
51
51
|
Reaction,
|
|
52
52
|
} from '@wireapp/protocol-messaging';
|
|
53
|
-
import {Encoder} from 'bazinga64';
|
|
54
53
|
|
|
55
54
|
import {
|
|
56
55
|
AssetService,
|
|
@@ -62,7 +61,7 @@ import {
|
|
|
62
61
|
PayloadBundleType,
|
|
63
62
|
} from '../conversation/';
|
|
64
63
|
import type {AssetContent, ClearedContent, DeletedContent, HiddenContent, RemoteData} from '../conversation/content/';
|
|
65
|
-
import type {CryptographyService
|
|
64
|
+
import type {CryptographyService} from '../cryptography/';
|
|
66
65
|
import * as AssetCryptography from '../cryptography/AssetCryptography.node';
|
|
67
66
|
import {isStringArray, isQualifiedIdArray, isQualifiedUserClients, isUserClients} from '../util/TypePredicateUtil';
|
|
68
67
|
import {MessageBuilder} from './message/MessageBuilder';
|
|
@@ -91,9 +90,19 @@ import type {
|
|
|
91
90
|
} from './message/OtrMessage';
|
|
92
91
|
|
|
93
92
|
export interface MessageSendingCallbacks {
|
|
94
|
-
onStart?: (message: GenericMessage) =>
|
|
93
|
+
onStart?: (message: GenericMessage) => Promise<boolean | undefined>;
|
|
95
94
|
onSuccess?: (message: GenericMessage, sentTime?: string) => void;
|
|
96
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Called whenever there is a clientmismatch returned from the server. Will also indicate the sending status of the message (if it was already sent or not)
|
|
97
|
+
*
|
|
98
|
+
* @param status The mismatch info
|
|
99
|
+
* @param wasSent Indicate whether the message was already sent or if it can still be canceled
|
|
100
|
+
* @return
|
|
101
|
+
*/
|
|
102
|
+
onClientMismatch?: (
|
|
103
|
+
status: ClientMismatch | MessageSendingStatus,
|
|
104
|
+
wasSent: boolean,
|
|
105
|
+
) => undefined | Promise<boolean | undefined>;
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
export class ConversationService {
|
|
@@ -103,12 +112,12 @@ export class ConversationService {
|
|
|
103
112
|
|
|
104
113
|
constructor(
|
|
105
114
|
private readonly apiClient: APIClient,
|
|
106
|
-
|
|
115
|
+
cryptographyService: CryptographyService,
|
|
107
116
|
private readonly assetService: AssetService,
|
|
108
117
|
) {
|
|
109
118
|
this.messageTimer = new MessageTimer();
|
|
110
119
|
this.messageBuilder = new MessageBuilder(this.apiClient, this.assetService);
|
|
111
|
-
this.messageService = new MessageService(this.apiClient,
|
|
120
|
+
this.messageService = new MessageService(this.apiClient, cryptographyService);
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
private createEphemeral(originalGenericMessage: GenericMessage, expireAfterMillis: number): GenericMessage {
|
|
@@ -227,55 +236,6 @@ export class ConversationService {
|
|
|
227
236
|
return this.apiClient.conversation.api.getConversation(userId);
|
|
228
237
|
}
|
|
229
238
|
|
|
230
|
-
private async sendExternalGenericMessage(
|
|
231
|
-
sendingClientId: string,
|
|
232
|
-
conversationId: string,
|
|
233
|
-
asset: EncryptedAsset,
|
|
234
|
-
preKeyBundles: UserPreKeyBundleMap,
|
|
235
|
-
sendAsProtobuf?: boolean,
|
|
236
|
-
): Promise<ClientMismatch | undefined> {
|
|
237
|
-
if (preKeyBundles.none) {
|
|
238
|
-
const {cipherText, keyBytes, sha256} = asset;
|
|
239
|
-
const messageId = MessageBuilder.createId();
|
|
240
|
-
|
|
241
|
-
const externalMessage = {
|
|
242
|
-
otrKey: new Uint8Array(keyBytes),
|
|
243
|
-
sha256: new Uint8Array(sha256),
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
const base64CipherText = Encoder.toBase64(cipherText).asString;
|
|
247
|
-
|
|
248
|
-
const genericMessage = GenericMessage.create({
|
|
249
|
-
[GenericMessageType.EXTERNAL]: externalMessage,
|
|
250
|
-
messageId,
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
const plainTextArray = GenericMessage.encode(genericMessage).finish();
|
|
254
|
-
|
|
255
|
-
const recipients = await this.cryptographyService.encrypt(plainTextArray, preKeyBundles);
|
|
256
|
-
|
|
257
|
-
if (sendAsProtobuf) {
|
|
258
|
-
return this.messageService.sendOTRProtobufMessage(
|
|
259
|
-
sendingClientId,
|
|
260
|
-
recipients,
|
|
261
|
-
conversationId,
|
|
262
|
-
plainTextArray,
|
|
263
|
-
cipherText,
|
|
264
|
-
);
|
|
265
|
-
}
|
|
266
|
-
return this.messageService.sendOTRMessage(
|
|
267
|
-
sendingClientId,
|
|
268
|
-
recipients,
|
|
269
|
-
conversationId,
|
|
270
|
-
plainTextArray,
|
|
271
|
-
base64CipherText,
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
// todo: add federated sending here
|
|
275
|
-
}
|
|
276
|
-
return undefined;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
239
|
private async getQualifiedRecipientsForConversation(
|
|
280
240
|
conversationId: QualifiedId,
|
|
281
241
|
userIds?: QualifiedId[] | QualifiedUserClients,
|
|
@@ -287,13 +247,24 @@ export class ConversationService {
|
|
|
287
247
|
return this.getQualifiedPreKeyBundle(recipientIds);
|
|
288
248
|
}
|
|
289
249
|
|
|
250
|
+
private async getRecipientsForConversation(
|
|
251
|
+
conversationId: string,
|
|
252
|
+
userIds?: string[] | UserClients,
|
|
253
|
+
): Promise<UserClients | UserPreKeyBundleMap> {
|
|
254
|
+
if (isUserClients(userIds)) {
|
|
255
|
+
return userIds;
|
|
256
|
+
}
|
|
257
|
+
return this.getPreKeyBundleMap(conversationId, userIds);
|
|
258
|
+
}
|
|
259
|
+
|
|
290
260
|
/**
|
|
291
|
-
* Sends a message to a
|
|
261
|
+
* Sends a message to a conversation
|
|
292
262
|
*
|
|
293
263
|
* @param sendingClientId The clientId from which the message is sent
|
|
294
264
|
* @param conversationId The conversation in which to send the message
|
|
295
|
-
* @param conversationDomain The domain where the conversation lives
|
|
296
265
|
* @param genericMessage The payload of the message to send
|
|
266
|
+
* @param options.domain? The federated domain the server runs on. Should only be set for federation enabled envs
|
|
267
|
+
* @param options.sendAsProtobuf? Will send the message as a protobuf payload
|
|
297
268
|
* @param options.userIds? can be either a QualifiedId[] or QualfiedUserClients or undefined. The type has some effect on the behavior of the method.
|
|
298
269
|
* When given undefined the method will fetch both the members of the conversations and their devices. No ClientMismatch can happen in that case
|
|
299
270
|
* When given a QualifiedId[] the method will fetch the freshest list of devices for those users (since they are not given by the consumer). As a consequence no ClientMismatch error will trigger and we will ignore missing clients when sending
|
|
@@ -301,24 +272,6 @@ export class ConversationService {
|
|
|
301
272
|
* @param options.onClientMismatch? Will be called whenever there is a clientmismatch returned from the server. Needs to be combined with a userIds of type QualifiedUserClients
|
|
302
273
|
* @return Resolves with the message sending status from backend
|
|
303
274
|
*/
|
|
304
|
-
private async sendFederatedGenericMessage(
|
|
305
|
-
sendingClientId: string,
|
|
306
|
-
conversationId: QualifiedId,
|
|
307
|
-
genericMessage: GenericMessage,
|
|
308
|
-
options: {
|
|
309
|
-
userIds?: QualifiedId[] | QualifiedUserClients;
|
|
310
|
-
onClientMismatch?: (mismatch: MessageSendingStatus) => Promise<boolean | undefined>;
|
|
311
|
-
} = {},
|
|
312
|
-
): Promise<MessageSendingStatus> {
|
|
313
|
-
const plainTextArray = GenericMessage.encode(genericMessage).finish();
|
|
314
|
-
const recipients = await this.getQualifiedRecipientsForConversation(conversationId, options.userIds);
|
|
315
|
-
|
|
316
|
-
return this.messageService.sendFederatedOTRMessage(sendingClientId, conversationId, recipients, plainTextArray, {
|
|
317
|
-
reportMissing: isQualifiedUserClients(options.userIds), // we want to check mismatch in case the consumer gave an exact list of users/devices
|
|
318
|
-
onClientMismatch: options.onClientMismatch,
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
|
|
322
275
|
private async sendGenericMessage(
|
|
323
276
|
sendingClientId: string,
|
|
324
277
|
conversationId: string,
|
|
@@ -327,46 +280,33 @@ export class ConversationService {
|
|
|
327
280
|
domain?: string;
|
|
328
281
|
userIds?: string[] | QualifiedId[] | UserClients | QualifiedUserClients;
|
|
329
282
|
sendAsProtobuf?: boolean;
|
|
330
|
-
onClientMismatch?:
|
|
283
|
+
onClientMismatch?: MessageSendingCallbacks['onClientMismatch'];
|
|
331
284
|
} = {},
|
|
332
|
-
): Promise<ClientMismatch | MessageSendingStatus
|
|
285
|
+
): Promise<ClientMismatch | MessageSendingStatus> {
|
|
333
286
|
const {domain, userIds} = options;
|
|
287
|
+
const plainText = GenericMessage.encode(genericMessage).finish();
|
|
334
288
|
if (domain) {
|
|
335
289
|
if (isStringArray(userIds) || isUserClients(userIds)) {
|
|
336
|
-
throw new Error('Invalid userIds option for sending');
|
|
290
|
+
throw new Error('Invalid userIds option for sending to federated backend');
|
|
337
291
|
}
|
|
338
|
-
|
|
339
|
-
return this.
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
);
|
|
292
|
+
const recipients = await this.getQualifiedRecipientsForConversation({id: conversationId, domain}, userIds);
|
|
293
|
+
return this.messageService.sendFederatedMessage(sendingClientId, recipients, plainText, {
|
|
294
|
+
conversationId: {id: conversationId, domain},
|
|
295
|
+
reportMissing: isQualifiedUserClients(options.userIds), // we want to check mismatch in case the consumer gave an exact list of users/devices
|
|
296
|
+
onClientMismatch: mismatch => options.onClientMismatch?.(mismatch, false),
|
|
297
|
+
});
|
|
345
298
|
}
|
|
346
299
|
|
|
347
|
-
if (
|
|
300
|
+
if (!isStringArray(userIds) && !isUserClients(userIds)) {
|
|
348
301
|
throw new Error('Invalid userIds option for sending');
|
|
349
302
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
this.apiClient.validatedClientId,
|
|
358
|
-
conversationId,
|
|
359
|
-
encryptedAsset,
|
|
360
|
-
preKeyBundles,
|
|
361
|
-
options.sendAsProtobuf,
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const recipients = await this.cryptographyService.encrypt(plainTextArray, preKeyBundles);
|
|
366
|
-
|
|
367
|
-
return options.sendAsProtobuf
|
|
368
|
-
? this.messageService.sendOTRProtobufMessage(sendingClientId, recipients, conversationId, plainTextArray)
|
|
369
|
-
: this.messageService.sendOTRMessage(sendingClientId, recipients, conversationId, plainTextArray);
|
|
303
|
+
const recipients = await this.getRecipientsForConversation(conversationId, userIds);
|
|
304
|
+
return this.messageService.sendMessage(sendingClientId, recipients, plainText, {
|
|
305
|
+
conversationId,
|
|
306
|
+
sendAsProtobuf: options.sendAsProtobuf,
|
|
307
|
+
reportMissing: isUserClients(options.userIds), // we want to check mismatch in case the consumer gave an exact list of users/devices
|
|
308
|
+
onClientMismatch: mistmatch => options.onClientMismatch?.(mistmatch, false),
|
|
309
|
+
});
|
|
370
310
|
}
|
|
371
311
|
|
|
372
312
|
private generateButtonActionGenericMessage(payloadBundle: ButtonActionMessage): GenericMessage {
|
|
@@ -746,20 +686,6 @@ export class ConversationService {
|
|
|
746
686
|
};
|
|
747
687
|
}
|
|
748
688
|
|
|
749
|
-
private shouldSendAsExternal(plainText: Uint8Array, preKeyBundles: UserPreKeyBundleMap): boolean {
|
|
750
|
-
const EXTERNAL_MESSAGE_THRESHOLD_BYTES = 200 * 1024;
|
|
751
|
-
|
|
752
|
-
let clientCount = 0;
|
|
753
|
-
for (const user in preKeyBundles) {
|
|
754
|
-
clientCount += Object.keys(preKeyBundles[user]).length;
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
const messageInBytes = new Uint8Array(plainText).length;
|
|
758
|
-
const estimatedPayloadInBytes = clientCount * messageInBytes;
|
|
759
|
-
|
|
760
|
-
return estimatedPayloadInBytes > EXTERNAL_MESSAGE_THRESHOLD_BYTES;
|
|
761
|
-
}
|
|
762
|
-
|
|
763
689
|
public leaveConversation(conversationId: string): Promise<ConversationMemberLeaveEvent> {
|
|
764
690
|
return this.apiClient.conversation.api.deleteMember(conversationId, this.apiClient.context!.userId);
|
|
765
691
|
}
|
|
@@ -846,7 +772,8 @@ export class ConversationService {
|
|
|
846
772
|
* @param params.sendAsProtobuf?
|
|
847
773
|
* @param params.conversationDomain? The domain the conversation lives on (if given with QualifiedId[] or QualfiedUserClients in the userIds params, will send the message to the federated endpoint)
|
|
848
774
|
* @param params.callbacks? Optional callbacks that will be called when the message starts being sent and when it has been succesfully sent.
|
|
849
|
-
* @param
|
|
775
|
+
* @param callbacks.onStart Will be called before a message is actually sent. Returning 'false' will prevent the message from being sent
|
|
776
|
+
* @param callbacks.onClientMismatch? Will be called when a mismatch happens. Returning `false` from the callback will stop the sending attempt
|
|
850
777
|
* @return resolves with the sent message
|
|
851
778
|
*/
|
|
852
779
|
public async send<T extends OtrMessage = OtrMessage>({
|
|
@@ -923,14 +850,27 @@ export class ConversationService {
|
|
|
923
850
|
throw new Error(`No send method implemented for "${payloadBundle['type']}".`);
|
|
924
851
|
}
|
|
925
852
|
|
|
926
|
-
callbacks?.onStart?.(genericMessage)
|
|
853
|
+
if ((await callbacks?.onStart?.(genericMessage)) === false) {
|
|
854
|
+
// If the onStart call returns false, it means the consumer wants to cancel the message sending
|
|
855
|
+
return payloadBundle;
|
|
856
|
+
}
|
|
857
|
+
|
|
927
858
|
const response = await this.sendGenericMessage(
|
|
928
859
|
this.apiClient.validatedClientId,
|
|
929
860
|
payloadBundle.conversation,
|
|
930
861
|
genericMessage,
|
|
931
|
-
{
|
|
862
|
+
{
|
|
863
|
+
userIds,
|
|
864
|
+
sendAsProtobuf,
|
|
865
|
+
domain: conversationDomain,
|
|
866
|
+
onClientMismatch: callbacks?.onClientMismatch,
|
|
867
|
+
},
|
|
932
868
|
);
|
|
933
|
-
|
|
869
|
+
if (!this.isClearFromMismatch(response)) {
|
|
870
|
+
// We warn the consumer that there is a mismatch that did not prevent message sending
|
|
871
|
+
callbacks?.onClientMismatch?.(response, true);
|
|
872
|
+
}
|
|
873
|
+
callbacks?.onSuccess?.(genericMessage, response.time);
|
|
934
874
|
|
|
935
875
|
return {
|
|
936
876
|
...payloadBundle,
|
|
@@ -991,4 +931,12 @@ export class ConversationService {
|
|
|
991
931
|
conversation_role: conversationRole,
|
|
992
932
|
});
|
|
993
933
|
}
|
|
934
|
+
|
|
935
|
+
private isClearFromMismatch(mismatch: ClientMismatch | MessageSendingStatus): boolean {
|
|
936
|
+
const hasMissing = Object.keys(mismatch.missing || {}).length > 0;
|
|
937
|
+
const hasDeleted = Object.keys(mismatch.deleted || {}).length > 0;
|
|
938
|
+
const hasRedundant = Object.keys(mismatch.redundant || {}).length > 0;
|
|
939
|
+
const hasFailed = Object.keys((mismatch as MessageSendingStatus).failed_to_send || {}).length > 0;
|
|
940
|
+
return !hasMissing && !hasDeleted && !hasRedundant && !hasFailed;
|
|
941
|
+
}
|
|
994
942
|
}
|
|
@@ -1,23 +1,53 @@
|
|
|
1
1
|
import { APIClient } from '@wireapp/api-client';
|
|
2
|
-
import { ClientMismatch, MessageSendingStatus,
|
|
2
|
+
import { ClientMismatch, MessageSendingStatus, QualifiedUserClients, UserClients } from '@wireapp/api-client/src/conversation';
|
|
3
3
|
import { CryptographyService } from '../../cryptography';
|
|
4
|
-
import { QualifiedId, QualifiedUserPreKeyBundleMap } from '@wireapp/api-client/src/user';
|
|
4
|
+
import { QualifiedId, QualifiedUserPreKeyBundleMap, UserPreKeyBundleMap } from '@wireapp/api-client/src/user';
|
|
5
5
|
export declare class MessageService {
|
|
6
6
|
private readonly apiClient;
|
|
7
7
|
private readonly cryptographyService;
|
|
8
8
|
constructor(apiClient: APIClient, cryptographyService: CryptographyService);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Sends a message to a non-federated backend.
|
|
11
|
+
*
|
|
12
|
+
* @param sendingClientId The clientId of the current user
|
|
13
|
+
* @param recipients The list of recipients to send the message to
|
|
14
|
+
* @param plainText The plainText data to send
|
|
15
|
+
* @param options.conversationId? the conversation to send the message to. Will broadcast if not set
|
|
16
|
+
* @param options.reportMissing? trigger a mismatch error when there are missing recipients in the payload
|
|
17
|
+
* @param options.sendAsProtobuf?
|
|
18
|
+
* @param options.onClientMismatch? Called when a mismatch happens on the server
|
|
19
|
+
* @return the ClientMismatch status returned by the backend
|
|
20
|
+
*/
|
|
21
|
+
sendMessage(sendingClientId: string, recipients: UserClients | UserPreKeyBundleMap, plainText: Uint8Array, options?: {
|
|
22
|
+
conversationId?: string;
|
|
23
|
+
reportMissing?: boolean;
|
|
24
|
+
sendAsProtobuf?: boolean;
|
|
25
|
+
onClientMismatch?: (mismatch: ClientMismatch) => undefined | Promise<boolean | undefined>;
|
|
26
|
+
}): Promise<ClientMismatch>;
|
|
27
|
+
/**
|
|
28
|
+
* Sends a message to a federated backend.
|
|
29
|
+
*
|
|
30
|
+
* @param sendingClientId The clientId of the current user
|
|
31
|
+
* @param recipients The list of recipients to send the message to
|
|
32
|
+
* @param plainText The plainText data to send
|
|
33
|
+
* @param options.conversationId? the conversation to send the message to. Will broadcast if not set
|
|
34
|
+
* @param options.reportMissing? trigger a mismatch error when there are missing recipients in the payload
|
|
35
|
+
* @param options.sendAsProtobuf?
|
|
36
|
+
* @param options.onClientMismatch? Called when a mismatch happens on the server
|
|
37
|
+
* @return the MessageSendingStatus returned by the backend
|
|
38
|
+
*/
|
|
39
|
+
sendFederatedMessage(sendingClientId: string, recipients: QualifiedUserClients | QualifiedUserPreKeyBundleMap, plainText: Uint8Array, options: {
|
|
13
40
|
assetData?: Uint8Array;
|
|
41
|
+
conversationId?: QualifiedId;
|
|
14
42
|
reportMissing?: boolean;
|
|
15
|
-
onClientMismatch?: (mismatch: MessageSendingStatus) => Promise<boolean | undefined>;
|
|
43
|
+
onClientMismatch?: (mismatch: MessageSendingStatus) => undefined | Promise<boolean | undefined>;
|
|
16
44
|
}): Promise<MessageSendingStatus>;
|
|
17
|
-
|
|
18
|
-
private
|
|
19
|
-
private
|
|
20
|
-
private
|
|
45
|
+
private sendFederatedOtrMessage;
|
|
46
|
+
private sendOTRMessage;
|
|
47
|
+
private generateExternalPayload;
|
|
48
|
+
private shouldSendAsExternal;
|
|
49
|
+
private isClientMismatchError;
|
|
50
|
+
private reencryptAfterMismatch;
|
|
21
51
|
/**
|
|
22
52
|
* Will re-encrypt a message when there were some missing clients in the initial send (typically when the server replies with a client mismatch error)
|
|
23
53
|
*
|
|
@@ -26,6 +56,6 @@ export declare class MessageService {
|
|
|
26
56
|
* @param {Uint8Array} plainText The text that should be encrypted for the missing clients
|
|
27
57
|
* @return resolves with a new message payload that can be sent
|
|
28
58
|
*/
|
|
29
|
-
private
|
|
30
|
-
private
|
|
59
|
+
private reencryptAfterFederatedMismatch;
|
|
60
|
+
private sendOTRProtobufMessage;
|
|
31
61
|
}
|