Save attachments with macOS quarantine attribute
* Attachments: Always save file to downloads directory, show toast * Add new build:dev command for casual builds
This commit is contained in:
parent
65befde0fa
commit
1bf9ca7233
11 changed files with 202 additions and 52 deletions
|
@ -1,11 +1,22 @@
|
|||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
const { app, shell, remote } = require('electron');
|
||||
|
||||
const pify = require('pify');
|
||||
const glob = require('glob');
|
||||
const fse = require('fs-extra');
|
||||
const toArrayBuffer = require('to-arraybuffer');
|
||||
const { map, isArrayBuffer, isString } = require('lodash');
|
||||
const sanitizeFilename = require('sanitize-filename');
|
||||
const getGuid = require('uuid/v4');
|
||||
|
||||
let xattr;
|
||||
try {
|
||||
// eslint-disable-next-line global-require, import/no-extraneous-dependencies
|
||||
xattr = require('fs-xattr');
|
||||
} catch (e) {
|
||||
console.log('x-attr dependncy did not load successfully');
|
||||
}
|
||||
|
||||
const PATH = 'attachments.noindex';
|
||||
const STICKER_PATH = 'stickers.noindex';
|
||||
|
@ -153,6 +164,70 @@ exports.copyIntoAttachmentsDirectory = root => {
|
|||
};
|
||||
};
|
||||
|
||||
exports.writeToDownloads = async ({ data, name }) => {
|
||||
const appToUse = app || remote.app;
|
||||
const downloadsPath =
|
||||
appToUse.getPath('downloads') || appToUse.getPath('home');
|
||||
const sanitized = sanitizeFilename(name);
|
||||
|
||||
const extension = path.extname(sanitized);
|
||||
const basename = path.basename(sanitized, extension);
|
||||
const getCandidateName = count => `${basename} (${count})${extension}`;
|
||||
|
||||
const existingFiles = await fse.readdir(downloadsPath);
|
||||
let candidateName = sanitized;
|
||||
let count = 0;
|
||||
while (existingFiles.includes(candidateName)) {
|
||||
count += 1;
|
||||
candidateName = getCandidateName(count);
|
||||
}
|
||||
|
||||
const target = path.join(downloadsPath, candidateName);
|
||||
const normalized = path.normalize(target);
|
||||
if (!normalized.startsWith(downloadsPath)) {
|
||||
throw new Error('Invalid filename!');
|
||||
}
|
||||
|
||||
await fse.writeFile(normalized, Buffer.from(data));
|
||||
|
||||
if (process.platform === 'darwin' && xattr) {
|
||||
// kLSQuarantineTypeInstantMessageAttachment
|
||||
const type = '0003';
|
||||
|
||||
// Hexadecimal seconds since epoch
|
||||
const timestamp = Math.trunc(Date.now() / 1000).toString(16);
|
||||
|
||||
const appName = 'Signal';
|
||||
const guid = getGuid();
|
||||
|
||||
// https://ilostmynotes.blogspot.com/2012/06/gatekeeper-xprotect-and-quarantine.html
|
||||
const attrValue = `${type};${timestamp};${appName};${guid}`;
|
||||
|
||||
await xattr.set(normalized, 'com.apple.quarantine', attrValue);
|
||||
}
|
||||
|
||||
return {
|
||||
fullPath: normalized,
|
||||
name: candidateName,
|
||||
};
|
||||
};
|
||||
|
||||
exports.openFileInDownloads = async name => {
|
||||
const shellToUse = shell || remote.shell;
|
||||
const appToUse = app || remote.app;
|
||||
|
||||
const downloadsPath =
|
||||
appToUse.getPath('downloads') || appToUse.getPath('home');
|
||||
const target = path.join(downloadsPath, name);
|
||||
|
||||
const normalized = path.normalize(target);
|
||||
if (!normalized.startsWith(downloadsPath)) {
|
||||
throw new Error('Invalid filename!');
|
||||
}
|
||||
|
||||
shellToUse.showItemInFolder(normalized);
|
||||
};
|
||||
|
||||
// createWriterForNew :: AttachmentsPath ->
|
||||
// ArrayBuffer ->
|
||||
// IO (Promise RelativePath)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue