Compare commits
No commits in common. "b59f40830b591575d038dea76a78121b4933a265" and "e99ec8e88ae379c724766e068a32a2bc027907d8" have entirely different histories.
b59f40830b
...
e99ec8e88a
20 changed files with 210 additions and 1449 deletions
|
@ -5,6 +5,7 @@ edition = "2021"
|
||||||
description = "Get the raw database from your Signal backup. Written for webassembly"
|
description = "Get the raw database from your Signal backup. Written for webassembly"
|
||||||
repository = "https://git.duskflower.dev/duskflower/signal-decrypt-backup-wasm"
|
repository = "https://git.duskflower.dev/duskflower/signal-decrypt-backup-wasm"
|
||||||
license = "GPL-3-only"
|
license = "GPL-3-only"
|
||||||
|
license-file = "LICENSE"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
22
src/lib.rs
22
src/lib.rs
|
@ -41,14 +41,14 @@ type HmacSha256 = Hmac<Sha256>;
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub struct DecryptionResult {
|
pub struct DecryptionResult {
|
||||||
database_statements: Vec<String>,
|
database_bytes: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
impl DecryptionResult {
|
impl DecryptionResult {
|
||||||
#[wasm_bindgen(getter)]
|
#[wasm_bindgen(getter)]
|
||||||
pub fn database_statements(&self) -> Vec<String> {
|
pub fn database_bytes(&self) -> Vec<u8> {
|
||||||
self.database_statements.clone()
|
self.database_bytes.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,7 +355,7 @@ pub struct BackupDecryptor {
|
||||||
keys: Option<Keys>,
|
keys: Option<Keys>,
|
||||||
header_data: Option<HeaderData>,
|
header_data: Option<HeaderData>,
|
||||||
initialisation_vector: Option<Vec<u8>>,
|
initialisation_vector: Option<Vec<u8>>,
|
||||||
database_statements: Vec<String>,
|
database_bytes: Vec<u8>,
|
||||||
ciphertext_buf: Vec<u8>,
|
ciphertext_buf: Vec<u8>,
|
||||||
plaintext_buf: Vec<u8>,
|
plaintext_buf: Vec<u8>,
|
||||||
total_file_size: usize,
|
total_file_size: usize,
|
||||||
|
@ -379,7 +379,7 @@ impl BackupDecryptor {
|
||||||
keys: None,
|
keys: None,
|
||||||
header_data: None,
|
header_data: None,
|
||||||
initialisation_vector: None,
|
initialisation_vector: None,
|
||||||
database_statements: Vec::new(),
|
database_bytes: Vec::new(),
|
||||||
ciphertext_buf: Vec::new(),
|
ciphertext_buf: Vec::new(),
|
||||||
plaintext_buf: Vec::new(),
|
plaintext_buf: Vec::new(),
|
||||||
total_file_size: 0,
|
total_file_size: 0,
|
||||||
|
@ -544,7 +544,8 @@ impl BackupDecryptor {
|
||||||
if let Some(version) = backup_frame.version {
|
if let Some(version) = backup_frame.version {
|
||||||
if let Some(ver_num) = version.version {
|
if let Some(ver_num) = version.version {
|
||||||
let pragma_sql = format!("PRAGMA user_version = {}", ver_num);
|
let pragma_sql = format!("PRAGMA user_version = {}", ver_num);
|
||||||
self.database_statements.push(pragma_sql);
|
self.database_bytes.extend_from_slice(pragma_sql.as_bytes());
|
||||||
|
self.database_bytes.push(b';');
|
||||||
}
|
}
|
||||||
} else if let Some(statement) = backup_frame.statement {
|
} else if let Some(statement) = backup_frame.statement {
|
||||||
if let Some(sql) = statement.statement {
|
if let Some(sql) = statement.statement {
|
||||||
|
@ -561,10 +562,13 @@ impl BackupDecryptor {
|
||||||
|
|
||||||
process_parameter_placeholders(&sql, ¶ms)?
|
process_parameter_placeholders(&sql, ¶ms)?
|
||||||
} else {
|
} else {
|
||||||
sql.clone()
|
sql
|
||||||
};
|
};
|
||||||
|
|
||||||
self.database_statements.push(processed_sql);
|
// Add to concatenated string
|
||||||
|
self.database_bytes
|
||||||
|
.extend_from_slice(processed_sql.as_bytes());
|
||||||
|
self.database_bytes.push(b';');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if backup_frame.preference.is_some() || backup_frame.key_value.is_some()
|
} else if backup_frame.preference.is_some() || backup_frame.key_value.is_some()
|
||||||
|
@ -624,7 +628,7 @@ impl BackupDecryptor {
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn finish(self) -> Result<DecryptionResult, JsValue> {
|
pub fn finish(self) -> Result<DecryptionResult, JsValue> {
|
||||||
Ok(DecryptionResult {
|
Ok(DecryptionResult {
|
||||||
database_statements: self.database_statements,
|
database_bytes: self.database_bytes,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
29
test/.gitignore
vendored
29
test/.gitignore
vendored
|
@ -1,29 +0,0 @@
|
||||||
|
|
||||||
dist
|
|
||||||
.solid
|
|
||||||
.output
|
|
||||||
.vercel
|
|
||||||
.netlify
|
|
||||||
.vinxi
|
|
||||||
app.config.timestamp_*.js
|
|
||||||
|
|
||||||
# Environment
|
|
||||||
.env
|
|
||||||
.env*.local
|
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
|
||||||
|
|
||||||
# IDEs and editors
|
|
||||||
/.idea
|
|
||||||
.project
|
|
||||||
.classpath
|
|
||||||
*.launch
|
|
||||||
.settings/
|
|
||||||
|
|
||||||
# Temp
|
|
||||||
gitignore
|
|
||||||
|
|
||||||
# System Files
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
|
@ -1,36 +1,15 @@
|
||||||
## Usage
|
# test
|
||||||
|
|
||||||
Those templates dependencies are maintained via [pnpm](https://pnpm.io) via `pnpm up -Lri`.
|
To install dependencies:
|
||||||
|
|
||||||
This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ npm install # or pnpm install or yarn install
|
bun install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
|
To run:
|
||||||
|
|
||||||
## Available Scripts
|
```bash
|
||||||
|
bun run index.js
|
||||||
|
```
|
||||||
|
|
||||||
In the project directory, you can run:
|
This project was created using `bun init` in bun v1.1.20. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|
||||||
|
|
||||||
### `npm run dev` or `npm start`
|
|
||||||
|
|
||||||
Runs the app in the development mode.<br>
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
|
||||||
|
|
||||||
The page will reload if you make edits.<br>
|
|
||||||
|
|
||||||
### `npm run build`
|
|
||||||
|
|
||||||
Builds the app for production to the `dist` folder.<br>
|
|
||||||
It correctly bundles Solid in production mode and optimizes the build for the best performance.
|
|
||||||
|
|
||||||
The build is minified and the filenames include the hashes.<br>
|
|
||||||
Your app is ready to be deployed!
|
|
||||||
|
|
||||||
## Deployment
|
|
||||||
|
|
||||||
You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.)
|
|
||||||
|
|
||||||
## This project was created with the [Solid CLI](https://solid-cli.netlify.app)
|
|
||||||
|
|
21
test/dist/index.html
vendored
Normal file
21
test/dist/index.html
vendored
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<!-- <link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" /> -->
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root">
|
||||||
|
<form id="test-form">
|
||||||
|
<input type="password" id="passphrase-input" />
|
||||||
|
<input type="file" id="backup-input" />
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="./index.js" type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
100
test/dist/index.js
vendored
Normal file
100
test/dist/index.js
vendored
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import init, { BackupDecryptor } from "../pkg/signal_decrypt_backup_wasm.js";
|
||||||
|
|
||||||
|
let isInitialized = false;
|
||||||
|
|
||||||
|
async function initialize() {
|
||||||
|
if (!isInitialized) {
|
||||||
|
await init().catch((err) =>
|
||||||
|
console.error("Failed to initialize WASM module: ", err),
|
||||||
|
);
|
||||||
|
isInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function decryptBackup(file, passphrase, progressCallback) {
|
||||||
|
await initialize();
|
||||||
|
|
||||||
|
const fileSize = file.size;
|
||||||
|
console.log("Starting decryption of file size:", fileSize);
|
||||||
|
const decryptor = new BackupDecryptor();
|
||||||
|
decryptor.set_progress_callback(fileSize, (percentage) =>
|
||||||
|
console.info(`${percentage}% done`),
|
||||||
|
);
|
||||||
|
const chunkSize = 1024 * 1024 * 40; // 40MB chunks
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (offset < file.size) {
|
||||||
|
// console.log(`Processing chunk at offset ${offset}`);
|
||||||
|
const chunk = file.slice(offset, offset + chunkSize);
|
||||||
|
const arrayBuffer = await chunk.arrayBuffer();
|
||||||
|
const uint8Array = new Uint8Array(arrayBuffer);
|
||||||
|
|
||||||
|
decryptor.feed_data(uint8Array);
|
||||||
|
|
||||||
|
let done = false;
|
||||||
|
while (!done) {
|
||||||
|
try {
|
||||||
|
done = await decryptor.process_chunk(passphrase, progressCallback);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Error processing chunk:", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += chunkSize;
|
||||||
|
// console.log(`Completed chunk, new offset: ${offset}`);
|
||||||
|
// if (performance.memory) {
|
||||||
|
// const memoryInfo = performance.memory;
|
||||||
|
// console.log(`Total JS Heap Size: ${memoryInfo.totalJSHeapSize} bytes`);
|
||||||
|
// console.log(`Used JS Heap Size: ${memoryInfo.usedJSHeapSize} bytes`);
|
||||||
|
// console.log(`JS Heap Size Limit: ${memoryInfo.jsHeapSizeLimit} bytes`);
|
||||||
|
// } else {
|
||||||
|
// console.log("Memory information is not available in this environment.");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log("All chunks processed, finishing up");
|
||||||
|
const result = decryptor.finish();
|
||||||
|
|
||||||
|
decryptor.free();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Decryption failed:", e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function decrypt(file, passphrase) {
|
||||||
|
try {
|
||||||
|
const result = await decryptBackup(file, passphrase);
|
||||||
|
|
||||||
|
console.log(result, result.database_bytes);
|
||||||
|
|
||||||
|
// console.log("Database bytes length:", result.databaseBytes.length);
|
||||||
|
console.log(
|
||||||
|
"Database bytes as string (partly)",
|
||||||
|
new TextDecoder().decode(result.database_bytes.slice(0, 1024 * 50)),
|
||||||
|
);
|
||||||
|
// console.log("Preferences:", result.preferences);
|
||||||
|
// console.log("Key values:", result.keyValues);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Decryption failed:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {HTMLInputElement}
|
||||||
|
*/
|
||||||
|
const passphraseInput = document.querySelector("#passphrase-input");
|
||||||
|
/**
|
||||||
|
* @type {HTMLInputElement}
|
||||||
|
*/
|
||||||
|
const backupInput = document.querySelector("#backup-input");
|
||||||
|
|
||||||
|
backupInput.addEventListener("change", (event) => {
|
||||||
|
const file = event.currentTarget.files[0];
|
||||||
|
|
||||||
|
decrypt(file, passphraseInput.value);
|
||||||
|
});
|
|
@ -1,16 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" />
|
|
||||||
<title>Solid App</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
|
|
||||||
<script src="/src/index.tsx" type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
34
test/index.js
Normal file
34
test/index.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
const server = Bun.serve({
|
||||||
|
static: {
|
||||||
|
"/": new Response(await Bun.file("./dist/index.html").bytes(), {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/html",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
"/index.js": new Response(await Bun.file("./dist/index.js").bytes(), {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/javascript",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
"/pkg/signal_decrypt_backup_wasm.js": new Response(
|
||||||
|
await Bun.file("../pkg/signal_decrypt_backup_wasm.js").bytes(),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "text/javascript",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
"/pkg/signal_decrypt_backup_wasm_bg.wasm": new Response(
|
||||||
|
await Bun.file("../pkg/signal_decrypt_backup_wasm_bg.wasm").bytes(),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/wasm",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
fetch(req) {
|
||||||
|
console.log(req);
|
||||||
|
return new Response("404!");
|
||||||
|
},
|
||||||
|
});
|
27
test/jsconfig.json
Normal file
27
test/jsconfig.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Enable latest features
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,24 +1,11 @@
|
||||||
{
|
{
|
||||||
"name": "vite-template-solid",
|
"name": "test",
|
||||||
"version": "0.0.0",
|
"module": "index.js",
|
||||||
"description": "",
|
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
|
||||||
"start": "vite",
|
|
||||||
"dev": "vite",
|
|
||||||
"build": "vite build",
|
|
||||||
"serve": "vite preview"
|
|
||||||
},
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@duskflower/signal-decrypt-backup-wasm": "../pkg",
|
"@types/bun": "latest"
|
||||||
"typescript": "^5.7.2",
|
|
||||||
"vite": "^6.0.0",
|
|
||||||
"vite-plugin-solid": "^2.11.0",
|
|
||||||
"vite-plugin-wasm": "^3.4.1"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"peerDependencies": {
|
||||||
"@sqlite.org/sqlite-wasm": "3.47.2-build1",
|
"typescript": "^5.0.0"
|
||||||
"solid-js": "^1.9.3"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
1089
test/pnpm-lock.yaml
generated
1089
test/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -1,33 +0,0 @@
|
||||||
.App {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
animation: logo-spin infinite 20s linear;
|
|
||||||
height: 40vmin;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
background-color: #282c34;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
color: #b318f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes logo-spin {
|
|
||||||
from {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
144
test/src/App.tsx
144
test/src/App.tsx
|
@ -1,144 +0,0 @@
|
||||||
import { createEffect, createSignal, on, type Component } from "solid-js";
|
|
||||||
|
|
||||||
import { BackupDecryptor } from "@duskflower/signal-decrypt-backup-wasm";
|
|
||||||
import { db } from "./db";
|
|
||||||
|
|
||||||
export async function decryptBackup(
|
|
||||||
file: File,
|
|
||||||
passphrase: string,
|
|
||||||
progressCallback: (progres: number) => void,
|
|
||||||
) {
|
|
||||||
const fileSize = file.size;
|
|
||||||
console.log("Starting decryption of file size:", fileSize);
|
|
||||||
const decryptor = new BackupDecryptor();
|
|
||||||
decryptor.set_progress_callback(fileSize, (percentage: number) =>
|
|
||||||
console.info(`${percentage}% done`),
|
|
||||||
);
|
|
||||||
const chunkSize = 1024 * 1024 * 40; // 40MB chunks
|
|
||||||
let offset = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
while (offset < file.size) {
|
|
||||||
// console.log(`Processing chunk at offset ${offset}`);
|
|
||||||
const chunk = file.slice(offset, offset + chunkSize);
|
|
||||||
const arrayBuffer = await chunk.arrayBuffer();
|
|
||||||
const uint8Array = new Uint8Array(arrayBuffer);
|
|
||||||
|
|
||||||
decryptor.feed_data(uint8Array);
|
|
||||||
|
|
||||||
let done = false;
|
|
||||||
while (!done) {
|
|
||||||
try {
|
|
||||||
done = decryptor.process_chunk(passphrase);
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error processing chunk:", e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset += chunkSize;
|
|
||||||
// console.log(`Completed chunk, new offset: ${offset}`);
|
|
||||||
// if (performance.memory) {
|
|
||||||
// const memoryInfo = performance.memory;
|
|
||||||
// console.log(`Total JS Heap Size: ${memoryInfo.totalJSHeapSize} bytes`);
|
|
||||||
// console.log(`Used JS Heap Size: ${memoryInfo.usedJSHeapSize} bytes`);
|
|
||||||
// console.log(`JS Heap Size Limit: ${memoryInfo.jsHeapSizeLimit} bytes`);
|
|
||||||
// } else {
|
|
||||||
// console.log("Memory information is not available in this environment.");
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// console.log("All chunks processed, finishing up");
|
|
||||||
const result = decryptor.finish();
|
|
||||||
|
|
||||||
// decryptor.free();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Decryption failed:", e);
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function decrypt(file: File, passphrase: string) {
|
|
||||||
try {
|
|
||||||
const result = await decryptBackup(file, passphrase, console.info);
|
|
||||||
|
|
||||||
// console.log(result, result.database_bytes);
|
|
||||||
|
|
||||||
// console.log("Database bytes length:", result.databaseBytes.length);
|
|
||||||
// console.log(
|
|
||||||
// "Database bytes as string (partly)",
|
|
||||||
// new TextDecoder().decode(result.database_bytes.slice(0, 1024 * 50)),
|
|
||||||
// );
|
|
||||||
|
|
||||||
// console.log(result.database_statements);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
// console.log("Preferences:", result.preferences);
|
|
||||||
// console.log("Key values:", result.keyValues);
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Decryption failed:", error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const App: Component = () => {
|
|
||||||
const [passphrase, setPassphrase] = createSignal("");
|
|
||||||
const [backupFile, setBackupFile] = createSignal<File>();
|
|
||||||
|
|
||||||
createEffect(
|
|
||||||
on(
|
|
||||||
backupFile,
|
|
||||||
(currentBackupFile) => {
|
|
||||||
if (currentBackupFile) {
|
|
||||||
decrypt(currentBackupFile, passphrase()).then((result) => {
|
|
||||||
if (result) {
|
|
||||||
for (const statement of result.database_statements) {
|
|
||||||
try {
|
|
||||||
console.log("executing");
|
|
||||||
db.exec(statement);
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(`statement failed: ${statement}`, {
|
|
||||||
cause: e,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("All statements executed");
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
db.exec("SELECT * from message", {
|
|
||||||
returnValue: "resultRows",
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
defer: true,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form id="test-form">
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id="passphrase-input"
|
|
||||||
onChange={(event) => {
|
|
||||||
setPassphrase(event.currentTarget.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
id="backup-input"
|
|
||||||
onChange={(event) => {
|
|
||||||
setBackupFile(event.currentTarget.files?.[0]);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default App;
|
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
|
@ -1,21 +0,0 @@
|
||||||
import sqlite3InitModule, {
|
|
||||||
type Database,
|
|
||||||
type OpfsDatabase,
|
|
||||||
} from "@sqlite.org/sqlite-wasm";
|
|
||||||
|
|
||||||
export let db: OpfsDatabase | Database;
|
|
||||||
|
|
||||||
const sqlite3 = (await sqlite3InitModule()).oo1;
|
|
||||||
if (!sqlite3) {
|
|
||||||
throw new Error("fail to load sqlite");
|
|
||||||
}
|
|
||||||
const path = "/signal.db";
|
|
||||||
|
|
||||||
if (sqlite3.OpfsDb) {
|
|
||||||
console.log("support OPFS");
|
|
||||||
db = new sqlite3.OpfsDb(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("doesn't support OPFS");
|
|
||||||
|
|
||||||
db = new sqlite3.DB(path);
|
|
|
@ -1,13 +0,0 @@
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto',
|
|
||||||
'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
|
|
||||||
'Helvetica Neue', sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
|
||||||
monospace;
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
/* @refresh reload */
|
|
||||||
import { render } from 'solid-js/web';
|
|
||||||
|
|
||||||
import './index.css';
|
|
||||||
import App from './App';
|
|
||||||
|
|
||||||
const root = document.getElementById('root');
|
|
||||||
|
|
||||||
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
|
|
||||||
throw new Error(
|
|
||||||
'Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render(() => <App />, root!);
|
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 166 155.3"><path d="M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z" fill="#76b3e1"/><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="27.5" y1="3" x2="152" y2="63.5"><stop offset=".1" stop-color="#76b3e1"/><stop offset=".3" stop-color="#dcf2fd"/><stop offset="1" stop-color="#76b3e1"/></linearGradient><path d="M163 35S110-4 69 5l-3 1c-6 2-11 5-14 9l-2 3-15 26 26 5c11 7 25 10 38 7l46 9 18-30z" opacity=".3" fill="url(#a)"/><path d="M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z" fill="#518ac8"/><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="95.8" y1="32.6" x2="74" y2="105.2"><stop offset="0" stop-color="#76b3e1"/><stop offset=".5" stop-color="#4377bb"/><stop offset="1" stop-color="#1f3b77"/></linearGradient><path d="M52 35l-4 1c-17 5-22 21-13 35 10 13 31 20 48 15l62-21S92 26 52 35z" opacity=".3" fill="url(#b)"/><linearGradient id="c" gradientUnits="userSpaceOnUse" x1="18.4" y1="64.2" x2="144.3" y2="149.8"><stop offset="0" stop-color="#315aa9"/><stop offset=".5" stop-color="#518ac8"/><stop offset="1" stop-color="#315aa9"/></linearGradient><path d="M134 80a45 45 0 00-48-15L24 85 4 120l112 19 20-36c4-7 3-15-2-23z" fill="url(#c)"/><linearGradient id="d" gradientUnits="userSpaceOnUse" x1="75.2" y1="74.5" x2="24.4" y2="260.8"><stop offset="0" stop-color="#4377bb"/><stop offset=".5" stop-color="#1a336b"/><stop offset="1" stop-color="#1a336b"/></linearGradient><path d="M114 115a45 45 0 00-48-15L4 120s53 40 94 30l3-1c17-5 23-21 13-34z" fill="url(#d)"/></svg>
|
|
Before Width: | Height: | Size: 1.6 KiB |
|
@ -1,15 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"strict": true,
|
|
||||||
"target": "ESNext",
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"jsxImportSource": "solid-js",
|
|
||||||
"types": ["vite/client"],
|
|
||||||
"noEmit": true,
|
|
||||||
"isolatedModules": true
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { defineConfig } from "vite";
|
|
||||||
import solidPlugin from "vite-plugin-solid";
|
|
||||||
import wasm from "vite-plugin-wasm";
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [solidPlugin(), wasm()],
|
|
||||||
optimizeDeps: {
|
|
||||||
exclude: ["signal-decrypt-backup-wasm", "@sqlite.org/sqlite-wasm"],
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
port: 3000,
|
|
||||||
},
|
|
||||||
build: {
|
|
||||||
target: "esnext",
|
|
||||||
},
|
|
||||||
});
|
|
Loading…
Add table
Add a link
Reference in a new issue