signal-desktop/sticker-creator/preload.js
Ken Powers a90246cbe5 Passive UUID support
Co-authored-by: Scott Nonnenberg <scott@signal.org>
2020-03-24 16:59:35 -07:00

180 lines
4.9 KiB
JavaScript

/* global window */
const { ipcRenderer: ipc, remote } = require('electron');
const sharp = require('sharp');
const pify = require('pify');
const { readFile } = require('fs');
const config = require('url').parse(window.location.toString(), true).query;
const { noop, uniqBy } = require('lodash');
const pMap = require('p-map');
const { deriveStickerPackKey } = require('../js/modules/crypto');
const { makeGetter } = require('../preload_utils');
const { dialog } = remote;
const { systemPreferences } = remote.require('electron');
window.ROOT_PATH = window.location.href.startsWith('file') ? '../../' : '/';
window.PROTO_ROOT = '../../protos';
window.getEnvironment = () => config.environment;
window.getVersion = () => config.version;
window.getGuid = require('uuid/v4');
window.PQueue = require('p-queue').default;
window.localeMessages = ipc.sendSync('locale-data');
require('../js/logging');
window.log.info('sticker-creator starting up...');
const Signal = require('../js/modules/signal');
window.Signal = Signal.setup({});
const { initialize: initializeWebAPI } = require('../js/modules/web_api');
const WebAPI = initializeWebAPI({
url: config.serverUrl,
cdnUrl: config.cdnUrl,
certificateAuthority: config.certificateAuthority,
contentProxyUrl: config.contentProxyUrl,
proxyUrl: config.proxyUrl,
version: config.version,
});
window.convertToWebp = async (path, width = 512, height = 512) => {
const imgBuffer = await pify(readFile)(path);
const sharpImg = sharp(imgBuffer);
const meta = await sharpImg.metadata();
const buffer = await sharpImg
.resize({
width,
height,
fit: 'contain',
background: { r: 0, g: 0, b: 0, alpha: 0 },
})
.webp()
.toBuffer();
return {
path,
buffer,
src: `data:image/webp;base64,${buffer.toString('base64')}`,
meta,
};
};
window.encryptAndUpload = async (
manifest,
stickers,
cover,
onProgress = noop
) => {
const usernameItem = await window.Signal.Data.getItemById('uuid_id');
const oldUsernameItem = await window.Signal.Data.getItemById('number_id');
const passwordItem = await window.Signal.Data.getItemById('password');
if (!oldUsernameItem || !passwordItem) {
const { message } = window.localeMessages[
'StickerCreator--Authentication--error'
];
dialog.showMessageBox({
type: 'warning',
message,
});
throw new Error(message);
}
const { value: username } = usernameItem;
const { value: oldUsername } = oldUsernameItem;
const { value: password } = passwordItem;
const packKey = window.libsignal.crypto.getRandomBytes(32);
const encryptionKey = await deriveStickerPackKey(packKey);
const iv = window.libsignal.crypto.getRandomBytes(16);
const server = WebAPI.connect({
username: username || oldUsername,
password,
});
const uniqueStickers = uniqBy([...stickers, { webp: cover }], 'webp');
const manifestProto = new window.textsecure.protobuf.StickerPack();
manifestProto.title = manifest.title;
manifestProto.author = manifest.author;
manifestProto.stickers = stickers.map(({ emoji }, id) => {
const s = new window.textsecure.protobuf.StickerPack.Sticker();
s.id = id;
s.emoji = emoji;
return s;
});
const coverSticker = new window.textsecure.protobuf.StickerPack.Sticker();
coverSticker.id =
uniqueStickers.length === stickers.length ? 0 : uniqueStickers.length - 1;
coverSticker.emoji = '';
manifestProto.cover = coverSticker;
const encryptedManifest = await encrypt(
manifestProto.toArrayBuffer(),
encryptionKey,
iv
);
const encryptedStickers = await pMap(
uniqueStickers,
({ webp }) => encrypt(webp.buffer, encryptionKey, iv),
{ concurrency: 3 }
);
const packId = await server.putStickers(
encryptedManifest,
encryptedStickers,
onProgress
);
const hexKey = window.Signal.Crypto.hexFromBytes(packKey);
ipc.send('install-sticker-pack', packId, hexKey);
return { packId, key: hexKey };
};
async function encrypt(data, key, iv) {
const { ciphertext } = await window.textsecure.crypto.encryptAttachment(
// Convert Node Buffer to ArrayBuffer
window.Signal.Crypto.concatenateBytes(data),
key,
iv
);
return ciphertext;
}
const getThemeSetting = makeGetter('theme-setting');
async function resolveTheme() {
const theme = (await getThemeSetting()) || 'light';
if (process.platform === 'darwin' && theme === 'system') {
return systemPreferences.isDarkMode() ? 'dark' : 'light';
}
return theme;
}
async function applyTheme() {
window.document.body.classList.remove('dark-theme');
window.document.body.classList.remove('light-theme');
window.document.body.classList.add(`${await resolveTheme()}-theme`);
}
window.addEventListener('DOMContentLoaded', applyTheme);
if (systemPreferences && systemPreferences.subscribeNotification) {
systemPreferences.subscribeNotification(
'AppleInterfaceThemeChangedNotification',
applyTheme
);
}
window.log.info('sticker-creator preload complete...');