Implement MessageSender

pull/1161/head
Mikunj 5 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
class LokiAppDotNetServerAPI {
export default class LokiAppDotNetServerAPI {
constructor(ourKey, url) {
this.ourKey = ourKey;
this.channels = [];
@ -2314,5 +2314,3 @@ class LokiPublicChannelAPI {
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 || [];
}
class LokiMessageAPI {
export default class LokiMessageAPI {
constructor(ourKey) {
this.jobQueue = new window.JobQueue();
this.sendingData = {};
@ -76,6 +76,16 @@ class LokiMessageAPI {
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 = {}) {
const {
isPublic = false,
@ -600,5 +610,3 @@ class LokiMessageAPI {
// 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 LokiAppDotNetAPI = require('./loki_app_dot_net_api');
class LokiPublicChatFactoryAPI extends EventEmitter {
export default class LokiPublicChatFactoryAPI extends EventEmitter {
constructor(ourKey) {
super();
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;
}
export type Base64String = string;
/**
* Encrypt `plainTextBuffer` with given `encryptionType` for `device`.
*
@ -41,7 +39,7 @@ export async function encrypt(
encryptionType: EncryptionType
): Promise<{
envelopeType: SignalService.Envelope.Type;
cipherText: Base64String;
cipherText: Uint8Array;
}> {
const plainText = padPlainTextBuffer(plainTextBuffer);
const address = new libsignal.SignalProtocolAddress(device, 1);
@ -71,7 +69,7 @@ async function encryptUsingSealedSender(
innerCipherText: CipherTextObject
): Promise<{
envelopeType: SignalService.Envelope.Type;
cipherText: Base64String;
cipherText: Uint8Array;
}> {
const ourNumber = await UserUtil.getCurrentDevicePubKey();
if (!ourNumber) {
@ -94,6 +92,6 @@ async function encryptUsingSealedSender(
return {
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 { AttachmentType } from '../../../types/Attachment';
import { QuotedAttachmentType } from '../../../components/conversation/Quote';
import { AttachmentPointer, Preview, Quote } from './content';
interface OpenGroupMessageParams extends MessageParams {
interface OpenGroup {
server: string;
attachments?: Array<AttachmentType>;
channel: number;
conversationId: string;
}
interface OpenGroupMessageParams extends MessageParams {
group: OpenGroup;
attachments: Array<AttachmentPointer>;
preview: Array<Preview>;
body?: string;
quote?: QuotedAttachmentType;
quote?: Quote;
}
export class OpenGroupMessage extends Message {
public readonly server: string;
public readonly group: OpenGroup;
public readonly body?: string;
public readonly attachments?: Array<AttachmentType>;
public readonly quote?: QuotedAttachmentType;
public readonly attachments: Array<AttachmentPointer>;
public readonly quote?: Quote;
public readonly preview: Array<Preview>;
constructor({
timestamp,
server,
group,
attachments,
body,
quote,
identifier,
preview,
}: OpenGroupMessageParams) {
super({ timestamp, identifier });
this.server = server;
this.group = group;
this.body = body;
this.attachments = attachments;
this.quote = quote;
this.preview = preview;
}
}

@ -2,13 +2,110 @@
import { RawMessage } from '../types/RawMessage';
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> {
return Promise.resolve();
// ================ Regular ================
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(
message: OpenGroupMessage
): Promise<void> {
return Promise.resolve();
): Promise<boolean> {
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 { SignalInterface } from './types/signal';
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 {
seedNodeList: any;
@ -8,7 +10,6 @@ interface WindowInterface extends Window {
WebAPI: any;
LokiSnodeAPI: any;
SenderKeyAPI: any;
LokiMessageAPI: any;
StubMessageAPI: any;
StubAppDotNetApi: any;
LokiPublicChatAPI: any;
@ -72,6 +73,9 @@ interface WindowInterface extends Window {
lokiFeatureFlags: any;
resetDatabase: any;
lokiMessageAPI: LokiMessageAPI;
lokiPublicChatAPI: LokiPublicChatFactoryAPI;
}
// In the case for tests
@ -133,3 +137,6 @@ export const attemptConnection = window.attemptConnection;
export const libloki = window.libloki;
export const libsignal = window.libsignal;
export const textsecure = window.textsecure;
export const lokiMessageAPI = window.lokiMessageAPI;
export const lokiPublicChatAPI = window.lokiPublicChatAPI;

Loading…
Cancel
Save