vietnamese

This commit is contained in:
nnduc
2026-10-24 17:46:20 +07:00
parent 5467b0aa2c
commit a3cf10cb0a
33 changed files with 4306 additions and 94 deletions
+56 -14
View File
@@ -11,6 +11,8 @@ var Upload = {
canvas: null,
thumbhash: null,
uploadButton: null,
// OMEMO media sharing (XEP-0454): {blob, fragment} while encrypting a chat upload
encrypted: null,
init: function (appendDate) {
Upload.launchInitiated();
@@ -37,15 +39,49 @@ var Upload = {
}
Upload.name = name + '.' + extension;
Upload.encrypted = null;
Upload_ajaxPrepare({
name: Upload.name,
size: Upload.file.size,
type: Upload.file.type
});
if (Upload.prependName == 'chat' && typeof Chat != 'undefined' && Chat.isEncrypted()) {
Upload.encrypt(Upload.file, extension).then(() => {
// Random name and no type: the upload service learns nothing
Upload_ajaxPrepare({
name: Upload.encrypted.name,
size: Upload.encrypted.blob.size,
type: 'application/octet-stream'
});
}).catch(e => {
console.error(e);
Upload.launchFailed();
Upload_ajaxFailed();
});
} else {
Upload_ajaxPrepare({
name: Upload.name,
size: Upload.file.size,
type: Upload.file.type
});
}
}
},
// AES-256-GCM, the key and IV go in the aesgcm:// URL fragment sent with OMEMO
encrypt: async function (file, extension) {
const iv = crypto.getRandomValues(new Uint8Array(12));
const key = crypto.getRandomValues(new Uint8Array(32));
const keyObj = await crypto.subtle.importKey('raw', key, 'AES-GCM', false, ['encrypt']);
const cipher = await crypto.subtle.encrypt({ name: 'AES-GCM', iv: iv }, keyObj, await file.arrayBuffer());
const hex = bytes => Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
extension = (extension || '').toLowerCase();
if (!/^[a-z0-9]{1,10}$/.test(extension)) extension = 'bin';
Upload.encrypted = {
blob: new Blob([cipher], { type: 'application/octet-stream' }),
name: hex(crypto.getRandomValues(new Uint8Array(16))) + '.' + extension,
fragment: hex(iv) + hex(key)
};
},
openFile: function () {
Upload.clear();
document.querySelector('input#file').click();
@@ -87,13 +123,18 @@ var Upload = {
},
launchAttached: function () {
for (var i = 0; i < Upload.attached.length; i++) {
Upload.attached[i]({
// Encrypted: only the aesgcm:// URL, no thumbnail in clear
let file = Upload.encrypted
? { id: Upload.id, aesgcm: Upload.get.replace(/^https?:\/\//, 'aesgcm://') + '#' + Upload.encrypted.fragment }
: {
id: Upload.id,
thumbhash: Upload.thumbhash ?? null,
thumbhashWidth: Upload.canvas ? Upload.canvas.width : null,
thumbhashHeight: Upload.canvas ? Upload.canvas.height : null
});
};
for (var i = 0; i < Upload.attached.length; i++) {
Upload.attached[i](file);
}
},
@@ -267,9 +308,10 @@ var Upload = {
}
},
request: function (route, id) {
request: function (route, id, get) {
Upload.xhr = new XMLHttpRequest();
Upload.id = id;
Upload.get = get;
if (Upload.uploadButton) {
Upload.uploadButton.classList.add('disabled');
@@ -310,7 +352,11 @@ var Upload = {
if (Upload.file != null) {
const formData = new FormData();
formData.append(Upload.id, Upload.file, Upload.name);
if (Upload.encrypted) {
formData.append(Upload.id, Upload.encrypted.blob, Upload.encrypted.name);
} else {
formData.append(Upload.id, Upload.file, Upload.name);
}
Upload.xhr.send(formData);
Upload.setProgress('arrow_upload_ready', '');
@@ -354,10 +400,6 @@ var Upload = {
Upload.preview(Upload.file);
}
if (typeof Chat != 'undefined' && Chat.isEncrypted()) {
document.querySelector('#upload').classList.add('encrypted');
}
document.querySelector('#upload div.drop').addEventListener('drop', ev => {
ev.preventDefault();