2018-08-29 05:19:18 +02:00
|
|
|
import EventListener from './eventlistener';
|
|
|
|
import asar from 'asar';
|
2018-08-30 04:39:23 +02:00
|
|
|
import fs from 'fs';
|
|
|
|
import path from 'path';
|
2018-08-29 05:19:18 +02:00
|
|
|
|
|
|
|
import { Modals } from 'ui';
|
|
|
|
import { Utils } from 'common';
|
2018-08-29 22:02:41 +02:00
|
|
|
import PluginManager from './pluginmanager';
|
2018-08-30 04:39:23 +02:00
|
|
|
import Globals from './globals';
|
2018-08-30 06:05:14 +02:00
|
|
|
import Security from './security';
|
2018-08-30 16:34:18 +02:00
|
|
|
import { ReactComponents } from './reactcomponents';
|
|
|
|
import Reflection from './reflection';
|
|
|
|
import DiscordApi from './discordapi';
|
2018-08-29 05:19:18 +02:00
|
|
|
|
2018-08-30 16:34:18 +02:00
|
|
|
export default class PackageInstaller {
|
2018-08-29 05:19:18 +02:00
|
|
|
|
2018-08-30 16:34:18 +02:00
|
|
|
/**
|
|
|
|
* Handler for drag and drop package install
|
|
|
|
* @param {String} filePath Path to local file
|
|
|
|
* @param {String} channelId Current channel id
|
|
|
|
*/
|
|
|
|
static async dragAndDropHandler(filePath, channelId) {
|
2018-08-29 05:19:18 +02:00
|
|
|
try {
|
2018-08-30 16:34:18 +02:00
|
|
|
const config = JSON.parse(asar.extractFile(filePath, 'config.json').toString());
|
2018-08-29 05:19:18 +02:00
|
|
|
const { info, main } = config;
|
|
|
|
|
|
|
|
let icon = null;
|
|
|
|
if (info.icon && info.icon_type) {
|
2018-08-30 16:34:18 +02:00
|
|
|
const extractIcon = asar.extractFile(filePath, info.icon);
|
2018-08-29 05:19:18 +02:00
|
|
|
icon = `data:${info.icon_type};base64,${Utils.arrayBufferToBase64(extractIcon)}`;
|
|
|
|
}
|
|
|
|
|
2018-08-30 08:08:52 +02:00
|
|
|
const isPlugin = info.type && info.type === 'plugin' || main.endsWith('.js');
|
2018-08-30 04:39:23 +02:00
|
|
|
|
2018-08-29 05:19:18 +02:00
|
|
|
// Show install modal
|
2018-08-30 16:34:18 +02:00
|
|
|
const modalResult = await Modals.installModal(isPlugin ? 'plugin' : 'theme', config, filePath, icon).promise;
|
2018-08-29 05:19:18 +02:00
|
|
|
|
|
|
|
if (modalResult === 0) {
|
|
|
|
// Upload it instead
|
|
|
|
}
|
|
|
|
|
2018-08-30 16:34:18 +02:00
|
|
|
} catch (err) {
|
|
|
|
console.log(err);
|
|
|
|
}
|
2018-08-29 05:19:18 +02:00
|
|
|
}
|
|
|
|
|
2018-08-30 06:05:14 +02:00
|
|
|
/**
|
|
|
|
* Hash and verify a package
|
|
|
|
* @param {Byte[]|String} bytesOrPath byte array of binary or path to local file
|
2018-08-30 08:08:52 +02:00
|
|
|
* @param {String} id Package id
|
2018-08-30 06:05:14 +02:00
|
|
|
*/
|
2018-08-30 08:08:52 +02:00
|
|
|
static async verifyPackage(bytesOrPath, id) {
|
2018-08-30 06:05:14 +02:00
|
|
|
const bytes = typeof bytesOrPath === 'string' ? fs.readFileSync(bytesOrPath) : bytesOrPath;
|
|
|
|
// Temporary hash to simulate response from server
|
2018-08-30 08:08:52 +02:00
|
|
|
const tempVerified = ['2e3532ee366816adc37b0f478bfef35e03f96e7aeee9b115f5918ef6a4e94de8', '06a2eb4e37b926354ab80cd83207db67e544c932e9beddce545967a21f8db5aa'];
|
2018-08-30 06:05:14 +02:00
|
|
|
const hashBytes = Security.hash('sha256', bytes, 'hex');
|
|
|
|
|
2018-08-30 08:08:52 +02:00
|
|
|
return tempVerified.includes(hashBytes);
|
2018-08-30 06:05:14 +02:00
|
|
|
}
|
|
|
|
|
2018-08-30 04:39:23 +02:00
|
|
|
// TODO lots of stuff
|
|
|
|
/**
|
|
|
|
* Installs or updates defined package
|
|
|
|
* @param {Byte[]|String} bytesOrPath byte array of binary or path to local file
|
|
|
|
* @param {String} name Package name
|
|
|
|
* @param {Boolean} update Does an older version already exist
|
|
|
|
*/
|
2018-08-30 08:08:52 +02:00
|
|
|
static async installPackage(bytesOrPath, id, update = false) {
|
2018-08-30 17:30:49 +02:00
|
|
|
try {
|
|
|
|
const bytes = typeof bytesOrPath === 'string' ? fs.readFileSync(bytesOrPath) : bytesOrPath;
|
2018-08-30 06:05:14 +02:00
|
|
|
|
2018-08-30 17:30:49 +02:00
|
|
|
const outputName = `${id}.bd`;
|
|
|
|
const outputPath = path.join(Globals.getPath('plugins'), outputName);
|
|
|
|
fs.writeFileSync(outputPath, bytes);
|
2018-08-30 04:39:23 +02:00
|
|
|
|
2018-08-30 17:30:49 +02:00
|
|
|
let newContent = null;
|
|
|
|
|
|
|
|
if (!update) {
|
|
|
|
newContent = await PluginManager.preloadPackedContent(outputName);
|
|
|
|
} else {
|
|
|
|
newContent = await PluginManager.reloadContent(PluginManager.getPluginById(id));
|
|
|
|
}
|
|
|
|
|
|
|
|
return newContent;
|
2018-08-30 04:39:23 +02:00
|
|
|
|
2018-08-30 17:30:49 +02:00
|
|
|
} catch (err) {
|
|
|
|
throw err;
|
|
|
|
}
|
2018-08-30 04:39:23 +02:00
|
|
|
}
|
|
|
|
|
2018-08-30 16:34:18 +02:00
|
|
|
/**
|
|
|
|
* Patches Discord upload area for .bd files
|
|
|
|
*/
|
|
|
|
static async uploadAreaPatch() {
|
|
|
|
const { selector } = Reflection.resolve('uploadArea');
|
|
|
|
this.UploadArea = await ReactComponents.getComponent('UploadArea', { selector });
|
|
|
|
|
|
|
|
const reflect = Reflection.DOM(selector);
|
|
|
|
const stateNode = reflect.getComponentStateNode(this.UploadArea);
|
|
|
|
const callback = function (e) {
|
|
|
|
if (!e.dataTransfer.files.length || !e.dataTransfer.files[0].name.endsWith('.bd')) return;
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
e.stopImmediatePropagation();
|
|
|
|
stateNode.clearDragging();
|
|
|
|
|
|
|
|
PackageInstaller.dragAndDropHandler(e.dataTransfer.files[0].path, DiscordApi.currentChannel.id);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Remove their handler, add ours, then read theirs to give ours priority to stop theirs when we get a .bd file.
|
|
|
|
reflect.element.removeEventListener('drop', stateNode.handleDrop);
|
|
|
|
reflect.element.addEventListener('drop', callback);
|
|
|
|
reflect.element.addEventListener('drop', stateNode.handleDrop);
|
|
|
|
|
|
|
|
this.unpatchUploadArea = function () {
|
|
|
|
reflect.element.removeEventListener('drop', callback);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-08-29 05:19:18 +02:00
|
|
|
}
|