Compare commits
No commits in common. "253fccaf7879f5dfa2ca34694513e851fec9096b" and "c73af508571dd71897f750a05669c765ac50ca2c" have entirely different histories.
253fccaf78
...
c73af50857
19 changed files with 654 additions and 902 deletions
631
Cargo.lock
generated
631
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -113,4 +113,5 @@ There are a few reasons to not use OmegaUpload:
|
||||||
- Cannot download files larger than 512 MiB through the web frontend—this
|
- Cannot download files larger than 512 MiB through the web frontend—this
|
||||||
is a technical limitation of the current web frontend not using a web worker
|
is a technical limitation of the current web frontend not using a web worker
|
||||||
in addition to the fact that browsers are not optimized for XChaCha20.
|
in addition to the fact that browsers are not optimized for XChaCha20.
|
||||||
|
- Right now, you must upload via the CLI tool.
|
||||||
- The frontend uses WASM, which is a novel attack surface.
|
- The frontend uses WASM, which is a novel attack surface.
|
||||||
|
|
|
@ -22,6 +22,6 @@ CUR_DIR=$(pwd)
|
||||||
PROJECT_TOP_LEVEL=$(git rev-parse --show-toplevel)
|
PROJECT_TOP_LEVEL=$(git rev-parse --show-toplevel)
|
||||||
|
|
||||||
cd "$PROJECT_TOP_LEVEL" || exit 1
|
cd "$PROJECT_TOP_LEVEL" || exit 1
|
||||||
git submodule update --remote
|
git submodule foreach git pull
|
||||||
|
|
||||||
cd "$CUR_DIR"
|
cd "$CUR_DIR"
|
|
@ -9,9 +9,10 @@ license = "GPL-3.0-or-later"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
omegaupload-common = { path = "../common" }
|
omegaupload-common = "0.2"
|
||||||
anyhow = "1.0.58"
|
|
||||||
atty = "0.2.14"
|
anyhow = "1"
|
||||||
clap = { version = "3.2.15", features = ["derive"] }
|
atty = "0.2"
|
||||||
reqwest = { version = "0.11.11", default-features = false, features = ["rustls-tls", "blocking"] }
|
clap = { version = "3", features = ["derive"] }
|
||||||
rpassword = "7.0.0"
|
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "blocking"] }
|
||||||
|
rpassword = "5"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::secrecy::{ExposeSecret, SecretString};
|
use omegaupload_common::secrecy::{ExposeSecret, SecretString};
|
||||||
|
|
||||||
pub struct Builder {
|
pub struct Builder {
|
||||||
decryption_key: SecretString,
|
decryption_key: SecretString,
|
||||||
|
@ -8,7 +8,6 @@ pub struct Builder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Builder {
|
impl Builder {
|
||||||
#[must_use]
|
|
||||||
pub fn new(decryption_key: SecretString) -> Self {
|
pub fn new(decryption_key: SecretString) -> Self {
|
||||||
Self {
|
Self {
|
||||||
decryption_key,
|
decryption_key,
|
||||||
|
@ -18,7 +17,6 @@ impl Builder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub const fn needs_password(mut self) -> Self {
|
pub const fn needs_password(mut self) -> Self {
|
||||||
self.needs_password = true;
|
self.needs_password = true;
|
||||||
self
|
self
|
||||||
|
@ -26,7 +24,6 @@ impl Builder {
|
||||||
|
|
||||||
// False positive
|
// False positive
|
||||||
#[allow(clippy::missing_const_for_fn)]
|
#[allow(clippy::missing_const_for_fn)]
|
||||||
#[must_use]
|
|
||||||
pub fn file_name(mut self, name: String) -> Self {
|
pub fn file_name(mut self, name: String) -> Self {
|
||||||
self.file_name = Some(name);
|
self.file_name = Some(name);
|
||||||
self
|
self
|
||||||
|
@ -34,13 +31,11 @@ impl Builder {
|
||||||
|
|
||||||
// False positive
|
// False positive
|
||||||
#[allow(clippy::missing_const_for_fn)]
|
#[allow(clippy::missing_const_for_fn)]
|
||||||
#[must_use]
|
|
||||||
pub fn language(mut self, language: String) -> Self {
|
pub fn language(mut self, language: String) -> Self {
|
||||||
self.language = Some(language);
|
self.language = Some(language);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn build(self) -> SecretString {
|
pub fn build(self) -> SecretString {
|
||||||
if !self.needs_password && self.file_name.is_none() && self.language.is_none() {
|
if !self.needs_password && self.file_name.is_none() && self.language.is_none() {
|
||||||
return self.decryption_key;
|
return self.decryption_key;
|
|
@ -24,7 +24,6 @@ use anyhow::{anyhow, bail, Context, Result};
|
||||||
use atty::Stream;
|
use atty::Stream;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use omegaupload_common::crypto::{open_in_place, seal_in_place};
|
use omegaupload_common::crypto::{open_in_place, seal_in_place};
|
||||||
use omegaupload_common::fragment::Builder;
|
|
||||||
use omegaupload_common::secrecy::{ExposeSecret, SecretString, SecretVec};
|
use omegaupload_common::secrecy::{ExposeSecret, SecretString, SecretVec};
|
||||||
use omegaupload_common::{
|
use omegaupload_common::{
|
||||||
base64, Expiration, ParsedUrl, Url, API_ENDPOINT, EXPIRATION_HEADER_NAME,
|
base64, Expiration, ParsedUrl, Url, API_ENDPOINT, EXPIRATION_HEADER_NAME,
|
||||||
|
@ -32,7 +31,11 @@ use omegaupload_common::{
|
||||||
use reqwest::blocking::Client;
|
use reqwest::blocking::Client;
|
||||||
use reqwest::header::EXPIRES;
|
use reqwest::header::EXPIRES;
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use rpassword::prompt_password;
|
use rpassword::prompt_password_stderr;
|
||||||
|
|
||||||
|
use crate::fragment::Builder;
|
||||||
|
|
||||||
|
mod fragment;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
struct Opts {
|
struct Opts {
|
||||||
|
@ -117,7 +120,8 @@ fn handle_upload(
|
||||||
}
|
}
|
||||||
|
|
||||||
let password = if password {
|
let password = if password {
|
||||||
let maybe_password = prompt_password("Please set the password for this paste: ")?;
|
let maybe_password =
|
||||||
|
prompt_password_stderr("Please set the password for this paste: ")?;
|
||||||
Some(SecretVec::new(maybe_password.into_bytes()))
|
Some(SecretVec::new(maybe_password.into_bytes()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -196,7 +200,8 @@ fn handle_download(mut url: ParsedUrl) -> Result<()> {
|
||||||
|
|
||||||
let password = if url.needs_password {
|
let password = if url.needs_password {
|
||||||
// Only print prompt on interactive, else it messes with output
|
// Only print prompt on interactive, else it messes with output
|
||||||
let maybe_password = prompt_password("Please enter the password to access this paste: ")?;
|
let maybe_password =
|
||||||
|
prompt_password_stderr("Please enter the password to access this paste: ")?;
|
||||||
Some(SecretVec::new(maybe_password.into_bytes()))
|
Some(SecretVec::new(maybe_password.into_bytes()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -9,24 +9,24 @@ license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.13.0"
|
base64 = "0.13"
|
||||||
bytes = { version = "1.2.0", features = ["serde"] }
|
bytes = { version = "1", features = ["serde"] }
|
||||||
chacha20poly1305 = { version = "0.9.1", features = ["stream", "std"] }
|
chacha20poly1305 = { version = "0.9", features = ["stream", "std"] }
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
headers = "0.3.7"
|
headers = "0.3"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1"
|
||||||
rand = "0.8.5"
|
rand = "0.8"
|
||||||
secrecy = "0.8.0"
|
secrecy = "0.8"
|
||||||
serde = { version = "1.0.140", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
thiserror = "1.0.31"
|
thiserror = "1"
|
||||||
typenum = "1.15.0"
|
typenum = "1"
|
||||||
url = "2.2.2"
|
url = "2"
|
||||||
argon2 = "0.4.1"
|
argon2 = "0.3.1"
|
||||||
|
|
||||||
# Wasm features
|
# Wasm features
|
||||||
gloo-console = { version = "0.2.1", optional = true }
|
gloo-console = { version = "0.2", optional = true }
|
||||||
reqwasm = { version = "0.5.0", optional = true }
|
http = { version = "0.2", optional = true }
|
||||||
http = { version = "0.2.8", optional = true }
|
web-sys = { version = "0.3", features = ["Headers"], optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
wasm = ["gloo-console", "reqwasm", "http"]
|
wasm = ["gloo-console", "http", "web-sys"]
|
|
@ -294,43 +294,3 @@ fn get_argon2() -> Argon2<'static> {
|
||||||
pub fn get_csrng() -> impl CryptoRng + Rng {
|
pub fn get_csrng() -> impl CryptoRng + Rng {
|
||||||
rand::thread_rng()
|
rand::thread_rng()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::open_in_place;
|
|
||||||
use super::seal_in_place;
|
|
||||||
use crate::crypto::SecretVec;
|
|
||||||
|
|
||||||
macro_rules! test_encryption {
|
|
||||||
($($name:ident, $content:expr, $password:expr),*) => {
|
|
||||||
$(
|
|
||||||
#[test]
|
|
||||||
fn $name() {
|
|
||||||
let mut m = $content;
|
|
||||||
let n: Vec<u8> = $content;
|
|
||||||
let key = seal_in_place(&mut m, $password).unwrap();
|
|
||||||
assert_ne!(m, n);
|
|
||||||
assert!(open_in_place(&mut m, &key, $password).is_ok());
|
|
||||||
assert_eq!(m, n);
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
test_encryption!(empty, vec![], None);
|
|
||||||
test_encryption!(
|
|
||||||
empty_password,
|
|
||||||
vec![],
|
|
||||||
Some(SecretVec::from(b"password".to_vec()))
|
|
||||||
);
|
|
||||||
test_encryption!(
|
|
||||||
normal,
|
|
||||||
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
||||||
None
|
|
||||||
);
|
|
||||||
test_encryption!(
|
|
||||||
normal_password,
|
|
||||||
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
|
|
||||||
Some(SecretVec::from(b"password".to_vec()))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#![warn(clippy::nursery, clippy::pedantic)]
|
#![warn(clippy::nursery, clippy::pedantic)]
|
||||||
// False positive: https://github.com/rust-lang/rust-clippy/issues/6902
|
|
||||||
#![allow(clippy::use_self)]
|
|
||||||
|
|
||||||
//! Contains common functions and structures used by multiple projects
|
//! Contains common functions and structures used by multiple projects
|
||||||
|
|
||||||
|
@ -41,7 +39,6 @@ use crate::crypto::Key;
|
||||||
|
|
||||||
pub mod base64;
|
pub mod base64;
|
||||||
pub mod crypto;
|
pub mod crypto;
|
||||||
pub mod fragment;
|
|
||||||
|
|
||||||
pub const API_ENDPOINT: &str = "/api";
|
pub const API_ENDPOINT: &str = "/api";
|
||||||
|
|
||||||
|
@ -233,10 +230,10 @@ expiration_from_str! {
|
||||||
impl Display for Expiration {
|
impl Display for Expiration {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::BurnAfterReading | Self::BurnAfterReadingWithDeadline(_) => {
|
Expiration::BurnAfterReading | Expiration::BurnAfterReadingWithDeadline(_) => {
|
||||||
write!(f, "This item has been burned. You now have the only copy.")
|
write!(f, "This item has been burned. You now have the only copy.")
|
||||||
}
|
}
|
||||||
Self::UnixTime(time) => write!(
|
Expiration::UnixTime(time) => write!(
|
||||||
f,
|
f,
|
||||||
"{}",
|
"{}",
|
||||||
time.format("This item will expire on %A, %B %-d, %Y at %T %Z.")
|
time.format("This item will expire on %A, %B %-d, %Y at %T %Z.")
|
||||||
|
@ -251,7 +248,7 @@ lazy_static! {
|
||||||
|
|
||||||
impl Header for Expiration {
|
impl Header for Expiration {
|
||||||
fn name() -> &'static HeaderName {
|
fn name() -> &'static HeaderName {
|
||||||
&EXPIRATION_HEADER_NAME
|
&*EXPIRATION_HEADER_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode<'i, I>(values: &mut I) -> Result<Self, headers::Error>
|
fn decode<'i, I>(values: &mut I) -> Result<Self, headers::Error>
|
||||||
|
@ -285,8 +282,6 @@ impl From<&Expiration> for HeaderValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Expiration> for HeaderValue {
|
impl From<Expiration> for HeaderValue {
|
||||||
// False positive: https://github.com/rust-lang/rust-clippy/issues/9095
|
|
||||||
#[allow(clippy::needless_borrow)]
|
|
||||||
fn from(expiration: Expiration) -> Self {
|
fn from(expiration: Expiration) -> Self {
|
||||||
(&expiration).into()
|
(&expiration).into()
|
||||||
}
|
}
|
||||||
|
@ -295,12 +290,14 @@ impl From<Expiration> for HeaderValue {
|
||||||
pub struct ParseHeaderValueError;
|
pub struct ParseHeaderValueError;
|
||||||
|
|
||||||
#[cfg(feature = "wasm")]
|
#[cfg(feature = "wasm")]
|
||||||
impl TryFrom<reqwasm::http::Headers> for Expiration {
|
impl TryFrom<web_sys::Headers> for Expiration {
|
||||||
type Error = ParseHeaderValueError;
|
type Error = ParseHeaderValueError;
|
||||||
|
|
||||||
fn try_from(headers: reqwasm::http::Headers) -> Result<Self, Self::Error> {
|
fn try_from(headers: web_sys::Headers) -> Result<Self, Self::Error> {
|
||||||
headers
|
headers
|
||||||
.get(http::header::EXPIRES.as_str())
|
.get(http::header::EXPIRES.as_str())
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.and_then(|v| Self::try_from(v).ok())
|
.and_then(|v| Self::try_from(v).ok())
|
||||||
.ok_or(ParseHeaderValueError)
|
.ok_or(ParseHeaderValueError)
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
"highlight.js": "^11.4.0",
|
"highlight.js": "^11.4.0",
|
||||||
"highlightjs-line-numbers.js": "^2.8.0",
|
"highlightjs-line-numbers.js": "^2.8.0",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2"
|
||||||
"source-map-loader": "^4.0.0"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack --mode production",
|
"build": "webpack --mode production",
|
||||||
|
|
|
@ -7,24 +7,24 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
omegaupload-common = { path = "../common" }
|
omegaupload-common = { path = "../common" }
|
||||||
anyhow = "1.0.58"
|
anyhow = "1"
|
||||||
axum = { version = "0.5.14", features = ["http2", "headers"] }
|
axum = { version = "0.4", features = ["http2", "headers"] }
|
||||||
bincode = "1.3.3"
|
bincode = "1"
|
||||||
# We don't care about which version (We want to match with axum), we just need
|
# We don't care about which version (We want to match with axum), we just need
|
||||||
# to enable the feature
|
# to enable the feature
|
||||||
bytes = { version = "1.2.0", features = ["serde"] }
|
bytes = { version = "*", features = ["serde"] }
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
futures = "0.3.21"
|
futures = "0.3"
|
||||||
# We just need to pull in whatever axum is pulling in
|
# We just need to pull in whatever axum is pulling in
|
||||||
headers = "0.3.7"
|
headers = "*"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1"
|
||||||
# Disable `random()` and `thread_rng()`
|
# Disable `random()` and `thread_rng()`
|
||||||
rand = { version = "0.8.5", default-features = false }
|
rand = { version = "0.8", default_features = false }
|
||||||
rocksdb = { version = "0.18.0", default-features = false, features = ["zstd"] }
|
rocksdb = { version = "0.18", default_features = false, features = ["zstd"] }
|
||||||
serde = { version = "1.0.140", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
signal-hook = "0.3.14"
|
signal-hook = "0.3"
|
||||||
signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"] }
|
signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
|
||||||
tokio = { version = "1.20.1", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||||
tower-http = { version = "0.3.4", features = ["fs"] }
|
tower-http = { version = "0.2", features = ["fs"] }
|
||||||
tracing = "0.1.35"
|
tracing = { version = "0.1" }
|
||||||
tracing-subscriber = "0.3.15"
|
tracing-subscriber = "0.3"
|
|
@ -27,7 +27,7 @@ use axum::extract::{Extension, Path, TypedHeader};
|
||||||
use axum::http::header::EXPIRES;
|
use axum::http::header::EXPIRES;
|
||||||
use axum::http::StatusCode;
|
use axum::http::StatusCode;
|
||||||
use axum::routing::{get, get_service, post};
|
use axum::routing::{get, get_service, post};
|
||||||
use axum::Router;
|
use axum::{AddExtensionLayer, Router};
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use headers::HeaderMap;
|
use headers::HeaderMap;
|
||||||
|
@ -103,7 +103,7 @@ async fn main() -> Result<()> {
|
||||||
&format!("{API_ENDPOINT}/:code"),
|
&format!("{API_ENDPOINT}/:code"),
|
||||||
get(paste::<SHORT_CODE_SIZE>).delete(delete::<SHORT_CODE_SIZE>),
|
get(paste::<SHORT_CODE_SIZE>).delete(delete::<SHORT_CODE_SIZE>),
|
||||||
)
|
)
|
||||||
.layer(axum::Extension(db))
|
.layer(AddExtensionLayer::new(db))
|
||||||
.into_make_service()
|
.into_make_service()
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -9,27 +9,27 @@ crate-type = ["cdylib"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
omegaupload-common = { path = "../common", features = ["wasm"] }
|
omegaupload-common = { path = "../common", features = ["wasm"] }
|
||||||
# Enables wasm support
|
# Enables wasm support
|
||||||
getrandom = { version = "0.2.7", features = ["js"] }
|
getrandom = { version = "*", features = ["js"] }
|
||||||
|
|
||||||
anyhow = "1.0.58"
|
anyhow = "1"
|
||||||
bytes = "1.2.0"
|
bytes = "1"
|
||||||
byte-unit = "4.0.14"
|
byte-unit = "4"
|
||||||
console_error_panic_hook = "0.1.7"
|
console_error_panic_hook = "0.1"
|
||||||
gloo-console = "0.2.1"
|
gloo-console = "0.2"
|
||||||
http = "0.2.8"
|
http = "0.2"
|
||||||
js-sys = "0.3.59"
|
js-sys = "0.3"
|
||||||
mime_guess = "2.0.4"
|
mime_guess = "2"
|
||||||
reqwasm = "0.5.0"
|
reqwasm = "0.4"
|
||||||
tree_magic_mini = { version = "3.0.3", features = ["with-gpl-data"] }
|
tree_magic_mini = { version = "3", features = ["with-gpl-data"] }
|
||||||
serde = { version = "1.0.140", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
wasm-bindgen = { version = "0.2.82", features = ["serde-serialize"] }
|
wasm-bindgen = { version = "0.2", features = ["serde-serialize"]}
|
||||||
wasm-bindgen-futures = "0.4.32"
|
wasm-bindgen-futures = "0.4"
|
||||||
zip = { version = "0.6.2", default-features = false, features = ["deflate"] }
|
zip = { version = "0.5", default-features = false, features = ["deflate"] }
|
||||||
flate2 = "1.0.24"
|
flate2 = "1.0.22"
|
||||||
tar = "0.4.38"
|
tar = "0.4.38"
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
version = "0.3.59"
|
version = "0.3"
|
||||||
features = [
|
features = [
|
||||||
"BlobPropertyBag",
|
"BlobPropertyBag",
|
||||||
"TextDecoder",
|
"TextDecoder",
|
||||||
|
|
|
@ -25,12 +25,9 @@ use gloo_console::{error, log};
|
||||||
use http::uri::PathAndQuery;
|
use http::uri::PathAndQuery;
|
||||||
use http::{StatusCode, Uri};
|
use http::{StatusCode, Uri};
|
||||||
use js_sys::{Array, JsString, Object, Uint8Array};
|
use js_sys::{Array, JsString, Object, Uint8Array};
|
||||||
use omegaupload_common::base64;
|
|
||||||
use omegaupload_common::crypto::seal_in_place;
|
|
||||||
use omegaupload_common::crypto::{Error as CryptoError, Key};
|
use omegaupload_common::crypto::{Error as CryptoError, Key};
|
||||||
use omegaupload_common::fragment::Builder;
|
use omegaupload_common::secrecy::{Secret, SecretVec};
|
||||||
use omegaupload_common::secrecy::{ExposeSecret, Secret, SecretString, SecretVec};
|
use omegaupload_common::{Expiration, PartialParsedUrl};
|
||||||
use omegaupload_common::{Expiration, PartialParsedUrl, Url};
|
|
||||||
use reqwasm::http::Request;
|
use reqwasm::http::Request;
|
||||||
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
|
use wasm_bindgen::prelude::{wasm_bindgen, Closure};
|
||||||
use wasm_bindgen::{JsCast, JsValue};
|
use wasm_bindgen::{JsCast, JsValue};
|
||||||
|
@ -53,8 +50,6 @@ extern "C" {
|
||||||
pub fn load_from_db(mime_type: JsString, name: Option<JsString>, language: Option<JsString>);
|
pub fn load_from_db(mime_type: JsString, name: Option<JsString>, language: Option<JsString>);
|
||||||
#[wasm_bindgen(js_name = renderMessage)]
|
#[wasm_bindgen(js_name = renderMessage)]
|
||||||
pub fn render_message(message: JsString);
|
pub fn render_message(message: JsString);
|
||||||
#[wasm_bindgen(js_name = createUploadUi)]
|
|
||||||
pub fn create_upload_ui();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window() -> Window {
|
fn window() -> Window {
|
||||||
|
@ -80,7 +75,7 @@ pub fn start() {
|
||||||
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
std::panic::set_hook(Box::new(console_error_panic_hook::hook));
|
||||||
|
|
||||||
if location().pathname().unwrap() == "/" {
|
if location().pathname().unwrap() == "/" {
|
||||||
create_upload_ui();
|
render_message("Go away".into());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,55 +166,6 @@ pub fn start() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
#[allow(clippy::future_not_send)]
|
|
||||||
pub fn encrypt_string(data: String) {
|
|
||||||
spawn_local(async move {
|
|
||||||
if let Err(e) = do_encrypt(data.into_bytes()).await {
|
|
||||||
log!(format!("[rs] Error encrypting string: {}", e));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
#[allow(clippy::future_not_send)]
|
|
||||||
pub fn encrypt_array_buffer(data: Vec<u8>) {
|
|
||||||
spawn_local(async move {
|
|
||||||
if let Err(e) = do_encrypt(data).await {
|
|
||||||
log!(format!("[rs] Error encrypting array buffer: {}", e));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::future_not_send)]
|
|
||||||
async fn do_encrypt(mut data: Vec<u8>) -> Result<()> {
|
|
||||||
let (data, key) = {
|
|
||||||
let enc_key = seal_in_place(&mut data, None)?;
|
|
||||||
let key = SecretString::new(base64::encode(&enc_key.expose_secret().as_ref()));
|
|
||||||
(data, key)
|
|
||||||
};
|
|
||||||
|
|
||||||
let s: String = location().to_string().into();
|
|
||||||
let mut url = Url::from_str(&s)?;
|
|
||||||
let fragment = Builder::new(key);
|
|
||||||
|
|
||||||
let js_data = Uint8Array::new_with_length(u32::try_from(data.len()).expect("Data too large"));
|
|
||||||
js_data.copy_from(&data);
|
|
||||||
|
|
||||||
let short_code = Request::post(url.as_ref())
|
|
||||||
.body(js_data)
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.text()
|
|
||||||
.await?;
|
|
||||||
url.set_path(&short_code);
|
|
||||||
url.set_fragment(Some(fragment.build().expose_secret()));
|
|
||||||
location()
|
|
||||||
.set_href(url.as_ref())
|
|
||||||
.expect("Unable to navigate to encrypted upload");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::future_not_send)]
|
#[allow(clippy::future_not_send)]
|
||||||
async fn fetch_resources(
|
async fn fetch_resources(
|
||||||
request_uri: Uri,
|
request_uri: Uri,
|
||||||
|
|
|
@ -95,14 +95,6 @@ img, audio, video {
|
||||||
max-width: 75vw;
|
max-width: 75vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-width: 75vw;
|
|
||||||
min-height: 75vh;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.primary {
|
.primary {
|
||||||
@extend .hljs;
|
@extend .hljs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,64 +16,14 @@
|
||||||
|
|
||||||
import './main.scss';
|
import './main.scss';
|
||||||
import ReactDom from 'react-dom';
|
import ReactDom from 'react-dom';
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { encrypt_string, encrypt_array_buffer } from '../pkg';
|
|
||||||
|
|
||||||
import hljs from 'highlight.js'
|
const hljs = require('highlight.js');
|
||||||
(window as any).hljs = hljs;
|
(window as any).hljs = hljs;
|
||||||
require('highlightjs-line-numbers.js');
|
require('highlightjs-line-numbers.js');
|
||||||
|
|
||||||
const FileForm = () => {
|
|
||||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const fr = new FileReader();
|
|
||||||
fr.onload = (e) => {
|
|
||||||
const { result } = e.target;
|
|
||||||
if (result instanceof ArrayBuffer) {
|
|
||||||
let data = new Uint8Array(result);
|
|
||||||
encrypt_array_buffer(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fr.readAsArrayBuffer((event.target as HTMLInputElement).files[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<input type="file" onChange={handleChange} />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const PasteForm = () => {
|
|
||||||
const [value, setValue] = useState("");
|
|
||||||
|
|
||||||
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
||||||
event.preventDefault();
|
|
||||||
encrypt_string(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<pre className='paste'>
|
|
||||||
<form className='hljs centered' onSubmit={handleSubmit}>
|
|
||||||
<textarea
|
|
||||||
placeholder="Sample text"
|
|
||||||
value={value}
|
|
||||||
onChange={(e) => setValue(e.target.value)}
|
|
||||||
/>
|
|
||||||
<input type="submit" value="submit" />
|
|
||||||
</form>
|
|
||||||
</pre>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function createUploadUi() {
|
|
||||||
const html = <main className='hljs centered fullscreen'>
|
|
||||||
<FileForm />
|
|
||||||
<PasteForm />
|
|
||||||
</main>;
|
|
||||||
|
|
||||||
ReactDom.render(html, document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadFromDb(mimeType: string, name?: string, language?: string) {
|
function loadFromDb(mimeType: string, name?: string, language?: string) {
|
||||||
let resolvedName: string;
|
let resolvedName;
|
||||||
if (name) {
|
if (name) {
|
||||||
resolvedName = name;
|
resolvedName = name;
|
||||||
} else {
|
} else {
|
||||||
|
@ -337,4 +287,4 @@ function getObjectUrl(data, mimeType?: string) {
|
||||||
|
|
||||||
window.addEventListener("hashchange", () => location.reload());
|
window.addEventListener("hashchange", () => location.reload());
|
||||||
|
|
||||||
export { renderMessage, createUploadUi, loadFromDb };
|
export { renderMessage, loadFromDb };
|
2
web/vendor/MPLUS_FONTS
vendored
2
web/vendor/MPLUS_FONTS
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 8690be3625964d9992e7be4bc3e1a61a80161cc6
|
Subproject commit a1268635894c5ee23dfdece570418ca07b66c3fc
|
|
@ -2,7 +2,6 @@ const path = require('path');
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
|
const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
|
||||||
const { SourceMapDevToolPlugin } = require('webpack');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: './web/src/index.js',
|
entry: './web/src/index.js',
|
||||||
|
@ -22,8 +21,6 @@ module.exports = {
|
||||||
"css-loader",
|
"css-loader",
|
||||||
// Compiles Sass to CSS
|
// Compiles Sass to CSS
|
||||||
"sass-loader",
|
"sass-loader",
|
||||||
// source map for debugging
|
|
||||||
"source-map-loader"
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -44,7 +41,6 @@ module.exports = {
|
||||||
crateDirectory: path.resolve(__dirname, "web"),
|
crateDirectory: path.resolve(__dirname, "web"),
|
||||||
outDir: path.resolve(__dirname, "web/pkg"),
|
outDir: path.resolve(__dirname, "web/pkg"),
|
||||||
}),
|
}),
|
||||||
new SourceMapDevToolPlugin({}),
|
|
||||||
],
|
],
|
||||||
experiments: {
|
experiments: {
|
||||||
asyncWebAssembly: true,
|
asyncWebAssembly: true,
|
||||||
|
|
Loading…
Reference in a new issue