starting getContent, multiple file types

This commit is contained in:
Diego Mello 2024-05-17 14:04:45 -03:00
parent c67cb0deb4
commit f373972911
5 changed files with 179 additions and 106 deletions

View File

@ -69,4 +69,8 @@ export interface IShareAttachment {
canUpload: boolean;
error?: any;
uri: string;
encryption?: {
key: any;
iv: string;
};
}

View File

@ -12,6 +12,7 @@ import { store } from '../store/auxStore';
import { joinVectorData, randomPassword, splitVectorData, toString, utf8ToBuffer } from './utils';
import { EncryptionRoom } from './index';
import {
IAttachment,
IMessage,
ISubscription,
IUpload,
@ -44,6 +45,7 @@ class Encryption {
decrypt: Function;
encrypt: Function;
encryptText: Function;
encryptFile: Function;
encryptUpload: Function;
importRoomKey: Function;
};
@ -514,6 +516,37 @@ class Encryption {
return roomE2E.decrypt(message);
};
encryptFile = async (rid: string, attachment: IAttachment) => {
const db = database.active;
const subCollection = db.get('subscriptions');
try {
// Find the subscription
const subRecord = await subCollection.find(rid);
// Subscription is not encrypted at the moment
if (!subRecord.encrypted) {
// Send a non encrypted message
return attachment;
}
// If the client is not ready
if (!this.ready) {
// Wait for ready status
await this.establishing;
}
const roomE2E = await this.getRoomInstance(rid);
return roomE2E.encryptFile(rid, attachment);
} catch {
// Subscription not found
// or client can't be initialized (missing password)
}
// Send a non encrypted message
return attachment;
};
// Decrypt multiple messages
decryptMessages = (messages: Partial<IMessage>[]) =>
Promise.all(messages.map((m: Partial<IMessage>) => this.decryptMessage(m as IMessage)));

View File

@ -5,7 +5,7 @@ import ByteBuffer from 'bytebuffer';
import parse from 'url-parse';
import getSingleMessage from '../methods/getSingleMessage';
import { IMessage, IUpload, IUser } from '../../definitions';
import { IMessage, IShareAttachment, IUpload, IUser } from '../../definitions';
import Deferred from './helpers/deferred';
import { debounce } from '../methods/helpers';
import database from '../database';
@ -15,6 +15,9 @@ import {
bufferToB64,
bufferToB64URI,
bufferToUtf8,
encryptAESCTR,
exportAESCTR,
generateAESCTRKey,
joinVectorData,
splitVectorData,
toString,
@ -269,6 +272,77 @@ export default class EncryptionRoom {
return message;
};
// Encrypt file
encryptFile = async (rid: string, attachment: IShareAttachment) => {
if (!this.ready) {
return attachment;
}
try {
const { path } = attachment;
const vector = await SimpleCrypto.utils.randomBytes(16);
const key = await generateAESCTRKey();
const exportedKey = await exportAESCTR(key);
const encryptedFile = await encryptAESCTR(path, exportedKey.k, bufferToB64(vector));
const getContent = async (_id: string, fileUrl: string) => {
const attachments = [];
let att = {
title: attachment.filename,
type: attachment.type,
mime: attachment.type,
size: attachment.size,
description: attachment.description,
encryption: {
key: exportedKey,
iv: bufferToB64(vector)
}
};
if (/^image\/.+/.test(attachment.type)) {
att = {
...att,
image_url: fileUrl,
image_type: attachment.type,
image_size: attachment.size
};
} else if (/^audio\/.+/.test(attachment.type)) {
att = {
...att,
audio_url: fileUrl,
audio_type: attachment.type,
audio_size: attachment.size
};
} else if (/^video\/.+/.test(attachment.type)) {
att = {
...att,
video_url: fileUrl,
video_type: attachment.type,
video_size: attachment.size
};
}
attachments.push(att);
const data = EJSON.stringify({
attachments
});
return {
algorithm: 'rc.v1.aes-sha2',
ciphertext: await Encryption.encryptText(rid, data)
};
};
return {
encryptedFile,
getContent
};
} catch {
// Do nothing
}
return attachment;
};
// Decrypt text
decryptText = async (msg: string | ArrayBuffer) => {
if (!msg) {

View File

@ -3,6 +3,7 @@ import { settings as RocketChatSettings } from '@rocket.chat/sdk';
import isEmpty from 'lodash/isEmpty';
import RNFetchBlob, { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob';
import { Alert } from 'react-native';
import { sha256 } from 'js-sha256';
import { Encryption } from '../encryption';
import { IUpload, IUser, TUploadModel } from '../../definitions';
@ -45,59 +46,69 @@ export async function cancelUpload(item: TUploadModel, rid: string): Promise<voi
}
}
const createUploadRecord = async ({
rid,
fileInfo,
tmid,
isForceTryAgain
}: {
rid: string;
fileInfo: IUpload;
tmid: string | undefined;
isForceTryAgain?: boolean;
}) => {
const db = database.active;
const uploadsCollection = db.get('uploads');
const uploadPath = getUploadPath(fileInfo.path, rid);
let uploadRecord: TUploadModel;
try {
uploadRecord = await uploadsCollection.find(uploadPath);
if (uploadRecord.id && !isForceTryAgain) {
return Alert.alert(i18n.t('FileUpload_Error'), i18n.t('Upload_in_progress'));
}
} catch (error) {
try {
await db.write(async () => {
uploadRecord = await uploadsCollection.create(u => {
u._raw = sanitizedRaw({ id: uploadPath }, uploadsCollection.schema);
Object.assign(u, fileInfo);
if (tmid) {
u.tmid = tmid;
}
if (u.subscription) {
u.subscription.id = rid;
}
});
});
} catch (e) {
throw e;
}
}
};
export function sendFileMessage(
rid: string,
fileInfo: IUpload,
tmid: string | undefined,
server: string,
user: Partial<Pick<IUser, 'id' | 'token'>>,
isForceTryAgain?: boolean,
getContent?: Function
isForceTryAgain?: boolean
): Promise<FetchBlobResponse | void> {
return new Promise(async (resolve, reject) => {
try {
const { id, token } = user;
const uploadUrl = `${server}/api/v1/rooms.media/${rid}`;
fileInfo.rid = rid;
const db = database.active;
const uploadsCollection = db.get('uploads');
const uploadPath = getUploadPath(fileInfo.path, rid);
let uploadRecord: TUploadModel;
try {
uploadRecord = await uploadsCollection.find(uploadPath);
if (uploadRecord.id && !isForceTryAgain) {
return Alert.alert(i18n.t('FileUpload_Error'), i18n.t('Upload_in_progress'));
}
} catch (error) {
try {
await db.write(async () => {
uploadRecord = await uploadsCollection.create(u => {
u._raw = sanitizedRaw({ id: uploadPath }, uploadsCollection.schema);
Object.assign(u, fileInfo);
if (tmid) {
u.tmid = tmid;
}
if (u.subscription) {
u.subscription.id = rid;
}
});
});
} catch (e) {
return log(e);
}
}
// const encryptedFileInfo = await Encryption.encryptMessage(fileInfo);
fileInfo.path = fileInfo.path.startsWith('file://') ? fileInfo.path.substring(7) : fileInfo.path;
await createUploadRecord({ rid, fileInfo, tmid, isForceTryAgain });
const encryptedFileInfo = await Encryption.encryptFile(rid, fileInfo);
const { encryptedFile, getContent } = encryptedFileInfo;
const formData: IFileUpload[] = [];
formData.push({
name: 'file',
type: fileInfo.type,
filename: fileInfo.name || 'fileMessage',
uri: fileInfo.path
type: 'file',
filename: sha256(fileInfo.name || 'fileMessage'),
uri: encryptedFile
});
// if (fileInfo.description) {
@ -152,13 +163,12 @@ export function sendFileMessage(
}
return item;
});
const response = await RNFetchBlob.fetch('POST', uploadUrl, headers, data);
const response = await RNFetchBlob.fetch('POST', `${server}/api/v1/rooms.media/${rid}`, headers, data);
const json = response.json();
let content;
if (getContent) {
content = await getContent(json.file._id, json.file.url);
console.log('🚀 ~ returnnewPromise ~ content:', content);
}
const mediaConfirm = await fetch(`${server}/api/v1/rooms.mediaConfirm/${rid}/${json.file._id}`, {

View File

@ -5,9 +5,6 @@ import { Text, View } from 'react-native';
import { connect } from 'react-redux';
import ShareExtension from 'rn-extensions-share';
import { Q } from '@nozbe/watermelondb';
import SimpleCrypto from 'react-native-simple-crypto';
import EJSON from 'ejson';
import { sha256 } from 'js-sha256';
import { IMessageComposerRef, MessageComposerContainer } from '../../containers/MessageComposer';
import { InsideStackParamList } from '../../stacks/types';
@ -38,8 +35,6 @@ import {
import { sendFileMessage, sendMessage } from '../../lib/methods';
import { hasPermission, isAndroid, canUploadFile, isReadOnly, isBlocked } from '../../lib/methods/helpers';
import { RoomContext } from '../RoomView/context';
import { Encryption } from '../../lib/encryption';
import { bufferToB64, encryptAESCTR, exportAESCTR, generateAESCTRKey } from '../../lib/encryption/utils';
interface IShareViewState {
selected: IShareAttachment;
@ -257,70 +252,27 @@ class ShareView extends Component<IShareViewProps, IShareViewState> {
// Send attachment
if (attachments.length) {
await Promise.all(
attachments.map(async ({ filename: name, mime: type, description, size, canUpload }) => {
attachments.map(({ filename: name, mime: type, description, size, path, canUpload }) => {
if (!canUpload) {
return Promise.resolve();
}
try {
const { path } = attachments[0];
const vector = await SimpleCrypto.utils.randomBytes(16);
const key = await generateAESCTRKey();
const exportedKey = await exportAESCTR(key);
const encryptedFile = await encryptAESCTR(path, exportedKey.k, bufferToB64(vector));
const getContent = async (_id: string, fileUrl: string) => {
const attachments = [];
const attachment = {
title: name,
type: 'file',
description,
// title_link: fileUrl,
// title_link_download: true,
encryption: {
key: exportedKey,
iv: bufferToB64(vector)
},
image_url: fileUrl,
image_type: type,
image_size: size
};
attachments.push(attachment);
const data = EJSON.stringify({
attachments
});
return {
algorithm: 'rc.v1.aes-sha2',
ciphertext: await Encryption.encryptText(room.rid, data)
};
};
// Send the file message with the encrypted path
return sendFileMessage(
room.rid,
{
rid: room.rid,
name: sha256(name),
description,
size,
type: 'file',
path: encryptedFile,
store: 'Uploads',
msg
},
thread?.id,
server,
{ id: user.id, token: user.token },
undefined,
getContent
);
} catch (e) {
console.error(e);
return Promise.reject(e);
}
return sendFileMessage(
room.rid,
{
rid: room.rid,
name,
description,
size,
type,
path,
store: 'Uploads',
msg
},
thread?.id,
server,
{ id: user.id, token: user.token }
);
})
);