Implement MessageSender

pull/1161/head
Mikunj 6 years ago
parent cbc32b9989
commit 1dad49057b

@ -0,0 +1,25 @@
import {
Quote,
AttachmentPointer,
Preview,
} from '../../ts/session/messages/outgoing';
declare class LokiAppDotNetServerAPI {
constructor(ourKey: string, url: string);
findOrCreateChannel(
api: LokiPublicChatFactoryAPI,
channelId: number,
conversationId: string
): Promise<LokiPublicChannelAPI>;
}
export interface LokiPublicChannelAPI {
sendMessage(data: {
quote?: Quote;
attachments: Array<AttachmentPointer>;
preview: Array<Preview>;
body?: string;
}): Promise<boolean>;
}
export default LokiAppDotNetServerAPI;

@ -478,7 +478,7 @@ const serverRequest = async (endpoint, options = {}) => {
}; };
// the core ADN class that handles all communication with a specific server // the core ADN class that handles all communication with a specific server
class LokiAppDotNetServerAPI { export default class LokiAppDotNetServerAPI {
constructor(ourKey, url) { constructor(ourKey, url) {
this.ourKey = ourKey; this.ourKey = ourKey;
this.channels = []; this.channels = [];
@ -2314,5 +2314,3 @@ class LokiPublicChannelAPI {
return false; return false;
} }
} }
module.exports = LokiAppDotNetServerAPI;

@ -0,0 +1,11 @@
declare class LokiMessageAPI {
constructor(ourKey: string);
sendMessage(
pubKey: string,
data: Uint8Array,
messageTimeStamp: number,
ttl: number
): Promise<void>;
}
export default LokiMessageAPI;

@ -67,7 +67,7 @@ async function _retrieveNextMessages(nodeData, pubkey) {
return result.messages || []; return result.messages || [];
} }
class LokiMessageAPI { export default class LokiMessageAPI {
constructor(ourKey) { constructor(ourKey) {
this.jobQueue = new window.JobQueue(); this.jobQueue = new window.JobQueue();
this.sendingData = {}; this.sendingData = {};
@ -76,6 +76,16 @@ class LokiMessageAPI {
this.groupIdsToPoll = {}; this.groupIdsToPoll = {};
} }
/**
* Refactor note: We should really clean this up ... it's very messy
*
* We need to split it into 2 sends:
* - Snodes
* - Open Groups
*
* Mikunj:
* Temporarily i've made it so `MessageSender` handles open group sends and calls this function for regular sends.
*/
async sendMessage(pubKey, data, messageTimeStamp, ttl, options = {}) { async sendMessage(pubKey, data, messageTimeStamp, ttl, options = {}) {
const { const {
isPublic = false, isPublic = false,
@ -600,5 +610,3 @@ class LokiMessageAPI {
// no, our caller already handles this... // no, our caller already handles this...
} }
} }
module.exports = LokiMessageAPI;

@ -0,0 +1,13 @@
import { LokiPublicChannelAPI } from './loki_app_dot_net_api';
declare class LokiPublicChatFactoryAPI {
constructor(ourKey: string);
findOrCreateServer(url: string): Promise<void>;
findOrCreateChannel(
url: string,
channelId: number,
conversationId: string
): Promise<LokiPublicChannelAPI>;
}
export default LokiPublicChatFactoryAPI;

@ -3,7 +3,7 @@ const EventEmitter = require('events');
const nodeFetch = require('node-fetch'); const nodeFetch = require('node-fetch');
const LokiAppDotNetAPI = require('./loki_app_dot_net_api'); const LokiAppDotNetAPI = require('./loki_app_dot_net_api');
class LokiPublicChatFactoryAPI extends EventEmitter { export default class LokiPublicChatFactoryAPI extends EventEmitter {
constructor(ourKey) { constructor(ourKey) {
super(); super();
this.ourKey = ourKey; this.ourKey = ourKey;
@ -199,5 +199,3 @@ class LokiPublicChatFactoryAPI extends EventEmitter {
); );
} }
} }
module.exports = LokiPublicChatFactoryAPI;

@ -25,8 +25,6 @@ function getPaddedMessageLength(originalLength: number): number {
return messagePartCount * 160; return messagePartCount * 160;
} }
export type Base64String = string;
/** /**
* Encrypt `plainTextBuffer` with given `encryptionType` for `device`. * Encrypt `plainTextBuffer` with given `encryptionType` for `device`.
* *
@ -41,7 +39,7 @@ export async function encrypt(
encryptionType: EncryptionType encryptionType: EncryptionType
): Promise<{ ): Promise<{
envelopeType: SignalService.Envelope.Type; envelopeType: SignalService.Envelope.Type;
cipherText: Base64String; cipherText: Uint8Array;
}> { }> {
const plainText = padPlainTextBuffer(plainTextBuffer); const plainText = padPlainTextBuffer(plainTextBuffer);
const address = new libsignal.SignalProtocolAddress(device, 1); const address = new libsignal.SignalProtocolAddress(device, 1);
@ -71,7 +69,7 @@ async function encryptUsingSealedSender(
innerCipherText: CipherTextObject innerCipherText: CipherTextObject
): Promise<{ ): Promise<{
envelopeType: SignalService.Envelope.Type; envelopeType: SignalService.Envelope.Type;
cipherText: Base64String; cipherText: Uint8Array;
}> { }> {
const ourNumber = await UserUtil.getCurrentDevicePubKey(); const ourNumber = await UserUtil.getCurrentDevicePubKey();
if (!ourNumber) { if (!ourNumber) {
@ -94,6 +92,6 @@ async function encryptUsingSealedSender(
return { return {
envelopeType: SignalService.Envelope.Type.UNIDENTIFIED_SENDER, envelopeType: SignalService.Envelope.Type.UNIDENTIFIED_SENDER,
cipherText: Buffer.from(cipherTextBuffer).toString('base64'), cipherText: new Uint8Array(cipherTextBuffer),
}; };
} }

@ -1,32 +1,41 @@
import { Message, MessageParams } from './Message'; import { Message, MessageParams } from './Message';
import { AttachmentType } from '../../../types/Attachment'; import { AttachmentPointer, Preview, Quote } from './content';
import { QuotedAttachmentType } from '../../../components/conversation/Quote';
interface OpenGroupMessageParams extends MessageParams { interface OpenGroup {
server: string; server: string;
attachments?: Array<AttachmentType>; channel: number;
conversationId: string;
}
interface OpenGroupMessageParams extends MessageParams {
group: OpenGroup;
attachments: Array<AttachmentPointer>;
preview: Array<Preview>;
body?: string; body?: string;
quote?: QuotedAttachmentType; quote?: Quote;
} }
export class OpenGroupMessage extends Message { export class OpenGroupMessage extends Message {
public readonly server: string; public readonly group: OpenGroup;
public readonly body?: string; public readonly body?: string;
public readonly attachments?: Array<AttachmentType>; public readonly attachments: Array<AttachmentPointer>;
public readonly quote?: QuotedAttachmentType; public readonly quote?: Quote;
public readonly preview: Array<Preview>;
constructor({ constructor({
timestamp, timestamp,
server, group,
attachments, attachments,
body, body,
quote, quote,
identifier, identifier,
preview,
}: OpenGroupMessageParams) { }: OpenGroupMessageParams) {
super({ timestamp, identifier }); super({ timestamp, identifier });
this.server = server; this.group = group;
this.body = body; this.body = body;
this.attachments = attachments; this.attachments = attachments;
this.quote = quote; this.quote = quote;
this.preview = preview;
} }
} }

@ -2,13 +2,110 @@
import { RawMessage } from '../types/RawMessage'; import { RawMessage } from '../types/RawMessage';
import { OpenGroupMessage } from '../messages/outgoing'; import { OpenGroupMessage } from '../messages/outgoing';
import { SignalService } from '../../protobuf';
import { UserUtil } from '../../util';
import { MessageEncrypter } from '../crypto';
import { lokiMessageAPI, lokiPublicChatAPI, textsecure } from '../../window';
export async function send(message: RawMessage): Promise<void> { // ================ Regular ================
return Promise.resolve();
export function canSendToSnode(): boolean {
// Seems like lokiMessageAPI is not always guaranteed to be initialized
return Boolean(lokiMessageAPI);
}
export async function send({
device,
plainTextBuffer,
encryption,
timestamp,
ttl,
}: RawMessage): Promise<void> {
if (!canSendToSnode()) {
throw new Error('lokiMessageAPI is not initialized.');
}
const { envelopeType, cipherText } = await MessageEncrypter.encrypt(
device,
plainTextBuffer,
encryption
);
const envelope = await buildEnvelope(envelopeType, timestamp, cipherText);
const data = wrapEnvelope(envelope);
// TODO: Somehow differentiate between Retryable and Regular erros
return lokiMessageAPI.sendMessage(device, data, timestamp, ttl);
}
async function buildEnvelope(
type: SignalService.Envelope.Type,
timestamp: number,
content: Uint8Array
): Promise<SignalService.Envelope> {
let source: string | undefined;
if (type !== SignalService.Envelope.Type.UNIDENTIFIED_SENDER) {
source = await UserUtil.getCurrentDevicePubKey();
}
return SignalService.Envelope.create({
type,
source,
sourceDevice: 1,
timestamp,
content,
});
}
/**
* This is an outdated practice and we should probably just send the envelope data directly.
* Something to think about in the future.
*/
function wrapEnvelope(envelope: SignalService.Envelope): Uint8Array {
const request = SignalService.WebSocketRequestMessage.create({
id: 0,
body: SignalService.Envelope.encode(envelope).finish(),
});
const websocket = SignalService.WebSocketMessage.create({
type: SignalService.WebSocketMessage.Type.REQUEST,
request,
});
return SignalService.WebSocketMessage.encode(websocket).finish();
} }
// ================ Open Group ================
export async function sendToOpenGroup( export async function sendToOpenGroup(
message: OpenGroupMessage message: OpenGroupMessage
): Promise<void> { ): Promise<boolean> {
return Promise.resolve(); const { group, quote, attachments, preview, body } = message;
const channelAPI = await lokiPublicChatAPI.findOrCreateChannel(
group.server,
group.channel,
group.conversationId
);
// Don't think returning true/false on `sendMessage` is a good way
// We should either: return nothing (success) or throw an error (failure)
return channelAPI.sendMessage({
quote,
attachments: attachments || [],
preview,
body,
});
// TODO: The below should be handled in whichever class calls this
/*
const res = await sendToOpenGroup(message);
if (!res) {
throw new textsecure.PublicChatError('Failed to send public chat message');
}
const messageEventData = {
pubKey,
timestamp: messageTimeStamp,
};
messageEventData.serverId = res;
window.Whisper.events.trigger('publicMessageSent', messageEventData);
*/
} }

@ -1,6 +1,8 @@
import { LibsignalProtocol } from './types/libsignal-protocol'; import { LibsignalProtocol } from './types/libsignal-protocol';
import { SignalInterface } from './types/signal'; import { SignalInterface } from './types/signal';
import { LocalizerType } from '../types/Util'; import { LocalizerType } from '../types/Util';
import LokiMessageAPI from '../../js/modules/loki_message_api';
import LokiPublicChatFactoryAPI from '../../js/modules/loki_public_chat_api';
interface WindowInterface extends Window { interface WindowInterface extends Window {
seedNodeList: any; seedNodeList: any;
@ -8,7 +10,6 @@ interface WindowInterface extends Window {
WebAPI: any; WebAPI: any;
LokiSnodeAPI: any; LokiSnodeAPI: any;
SenderKeyAPI: any; SenderKeyAPI: any;
LokiMessageAPI: any;
StubMessageAPI: any; StubMessageAPI: any;
StubAppDotNetApi: any; StubAppDotNetApi: any;
LokiPublicChatAPI: any; LokiPublicChatAPI: any;
@ -72,6 +73,9 @@ interface WindowInterface extends Window {
lokiFeatureFlags: any; lokiFeatureFlags: any;
resetDatabase: any; resetDatabase: any;
lokiMessageAPI: LokiMessageAPI;
lokiPublicChatAPI: LokiPublicChatFactoryAPI;
} }
// In the case for tests // In the case for tests
@ -133,3 +137,6 @@ export const attemptConnection = window.attemptConnection;
export const libloki = window.libloki; export const libloki = window.libloki;
export const libsignal = window.libsignal; export const libsignal = window.libsignal;
export const textsecure = window.textsecure; export const textsecure = window.textsecure;
export const lokiMessageAPI = window.lokiMessageAPI;
export const lokiPublicChatAPI = window.lokiPublicChatAPI;

Loading…
Cancel
Save