commit 5923d864f60d5d9b3ba2dc6280565e55e1945c55 Author: putyy Date: Fri Sep 1 16:43:55 2023 +0800 init diff --git a/.env.development b/.env.development new file mode 100755 index 0000000..90de821 --- /dev/null +++ b/.env.development @@ -0,0 +1 @@ +VITE_APP_API="" diff --git a/.env.production b/.env.production new file mode 100755 index 0000000..90de821 --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +VITE_APP_API="" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f05446c --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +dist-electron +release +*.local + +# Editor directories and files +.vscode/.debug.env +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.env + +# lockfile +package-lock.json +pnpm-lock.yaml +yarn.lock \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..22edc0e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 草鞋没号 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8328df0 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# res-downloader + +🎯 基于 [electron-vite-vue]( https://github.com/electron-vite/electron-vite-vue.git) +📦 操作简单、可获取不同类型的资源 +💪 支持获取视频、音频、图片、m3u8 +🖥 支持获取视频号、抖音、快手、小红书、酷狗音乐、qq音乐等网络资源 + +## 扩展开发 +```sh +git clone https://github.com/putyy/res-downloader + +cd res-downloader + +yarn install + +yarn run dev + +# 打包mac +yarn run build --mac + +# 打包win +yarn run build --win +``` + +## 软件截图 +![](public/show.png) + +## 实现原理 +> 通过代理网络抓包拦截响应,筛选出有用的资源,同fiddler、charles等抓包软件、浏览器F12打开控制也能达到目的,只不过这些软件需要手动进行筛选,对于小白用户上手还是有点难度,所以就有了本项目这样的软件。 + + +## 参考项目 + +- [WeChatVideoDownloader](https://github.com/lecepin/WeChatVideoDownloader) 原项目是react写的,且有段时间未更新,本项目参考原项目用vue3重写了一下,核心逻辑没什么变化,主要是增加了一些新的功能,再次感谢! diff --git a/auto-imports.d.ts b/auto-imports.d.ts new file mode 100755 index 0000000..1d89ee8 --- /dev/null +++ b/auto-imports.d.ts @@ -0,0 +1,9 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// noinspection JSUnusedGlobalSymbols +// Generated by unplugin-auto-import +export {} +declare global { + +} diff --git a/components.d.ts b/components.d.ts new file mode 100755 index 0000000..9603d8e --- /dev/null +++ b/components.d.ts @@ -0,0 +1,32 @@ +/* eslint-disable */ +/* prettier-ignore */ +// @ts-nocheck +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 +export {} + +declare module 'vue' { + export interface GlobalComponents { + ElButton: typeof import('element-plus/es')['ElButton'] + ElContainer: typeof import('element-plus/es')['ElContainer'] + ElFooter: typeof import('element-plus/es')['ElFooter'] + ElForm: typeof import('element-plus/es')['ElForm'] + ElFormItem: typeof import('element-plus/es')['ElFormItem'] + ElHeader: typeof import('element-plus/es')['ElHeader'] + ElIcon: typeof import('element-plus/es')['ElIcon'] + ElInput: typeof import('element-plus/es')['ElInput'] + ElMain: typeof import('element-plus/es')['ElMain'] + ElMenu: typeof import('element-plus/es')['ElMenu'] + ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] + ElOption: typeof import('element-plus/es')['ElOption'] + ElRow: typeof import('element-plus/es')['ElRow'] + ElSelect: typeof import('element-plus/es')['ElSelect'] + ElTable: typeof import('element-plus/es')['ElTable'] + ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] + Footer: typeof import('./src/components/layout/Footer.vue')['default'] + Index: typeof import('./src/components/layout/Index.vue')['default'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] + Sidebar: typeof import('./src/components/layout/Sidebar.vue')['default'] + } +} diff --git a/electron-builder.json5 b/electron-builder.json5 new file mode 100644 index 0000000..f18b43d --- /dev/null +++ b/electron-builder.json5 @@ -0,0 +1,45 @@ +/** + * @see https://www.electron.build/configuration/configuration + */ +{ + "$schema": "https://raw.githubusercontent.com/electron-userland/electron-builder/master/packages/app-builder-lib/scheme.json", + "appId": "com.putyy.ResDownloader", + "asar": true, + "directories": { + "output": "release/${version}" + }, + "files": [ + "dist-electron", + "dist", + "electron/res/**/*" + ], + "mac": { + "icon": "electron/res/icon/icons/mac/icon.icns", + "artifactName": "${productName}_${version}.${ext}", + "target": [ + "dmg" + ] + }, + "win": { + "icon": "electron/res/icon/icons/win/icon.ico", + "target": [ + { + "target": "nsis", + "arch": [ + "x64" + ] + } + ], + "artifactName": "${productName}_${version}.${ext}" + }, + "nsis": { + "oneClick": false, + "perMachine": false, + "allowElevation": true, + "allowToChangeInstallationDirectory": true, + "deleteAppDataOnUninstall": false + }, + "extraResources": [ + "electron/res" + ] +} diff --git a/electron/electron-env.d.ts b/electron/electron-env.d.ts new file mode 100644 index 0000000..b4c4211 --- /dev/null +++ b/electron/electron-env.d.ts @@ -0,0 +1,11 @@ +/// + +declare namespace NodeJS { + interface ProcessEnv { + VSCODE_DEBUG?: 'true' + DIST_ELECTRON: string + DIST: string + /** /dist/ or /public/ */ + VITE_PUBLIC: string + } +} diff --git a/electron/main/cert.ts b/electron/main/cert.ts new file mode 100755 index 0000000..99699e1 --- /dev/null +++ b/electron/main/cert.ts @@ -0,0 +1,47 @@ +import CONFIG from './const' +import {mkdirp} from 'mkdirp' +import fs from 'fs' +import path from 'path' +import {clipboard, dialog} from 'electron' +import spawn from 'cross-spawn' + +export function checkCertInstalled() { + return fs.existsSync(CONFIG.INSTALL_CERT_FLAG) +} + +export async function installCert(checkInstalled = true) { + if (checkInstalled && checkCertInstalled()) { + return; + } + mkdirp.sync(path.dirname(CONFIG.INSTALL_CERT_FLAG)) + + if (process.platform === 'darwin') { + return new Promise((resolve, reject) => { + clipboard.writeText( + `echo "输入本地登录密码" && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${CONFIG.CERT_PUBLIC_PATH}" && touch ${CONFIG.INSTALL_CERT_FLAG} && echo "安装完成"`, + ) + dialog.showMessageBoxSync({ + type: 'info', + message: `命令已复制到剪贴板,粘贴命令到终端并运行以安装并信任证书`, + }); + + reject() + }); + } else { + return new Promise((resolve: any, reject) => { + const result = spawn.sync(CONFIG.WIN_CERT_INSTALL_HELPER, [ + '-c', + '-add', + CONFIG.CERT_PUBLIC_PATH, + '-s', + 'root', + ]); + if (result.stdout.toString().indexOf('Succeeded') > -1) { + fs.writeFileSync(CONFIG.INSTALL_CERT_FLAG, '') + resolve() + } else { + reject() + } + }) + } +} diff --git a/electron/main/const.ts b/electron/main/const.ts new file mode 100755 index 0000000..010b209 --- /dev/null +++ b/electron/main/const.ts @@ -0,0 +1,30 @@ +import path from 'path' +import isDev from 'electron-is-dev' +import os from 'os' +import {app} from 'electron' + +const APP_PATH = app.getAppPath(); +// 对于一些 shell 去执行的文件,asar 目录下无法使用。配合 extraResources +const EXECUTABLE_PATH = path.join( + APP_PATH.indexOf('app.asar') > -1 + ? APP_PATH.substring(0, APP_PATH.indexOf('app.asar')) + : APP_PATH, + 'electron/res', +) + +const HOME_PATH = path.join(os.homedir(), '.res-downloader@putyy') + +export default { + IS_DEV: isDev, + EXECUTABLE_PATH, + HOME_PATH, + CERT_PRIVATE_PATH: path.join(EXECUTABLE_PATH, './keys/private.pem'), + CERT_PUBLIC_PATH: path.join(EXECUTABLE_PATH, './keys/public.pem'), + INSTALL_CERT_FLAG: path.join(HOME_PATH, './installed.lock'), + WIN_CERT_INSTALL_HELPER: path.join(EXECUTABLE_PATH, './w_c.exe'), + APP_CN_NAME: '资源下载器', + APP_EN_NAME: 'ResDownloader', + REGEDIT_VBS_PATH: path.join(EXECUTABLE_PATH, './regedit-vbs'), + OPEN_SSL_BIN_PATH: path.join(EXECUTABLE_PATH, './openssl/openssl.exe'), + OPEN_SSL_CNF_PATH: path.join(EXECUTABLE_PATH, './openssl/openssl.cnf'), +}; diff --git a/electron/main/index.ts b/electron/main/index.ts new file mode 100644 index 0000000..6a3dbac --- /dev/null +++ b/electron/main/index.ts @@ -0,0 +1,182 @@ +import {app, BrowserWindow, shell, ipcMain, Menu} from 'electron' +import {release} from 'node:os' +import {join} from 'node:path' +import CONFIG from './const' +import initIPC, {setWin} from './ipc' + +// The built directory structure +// +// ├─┬ dist-electron +// │ ├─┬ main +// │ │ └── index.js > Electron-Main +// │ └─┬ preload +// │ └── index.js > Preload-Scripts +// ├─┬ dist +// │ └── index.html > Electron-Renderer +// +process.env.DIST_ELECTRON = join(__dirname, '..') +process.env.DIST = join(process.env.DIST_ELECTRON, '../dist') +process.env.VITE_PUBLIC = process.env.VITE_DEV_SERVER_URL + ? join(process.env.DIST_ELECTRON, '../public') + : process.env.DIST + +// Disable GPU Acceleration for Windows 7 +if (release().startsWith('6.1')) app.disableHardwareAcceleration() + +// Set application name for Windows 10+ notifications +if (process.platform === 'win32') app.setAppUserModelId(app.getName()) + +if (!app.requestSingleInstanceLock()) { + app.quit() + process.exit(0) +} + +// Remove electron security warnings +// This warning only shows in development mode +// Read more on https://www.electronjs.org/docs/latest/tutorial/security +// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true' + +app.commandLine.appendSwitch('--no-proxy-server') +process.on('uncaughtException', () => { +}); +process.on('unhandledRejection', () => { +}); + + +let mainWindow: BrowserWindow | null = null +let previewWin: BrowserWindow | null = null + +// Here, you can also use other preload +const preload = join(__dirname, '../preload/index.js') +const url = process.env.VITE_DEV_SERVER_URL +const indexHtml = join(process.env.DIST, 'index.html') + +// app.whenReady().then(createWindow) + +app.on('window-all-closed', () => { + mainWindow = null + if (process.platform !== 'darwin') app.quit() +}) + +app.on('second-instance', () => { + if (mainWindow) { + // Focus on the main window if the user tried to open another + if (mainWindow.isMinimized()) mainWindow.restore() + mainWindow.focus() + } +}) + +app.on('activate', () => { + const allWindows = BrowserWindow.getAllWindows() + if (allWindows.length) { + allWindows[0].focus() + } else { + createWindow() + createPreviewWindow(mainWindow) + setWin(mainWindow, previewWin) + } +}) + +// New window example arg: new windows url +ipcMain.handle('open-win', (_, arg) => { + const childWindow = new BrowserWindow({ + webPreferences: { + preload, + nodeIntegration: true, + contextIsolation: false, + }, + }) + + if (process.env.VITE_DEV_SERVER_URL) { + childWindow.loadURL(`${url}#${arg}`) + } else { + childWindow.loadFile(indexHtml, {hash: arg}) + } +}) + +function createWindow() { + Menu.setApplicationMenu(null); + + mainWindow = new BrowserWindow({ + title: 'Main window', + icon: join(process.env.VITE_PUBLIC, 'favicon.ico'), + width: 800, + height: 600, + webPreferences: { + preload, + // Warning: Enable nodeIntegration and disable contextIsolation is not secure in production + // Consider using contextBridge.exposeInMainWorld + // Read more on https://www.electronjs.org/docs/latest/tutorial/context-isolation + nodeIntegration: true, + contextIsolation: false, + webSecurity: false, + }, + }) + + if (process.env.VITE_DEV_SERVER_URL) { // electron-vite-vue#298 + mainWindow.loadURL(url).then(r => { + }) + // Open devTool if the app is not packaged + // mainWindow.webContents.openDevTools() + } else { + mainWindow.loadFile(indexHtml).then(r => { + }) + } + + + CONFIG.IS_DEV && mainWindow.webContents.openDevTools() + + // Test actively push message to the Electron-Renderer + mainWindow.webContents.on('did-finish-load', () => { + mainWindow?.webContents.send('main-process-message', new Date().toLocaleString()) + }) + + // Make all links open with the browser, not with the application + mainWindow.webContents.setWindowOpenHandler(({url}) => { + if (url.startsWith('https:')) shell.openExternal(url) + return {action: 'deny'} + }) + // win.webContents.on('will-navigate', (event, url) => { }) #344 +} + +function createPreviewWindow(parent: BrowserWindow) { + previewWin = new BrowserWindow({ + parent: parent, + width: 600, + height: 400, + show: false, + // paintWhenInitiallyHidden: false, + webPreferences: { + webSecurity: false, + nodeIntegration: true, + contextIsolation: false, + }, + }) + + // previewWin.hide() + previewWin.setTitle("预览") + + previewWin.webContents.session.on('will-download', (event, item, webContents) => { + // console.log("取消下载") + item.cancel() + }) + + previewWin.on("page-title-updated", (event) => { + // 阻止该事件 + event.preventDefault() + }) + + previewWin.on("close", (event) => { + // 不关闭窗口 + event.preventDefault() + // 影藏窗口 + previewWin.hide() + }) +} + +app.whenReady().then(() => { + initIPC() + createWindow() + createPreviewWindow(mainWindow) + setWin(mainWindow, previewWin) +}) diff --git a/electron/main/ipc.ts b/electron/main/ipc.ts new file mode 100755 index 0000000..9662ad4 --- /dev/null +++ b/electron/main/ipc.ts @@ -0,0 +1,268 @@ +import {ipcMain, dialog, BrowserWindow, app, shell} from 'electron' +import {startServer} from './proxyServer' +import {installCert, checkCertInstalled} from './cert' +import {downloadFile} from './utils' +// @ts-ignore +import {hexMD5} from '../../src/common/md5' +import fs from "fs" +import * as urlTool from "url" +import CryptoJS from 'crypto-js' +import {closeProxy, setProxy} from "./setProxy" +import log from "electron-log" + +let getMac = require("getmac").default +let win: BrowserWindow +let previewWin: BrowserWindow +let isStartProxy = false +let isOpenProxy = false +let videoList = {} +let aesKey = "as5d45as4d6qe6wqfar6gt4749q6y7w6h34v64tv7t37ty5qwtv6t6qv" +let qqReg = RegExp("finder.video.qq.com") + +const toSize = (size: number) => { + if (size > 1048576) { + return (size / 1048576).toFixed(2) + "MB" + } + if (size > 1024) { + return (size / 1024).toFixed(2) + "KB" + } + return size + 'b' +} + +const suffix = (type: string) => { + switch (type) { + case "video/mp4": + return ".mp4"; + case "image/png": + return ".png"; + case "image/webp": + return ".webp"; + case "image/svg+xml": + return ".svg"; + case "image/gif": + return ".gif"; + case "audio/mpeg": + return ".mp3"; + case "application/vnd.apple.mpegurl": + return ".m3u8"; + } +} + +export default function initIPC() { + + ipcMain.handle('invoke_app_is_init', async (event, arg) => { + // 初始化应用 安装证书相关 + return checkCertInstalled() + }) + + ipcMain.handle('invoke_init_app', (event, arg) => { + // 开始 初始化应用 安装证书相关 + // console.log('invoke_init_app') + return installCert(false) + }) + + ipcMain.handle('invoke_start_proxy', async (event, arg) => { + // 启动代理服务 + if (isStartProxy) { + if (isOpenProxy === false) { + isOpenProxy = true + setProxy('127.0.0.1', 8899) + .then(() => { + }) + .catch((err) => { + }) + } + return + } + isStartProxy = true + isOpenProxy = true + return startServer({ + // @ts-ignore + interceptCallback: phase => async (req, res) => { + // 拦截响应 + if (phase === 'response') { + let ctype = res?._data?.headers?.['content-type'] + let url_sign: string = hexMD5(req.fullUrl()) + let res_url = req.fullUrl() + let urlInfo = urlTool.parse(res_url, true) + switch (ctype) { + case "video/mp4": + if (videoList.hasOwnProperty(url_sign) === false) { + videoList[url_sign] = req.fullUrl() + let high_url = '' + let down_url = res_url + if (qqReg.test(down_url)) { + down_url = down_url.replace("finder.video.qq.com/251/20302", "finder.video.qq.com/251/20304") + urlInfo = urlTool.parse(down_url, true) + high_url = urlInfo.protocol + "//" + urlInfo.hostname + urlInfo.pathname + + '?encfilekey=' + urlInfo.query?.encfilekey + + '&token=' + urlInfo.query?.token + } + + win?.webContents?.send?.('on_get_queue', { + url_sign: url_sign, + url: down_url, + down_url: down_url, + high_url: high_url, + platform: urlInfo.hostname, + size: toSize(res?._data?.headers?.['content-length'] ?? 0), + type: ctype, + type_str: 'video', + progress_bar: '', + save_path: '', + downing: false + }) + } + break; + case "image/png": + case "image/webp": + case "image/svg+xml": + case "image/gif": + win?.webContents?.send?.('on_get_queue', { + url_sign: url_sign, + url: res_url, + down_url: res_url, + high_url: '', + platform: urlInfo.hostname, + size: toSize(res?._data?.headers?.['content-length'] ?? 0), + type: ctype, + type_str: 'image', + progress_bar: '', + save_path: '', + downing: false + }) + break; + case "audio/mpeg": + win?.webContents?.send?.('on_get_queue', { + url_sign: url_sign, + url: res_url, + down_url: res_url, + high_url: '', + platform: urlInfo.hostname, + size: toSize(res?._data?.headers?.['content-length'] ?? 0), + type: ctype, + type_str: 'audio', + progress_bar: '', + save_path: '', + downing: false + }) + break; + case "application/vnd.apple.mpegurl": + win?.webContents?.send?.('on_get_queue', { + url_sign: url_sign, + url: res_url, + down_url: res_url, + high_url: '', + platform: urlInfo.hostname, + size: toSize(res?._data?.headers?.['content-length'] ?? 0), + type: ctype, + type_str: 'm3u8', + progress_bar: '', + save_path: '', + downing: false + }) + break; + + } + + } + }, + setProxyErrorCallback: err => { + isStartProxy = false + isOpenProxy = false + }, + }) + }) + + ipcMain.handle('invoke_close_proxy', (event, arg) => { + // 关闭代理 + try { + isOpenProxy = false + return closeProxy() + } catch (error) { + log.log("--------------closeProxy error--------------", error) + } + + }) + + ipcMain.handle('invoke_select_down_dir', async (event, arg) => { + // 选择下载位置 + const result = dialog.showOpenDialogSync({title: '保存', properties: ['openDirectory']}) + if (!result?.[0]) { + return false + } + + return result?.[0] + }) + + ipcMain.handle('invoke_file_exists', async (event, {save_path, url}) => { + let url_sign = hexMD5(url) + let res = fs.existsSync(`${save_path}/${url_sign}.mp4`) + return {is_file: res, fileName: `${save_path}/${url_sign}.mp4`} + }) + + ipcMain.handle('invoke_down_file', async (event, {index, data, save_path, high}) => { + let down_url = data.down_url + if (high && data.high_url) { + down_url = data.high_url + } + + if (!down_url) { + return false + } + + let url_sign = hexMD5(down_url) + let save_path_file = `${save_path}/${url_sign}` + suffix(data.type) + if (fs.existsSync(save_path_file)) { + return {fullFileName: save_path_file, totalLen: ""} + } + // 开始下载 + return downloadFile( + down_url, + save_path_file, + (res) => { + return save_path_file + } + ).catch(err => { + // console.log('invoke_down_file:err', err) + return false + }) + }) + + ipcMain.handle('invoke_get_mac', async (event) => { + let mac = getMac() + if (mac === "") { + return "" + } + return CryptoJS.AES.encrypt(mac, CryptoJS.enc.Hex.parse(aesKey), { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }).ciphertext.toString() + }) + + ipcMain.handle('invoke_resources_preview', async (event, {url}) => { + if (!url) { + return + } + + previewWin.loadURL(url).then(r => { + return + }).catch(res => { + }) + previewWin.show() + return + }) + + ipcMain.handle('invoke_open_default_browser', (event, {url}) => { + shell.openExternal(url).then(r => {}) + }) + + ipcMain.handle('invoke_open_dir', (event, {dir}) => { + shell.openPath(dir).then(r => {}) + }) +} + +export function setWin(w, p) { + win = w + previewWin = p +} \ No newline at end of file diff --git a/electron/main/proxyServer.ts b/electron/main/proxyServer.ts new file mode 100755 index 0000000..4bec9f5 --- /dev/null +++ b/electron/main/proxyServer.ts @@ -0,0 +1,70 @@ +import fs from 'fs' +import log from 'electron-log' +import CONFIG from './const' +import {closeProxy, setProxy} from './setProxy' +import {app} from "electron" + +const hoXy = require('hoxy') + +if (process.platform === 'win32') { + process.env.OPENSSL_BIN = CONFIG.OPEN_SSL_BIN_PATH + process.env.OPENSSL_CONF = CONFIG.OPEN_SSL_CNF_PATH +} + +export async function startServer({ + interceptCallback = f => f => f, + setProxyErrorCallback = f => f, + }) { + // const port = await getPort() + const port = 8899 + + return new Promise(async (resolve: any, reject) => { + const proxy = hoXy + .createServer({ + certAuthority: { + key: fs.readFileSync(CONFIG.CERT_PRIVATE_PATH), + cert: fs.readFileSync(CONFIG.CERT_PUBLIC_PATH), + }, + }) + .listen(port, () => { + setProxy('127.0.0.1', port) + .then(() => { + log.log("--------------setProxy success--------------") + resolve() + }) + .catch((err) => { + log.log("--------------setProxy error--------------") + // setProxyErrorCallback(data); + setProxyErrorCallback({}); + reject('设置代理失败'); + }); + }) + .on('error', err => { + log.log("--------------proxy err--------------", err) + }); + + proxy.intercept( + { + phase: 'request', + }, + interceptCallback('request'), + ); + + proxy.intercept( + { + phase: 'response', + }, + interceptCallback('response'), + ) + }) +} + +app.on('before-quit', async e => { + e.preventDefault() + try { + await closeProxy() + log.log("--------------closeProxy success--------------") + } catch (error) { + } + app.exit() +}) diff --git a/electron/main/setProxy.ts b/electron/main/setProxy.ts new file mode 100755 index 0000000..a5d6903 --- /dev/null +++ b/electron/main/setProxy.ts @@ -0,0 +1,116 @@ +import {exec} from 'child_process' +// @ts-ignore +import regedit from 'regedit' +import CONFIG from './const' + +regedit.setExternalVBSLocation(CONFIG.REGEDIT_VBS_PATH) + +export async function setProxy(host, port) { + if (process.platform === 'darwin') { + const networks = await getMacAvailableNetworks() + + // @ts-ignore + if (networks.length === 0) { + throw 'no network' + } + + return Promise.all( + // @ts-ignore + networks.map(network => { + return new Promise((resolve, reject) => { + exec(`networksetup -setsecurewebproxy "${network}" ${host} ${port}`, error => { + if (error) { + reject(null) + } else { + resolve(network) + } + }); + }); + }), + ); + } else { + const valuesToPut = { + 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings': { + ProxyServer: { + value: `${host}:${port}`, + type: 'REG_SZ', + }, + ProxyEnable: { + value: 1, + type: 'REG_DWORD', + }, + }, + }; + // @ts-ignore + return regedit.promisified.putValue(valuesToPut) + } +} + +export async function closeProxy() { + if (process.platform === 'darwin') { + const networks = await getMacAvailableNetworks() + + // @ts-ignore + if (networks.length === 0) { + throw 'no network' + } + + return Promise.all( + // @ts-ignore + networks.map(network => { + return new Promise((resolve, reject) => { + exec(`networksetup -setsecurewebproxystate "${network}" off`, error => { + if (error) { + reject(null) + } else { + resolve(network) + } + }); + }); + }), + ); + } else { + const valuesToPut = { + 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings': { + ProxyEnable: { + value: 0, + type: 'REG_DWORD', + }, + }, + }; + // @ts-ignore + return regedit.promisified.putValue(valuesToPut) + } +} + +function getMacAvailableNetworks() { + return new Promise((resolve, reject) => { + exec('networksetup -listallnetworkservices', (error, stdout) => { + if (error) { + reject(error) + } else { + Promise.all( + stdout + .toString() + .split('\n') + .map(network => { + return new Promise(resolve => { + exec( + `networksetup getinfo "${network}" | grep "^IP address:\\s\\d"`, + (error, stdout) => { + if (error) { + resolve(null) + } else { + resolve(stdout ? network : null) + } + }, + ) + }) + }), + ).then(networks => { + resolve(networks.filter(Boolean)) + }) + } + }) + }) +} diff --git a/electron/main/utils.ts b/electron/main/utils.ts new file mode 100755 index 0000000..c9ea08e --- /dev/null +++ b/electron/main/utils.ts @@ -0,0 +1,65 @@ +import {app, dialog, shell} from 'electron' +import semver from 'semver' +import fs from 'fs' + +const axios = require('axios') + +// packageUrl 需要包含 { "version": "1.0.0" } 结构 +function checkUpdate( + // 可以使用加速地址 https://cdn.jsdelivr.net/gh/lecepin/electron-react-tpl/package.json + packageUrl = 'https://raw.githubusercontent.com/lecepin/electron-react-tpl/master/package.json', + downloadUrl = 'https://github.com/lecepin/electron-react-tpl/releases', +) { + axios.get(packageUrl) + .then(({data}) => { + if (semver.gt(data?.version, app.getVersion())) { + const result = dialog.showMessageBoxSync({ + message: '发现新版本,是否更新?', + type: 'question', + cancelId: 1, + defaultId: 0, + buttons: ['进入新版本下载页面', '取消'], + }); + + if (result === 0 && downloadUrl) { + shell.openExternal(downloadUrl).then(r => { + }) + } + } + }) + .catch(err => { + }) +} + +function downloadFile(url, fullFileName, progressCallback) { + return axios.get(url, { + responseType: 'stream', + headers: { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36', + }, + }).then(({data, headers}) => { + let currentLen = 0 + const totalLen = headers['content-length'] + + return new Promise((resolve, reject) => { + data.on('data', ({length}) => { + currentLen += length + // @ts-ignore + progressCallback?.(currentLen / totalLen) + }); + + data.on('error', err => reject(err)) + + data.pipe( + fs.createWriteStream(fullFileName).on('finish', () => { + resolve({ + fullFileName, + totalLen, + }); + }), + ); + }); + }); +} + +export {checkUpdate, downloadFile} diff --git a/electron/preload/index.ts b/electron/preload/index.ts new file mode 100644 index 0000000..ebf1276 --- /dev/null +++ b/electron/preload/index.ts @@ -0,0 +1,92 @@ +function domReady(condition: DocumentReadyState[] = ['complete', 'interactive']) { + return new Promise((resolve) => { + if (condition.includes(document.readyState)) { + resolve(true) + } else { + document.addEventListener('readystatechange', () => { + if (condition.includes(document.readyState)) { + resolve(true) + } + }) + } + }) +} + +const safeDOM = { + append(parent: HTMLElement, child: HTMLElement) { + if (!Array.from(parent.children).find(e => e === child)) { + return parent.appendChild(child) + } + }, + remove(parent: HTMLElement, child: HTMLElement) { + if (Array.from(parent.children).find(e => e === child)) { + return parent.removeChild(child) + } + }, +} + +/** + * https://tobiasahlin.com/spinkit + * https://connoratherton.com/loaders + * https://projects.lukehaas.me/css-loaders + * https://matejkustec.github.io/SpinThatShit + */ +function useLoading() { + const className = `loaders-css__square-spin` + const styleContent = ` +@keyframes square-spin { + 25% { transform: perspective(100px) rotateX(180deg) rotateY(0); } + 50% { transform: perspective(100px) rotateX(180deg) rotateY(180deg); } + 75% { transform: perspective(100px) rotateX(0) rotateY(180deg); } + 100% { transform: perspective(100px) rotateX(0) rotateY(0); } +} +.${className} > div { + animation-fill-mode: both; + width: 50px; + height: 50px; + background: #fff; + animation: square-spin 3s 0s cubic-bezier(0.09, 0.57, 0.49, 0.9) infinite; +} +.app-loading-wrap { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + background: #282c34; + z-index: 9; +} + ` + const oStyle = document.createElement('style') + const oDiv = document.createElement('div') + + oStyle.id = 'app-loading-style' + oStyle.innerHTML = styleContent + oDiv.className = 'app-loading-wrap' + oDiv.innerHTML = `
` + + return { + appendLoading() { + safeDOM.append(document.head, oStyle) + safeDOM.append(document.body, oDiv) + }, + removeLoading() { + safeDOM.remove(document.head, oStyle) + safeDOM.remove(document.body, oDiv) + }, + } +} + +// ---------------------------------------------------------------------- + +const { appendLoading, removeLoading } = useLoading() +domReady().then(appendLoading) + +window.onmessage = (ev) => { + ev.data.payload === 'removeLoading' && removeLoading() +} + +setTimeout(removeLoading, 4999) diff --git a/electron/res/icon/icon.png b/electron/res/icon/icon.png new file mode 100755 index 0000000..1f41af9 Binary files /dev/null and b/electron/res/icon/icon.png differ diff --git a/electron/res/icon/icons/mac/icon.icns b/electron/res/icon/icons/mac/icon.icns new file mode 100755 index 0000000..295d921 Binary files /dev/null and b/electron/res/icon/icons/mac/icon.icns differ diff --git a/electron/res/icon/icons/win/icon.ico b/electron/res/icon/icons/win/icon.ico new file mode 100755 index 0000000..147eb9a Binary files /dev/null and b/electron/res/icon/icons/win/icon.ico differ diff --git a/electron/res/index.html b/electron/res/index.html new file mode 100755 index 0000000..b973636 --- /dev/null +++ b/electron/res/index.html @@ -0,0 +1,12 @@ + + + + + + 资源下载器 + + + +
+ + diff --git a/electron/res/keys/private.key b/electron/res/keys/private.key new file mode 100755 index 0000000..8b994f1 --- /dev/null +++ b/electron/res/keys/private.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAsmAqn3hYd/YZcrfgqM1Q6xgHI50EBckbOkfCqTWS1yVFZjLF +bMehWb9xGFZJD21A5sxl4xelIWblhety+YTVa/mn2CEJh3je069oeULfXdzhhHyf +/ci0IloJhvX+2RJ+176uTKKcWhuOtNVs5VeFoHDoUcISnTqkaVyWeeLfafgrOW7w +N8ip128nuBx19ylIygb/DELmjKRRCSpx2vOw2JErTM8L5r0f4eWdqiwBOwu0NHWy +Svh9YG8B31UPga4I8FbFhybOP9cQNQPafOSfjwuZoi5CAtyJbwT7KyII9iMD74bZ +1mTx2xokmQ2TeiCSKSF8Mx9/8Gq+95mzvvIbRwIDAQABAoIBAHNt++caj9WBclJk +X4Oc6eJYuDX5o+LCk1YRngy12IJVYiWScWPFg8p6MouXOsw63Sb92mksofWNirYw ++UQzC5FGC7G3H12FgFzoQ+lEtxscluuPYlFukfMw5L1rbzG14FNo145MJHXDI4Qu +ILwA+T4sEorl1fndOwvbmJzjjcQaeRNz7/R9e6QTOlZ2+IEMKnHSBXXGJbDj6mPN ++f1/ec6nVENdxazgRCi0xfinyft4Ipst93Eb+wGcpk+J43aF+0leWQCdl6Y9U1Lz +zpv5H5XOQdwpX+dpuioRp73zwPwIialq+hTUN28Bn9U1jW2tjxUl/vgIpjy1s94a +UipRwSECgYEA57vYB+wGnxQxY9IPpr9H/y3HciIwCnuOEsWBzjYe8sIqBif2tEpO +OgDZZMQY7+JJrDQbDRs442TuRjKhJ5hiW+MyoiFWaYkBBoNVM8RBTkIjHfrh+uB2 +XT15FbEyyxo3n9QY610ZJFRnW4Uf5V0osjOqqUgQRrVXvamk6NQH6FkCgYEAxQ3v +jFYPL3EkZe1br6X0RM42ykGv5Di5Q6NnjpSPcyn9a2obA1cZuCd5S1lhrkuZGsdI +iFapeL+7vpts9gu9/ii9y+CgEKplOMmm0ZrChBKAcXMZvdDKV3y5SmTMZPas4X5i +hqNqatx9/J93sMYWc0CuoosDEJYKtSz8GE+1rJ8CgYAmp5rdl21zU7b5Y6zgr7+e +vVArpbBFz15fmzqP309CR0kjRb9NS6fI3SNmP5+5RBHt+7MXeJcAt3FXnFJtfGnL +0hY8HTuA1y2onHe17uLF3xpkgdj4NEEKRJrSF4DViEYHDyYo/JqZCMtE5OvxIp0L +PLsXCcJNSSqdpJKxk8zN4QKBgEuoxSAh7uStUWddUkXHt1kvwDO6MtmyuddxhxJk +kguKxMWYUNTgfXyKk3TN1caBOkDg4UWP2LQHEgPmU1jJO2K5q9362hpsAj9ilY2H +GUZygCSPKAQMhZQ/zDj3KM9fMxPFXfkKB5MOI8V6SQ9zjy0jWaoJK90TbvsPUZ/Y +Aw5LAoGADifZwCHPiXhTfJjOom2uBgXmL03yTXcCw4EDIX3ZR0sP6ACPQq4T4jxZ +UJLXLjOb2pzCq0c5+k0cG6ahYINq4tGOo+vQ9fDvhKg0nlf1FrzxSd7S12o+un2q ++U+dBllYIDlRMgMhXu9CxFDjUsCwPRmsBvmVZiH4XSs6QVnfn90= +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/electron/res/keys/private.pem b/electron/res/keys/private.pem new file mode 100755 index 0000000..8b994f1 --- /dev/null +++ b/electron/res/keys/private.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEAsmAqn3hYd/YZcrfgqM1Q6xgHI50EBckbOkfCqTWS1yVFZjLF +bMehWb9xGFZJD21A5sxl4xelIWblhety+YTVa/mn2CEJh3je069oeULfXdzhhHyf +/ci0IloJhvX+2RJ+176uTKKcWhuOtNVs5VeFoHDoUcISnTqkaVyWeeLfafgrOW7w +N8ip128nuBx19ylIygb/DELmjKRRCSpx2vOw2JErTM8L5r0f4eWdqiwBOwu0NHWy +Svh9YG8B31UPga4I8FbFhybOP9cQNQPafOSfjwuZoi5CAtyJbwT7KyII9iMD74bZ +1mTx2xokmQ2TeiCSKSF8Mx9/8Gq+95mzvvIbRwIDAQABAoIBAHNt++caj9WBclJk +X4Oc6eJYuDX5o+LCk1YRngy12IJVYiWScWPFg8p6MouXOsw63Sb92mksofWNirYw ++UQzC5FGC7G3H12FgFzoQ+lEtxscluuPYlFukfMw5L1rbzG14FNo145MJHXDI4Qu +ILwA+T4sEorl1fndOwvbmJzjjcQaeRNz7/R9e6QTOlZ2+IEMKnHSBXXGJbDj6mPN ++f1/ec6nVENdxazgRCi0xfinyft4Ipst93Eb+wGcpk+J43aF+0leWQCdl6Y9U1Lz +zpv5H5XOQdwpX+dpuioRp73zwPwIialq+hTUN28Bn9U1jW2tjxUl/vgIpjy1s94a +UipRwSECgYEA57vYB+wGnxQxY9IPpr9H/y3HciIwCnuOEsWBzjYe8sIqBif2tEpO +OgDZZMQY7+JJrDQbDRs442TuRjKhJ5hiW+MyoiFWaYkBBoNVM8RBTkIjHfrh+uB2 +XT15FbEyyxo3n9QY610ZJFRnW4Uf5V0osjOqqUgQRrVXvamk6NQH6FkCgYEAxQ3v +jFYPL3EkZe1br6X0RM42ykGv5Di5Q6NnjpSPcyn9a2obA1cZuCd5S1lhrkuZGsdI +iFapeL+7vpts9gu9/ii9y+CgEKplOMmm0ZrChBKAcXMZvdDKV3y5SmTMZPas4X5i +hqNqatx9/J93sMYWc0CuoosDEJYKtSz8GE+1rJ8CgYAmp5rdl21zU7b5Y6zgr7+e +vVArpbBFz15fmzqP309CR0kjRb9NS6fI3SNmP5+5RBHt+7MXeJcAt3FXnFJtfGnL +0hY8HTuA1y2onHe17uLF3xpkgdj4NEEKRJrSF4DViEYHDyYo/JqZCMtE5OvxIp0L +PLsXCcJNSSqdpJKxk8zN4QKBgEuoxSAh7uStUWddUkXHt1kvwDO6MtmyuddxhxJk +kguKxMWYUNTgfXyKk3TN1caBOkDg4UWP2LQHEgPmU1jJO2K5q9362hpsAj9ilY2H +GUZygCSPKAQMhZQ/zDj3KM9fMxPFXfkKB5MOI8V6SQ9zjy0jWaoJK90TbvsPUZ/Y +Aw5LAoGADifZwCHPiXhTfJjOom2uBgXmL03yTXcCw4EDIX3ZR0sP6ACPQq4T4jxZ +UJLXLjOb2pzCq0c5+k0cG6ahYINq4tGOo+vQ9fDvhKg0nlf1FrzxSd7S12o+un2q ++U+dBllYIDlRMgMhXu9CxFDjUsCwPRmsBvmVZiH4XSs6QVnfn90= +-----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/electron/res/keys/public.crt b/electron/res/keys/public.crt new file mode 100755 index 0000000..0735da0 --- /dev/null +++ b/electron/res/keys/public.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICuDCCAaACCQC7PQmrxgWOlTANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJs +ZWNlcGluLTIwMjItMDUtMTkwIBcNMjIwNTE5MTI1NjA0WhgPMzAyMTA5MTkxMjU2 +MDRaMB0xGzAZBgNVBAMMEmxlY2VwaW4tMjAyMi0wNS0xOTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBALJgKp94WHf2GXK34KjNUOsYByOdBAXJGzpHwqk1 +ktclRWYyxWzHoVm/cRhWSQ9tQObMZeMXpSFm5YXrcvmE1Wv5p9ghCYd43tOvaHlC +313c4YR8n/3ItCJaCYb1/tkSfte+rkyinFobjrTVbOVXhaBw6FHCEp06pGlclnni +32n4Kzlu8DfIqddvJ7gcdfcpSMoG/wxC5oykUQkqcdrzsNiRK0zPC+a9H+Hlnaos +ATsLtDR1skr4fWBvAd9VD4GuCPBWxYcmzj/XEDUD2nzkn48LmaIuQgLciW8E+ysi +CPYjA++G2dZk8dsaJJkNk3ogkikhfDMff/BqvveZs77yG0cCAwEAATANBgkqhkiG +9w0BAQsFAAOCAQEADymHk+wLJAdv3p+4hHo57VLaBtwVYXc5oRUbUzgMYTTtPWIs +xuILEqXftMspt6PzdEt0V1WeCWNyypsAbur/CKpAOoVjBDPIo09TiYnYIn9xt5wQ +AmR5kVEZheuazcvzW3C9NAY1T6QDmxNvFCiCXRbtklOg2HqFDZX+pkj8CylQ9TDk +rroUg17b/FD1ds1uyPXzucEWfxqkOaujvsCnzrbFs9luB5VfM+QzLU+l9QRN9Tmj +z7CpGuP6vKvhXJLUjXkZ0q5JyL5wEAe6Ttbu+c/8HhPFKQsW6q/lQSDo0v0LGDrd +ikjWXhSrVjd8+qTTVgia/UNqv/wi+bkWnVdRzQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/electron/res/keys/public.pem b/electron/res/keys/public.pem new file mode 100755 index 0000000..0735da0 --- /dev/null +++ b/electron/res/keys/public.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICuDCCAaACCQC7PQmrxgWOlTANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJs +ZWNlcGluLTIwMjItMDUtMTkwIBcNMjIwNTE5MTI1NjA0WhgPMzAyMTA5MTkxMjU2 +MDRaMB0xGzAZBgNVBAMMEmxlY2VwaW4tMjAyMi0wNS0xOTCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBALJgKp94WHf2GXK34KjNUOsYByOdBAXJGzpHwqk1 +ktclRWYyxWzHoVm/cRhWSQ9tQObMZeMXpSFm5YXrcvmE1Wv5p9ghCYd43tOvaHlC +313c4YR8n/3ItCJaCYb1/tkSfte+rkyinFobjrTVbOVXhaBw6FHCEp06pGlclnni +32n4Kzlu8DfIqddvJ7gcdfcpSMoG/wxC5oykUQkqcdrzsNiRK0zPC+a9H+Hlnaos +ATsLtDR1skr4fWBvAd9VD4GuCPBWxYcmzj/XEDUD2nzkn48LmaIuQgLciW8E+ysi +CPYjA++G2dZk8dsaJJkNk3ogkikhfDMff/BqvveZs77yG0cCAwEAATANBgkqhkiG +9w0BAQsFAAOCAQEADymHk+wLJAdv3p+4hHo57VLaBtwVYXc5oRUbUzgMYTTtPWIs +xuILEqXftMspt6PzdEt0V1WeCWNyypsAbur/CKpAOoVjBDPIo09TiYnYIn9xt5wQ +AmR5kVEZheuazcvzW3C9NAY1T6QDmxNvFCiCXRbtklOg2HqFDZX+pkj8CylQ9TDk +rroUg17b/FD1ds1uyPXzucEWfxqkOaujvsCnzrbFs9luB5VfM+QzLU+l9QRN9Tmj +z7CpGuP6vKvhXJLUjXkZ0q5JyL5wEAe6Ttbu+c/8HhPFKQsW6q/lQSDo0v0LGDrd +ikjWXhSrVjd8+qTTVgia/UNqv/wi+bkWnVdRzQ== +-----END CERTIFICATE----- \ No newline at end of file diff --git a/electron/res/openssl/HashInfo.txt b/electron/res/openssl/HashInfo.txt new file mode 100755 index 0000000..d9b55dd Binary files /dev/null and b/electron/res/openssl/HashInfo.txt differ diff --git a/electron/res/openssl/OpenSSL License.txt b/electron/res/openssl/OpenSSL License.txt new file mode 100755 index 0000000..3090896 --- /dev/null +++ b/electron/res/openssl/OpenSSL License.txt @@ -0,0 +1,126 @@ + + LICENSE ISSUES + ============== + + The OpenSSL toolkit stays under a dual license, i.e. both the conditions of + the OpenSSL License and the original SSLeay license apply to the toolkit. + See below for the actual license texts. Actually both licenses are BSD-style + Open Source licenses. In case of any license issues related to OpenSSL + please contact openssl-core@openssl.org. + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2016 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ diff --git a/electron/res/openssl/ReadMe.txt b/electron/res/openssl/ReadMe.txt new file mode 100755 index 0000000..958c273 --- /dev/null +++ b/electron/res/openssl/ReadMe.txt @@ -0,0 +1,59 @@ +============================================================================= +OpenSSL v1.0.2q Precompiled Binaries for Win32 +----------------------------------------------------------------------------- + + *** Release Information *** + +Release Date: Nov 22, 2018 + +Author: Frederik A. Winkelsdorf (opendec.wordpress.com) + for the Indy Project (www.indyproject.org) + +Requirements: Indy 10.5.5+ (SVN Version or Delphi 2009 and newer) + +Dependencies: The libraries have no noteworthy dependencies + +Installation: Copy both DLL files into your application directory + +Supported OS: Windows 2000 up to Windows 10 + +----------------------------------------------------------------------------- + + *** Legal Disclaimer *** + +THIS SOFTWARE IS PROVIDED BY ITS AUTHOR AND THE INDY PROJECT "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +OpenSSL license terms are provided in the file "OpenSSL License.txt". + +PLEASE CHECK IF YOU NEED TO COMPLY WITH EXPORT RESTRICTIONS FOR CRYPTOGRAPHIC +SOFTWARE AND/OR PATENTS. + +----------------------------------------------------------------------------- + + *** Build Information Win32 *** + +Built with: Microsoft Visual C++ 2008 Express Edition + The Netwide Assembler (NASM) v2.11.08 Win32 + Strawberry Perl v5.22.0.1 Win32 Portable + Windows PowerShell + FinalBuilder 7 + +Commands: perl configure VC-WIN32 + ms\do_nasm + adjusted ms\ntdll.mak (replaced "/MD" with "/MT") + adjusted ms\version32.rc (Indy Information inserted) + nmake -f ms\ntdll.mak + nmake -f ms\ntdll.mak test + editbin.exe /rebase:base=0x11000000 libeay32.dll + editbin.exe /rebase:base=0x12000000 ssleay32.dll + +============================================================================= \ No newline at end of file diff --git a/electron/res/openssl/libeay32.dll b/electron/res/openssl/libeay32.dll new file mode 100755 index 0000000..040807c Binary files /dev/null and b/electron/res/openssl/libeay32.dll differ diff --git a/electron/res/openssl/openssl.cnf b/electron/res/openssl/openssl.cnf new file mode 100755 index 0000000..cb19a55 --- /dev/null +++ b/electron/res/openssl/openssl.cnf @@ -0,0 +1,350 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# Note that you can include other files from the main configuration +# file using the .include directive. +#.include filename + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +# extensions = +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] + +# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +# Policies used by the TSA examples. +tsa_policy1 = 1.2.3.4.1 +tsa_policy2 = 1.2.3.4.5.6 +tsa_policy3 = 1.2.3.4.5.7 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +dir = ./demoCA # Where everything is kept +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several certs with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key + +x509_extensions = usr_cert # The extensions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +# copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = default # use public key default MD +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 2048 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extensions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString (PKIX recommendation before 2004) +# utf8only: only UTF8Strings (PKIX recommendation after 2004). +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. +string_mask = utf8only + +# req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = AU +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Some-State + +localityName = Locality Name (eg, city) + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (e.g. server FQDN or YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This is required for TSA certificates. +# extendedKeyUsage = critical,timeStamping + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +[ v3_ca ] + + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer + +basicConstraints = critical,CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo + +#################################################################### +[ tsa ] + +default_tsa = tsa_config1 # the default TSA section + +[ tsa_config1 ] + +# These are used by the TSA reply generation only. +dir = ./demoCA # TSA root directory +serial = $dir/tsaserial # The current serial number (mandatory) +crypto_device = builtin # OpenSSL engine to use for signing +signer_cert = $dir/tsacert.pem # The TSA signing certificate + # (optional) +certs = $dir/cacert.pem # Certificate chain to include in reply + # (optional) +signer_key = $dir/private/tsakey.pem # The TSA private key (optional) +signer_digest = sha256 # Signing digest to use. (Optional) +default_policy = tsa_policy1 # Policy if request did not specify it + # (optional) +other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) +digests = sha1, sha256, sha384, sha512 # Acceptable message digests (mandatory) +accuracy = secs:1, millisecs:500, microsecs:100 # (optional) +clock_precision_digits = 0 # number of digits after dot. (optional) +ordering = yes # Is ordering defined for timestamps? + # (optional, default: no) +tsa_name = yes # Must the TSA name be included in the reply? + # (optional, default: no) +ess_cert_id_chain = no # Must the ESS cert id chain be included? + # (optional, default: no) +ess_cert_id_alg = sha1 # algorithm to compute certificate + # identifier (optional, default: sha1) \ No newline at end of file diff --git a/electron/res/openssl/openssl.exe b/electron/res/openssl/openssl.exe new file mode 100755 index 0000000..9648f2e Binary files /dev/null and b/electron/res/openssl/openssl.exe differ diff --git a/electron/res/openssl/ssleay32.dll b/electron/res/openssl/ssleay32.dll new file mode 100755 index 0000000..c0294de Binary files /dev/null and b/electron/res/openssl/ssleay32.dll differ diff --git a/electron/res/regedit-vbs/1.wsf b/electron/res/regedit-vbs/1.wsf new file mode 100755 index 0000000..26a383b --- /dev/null +++ b/electron/res/regedit-vbs/1.wsf @@ -0,0 +1,26 @@ + + + \ No newline at end of file diff --git a/electron/res/regedit-vbs/ArchitectureAgnosticRegistry.vbs b/electron/res/regedit-vbs/ArchitectureAgnosticRegistry.vbs new file mode 100755 index 0000000..6057da7 --- /dev/null +++ b/electron/res/regedit-vbs/ArchitectureAgnosticRegistry.vbs @@ -0,0 +1,75 @@ +' Notes: wanted to implement this using a class but: +' 1. No matter what I did I could not assign the result of GetObject to a private member +' 2. It looks as if all methods were treated as subs from the outside world which is not good since +' some of these need to return a value +' + +Set private_oReg = GetObject("winmgmts:\root\default:StdRegProv") + +Function SetStringValue(constHive, strSubKey, strValueName, strValue) + SetStringValue = private_oReg.SetStringValue(constHive, strSubKey, strValueName, strValue) +End Function + +Sub GetStringValue(constHive, strKey, strValueName, strValue) + private_oReg.GetStringValue constHive, strKey, strValueName, strValue +End Sub + +Function SetExpandedStringValue(constHive, strSubKey, strValueName, strValue) + SetExpandedStringValue = private_oReg.SetExpandedStringValue(constHive, strSubKey, strValueName, strValue) +End Function + +Sub GetExpandedStringValue(constHive, strKey, strValueName, strValue) + private_oReg.GetExpandedStringValue constHive, strKey, strValueName, strValue +End Sub + +Function SetMultiStringValue(constHive, strSubKey, strValueName, arrValue) + SetMultiStringValue = private_oReg.SetMultiStringValue(constHive, strSubKey, strValueName, arrValue) +End Function + +Sub GetMultiStringValue(constHive, strKey, strValueName, arrStrValue) + private_oReg.GetMultiStringValue constHive, strKey, strValueName, arrStrValue +End Sub + +Function SetDWORDValue(constHive, strSubKey, strValueName, arrValue) + SetDWORDValue = private_oReg.SetDWORDValue(constHive, strSubKey, strValueName, arrValue) +End Function + +Sub GetDWORDValue(constHive, strKey, strValueName, intDWordValue) + private_oReg.GetDWORDValue constHive, strKey, strValueName, intDWordValue +End Sub + +Function SetQWORDValue(constHive, strSubKey, strValueName, strQWordValue) + SetQWORDValue = private_oReg.SetQWORDValue(constHive, strSubKey, strValueName, strQWordValue) +End Function + +Sub GetQWORDValue(constHive, strKey, strValueName, intQWordValue) + private_oReg.GetQWORDValue constHive, strKey, strValueName, intQWordValue +End Sub + +Function SetBinaryValue(constHive, strSubKey, strValueName, arrValue) + SetBinaryValue = private_oReg.SetBinaryValue(constHive, strSubKey, strValueName, arrValue) +End Function + +Sub GetBinaryValue(constHive, strKey, strValueName, arrBinaryValue) + private_oReg.GetBinaryValue constHive, strKey, strValueName, arrBinaryValue +End Sub + +Function EnumKey(constHive, strSubKey, arrKeyNames) + EnumKey = private_oReg.EnumKey(constHive, strSubKey, arrKeyNames) +End Function + +Function EnumValues(constHive, strSubKey, arrValueNames, arrValueTypes) + EnumValues = private_oReg.EnumValues(constHive, strSubKey, arrValueNames, arrValueTypes) +End Function + +Function CreateKey(constHive, strSubKey) + CreateKey = private_oReg.CreateKey(constHive, strSubKey) +End Function + +Function DeleteKey(constHive, strSubKey) + DeleteKey = private_oReg.DeleteKey(constHive, strSubKey) +End Function + +Function DeleteValue(constHive, strSubKey, strValue) + DeleteValue = private_oReg.DeleteValue(constHive, strSubKey, strValue) +End Function diff --git a/electron/res/regedit-vbs/ArchitectureSpecificRegistry.vbs b/electron/res/regedit-vbs/ArchitectureSpecificRegistry.vbs new file mode 100755 index 0000000..28a77c9 --- /dev/null +++ b/electron/res/regedit-vbs/ArchitectureSpecificRegistry.vbs @@ -0,0 +1,358 @@ +' Notes: wanted to implement this using a class but: +' 1. No matter what I did I could not assign the result of GetObject to a private member +' 2. It looks as if all methods were treated as subs from the outside world which is not good since +' some of these need to return a value + +' should be removed when migration is complete +Set private_oReg = GetObject("winmgmts:\root\default:StdRegProv") + +Set private_oCtx = CreateObject("WbemScripting.SWbemNamedValueSet") +private_oCtx.Add "__ProviderArchitecture", CInt(OSArchitecture) + +Set private_oLocator = CreateObject("Wbemscripting.SWbemLocator") +Set private_oServices = private_oLocator.ConnectServer(".", "root\default","","",,,,private_oCtx) +Set private_oRegSpecific = private_oServices.Get("StdRegProv") + +Function CheckAccess(hDefKey,sSubKeyName,uRequired, bGranted ) + Set Inparams = private_oRegSpecific.Methods_("CheckAccess").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.uRequired = uRequired + + set Outparams = private_oRegSpecific.ExecMethod_("CheckAccess", Inparams,,private_oCtx) + + bGranted = Outparams.bGranted + + + CheckAccess = 0 + +End Function + +Function CreateKey(hDefKey,sSubKeyName) + Set Inparams = private_oRegSpecific.Methods_("CreateKey").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + set Outparams = private_oRegSpecific.ExecMethod_("CreateKey", Inparams,,private_oCtx) + + + CreateKey = 0 + +End Function + +Function DeleteKey(hDefKey,sSubKeyName) + Set Inparams = private_oRegSpecific.Methods_("DeleteKey").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + set Outparams = private_oRegSpecific.ExecMethod_("DeleteKey", Inparams,,private_oCtx) + + + DeleteKey = 0 + +End Function + +Function DeleteValue(hDefKey,sSubKeyName,sValueName) + Set Inparams = private_oRegSpecific.Methods_("DeleteValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + set Outparams = private_oRegSpecific.ExecMethod_("DeleteValue", Inparams,,private_oCtx) + + + DeleteValue = 0 + +End Function + +Function EnumKey(hDefKey,sSubKeyName, sNames ) + Set Inparams = private_oRegSpecific.Methods_("EnumKey").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + set Outparams = private_oRegSpecific.ExecMethod_("EnumKey", Inparams,,private_oCtx) + + sNames = Outparams.sNames + + + EnumKey = 0 + +End Function + +Function EnumValues(hDefKey,sSubKeyName, sNames,Types ) + Set Inparams = private_oRegSpecific.Methods_("EnumValues").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + set Outparams = private_oRegSpecific.ExecMethod_("EnumValues", Inparams,,private_oCtx) + + sNames = Outparams.sNames + + Types = Outparams.Types + + + EnumValues = 0 + +End Function + +Function GetBinaryValue(hDefKey,sSubKeyName,sValueName, uValue ) + Set Inparams = private_oRegSpecific.Methods_("GetBinaryValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + set Outparams = private_oRegSpecific.ExecMethod_("GetBinaryValue", Inparams,,private_oCtx) + + uValue = Outparams.uValue + + + GetBinaryValue = 0 + +End Function + +Function GetDWORDValue(hDefKey,sSubKeyName,sValueName, uValue ) + Set Inparams = private_oRegSpecific.Methods_("GetDWORDValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + set Outparams = private_oRegSpecific.ExecMethod_("GetDWORDValue", Inparams,,private_oCtx) + + uValue = Outparams.uValue + + + GetDWORDValue = 0 + +End Function + +Function GetExpandedStringValue(hDefKey,sSubKeyName,sValueName, sValue ) + Set Inparams = private_oRegSpecific.Methods_("GetExpandedStringValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + set Outparams = private_oRegSpecific.ExecMethod_("GetExpandedStringValue", Inparams,,private_oCtx) + + sValue = Outparams.sValue + + + GetExpandedStringValue = 0 + +End Function + +Function GetMultiStringValue(hDefKey,sSubKeyName,sValueName, sValue ) + Set Inparams = private_oRegSpecific.Methods_("GetMultiStringValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + set Outparams = private_oRegSpecific.ExecMethod_("GetMultiStringValue", Inparams,,private_oCtx) + + sValue = Outparams.sValue + + + GetMultiStringValue = 0 + +End Function + +Function GetQWORDValue(hDefKey,sSubKeyName,sValueName, uValue ) + Set Inparams = private_oRegSpecific.Methods_("GetQWORDValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + set Outparams = private_oRegSpecific.ExecMethod_("GetQWORDValue", Inparams,,private_oCtx) + + uValue = Outparams.uValue + + + GetQWORDValue = 0 + +End Function + +Function GetSecurityDescriptor(hDefKey,sSubKeyName, Descriptor ) + Set Inparams = private_oRegSpecific.Methods_("GetSecurityDescriptor").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + set Outparams = private_oRegSpecific.ExecMethod_("GetSecurityDescriptor", Inparams,,private_oCtx) + + Descriptor = Outparams.Descriptor + + + GetSecurityDescriptor = 0 + +End Function + +Function GetStringValue(hDefKey,sSubKeyName,sValueName, sValue ) + Set Inparams = private_oRegSpecific.Methods_("GetStringValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + set Outparams = private_oRegSpecific.ExecMethod_("GetStringValue", Inparams,,private_oCtx) + + sValue = Outparams.sValue + + + GetStringValue = 0 + +End Function + +Function SetBinaryValue(hDefKey,sSubKeyName,sValueName,uValue) + Set Inparams = private_oRegSpecific.Methods_("SetBinaryValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + Inparams.uValue = uValue + + set Outparams = private_oRegSpecific.ExecMethod_("SetBinaryValue", Inparams,,private_oCtx) + + + SetBinaryValue = 0 + +End Function + +Function SetDWORDValue(hDefKey,sSubKeyName,sValueName,uValue) + Set Inparams = private_oRegSpecific.Methods_("SetDWORDValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + Inparams.uValue = uValue + + set Outparams = private_oRegSpecific.ExecMethod_("SetDWORDValue", Inparams,,private_oCtx) + + + SetDWORDValue = 0 + +End Function + +Function SetExpandedStringValue(hDefKey,sSubKeyName,sValueName,sValue) + Set Inparams = private_oRegSpecific.Methods_("SetExpandedStringValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + Inparams.sValue = sValue + + set Outparams = private_oRegSpecific.ExecMethod_("SetExpandedStringValue", Inparams,,private_oCtx) + + + SetExpandedStringValue = 0 + +End Function + +Function SetMultiStringValue(hDefKey,sSubKeyName,sValueName,sValue) + Set Inparams = private_oRegSpecific.Methods_("SetMultiStringValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + Inparams.sValue = sValue + + set Outparams = private_oRegSpecific.ExecMethod_("SetMultiStringValue", Inparams,,private_oCtx) + + + SetMultiStringValue = 0 + +End Function + +Function SetQWORDValue(hDefKey,sSubKeyName,sValueName,uValue) + Set Inparams = private_oRegSpecific.Methods_("SetQWORDValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + Inparams.uValue = uValue + + set Outparams = private_oRegSpecific.ExecMethod_("SetQWORDValue", Inparams,,private_oCtx) + + + SetQWORDValue = 0 + +End Function + +Function SetSecurityDescriptor(hDefKey,sSubKeyName,Descriptor) + Set Inparams = private_oRegSpecific.Methods_("SetSecurityDescriptor").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.Descriptor = Descriptor + + set Outparams = private_oRegSpecific.ExecMethod_("SetSecurityDescriptor", Inparams,,private_oCtx) + + + SetSecurityDescriptor = 0 + +End Function + +Function SetStringValue(hDefKey,sSubKeyName,sValueName,sValue) + Set Inparams = private_oRegSpecific.Methods_("SetStringValue").Inparameters + + Inparams.hDefKey = hDefKey + + Inparams.sSubKeyName = sSubKeyName + + Inparams.sValueName = sValueName + + Inparams.sValue = sValue + + set Outparams = private_oRegSpecific.ExecMethod_("SetStringValue", Inparams,,private_oCtx) + + + SetStringValue = 0 + +End Function diff --git a/electron/res/regedit-vbs/JsonSafeTest.wsf b/electron/res/regedit-vbs/JsonSafeTest.wsf new file mode 100755 index 0000000..336c4a9 --- /dev/null +++ b/electron/res/regedit-vbs/JsonSafeTest.wsf @@ -0,0 +1,7 @@ + + + diff --git a/electron/res/regedit-vbs/regCreateKey.wsf b/electron/res/regedit-vbs/regCreateKey.wsf new file mode 100755 index 0000000..0577f3f --- /dev/null +++ b/electron/res/regedit-vbs/regCreateKey.wsf @@ -0,0 +1,32 @@ + + + \ No newline at end of file diff --git a/electron/res/regedit-vbs/regDeleteKey.wsf b/electron/res/regedit-vbs/regDeleteKey.wsf new file mode 100755 index 0000000..8c557e0 --- /dev/null +++ b/electron/res/regedit-vbs/regDeleteKey.wsf @@ -0,0 +1,29 @@ + + + \ No newline at end of file diff --git a/electron/res/regedit-vbs/regDeleteValue.wsf b/electron/res/regedit-vbs/regDeleteValue.wsf new file mode 100755 index 0000000..8e92dc9 --- /dev/null +++ b/electron/res/regedit-vbs/regDeleteValue.wsf @@ -0,0 +1,29 @@ + + + diff --git a/electron/res/regedit-vbs/regList.wsf b/electron/res/regedit-vbs/regList.wsf new file mode 100755 index 0000000..3b6a1c3 --- /dev/null +++ b/electron/res/regedit-vbs/regList.wsf @@ -0,0 +1,49 @@ +' +' Lists the sub keys and values of a given registry key +' +' cscript regList.wsg HKLM\Software +' +' Will Yield: +' +' { +' "hklm\software": { +' "keys": [ .. array of sub keys .. ], +' "values": { +' "moo": { +' "type": "REG_SZ", +' "value": "bar" +' } +' } +' } +' } + + + \ No newline at end of file diff --git a/electron/res/regedit-vbs/regListStream.wsf b/electron/res/regedit-vbs/regListStream.wsf new file mode 100755 index 0000000..c793da3 --- /dev/null +++ b/electron/res/regedit-vbs/regListStream.wsf @@ -0,0 +1,46 @@ +' +' Lists the sub keys and values of a given registry key, this script is slightly different +' than regList because it reads stdin for the keys to list +' +' echo HKLM\Software | cscript regListStream.wsf A +' +' Will Yield: +' +' { +' "hklm\software": { +' "keys": [ .. array of sub keys .. ], +' "values": { +' "moo": { +' "type": "REG_SZ", +' "value": "bar" +' } +' } +' } +' } + + + \ No newline at end of file diff --git a/electron/res/regedit-vbs/regPutValue.wsf b/electron/res/regedit-vbs/regPutValue.wsf new file mode 100755 index 0000000..d7a042a --- /dev/null +++ b/electron/res/regedit-vbs/regPutValue.wsf @@ -0,0 +1,56 @@ + + + \ No newline at end of file diff --git a/electron/res/regedit-vbs/regUtil.vbs b/electron/res/regedit-vbs/regUtil.vbs new file mode 100755 index 0000000..73f191e --- /dev/null +++ b/electron/res/regedit-vbs/regUtil.vbs @@ -0,0 +1,358 @@ +' TODO: consider incorporating a json writer of some sort instead of adhoc solution like the following +' e.g: http://demon.tw/my-work/vbs-json.html + +const HKEY_CLASSES_ROOT = &H80000000 +const HKEY_CURRENT_USER = &H80000001 +const HKEY_LOCAL_MACHINE = &H80000002 +const HKEY_USERS = &H80000003 +const HKEY_CURRENT_CONFIG = &H80000005 + +Sub LoadRegistryImplementationByOSArchitecture() + If IsNull(OSArchitecture) Then + WriteLineErr "missing OSArchitecture global. did not call util.DetermineOSArchitecture? or Forgot to load util.vbs?" + WScript.Quit 25125 + End If + + If OSArchitecture = "A" Then + Include "ArchitectureAgnosticRegistry.vbs" + Else + Include "ArchitectureSpecificRegistry.vbs" + End If +End Sub + +Function PutValue(constHive, strSubKey, strValueName, strValue, strType) + Select Case UCase(strType) + + Case "REG_SZ" + PutValue = SetStringValue(constHive, strSubKey, strValueName, strValue) + + Case "REG_EXPAND_SZ" + PutValue = SetExpandedStringValue(constHive, strSubKey, strValueName, strValue) + + Case "REG_BINARY" + PutValue = SetBinaryValue(constHive, strSubKey, strValueName, ToBinaryValue(strValue)) + + Case "REG_NONE" + PutValue = SetBinaryValue(constHive, strSubKey, strValueName, ToBinaryValue(strValue)) + + ' TODO: need to check that indeed int is the right type here + Case "REG_DWORD" + PutValue = SetDWORDValue(constHive, strSubKey, strValueName, CDbl(strValue)) + + Case "REG_MULTI_SZ" + PutValue = SetMultiStringValue(constHive, strSubKey, strValueName, Split(strValue, ",")) + + Case "REG_QWORD" + PutValue = SetQWORDValue(constHive, strSubKey, strValueName, strValue) + + Case "REG_DEFAULT" + PutValue = SetStringValue(constHive, strSubKey, "", strValue) + + Case Else + PutValue = SetStringValue(constHive, strSubKey, strValueName, strValue) + + End Select +End Function + +' render the child of a sub path strSubKey in hive constHive +' as json. +Sub ListChildrenAsJson(constHive, strSubKey) + ' start outputting json to stdout + Write "{" + + Dim e1: e1 = EnumKey (constHive, strSubKey, arrKeyNames) + If e1 <> 0 Then + Write """exists"": false," + Dim arrValueNames: arrValueNames = null + Else + Write """exists"": true," + + Dim e2: e2 = EnumValues (constHive, strSubKey, arrValueNames, arrValueTypes) + If e2 <> 0 Then + WScript.Quit e2 + End If + End If + + Write """keys"": [" + If Not IsNull(arrKeyNames) Then + For x = 0 To UBound(arrKeyNames) + If (x > 0) Then + Write "," + End If + + Write """" & JsonSafe(arrKeyNames(x)) & """" + Next + End If + Write "]," + ' TODO: some duplicity of code between the two paths of this condition, this needs to be address at some point + Write """values"":{" + If Not IsNull(arrValueNames) Then + For y = 0 To UBound(arrValueNames) + If y > 0 Then + Write "," + End If + + strValueName = arrValueNames(y) + intValueType = arrValueTypes(y) + + ' assign the value to varValue + GetValueByType constHive, strSubKey, strValueName, intValueType, varValue + + WriteValue strValueName, intValueType, varValue + Next + Else + ' fix for keys with only default values in them + ' see http://stackoverflow.com/questions/8840343/how-to-read-the-default-value-from-registry-in-vbscript + GetStringValue constHive, strSubKey, "", strDefaultValue + + If IsNull(strDefaultValue) = false and strDefaultValue <> "" Then + ' write the default value with REG_SZ + WriteValue "", 1, strDefaultValue + End If + End If + Write "}}" +End Sub + +Sub WriteValue (strValueName, intValueType, varValue) + Write """" + Write JsonSafe(strValueName) + Write """:{" + Write """type"": """ + Write RenderType(intValueType) + Write """," + Write """value"":" + Write RenderValueByType(intValueType, varValue) + Write "}" +End Sub + +' give a raw HKLM\something\somewhere +' output the hive constant and the subkey, in this case: +' HKEY_LOCAL_MACHINE will be assigned to outConstHive +' and something\somewhere will be assigned to outStrSubKey +Sub ParseHiveAndSubKey(strRawKey, outConstHive, outStrSubKey) + ' split into two parts to deduce the hive and the sub key + arrSplitted = Split(strRawKey, "\", 2, 1) + + If UBound(arrSplitted) > 0 Then + strHive = arrSplitted(0) + outStrSubKey = arrSplitted(1) + Else + strHive = strRawKey + outStrSubKey = "" + End If + + outConstHive = StringToHiveConst(UCase(strHive)) +End Sub + +Function ArrayRemoveAt(arr, pos) + Dim i + If IsArray(arr) Then + If pos >= 0 And pos <= UBound(arr) Then + For i = pos To UBound(arr) - 1 + arr(i) = arr(i + 1) + Next + ReDim Preserve arr(UBound(arr) - 1) + End If + End If +End Function + +Sub ParseHiveAndSubKeyAndValue(strRawKey, outConstHive, outStrSubKey, outStrValue) + ' split into two parts to deduce the hive and the sub key + arrSplitted = Split(strRawKey, "\", -1, 1) + + If UBound(arrSplitted) > 0 Then + strHive = arrSplitted(0) + outStrValue = arrSplitted(UBound(arrSplitted)) + test = ArrayRemoveAt(arrSplitted, UBound(arrSplitted)) + test = ArrayRemoveAt(arrSplitted, 0) + outStrSubKey = Join(arrSplitted, "\") + Else + strHive = strRawKey + outStrSubKey = "" + End If + + outConstHive = StringToHiveConst(UCase(strHive)) +End Sub + +Function StringToHiveConst(strHive) + + Select Case strHive + Case "HKCR" + StringToHiveConst = HKEY_CLASSES_ROOT + Case "HKCU" + StringToHiveConst = HKEY_CURRENT_USER + Case "HKLM" + StringToHiveConst = HKEY_LOCAL_MACHINE + Case "HKU" + StringToHiveConst = HKEY_USERS + Case "HKCC" + StringToHiveConst = HKEY_CURRENT_CONFIG + Case Else + StringToHiveConst = Null + End Select + +End Function + +' TODO: this entire "by type" should be transformed into OOP style +' where each type will have a class with render(), getValue() etc... + +' convert a value type number into a string label +Function RenderType(intType) + RenderType = "REG_UNKNOWN" + + Select Case intType + Case 0 + RenderType = "REG_NONE" + Case 1 + RenderType = "REG_SZ" + Case 2 + RenderType = "REG_EXPAND_SZ" + Case 3 + RenderType = "REG_BINARY" + Case 4 + RenderType = "REG_DWORD" + Case 7 + RenderType = "REG_MULTI_SZ" + Case 11 + RenderType = "REG_QWORD" + Case Else + ' TODO: should report / throw an error here + WriteErr("invalid Registry Value Type " & intType) + + End Select + +End Function + +' render by value type: +' string will return as a string with double quotes, e.g "value" +' multi string values which return as an array ot strings "["1", "2"]" (double quotes included ofc) +' numeric values like DWORD and QWORD just return as the number e.g. 1 +' byte arrays such as reg_binary return as an array of ints, e.g [1,2,3] +Function RenderValueByType(intType, varValue) + + Select Case intType + ' REG_NONE + Case 0 + RenderValueByType = "0" + + ' REG_SZ + Case 1 + RenderValueByType = """" & JsonSafe(varValue) & """" + + ' REG_EXPAND_SZ + Case 2 + RenderValueByType = """" & JsonSafe(varValue) & """" + + ' REG_BINARY + Case 3 + RenderValueByType = RenderByteArray(varValue) + + ' REG_DWORD + Case 4 + RenderValueByType= varValue + + ' REG_MULYI_SZ' + Case 7 + + RenderValueByType = RenderStringArray(varValue) + ' REG_QWORD + Case 11 + RenderValueByType = varValue + Case Else + ' TODO: should report / throw an error here + WriteErr("invalid Registry Value Type " & intType) + End Select + +End Function + +' get the value of a registry based on its value type and assign it to out parameter outVarValue +Sub GetValueByType(constHive, strKey, strValueName, intType, outVarValue) + + Select Case intType + ' REG_NONE + Case 0 + GetStringValue constHive, strKey, strValueName, "0" + Exit Sub + + ' REG_SZ + Case 1 + GetStringValue constHive, strKey, strValueName, outVarValue + Exit Sub + + ' REG_EXPAND_SZ + Case 2 + GetExpandedStringValue constHive, strKey, strValueName, outVarValue + Exit Sub + + ' REG_BINARY + Case 3 + GetBinaryValue constHive, strKey, strValueName, outVarValue + Exit Sub + + ' REG_DWORD + Case 4 + GetDWORDValue constHive, strKey, strValueName, outVarValue + + ' #21 - VBS does not support UInt32. This is the workaround + If outVarValue < 0 Then outVarValue = 4294967296 + outVarValue + + Exit Sub + + ' REG_MULYI_SZ' + Case 7 + GetMultiStringValue constHive, strKey, strValueName, outVarValue + Exit Sub + + ' REG_QWORD + Case 11 + GetQWORDValue constHive, strKey, strValueName, outVarValue + Exit Sub + + Case Else + ' TODO: should report / throw an error here + WriteErr("invalid Registry Value Type " & intType) + End Select + +End Sub + +' render a byte array as a json array of numbers +Function RenderByteArray(arr) + RenderByteArray = "[]" + + If Not IsNull(arr) Then + RenderByteArray = "[" & Join(arr, ",") & "]" + End If +End Function + +' render a string array as json string array +Function RenderStringArray(arr) + Result = "[" + If Not IsNull(arr) Then + For t = 0 To UBound(arr) + If (t > 0) Then + Result = Result & "," + End If + + Result = Result & """" & JsonSafe(arr(t)) & """" + Next + End If + Result = Result & "]" + + RenderStringArray = Result +End Function + +Function ToBinaryValue(strValue) + + arrValue = Split(strValue, ",") + + If IsNull(arrValue) Then + ToBinaryValue = Array() + Exit Function + End If + + For i = 0 To UBound(arrValue) + arrValue(i) = CInt(arrValue(i)) + Next + + ToBinaryValue = arrValue +End Function \ No newline at end of file diff --git a/electron/res/regedit-vbs/util.vbs b/electron/res/regedit-vbs/util.vbs new file mode 100755 index 0000000..c5fd2f8 --- /dev/null +++ b/electron/res/regedit-vbs/util.vbs @@ -0,0 +1,162 @@ +Set stdout = WScript.StdOut +Set stderr = WScript.StdErr +Set stdin = WScript.StdIn +Set args = WScript.Arguments +Set fs = CreateObject("scripting.filesystemobject") +Dim OSArchitecture + +Sub WriteErr(message) + stderr.Write message +End Sub + +Sub WriteLineErr(message) + stderr.WriteLine message +End Sub + +Sub Write(message) + stdout.Write message +End Sub + +Sub WriteLine(message) + stdout.WriteLine message +End Sub + +Function IndexOf(varNeedle, arrHaystack) + IndexOf = -1 + + If Not IsArray(arrHaystack) Then + Exit Function + End If + + For xyz = 0 To UBound(arrHaystack) + If arrHaystack(xyz) = varNeedle Then + IndexOf = xyz + Exit Function + End If + Next +End Function + +Sub CheckZeroArgs(message) + ' bail if args are missing + If args.Count = 0 Then + WriteLineErr message + WScript.Quit 25121 + End If +End Sub + +Dim ALLOWED_OS_ARCHITECTURE_VALUES: ALLOWED_OS_ARCHITECTURE_VALUES = Array("S", "A", "32", "64") + +' +' determine the architecture of the operating system, that will be used. there are 4 possibilities: +' A - means agnostic +' S - means that we want to use a specific architecture, but auto detect Item +' 32 - explicitly use 32 bit architecture +' 64 - explicitly use 64 bit architecture +' +Sub DetermineOSArchitecture() + strArchitecture = args(0) + + If IsNull(strArchitecture) Then + WriteLineErr "missing architecture argument" + WScript.Quit 25124 + End If + + strArchitecture = UCase(strArchitecture) + + If IndexOf(strArchitecture, ALLOWED_OS_ARCHITECTURE_VALUES) = -1 Then + WriteLineErr "invalid architecture argument" + WScript.Quit 25124 + End If + + If (strArchitecture = "S") Then + OSArchitecture = GetOSArchitecture() + If OSArchitecture = -1 Then + WriteLineErr "invalid os architecture detected " & OSArchitecture + WScript.Quit 25126 + End If + Else + OSArchitecture = strArchitecture + End If + +End Sub + +Sub Include(sPath) + ' TODO this is fragile, but should work for "modules" nested relatively to script root + include_ScriptPath = Left(WScript.ScriptFullName, InStr(WScript.ScriptFullName, WScript.ScriptName) - 2) + sPath = include_ScriptPath & "\" & sPath + + include_code = fs.OpenTextFile(sPath).ReadAll + ExecuteGlobal include_code +End Sub + +Function GetOSArchitecture() + + Dim ObjWMI, ColSettings, ObjProcessor + Dim StrComputer, ObjNetwork + + Set ObjWMI = GetObject("winmgmts:\Root\CIMV2") + Set ColSettings = ObjWMI.ExecQuery ("SELECT DataWidth, AddressWidth, Architecture FROM Win32_Processor") + + ' TODO: I make two assumptions here: + ' 1. Eveyone will have CPU0 device + ' 2. There is only one cpu defined in the wmi database (and if not, then they are all of the same architecture) + Set ObjProcessor = ColSettings.Item("Win32_Processor.DeviceID=""CPU0""") + + If ObjProcessor.Architecture = 0 AND ObjProcessor.AddressWidth = 32 Then + GetOSArchitecture = 32 + ElseIf (ObjProcessor.Architecture = 6 OR ObjProcessor.Architecture = 9) AND ObjProcessor.DataWidth = 64 AND ObjProcessor.AddressWidth = 32 Then + GetOSArchitecture = 32 + ElseIf (ObjProcessor.Architecture = 6 OR ObjProcessor.Architecture = 9) AND ObjProcessor.DataWidth = 64 AND ObjProcessor.AddressWidth = 64 Then + GetOSArchitecture = 64 + Else + GetOSArchitecture = -1 + End If + +End Function + +Function JsonSafe(inStrText) + If inStrText = "" Then + JsonSafe = "" + Exit Function + End If + Dim outStrText: outStrText = inStrText + outStrText = Replace(outStrText, "\", "\\") + outStrText = Replace(outStrText, vbcrlf, "\\r\\n") + outStrText = Replace(outStrText, vblf, "\\n") + outStrText = Replace(outStrText, vbcr, "\\r") + outStrText = Replace(outStrText, """", "\""") + outStrText = JsonU(outStrText) + JsonSafe = outStrText +End Function + +'TODO: need to change this function's name to something more appropriate +Function JsonU(astr) + + If isNull(astr) Then + JsonU = "" + Exit Function + End If + + Dim c + Dim utftext: utftext = "" + + For n = 1 To Len(astr) + c = CLng(AscW(Mid(astr, n, 1))) + + If c < 0 Then + c = &H10000 + c + End If + + If c < &H80 Then + utftext = utftext & Mid(astr, n, 1) + ElseIf c < &H100 Then + utftext = utftext & "\u00" & Hex(c) + ElseIf c < &H1000 Then + utftext = utftext & "\u0" & Hex(c) + Else + utftext = utftext & "\u" & Hex(c) + End If + Next + + JsonU = utftext +End Function diff --git a/electron/res/regedit-vbs/wsRegReadList.wsf b/electron/res/regedit-vbs/wsRegReadList.wsf new file mode 100755 index 0000000..397c424 --- /dev/null +++ b/electron/res/regedit-vbs/wsRegReadList.wsf @@ -0,0 +1,52 @@ +' +' Lists the values of a given registry path, this script takes its input from stdin +' +' cscript regListStream.wsf A "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\AppData" +' +' Will Yield: +' +' { +' "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\AppData": "value here" +' } + + + \ No newline at end of file diff --git a/electron/res/regedit-vbs/wsRegReadListStream.wsf b/electron/res/regedit-vbs/wsRegReadListStream.wsf new file mode 100755 index 0000000..602cd0b --- /dev/null +++ b/electron/res/regedit-vbs/wsRegReadListStream.wsf @@ -0,0 +1,47 @@ +' +' Lists the values of a given registry path, this script takes its input from stdin +' +' echo HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\AppData | cscript regListStream.wsf A +' +' Will Yield: +' +' { +' "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders\\AppData": "value here" +' } + + + \ No newline at end of file diff --git a/electron/res/w_c.exe b/electron/res/w_c.exe new file mode 100755 index 0000000..8607884 Binary files /dev/null and b/electron/res/w_c.exe differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..630dcca --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + + + 资源下载器 + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..d715ffe --- /dev/null +++ b/package.json @@ -0,0 +1,61 @@ +{ + "name": "res-downloader", + "version": "1.0.1", + "main": "dist-electron/main/index.js", + "description": "Really simple Electron + Vue + Vite boilerplate.", + "author": "putyy@qq.com", + "license": "MIT", + "private": true, + "keywords": [ + "electron", + "rollup", + "vite", + "vue3", + "vue" + ], + "debug": { + "env": { + "VITE_DEV_SERVER_URL": "http://127.0.0.1:3344/" + } + }, + "scripts": { + "dev": "vite", + "build": "vue-tsc --noEmit && vite build && electron-builder", + "preview": "vite preview" + }, + "devDependencies": { + "@element-plus/icons-vue": "^2.1.0", + "@vitejs/plugin-vue": "^4.3.3", + "cross-spawn": "^7.0.3", + "crypto-js": "^4.1.1", + "electron": "^26.0.0", + "electron-builder": "^24.6.3", + "electron-is-dev": "^2.0.0", + "electron-log": "^4.4.8", + "element-plus": "^2.3.12", + "get-port": "^7.0.0", + "less": "^4.2.0", + "less-loader": "^11.1.3", + "lodash": "^4.17.21", + "mkdirp": "^3.0.1", + "pug": "^3.0.2", + "pug-plain-loader": "^1.1.0", + "regedit": "^5.1.2", + "semver": "^7.5.4", + "typescript": "^5.1.6", + "unplugin-auto-import": "^0.16.6", + "unplugin-vue-components": "^0.25.2", + "vite": "^4.4.9", + "vite-plugin-electron": "^0.13.0-beta.3", + "vite-plugin-electron-renderer": "^0.14.5", + "vue": "^3.3.4", + "vue-router": "^4.2.4", + "vue-tsc": "^1.8.8" + }, + "dependencies": { + "axios": "^1.5.0", + "electron-store": "^8.1.0", + "getmac": "^5.20.0", + "hoxy": "^3.3.1" + } +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100755 index 0000000..1f41af9 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/show.png b/public/show.png new file mode 100644 index 0000000..70b2972 Binary files /dev/null and b/public/show.png differ diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..4d0a6d8 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,9 @@ + + + + + diff --git a/src/assets/logo.png b/src/assets/logo.png new file mode 100755 index 0000000..1f41af9 Binary files /dev/null and b/src/assets/logo.png differ diff --git a/src/common/api.ts b/src/common/api.ts new file mode 100755 index 0000000..c2ec3d4 --- /dev/null +++ b/src/common/api.ts @@ -0,0 +1,5 @@ +import request from './request' + +export function getPackageJson() { + return request.get("https://github.com/putyy/res-downloader/raw/main/package.json") +} \ No newline at end of file diff --git a/src/common/localStorage.ts b/src/common/localStorage.ts new file mode 100755 index 0000000..f9bc8a6 --- /dev/null +++ b/src/common/localStorage.ts @@ -0,0 +1,61 @@ +const localStorageCache = { + set: function set(key: string, value: any, time?: number) { + if (!localStorage) { + return false + } + if (!time || isNaN(time)) { + time = 1200 + } + try { + let expireDate = -1 + if (time !== -1) { + // @ts-ignore + expireDate = (new Date() - 1) + time * 1000 + } + localStorage.setItem(key, JSON.stringify({val: value, exp: expireDate})) + } catch (e) { + } + return true + }, + get: function get(key: string) { + try { + if (!localStorage) { + return null + } + let value = localStorage.getItem(key) + // @ts-ignore + let result = JSON.parse(value) + // @ts-ignore + let now = new Date() - 1 + if (!result) { + return null + }// 缓存不存在 + if (result.exp !== -1 && now > result.exp) { + // 缓存过期 + this.del(key) + return null + } + return result.val + } catch (e) { + this.del(key) + return null + } + }, + del: function del(key: string) { + if (!localStorage) { + return false + } + localStorage.removeItem(key) + return true + }, + // 清空所有缓存 + delAll: function delAll() { + if (!localStorage) { + return false + } + localStorage.clear() + return true + } +} + +export default localStorageCache diff --git a/src/common/md5.ts b/src/common/md5.ts new file mode 100755 index 0000000..a2432d7 --- /dev/null +++ b/src/common/md5.ts @@ -0,0 +1,144 @@ +function safeAdd(x:any, y:any) { + let lsw = (x & 0xFFFF) + (y & 0xFFFF) + let msw = (x >> 16) + (y >> 16) + (lsw >> 16) + return (msw << 16) | (lsw & 0xFFFF) +} + +function rol(num:any, cnt:any) { + return (num << cnt) | (num >>> (32 - cnt)) +} + +function cmn(q:any, a:any, b:any, x:any, s:any, t:any) { + return safeAdd(rol(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b) +} + +function ff(a:any, b:any, c:any, d:any, x:any, s:any, t:any) { + return cmn((b & c) | ((~b) & d), a, b, x, s, t) +} + +function gg(a:any, b:any, c:any, d:any, x:any, s:any, t:any) { + return cmn((b & d) | (c & (~d)), a, b, x, s, t) +} + +function hh(a:any, b:any, c:any, d:any, x:any, s:any, t:any) { + return cmn(b ^ c ^ d, a, b, x, s, t) +} + +function ii(a:any, b:any, c:any, d:any, x:any, s:any, t:any) { + return cmn(c ^ (b | (~d)), a, b, x, s, t) +} + +function coreMD5(x:any) { + let a = 1732584193 + let b = -271733879 + let c = -1732584194 + let d = 271733878 + + for (let i = 0; i < x.length; i += 16) { + let olda = a + let oldb = b + let oldc = c + let oldd = d + + a = ff(a, b, c, d, x[i], 7, -680876936) + d = ff(d, a, b, c, x[i + 1], 12, -389564586) + c = ff(c, d, a, b, x[i + 2], 17, 606105819) + b = ff(b, c, d, a, x[i + 3], 22, -1044525330) + a = ff(a, b, c, d, x[i + 4], 7, -176418897) + d = ff(d, a, b, c, x[i + 5], 12, 1200080426) + c = ff(c, d, a, b, x[i + 6], 17, -1473231341) + b = ff(b, c, d, a, x[i + 7], 22, -45705983) + a = ff(a, b, c, d, x[i + 8], 7, 1770035416) + d = ff(d, a, b, c, x[i + 9], 12, -1958414417) + c = ff(c, d, a, b, x[i + 10], 17, -42063) + b = ff(b, c, d, a, x[i + 11], 22, -1990404162) + a = ff(a, b, c, d, x[i + 12], 7, 1804603682) + d = ff(d, a, b, c, x[i + 13], 12, -40341101) + c = ff(c, d, a, b, x[i + 14], 17, -1502002290) + b = ff(b, c, d, a, x[i + 15], 22, 1236535329) + + a = gg(a, b, c, d, x[i + 1], 5, -165796510) + d = gg(d, a, b, c, x[i + 6], 9, -1069501632) + c = gg(c, d, a, b, x[i + 11], 14, 643717713) + b = gg(b, c, d, a, x[i], 20, -373897302) + a = gg(a, b, c, d, x[i + 5], 5, -701558691) + d = gg(d, a, b, c, x[i + 10], 9, 38016083) + c = gg(c, d, a, b, x[i + 15], 14, -660478335) + b = gg(b, c, d, a, x[i + 4], 20, -405537848) + a = gg(a, b, c, d, x[i + 9], 5, 568446438) + d = gg(d, a, b, c, x[i + 14], 9, -1019803690) + c = gg(c, d, a, b, x[i + 3], 14, -187363961) + b = gg(b, c, d, a, x[i + 8], 20, 1163531501) + a = gg(a, b, c, d, x[i + 13], 5, -1444681467) + d = gg(d, a, b, c, x[i + 2], 9, -51403784) + c = gg(c, d, a, b, x[i + 7], 14, 1735328473) + b = gg(b, c, d, a, x[i + 12], 20, -1926607734) + + a = hh(a, b, c, d, x[i + 5], 4, -378558) + d = hh(d, a, b, c, x[i + 8], 11, -2022574463) + c = hh(c, d, a, b, x[i + 11], 16, 1839030562) + b = hh(b, c, d, a, x[i + 14], 23, -35309556) + a = hh(a, b, c, d, x[i + 1], 4, -1530992060) + d = hh(d, a, b, c, x[i + 4], 11, 1272893353) + c = hh(c, d, a, b, x[i + 7], 16, -155497632) + b = hh(b, c, d, a, x[i + 10], 23, -1094730640) + a = hh(a, b, c, d, x[i + 13], 4, 681279174) + d = hh(d, a, b, c, x[i], 11, -358537222) + c = hh(c, d, a, b, x[i + 3], 16, -722521979) + b = hh(b, c, d, a, x[i + 6], 23, 76029189) + a = hh(a, b, c, d, x[i + 9], 4, -640364487) + d = hh(d, a, b, c, x[i + 12], 11, -421815835) + c = hh(c, d, a, b, x[i + 15], 16, 530742520) + b = hh(b, c, d, a, x[i + 2], 23, -995338651) + + a = ii(a, b, c, d, x[i], 6, -198630844) + d = ii(d, a, b, c, x[i + 7], 10, 1126891415) + c = ii(c, d, a, b, x[i + 14], 15, -1416354905) + b = ii(b, c, d, a, x[i + 5], 21, -57434055) + a = ii(a, b, c, d, x[i + 12], 6, 1700485571) + d = ii(d, a, b, c, x[i + 3], 10, -1894986606) + c = ii(c, d, a, b, x[i + 10], 15, -1051523) + b = ii(b, c, d, a, x[i + 1], 21, -2054922799) + a = ii(a, b, c, d, x[i + 8], 6, 1873313359) + d = ii(d, a, b, c, x[i + 15], 10, -30611744) + c = ii(c, d, a, b, x[i + 6], 15, -1560198380) + b = ii(b, c, d, a, x[i + 13], 21, 1309151649) + a = ii(a, b, c, d, x[i + 4], 6, -145523070) + d = ii(d, a, b, c, x[i + 11], 10, -1120210379) + c = ii(c, d, a, b, x[i + 2], 15, 718787259) + b = ii(b, c, d, a, x[i + 9], 21, -343485551) + + a = safeAdd(a, olda) + b = safeAdd(b, oldb) + c = safeAdd(c, oldc) + d = safeAdd(d, oldd) + } + return [a, b, c, d] +} + +function binl2hex(binarray:any) { + let hexTab = '0123456789abcdef' + let str = '' + for (let i = 0; i < binarray.length * 4; i++) { + str += hexTab.charAt((binarray[i >> 2] >> ((i % 4) * 8 + 4)) & 0xF) + + hexTab.charAt((binarray[i >> 2] >> ((i % 4) * 8)) & 0xF) + } + return str +} + +function str2binl(str:any) { + let nblk = ((str.length + 8) >> 6) + 1 // number of 16-word blocks + let blks = new Array(nblk * 16) + for (let i = 0; i < nblk * 16; i++) blks[i] = 0 + var i = 0 + for (i; i < str.length; i++) { + blks[i >> 2] |= (str.charCodeAt(i) & 0xFF) << ((i % 4) * 8) + } + blks[i >> 2] |= 0x80 << ((i % 4) * 8) + blks[nblk * 16 - 2] = str.length * 8 + return blks +} + +export function hexMD5(str:any) { + return binl2hex(coreMD5(str2binl(str))) +} diff --git a/src/common/request.ts b/src/common/request.ts new file mode 100755 index 0000000..a09a617 --- /dev/null +++ b/src/common/request.ts @@ -0,0 +1,71 @@ +import axios from 'axios' +import {ElMessage} from 'element-plus' +import {hexMD5} from "./md5" +import localStorageCache from "./localStorage" +import _ from "lodash" + +class RequestService { + private axios: any; + private requestList: any; + constructor() { + let that = this + that.requestList = [] + that.axios = axios.create({ + timeout: 60000, // 请求超时时间毫秒 + }) + + // 请求拦截 + that.axios.interceptors.request.use( + function (config: any) { + if (config.url.slice(0, 8) !== "https://") { + config.url = import.meta.env.VITE_APP_API + "/" + config.url + } + return config + }, + function (error: any) { + return Promise.reject(error) + } + ) + + // 响应拦截 + that.axios.interceptors.response.use( + function (response: any) { + return response + }, + function (error: any) { + // console.log(error) + return Promise.reject(error) + } + ) + } + + + get(url: string, data?: any) { + return this.axios.get(url, {params: data}).catch((err:any)=>{ + console.log('get-err', err) + }) + } + + post(url: string, data: any, isHandle?: any) { + isHandle = isHandle || true + if (isHandle){ + data = Object.keys(data).map(item => { + let value = data[item]; + if (_.isArray(value) || _.isObject(value)) { + value = JSON.stringify(value) + } + return encodeURIComponent(item) + '=' + encodeURIComponent(value) + }).join('&'); + } + return this.axios.post(url, data).catch((err:any)=>{ + console.log('post-err', err) + }) + } + + axiosObj() { + return this.axios + } +} + +const request = new RequestService() +export default request diff --git a/src/components/layout/Footer.vue b/src/components/layout/Footer.vue new file mode 100755 index 0000000..b59b821 --- /dev/null +++ b/src/components/layout/Footer.vue @@ -0,0 +1,49 @@ + + + + diff --git a/src/components/layout/Index.vue b/src/components/layout/Index.vue new file mode 100755 index 0000000..7d6438a --- /dev/null +++ b/src/components/layout/Index.vue @@ -0,0 +1,27 @@ + + + + diff --git a/src/components/layout/Sidebar.vue b/src/components/layout/Sidebar.vue new file mode 100755 index 0000000..9130f0f --- /dev/null +++ b/src/components/layout/Sidebar.vue @@ -0,0 +1,70 @@ + + + + diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100755 index 0000000..b7d279d --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1,8 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..39c50b2 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,19 @@ +import {Component, createApp} from 'vue' +import "./style.css" +import VueApp from './App.vue' +import './samples/node-api' +import * as ElementPlusIconsVue from '@element-plus/icons-vue' +import 'element-plus/dist/index.css' +import router from './route' + +const app = createApp(VueApp) + +for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component) +} + +app.use(router) + .mount('#app') + .$nextTick(() => { + postMessage({ payload: 'removeLoading' }, '*') + }) diff --git a/src/route.ts b/src/route.ts new file mode 100755 index 0000000..33d0623 --- /dev/null +++ b/src/route.ts @@ -0,0 +1,46 @@ +import {createMemoryHistory, createRouter} from 'vue-router' +// @ts-ignore +import localStorageCache from "./common/localStorage" + +const routes = [ + { + path: '/', + component: () => import('./components/layout/Index.vue'), + // 重定向 + redirect: {name: 'Index'}, + // 子路由 + children: [ + { + path: '/index', + name: 'Index', + component: () => import('./views/Index.vue'), + }, + { + path: '/about', + name: 'about', + component: () => import('./views/About.vue'), + }, + { + path: '/setting', + name: 'Setting', + component: () => import('./views/Setting.vue'), + }, + { + path: '/vip-parse', + name: 'VipParse', + component: () => import('./views/VipParse.vue'), + }, + ] + }, +] + +const router = createRouter({ + history: createMemoryHistory(), + routes: routes, +}) + +router.beforeEach((to, from, next) => { + next() +}) + +export default router diff --git a/src/samples/node-api.ts b/src/samples/node-api.ts new file mode 100644 index 0000000..f7a4d4d --- /dev/null +++ b/src/samples/node-api.ts @@ -0,0 +1,13 @@ +import { lstat } from 'node:fs/promises' +import { cwd } from 'node:process' +import { ipcRenderer } from 'electron' + +ipcRenderer.on('main-process-message', (_event, ...args) => { + console.log('[Receive Main-process message]:', ...args) +}) + +lstat(cwd()).then(stats => { + console.log('[fs.lstat]', stats) +}).catch(err => { + console.error(err) +}) diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..8fbb17a --- /dev/null +++ b/src/style.css @@ -0,0 +1,90 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #ffffff; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 100%; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +code { + background-color: #1a1a1a; + padding: 2px 4px; + margin: 0 4px; + border-radius: 4px; +} + +.card { + padding: 2em; +} + +#app { + width: 100%; + /*margin: 0 auto;*/ + /*padding: 2rem;*/ + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } + code { + background-color: #f9f9f9; + } +} diff --git a/src/views/About.vue b/src/views/About.vue new file mode 100644 index 0000000..8b10d23 --- /dev/null +++ b/src/views/About.vue @@ -0,0 +1,54 @@ + + + + \ No newline at end of file diff --git a/src/views/Index.vue b/src/views/Index.vue new file mode 100755 index 0000000..68fc05c --- /dev/null +++ b/src/views/Index.vue @@ -0,0 +1,295 @@ + + + + + diff --git a/src/views/Setting.vue b/src/views/Setting.vue new file mode 100755 index 0000000..ba7df81 --- /dev/null +++ b/src/views/Setting.vue @@ -0,0 +1,36 @@ + + \ No newline at end of file diff --git a/src/views/VipParse.vue b/src/views/VipParse.vue new file mode 100755 index 0000000..0d3e4e2 --- /dev/null +++ b/src/views/VipParse.vue @@ -0,0 +1,99 @@ + + + \ No newline at end of file diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..323c78a --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,7 @@ +/// + +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..6d0f5ee --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "moduleResolution": "Node", + "strict": true, + "jsx": "preserve", + "resolveJsonModule": true, + "isolatedModules": true, + "esModuleInterop": true, + "lib": ["ESNext", "DOM"], + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src"], + "references": [ + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..ed1b586 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts", "package.json", "electron"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..58a4ecd --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,81 @@ +import { rmSync } from 'node:fs' +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import electron from 'vite-plugin-electron' +import renderer from 'vite-plugin-electron-renderer' +import pkg from './package.json' +import AutoImport from 'unplugin-auto-import/vite' +import Components from 'unplugin-vue-components/vite' +import {ElementPlusResolver} from 'unplugin-vue-components/resolvers' + +// https://vitejs.dev/config/ +export default defineConfig(({ command }) => { + rmSync('dist-electron', { recursive: true, force: true }) + + const isServe = command === 'serve' + const isBuild = command === 'build' + const sourcemap = isServe || !!process.env.VSCODE_DEBUG + + return { + plugins: [ + vue(), + AutoImport({ + resolvers: [ElementPlusResolver()], + }), + Components({ + resolvers: [ElementPlusResolver()], + }), + electron([ + { + // Main-Process entry file of the Electron App. + entry: 'electron/main/index.ts', + onstart(options) { + if (process.env.VSCODE_DEBUG) { + console.log(/* For `.vscode/.debug.script.mjs` */'[startup] Electron App') + } else { + options.startup() + } + }, + vite: { + build: { + sourcemap, + minify: isBuild, + outDir: 'dist-electron/main', + rollupOptions: { + external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}), + }, + }, + }, + }, + { + entry: 'electron/preload/index.ts', + onstart(options) { + // Notify the Renderer-Process to reload the page when the Preload-Scripts build is complete, + // instead of restarting the entire Electron App. + options.reload() + }, + vite: { + build: { + sourcemap: sourcemap ? 'inline' : undefined, // #332 + minify: isBuild, + outDir: 'dist-electron/preload', + rollupOptions: { + external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}), + }, + }, + }, + } + ]), + // Use Node.js API in the Renderer-process + renderer(), + ], + server: process.env.VSCODE_DEBUG && (() => { + const url = new URL(pkg.debug.env.VITE_DEV_SERVER_URL) + return { + host: url.hostname, + port: +url.port, + } + })(), + clearScreen: false, + } +})