starting upload poc

This commit is contained in:
Diego Mello 2024-05-13 15:49:12 -03:00
parent 09bc78c068
commit fe1ea5678c
3 changed files with 240 additions and 138 deletions

View File

@ -43,6 +43,7 @@ class Encryption {
handshake: Function; handshake: Function;
decrypt: Function; decrypt: Function;
encrypt: Function; encrypt: Function;
encryptText: Function;
encryptFile: Function; encryptFile: Function;
decryptFile: Function; decryptFile: Function;
encryptUpload: Function; encryptUpload: Function;
@ -446,6 +447,11 @@ class Encryption {
}; };
}; };
encryptText = async (rid: string, text: string) => {
const roomE2E = await this.getRoomInstance(rid);
return roomE2E.encryptText(text);
};
// Encrypt a message // Encrypt a message
encryptMessage = async (message: IMessage | IUpload) => { encryptMessage = async (message: IMessage | IUpload) => {
const { rid } = message; const { rid } = message;

View File

@ -1,7 +1,7 @@
import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord';
import { settings as RocketChatSettings } from '@rocket.chat/sdk'; import { settings as RocketChatSettings } from '@rocket.chat/sdk';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob'; import RNFetchBlob, { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob';
import { Alert } from 'react-native'; import { Alert } from 'react-native';
import { Encryption } from '../encryption'; import { Encryption } from '../encryption';
@ -51,13 +51,14 @@ export function sendFileMessage(
tmid: string | undefined, tmid: string | undefined,
server: string, server: string,
user: Partial<Pick<IUser, 'id' | 'token'>>, user: Partial<Pick<IUser, 'id' | 'token'>>,
isForceTryAgain?: boolean isForceTryAgain?: boolean,
getContent?: Function
): Promise<FetchBlobResponse | void> { ): Promise<FetchBlobResponse | void> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
const { id, token } = user; const { id, token } = user;
const uploadUrl = `${server}/api/v1/rooms.upload/${rid}`; const uploadUrl = `${server}/api/v1/rooms.media/${rid}`;
fileInfo.rid = rid; fileInfo.rid = rid;
@ -89,7 +90,7 @@ export function sendFileMessage(
} }
} }
const encryptedFileInfo = await Encryption.encryptMessage(fileInfo); // const encryptedFileInfo = await Encryption.encryptMessage(fileInfo);
const formData: IFileUpload[] = []; const formData: IFileUpload[] = [];
formData.push({ formData.push({
@ -99,38 +100,38 @@ export function sendFileMessage(
uri: fileInfo.path uri: fileInfo.path
}); });
if (fileInfo.description) { // if (fileInfo.description) {
formData.push({ // formData.push({
name: 'description', // name: 'description',
data: encryptedFileInfo.description // data: encryptedFileInfo.description
}); // });
} // }
if (fileInfo.msg) { // if (fileInfo.msg) {
formData.push({ // formData.push({
name: 'msg', // name: 'msg',
data: fileInfo.msg // data: fileInfo.msg
}); // });
} // }
if (tmid) { // if (tmid) {
formData.push({ // formData.push({
name: 'tmid', // name: 'tmid',
data: tmid // data: tmid
}); // });
} // }
const { version: serverVersion } = store.getState().server; // const { version: serverVersion } = store.getState().server;
if (encryptedFileInfo.t === E2E_MESSAGE_TYPE && compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '6.8.0')) { // if (encryptedFileInfo.t === E2E_MESSAGE_TYPE && compareServerVersion(serverVersion, 'greaterThanOrEqualTo', '6.8.0')) {
formData.push({ // formData.push({
name: 't', // name: 't',
data: encryptedFileInfo.t // data: encryptedFileInfo.t
}); // });
formData.push({ // formData.push({
name: 'e2e', // name: 'e2e',
data: encryptedFileInfo.e2e // data: encryptedFileInfo.e2e
}); // });
} // }
const headers = { const headers = {
...RocketChatSettings.customHeaders, ...RocketChatSettings.customHeaders,
@ -139,61 +140,110 @@ export function sendFileMessage(
'X-User-Id': id 'X-User-Id': id
}; };
uploadQueue[uploadPath] = FileUpload.fetch('POST', uploadUrl, headers, formData); try {
const data = formData.map(item => {
uploadQueue[uploadPath].uploadProgress(async (loaded: number, total: number) => { if (item.uri) {
try { return {
await db.write(async () => { name: item.name,
await uploadRecord.update(u => { type: item.type,
u.progress = Math.floor((loaded / total) * 100); filename: item.filename,
}); data: RNFetchBlob.wrap(decodeURI(item.uri))
}); };
} catch (e) {
log(e);
}
});
uploadQueue[uploadPath].then(async response => {
if (response.respInfo.status >= 200 && response.respInfo.status < 400) {
// If response is all good...
try {
await db.write(async () => {
await uploadRecord.destroyPermanently();
});
resolve(response);
} catch (e) {
log(e);
} }
} else { return item;
try { });
await db.write(async () => { console.log('🚀 ~ data ~ data:', data);
await uploadRecord.update(u => { const response = await RNFetchBlob.fetch('POST', uploadUrl, headers, data);
u.error = true; console.log(response);
});
});
} catch (e) {
log(e);
}
try {
reject(response);
} catch (e) {
reject(e);
}
}
});
uploadQueue[uploadPath].catch(async error => { const json = response.json();
try { console.log('🚀 ~ returnnewPromise ~ json:', json);
await db.write(async () => {
await uploadRecord.update(u => { console.log('🚀 ~ returnnewPromise ~ getContent:', getContent);
u.error = true; let content;
}); if (getContent) {
}); content = await getContent(json.file._id, json.file.url);
} catch (e) { console.log('🚀 ~ returnnewPromise ~ content:', content);
log(e);
} }
reject(error);
}); const mediaConfirm = await fetch(`${server}/api/v1/rooms.mediaConfirm/${rid}/${json.file._id}`, {
method: 'POST',
headers,
body: JSON.stringify({
msg: fileInfo.msg,
tmid: fileInfo.tmid,
description: fileInfo.description,
t: fileInfo.t,
content
})
});
console.log('🚀 ~ returnnewPromise ~ mediaConfirm :', mediaConfirm);
} catch (e) {
console.error(e);
}
// uploadQueue[uploadPath] = FileUpload.fetch('POST', uploadUrl, headers, formData);
// uploadQueue[uploadPath].uploadProgress(async (loaded: number, total: number) => {
// try {
// await db.write(async () => {
// await uploadRecord.update(u => {
// u.progress = Math.floor((loaded / total) * 100);
// });
// });
// } catch (e) {
// log(e);
// }
// });
// uploadQueue[uploadPath].then(async response => {
// // If response is all good...
// if (response.respInfo.status >= 200 && response.respInfo.status < 400) {
// try {
// console.log('🚀 ~ returnnewPromise ~ response:', response);
// console.log('🚀 ~ returnnewPromise ~ response:', response.data);
// // if (getContent) {
// // const content = getContent(response.json().file._id, response.json().file.url);
// // console.log('🚀 ~ returnnewPromise ~ content:', content);
// // }
// await db.write(async () => {
// await uploadRecord.destroyPermanently();
// });
// resolve(response);
// } catch (e) {
// log(e);
// }
// } else {
// try {
// await db.write(async () => {
// await uploadRecord.update(u => {
// u.error = true;
// });
// });
// } catch (e) {
// log(e);
// }
// try {
// reject(response);
// } catch (e) {
// reject(e);
// }
// }
// });
// uploadQueue[uploadPath].catch(async error => {
// try {
// await db.write(async () => {
// await uploadRecord.update(u => {
// u.error = true;
// });
// });
// } catch (e) {
// log(e);
// }
// reject(error);
// });
} catch (e) { } catch (e) {
log(e); log(e);
} }

View File

@ -6,6 +6,7 @@ import { connect } from 'react-redux';
import ShareExtension from 'rn-extensions-share'; import ShareExtension from 'rn-extensions-share';
import { Q } from '@nozbe/watermelondb'; import { Q } from '@nozbe/watermelondb';
import SimpleCrypto from 'react-native-simple-crypto'; import SimpleCrypto from 'react-native-simple-crypto';
import EJSON from 'ejson';
import { IMessageComposerRef, MessageComposerContainer } from '../../containers/MessageComposer'; import { IMessageComposerRef, MessageComposerContainer } from '../../containers/MessageComposer';
import { InsideStackParamList } from '../../stacks/types'; import { InsideStackParamList } from '../../stacks/types';
@ -37,7 +38,15 @@ import { sendFileMessage, sendMessage } from '../../lib/methods';
import { hasPermission, isAndroid, canUploadFile, isReadOnly, isBlocked } from '../../lib/methods/helpers'; import { hasPermission, isAndroid, canUploadFile, isReadOnly, isBlocked } from '../../lib/methods/helpers';
import { RoomContext } from '../RoomView/context'; import { RoomContext } from '../RoomView/context';
import { Encryption } from '../../lib/encryption'; import { Encryption } from '../../lib/encryption';
import { b64URIToBuffer, decryptAESCTR, encryptAESCTR, exportAESCTR, generateAESCTRKey } from '../../lib/encryption/utils'; import {
Base64,
b64URIToBuffer,
bufferToB64,
decryptAESCTR,
encryptAESCTR,
exportAESCTR,
generateAESCTRKey
} from '../../lib/encryption/utils';
interface IShareViewState { interface IShareViewState {
selected: IShareAttachment; selected: IShareAttachment;
@ -252,64 +261,101 @@ class ShareView extends Component<IShareViewProps, IShareViewState> {
} }
try { try {
const { path } = attachments[0]; // Send attachment
const vector = await SimpleCrypto.utils.randomBytes(16); if (attachments.length) {
const key = await generateAESCTRKey(); console.log('🚀 ~ ShareView ~ send= ~ attachments:', attachments);
await Promise.all(
attachments.map(async ({ filename: name, mime: type, description, size, canUpload }) => {
if (!canUpload) {
return Promise.resolve(); // Resolve immediately if upload is not allowed
}
const exportedKey = exportAESCTR(key); try {
console.log('🚀 ~ ShareView ~ send= ~ exportedKey:', exportedKey, exportedKey.k); const { path } = attachments[0]; // Consider if you need a specific attachment or iterate over each
const vector = await SimpleCrypto.utils.randomBytes(16);
const key = await generateAESCTRKey();
const exportedKeyArrayBuffer = b64URIToBuffer(exportedKey.k); const exportedKey = await exportAESCTR(key);
console.log('🚀 ~ ShareView ~ send= ~ exportedKeyArrayBuffer:', exportedKeyArrayBuffer); console.log('🚀 ~ ShareView ~ send= ~ exportedKey:', exportedKey, exportedKey.k);
const encryptedFile = await encryptAESCTR(path, exportedKeyArrayBuffer, vector); const exportedKeyArrayBuffer = b64URIToBuffer(exportedKey.k);
console.log('🚀 ~ ShareView ~ send= ~ encryptedFile:', encryptedFile); console.log('🚀 ~ ShareView ~ send= ~ exportedKeyArrayBuffer:', exportedKeyArrayBuffer);
const decryptedFile = await decryptAESCTR(encryptedFile, exportedKeyArrayBuffer, vector); const encryptedFile = await encryptAESCTR(path, exportedKeyArrayBuffer, vector);
console.log('🚀 ~ ShareView ~ send= ~ decryptedFile:', decryptedFile); console.log('🚀 ~ ShareView ~ send= ~ encryptedFile:', encryptedFile);
} catch (e) {
console.error(e); const decryptedFile = await decryptAESCTR(encryptedFile, exportedKeyArrayBuffer, vector);
console.log('🚀 ~ ShareView ~ send= ~ decryptedFile:', decryptedFile);
const getContent = async (_id: string, fileUrl: string) => {
console.log('🚀 ~ ShareView ~ getContent ~ _id:', _id, fileUrl);
const attachments = [];
console.log('🚀 ~ ShareView ~ getContent ~ attachment.encryption.exportedKey:', vector, exportedKey);
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
});
console.log('🚀 ~ ShareView ~ getContent ~ attachments:', attachments, data);
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,
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); // Ensure that errors are propagated
}
})
);
// Send text message
} else if (text.length) {
await sendMessage(room.rid, text, thread?.id, { id: user.id, token: user.token } as IUser);
}
} catch {
if (!this.isShareExtension) {
const text = this.messageComposerRef.current?.getText();
this.finishShareView(text, this.state.selectedMessages);
}
} }
// try {
// // Send attachment
// if (attachments.length) {
// console.log('🚀 ~ ShareView ~ send= ~ attachments:', attachments);
// await Promise.all(
// attachments.map(async ({ filename: name, mime: type, description, size, path, canUpload }) => {
// if (canUpload) {
// return sendFileMessage(
// room.rid,
// {
// rid: room.rid,
// name,
// description,
// size,
// type,
// path,
// store: 'Uploads',
// msg
// },
// thread?.id,
// server,
// { id: user.id, token: user.token }
// );
// }
// return Promise.resolve();
// })
// );
// // Send text message
// } else if (text.length) {
// await sendMessage(room.rid, text, thread?.id, { id: user.id, token: user.token } as IUser);
// }
// } catch {
// if (!this.isShareExtension) {
// const text = this.messageComposerRef.current?.getText();
// this.finishShareView(text, this.state.selectedMessages);
// }
// }
// if it's share extension this should close // if it's share extension this should close
if (this.isShareExtension) { if (this.isShareExtension) {
sendLoadingEvent({ visible: false }); sendLoadingEvent({ visible: false });