2021-10-21 18:35:54 -07:00
|
|
|
#![warn(clippy::nursery, clippy::pedantic)]
|
|
|
|
|
2021-10-22 19:15:23 -07:00
|
|
|
use std::fmt::{Debug, Display, Formatter};
|
2021-10-17 14:15:29 -07:00
|
|
|
use std::str::FromStr;
|
2021-10-23 10:10:55 -07:00
|
|
|
use std::sync::Arc;
|
2021-10-17 14:15:29 -07:00
|
|
|
|
2021-10-22 19:15:23 -07:00
|
|
|
use anyhow::{anyhow, bail, Context};
|
2021-10-23 10:10:55 -07:00
|
|
|
use byte_unit::Byte;
|
2021-10-19 18:48:32 -07:00
|
|
|
use bytes::Bytes;
|
2021-10-23 10:10:55 -07:00
|
|
|
use decrypt::DecryptionAgent;
|
2021-10-19 02:18:33 -07:00
|
|
|
use downcast_rs::{impl_downcast, Downcast};
|
2021-10-22 19:15:23 -07:00
|
|
|
use gloo_console::log;
|
2021-10-19 23:53:22 -07:00
|
|
|
use http::header::EXPIRES;
|
2021-10-19 18:48:32 -07:00
|
|
|
use http::uri::PathAndQuery;
|
|
|
|
use http::{StatusCode, Uri};
|
2021-10-23 10:10:55 -07:00
|
|
|
use image::GenericImageView;
|
2021-10-22 19:15:23 -07:00
|
|
|
use js_sys::{Array, ArrayBuffer, Uint8Array};
|
2021-10-23 10:10:55 -07:00
|
|
|
use omegaupload_common::crypto::{open, open_in_place, Key, Nonce};
|
2021-10-19 23:53:22 -07:00
|
|
|
use omegaupload_common::{Expiration, PartialParsedUrl};
|
2021-10-23 10:10:55 -07:00
|
|
|
use reqwasm::http::Request;
|
2021-10-22 19:15:23 -07:00
|
|
|
use wasm_bindgen::JsCast;
|
|
|
|
use wasm_bindgen_futures::JsFuture;
|
|
|
|
use web_sys::TextDecoder;
|
|
|
|
use web_sys::{Blob, Url};
|
2021-10-23 10:10:55 -07:00
|
|
|
use yew::agent::Dispatcher;
|
2021-10-17 14:15:29 -07:00
|
|
|
use yew::utils::window;
|
2021-10-23 10:10:55 -07:00
|
|
|
use yew::worker::Agent;
|
|
|
|
use yew::{html, Bridge, Bridged, Component, ComponentLink, Html, ShouldRender};
|
|
|
|
use yew::{Dispatched, Properties};
|
2021-10-17 14:15:29 -07:00
|
|
|
use yew_router::router::Router;
|
|
|
|
use yew_router::Switch;
|
2021-10-19 18:48:32 -07:00
|
|
|
use yewtil::future::LinkFuture;
|
2021-10-17 14:15:29 -07:00
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
use crate::decrypt::{DecryptionAgentMessage, DecryptionParams, PasteContext};
|
|
|
|
|
|
|
|
mod decrypt;
|
|
|
|
|
2021-10-17 14:15:29 -07:00
|
|
|
fn main() {
|
|
|
|
yew::start_app::<App>();
|
|
|
|
}
|
|
|
|
|
|
|
|
struct App;
|
|
|
|
impl Component for App {
|
|
|
|
type Message = ();
|
|
|
|
type Properties = ();
|
|
|
|
|
|
|
|
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
|
|
|
|
Self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update(&mut self, _: Self::Message) -> ShouldRender {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
fn view(&self) -> Html {
|
|
|
|
html! {
|
|
|
|
<Router<Route> render={Router::render(render_route)} />
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Switch)]
|
|
|
|
enum Route {
|
|
|
|
#[to = "/!"]
|
|
|
|
Index,
|
|
|
|
#[rest]
|
|
|
|
Path(String),
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:35:54 -07:00
|
|
|
#[allow(clippy::needless_pass_by_value)]
|
2021-10-17 14:15:29 -07:00
|
|
|
fn render_route(route: Route) -> Html {
|
|
|
|
match route {
|
|
|
|
Route::Index => html! {
|
|
|
|
<main>
|
|
|
|
<p>{ "Hello world" }</p>
|
|
|
|
</main>
|
|
|
|
},
|
|
|
|
Route::Path(_) => html! {
|
|
|
|
<main>
|
|
|
|
<Paste/>
|
|
|
|
</main>
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
pub struct Paste {
|
2021-10-19 02:18:33 -07:00
|
|
|
state: Box<dyn PasteState>,
|
2021-10-23 10:10:55 -07:00
|
|
|
_listener: Box<dyn Bridge<DecryptionAgent>>,
|
2021-10-17 14:15:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Component for Paste {
|
2021-10-19 02:18:33 -07:00
|
|
|
type Message = Box<dyn PasteState>;
|
2021-10-17 14:15:29 -07:00
|
|
|
|
2021-10-16 09:50:11 -07:00
|
|
|
type Properties = ();
|
2021-10-17 14:15:29 -07:00
|
|
|
|
|
|
|
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
|
|
|
let url = String::from(window().location().to_string());
|
|
|
|
let request_uri = {
|
|
|
|
let mut uri_parts = url.parse::<Uri>().unwrap().into_parts();
|
2021-10-21 18:35:54 -07:00
|
|
|
if let Some(parts) = uri_parts.path_and_query.as_mut() {
|
|
|
|
*parts = PathAndQuery::from_str(&format!("/api{}", parts.path())).unwrap();
|
|
|
|
}
|
2021-10-17 14:15:29 -07:00
|
|
|
Uri::from_parts(uri_parts).unwrap()
|
|
|
|
};
|
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
let handle_decryption_result = |res: <DecryptionAgent as Agent>::Output| {
|
|
|
|
log!("Got decryption result back!");
|
|
|
|
match res {
|
|
|
|
Ok((decrypted, context)) => {
|
|
|
|
Box::new(PasteComplete::new(context.link, decrypted, context.expires))
|
|
|
|
as Box<dyn PasteState>
|
|
|
|
}
|
|
|
|
Err(e) => Box::new(PasteError(anyhow!("wtf"))) as Box<dyn PasteState>,
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let listener = DecryptionAgent::bridge(link.callback(handle_decryption_result));
|
|
|
|
|
2021-10-19 02:18:33 -07:00
|
|
|
let link_clone = link.clone();
|
2021-10-19 18:48:32 -07:00
|
|
|
link.send_future(async move {
|
2021-10-23 10:10:55 -07:00
|
|
|
match Request::get(&request_uri.to_string()).send().await {
|
2021-10-19 18:48:32 -07:00
|
|
|
Ok(resp) if resp.status() == StatusCode::OK => {
|
2021-10-19 23:53:22 -07:00
|
|
|
let expires = resp
|
|
|
|
.headers()
|
2021-10-23 10:10:55 -07:00
|
|
|
.get(EXPIRES.as_str())
|
|
|
|
.ok()
|
|
|
|
.flatten()
|
|
|
|
.as_deref()
|
2021-10-19 23:53:22 -07:00
|
|
|
.and_then(|v| Expiration::try_from(v).ok());
|
2021-10-23 10:10:55 -07:00
|
|
|
|
|
|
|
let data = {
|
|
|
|
Uint8Array::new(
|
|
|
|
&JsFuture::from(resp.as_raw().array_buffer().unwrap())
|
|
|
|
.await
|
|
|
|
.unwrap(),
|
|
|
|
)
|
|
|
|
.to_vec()
|
2021-10-19 18:48:32 -07:00
|
|
|
};
|
2021-10-19 02:18:33 -07:00
|
|
|
|
2021-10-22 19:15:23 -07:00
|
|
|
let info = url
|
|
|
|
.split_once('#')
|
|
|
|
.map(|(_, fragment)| PartialParsedUrl::from(fragment))
|
|
|
|
.unwrap_or_default();
|
|
|
|
let key = info.decryption_key.unwrap();
|
|
|
|
let nonce = info.nonce.unwrap();
|
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
let mut decryption_agent = DecryptionAgent::dispatcher();
|
|
|
|
|
|
|
|
let params = DecryptionParams::new(data, key, nonce, None);
|
|
|
|
let ctx = PasteContext::new(link_clone, expires);
|
|
|
|
decryption_agent.send(DecryptionAgentMessage::new(ctx, params));
|
|
|
|
Box::new(PasteDecrypting(decryption_agent)) as Box<dyn PasteState>
|
2021-10-19 02:18:33 -07:00
|
|
|
}
|
2021-10-21 18:35:54 -07:00
|
|
|
Ok(resp) if resp.status() == StatusCode::NOT_FOUND => {
|
|
|
|
Box::new(PasteNotFound) as Box<dyn PasteState>
|
|
|
|
}
|
|
|
|
Ok(resp) if resp.status() == StatusCode::BAD_REQUEST => {
|
|
|
|
Box::new(PasteBadRequest) as Box<dyn PasteState>
|
2021-10-19 02:18:33 -07:00
|
|
|
}
|
2021-10-21 18:35:54 -07:00
|
|
|
Ok(err) => {
|
|
|
|
Box::new(PasteError(anyhow!("Got {}.", err.status()))) as Box<dyn PasteState>
|
|
|
|
}
|
|
|
|
Err(err) => Box::new(PasteError(anyhow!("Got {}.", err))) as Box<dyn PasteState>,
|
2021-10-19 18:48:32 -07:00
|
|
|
}
|
|
|
|
});
|
2021-10-23 10:10:55 -07:00
|
|
|
|
2021-10-19 18:48:32 -07:00
|
|
|
Self {
|
|
|
|
state: Box::new(PasteLoading),
|
2021-10-23 10:10:55 -07:00
|
|
|
_listener: listener,
|
2021-10-17 14:15:29 -07:00
|
|
|
}
|
2021-10-16 09:50:11 -07:00
|
|
|
}
|
2021-10-17 14:15:29 -07:00
|
|
|
|
2021-10-19 02:18:33 -07:00
|
|
|
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
|
|
|
self.state = msg;
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
|
|
|
fn view(&self) -> Html {
|
|
|
|
if self.state.is::<PasteLoading>() {
|
|
|
|
return html! {
|
|
|
|
<p>{ "loading" }</p>
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
if self.state.is::<PasteDecrypting>() {
|
|
|
|
return html! {
|
|
|
|
"decrypting"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-10-19 02:18:33 -07:00
|
|
|
if self.state.is::<PasteNotFound>() {
|
|
|
|
return html! {
|
2021-10-22 19:15:23 -07:00
|
|
|
<section class={"hljs centered"}>
|
2021-10-21 18:35:54 -07:00
|
|
|
<p>{ "Either the paste has been burned or one never existed." }</p>
|
|
|
|
</section>
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.state.is::<PasteBadRequest>() {
|
|
|
|
return html! {
|
2021-10-22 19:15:23 -07:00
|
|
|
<section class={"hljs centered"}>
|
2021-10-21 18:35:54 -07:00
|
|
|
<p>{ "Bad Request. Is this a valid paste URL?" }</p>
|
|
|
|
</section>
|
2021-10-19 02:18:33 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(error) = self.state.downcast_ref::<PasteError>() {
|
|
|
|
return html! {
|
2021-10-22 19:15:23 -07:00
|
|
|
<section class={"hljs centered"}><p>{ error.0.to_string() }</p></section>
|
2021-10-19 02:18:33 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(partial_paste) = self.state.downcast_ref::<PastePartial>() {
|
|
|
|
return partial_paste.view();
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(paste) = self.state.downcast_ref::<PasteComplete>() {
|
|
|
|
return paste.view();
|
|
|
|
}
|
|
|
|
|
|
|
|
html! {
|
|
|
|
"An internal error occurred: client is in unknown state!"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct PasteError(anyhow::Error);
|
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
#[derive(Debug)]
|
2021-10-19 02:18:33 -07:00
|
|
|
struct PastePartial {
|
|
|
|
parent: ComponentLink<Paste>,
|
2021-10-23 10:10:55 -07:00
|
|
|
dispatcher: Dispatcher<DecryptionAgent>,
|
2021-10-19 23:53:22 -07:00
|
|
|
data: Bytes,
|
|
|
|
expires: Option<Expiration>,
|
2021-10-19 02:18:33 -07:00
|
|
|
key: Option<Key>,
|
|
|
|
nonce: Option<Nonce>,
|
|
|
|
password: Option<Key>,
|
|
|
|
needs_pw: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Properties, Clone)]
|
|
|
|
struct PasteComplete {
|
2021-10-22 19:15:23 -07:00
|
|
|
parent: ComponentLink<Paste>,
|
|
|
|
decrypted: DecryptedData,
|
2021-10-19 23:53:22 -07:00
|
|
|
expires: Option<Expiration>,
|
2021-10-22 19:15:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
2021-10-23 10:10:55 -07:00
|
|
|
pub enum DecryptedData {
|
|
|
|
String(Arc<String>),
|
|
|
|
Blob(Arc<Blob>),
|
|
|
|
Image(Arc<Blob>, (u32, u32), usize),
|
2021-10-19 02:18:33 -07:00
|
|
|
}
|
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
pub trait PasteState: Downcast {}
|
2021-10-19 02:18:33 -07:00
|
|
|
impl_downcast!(PasteState);
|
2021-10-21 18:35:54 -07:00
|
|
|
|
2021-10-19 02:18:33 -07:00
|
|
|
impl PasteState for PasteError {}
|
|
|
|
impl PasteState for PastePartial {}
|
|
|
|
impl PasteState for PasteComplete {}
|
|
|
|
|
2021-10-21 18:35:54 -07:00
|
|
|
macro_rules! impl_paste_type_state {
|
|
|
|
(
|
|
|
|
$($state:ident),* $(,)?
|
|
|
|
) => {
|
|
|
|
$(
|
|
|
|
struct $state;
|
|
|
|
impl PasteState for $state {}
|
|
|
|
)*
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_paste_type_state!(PasteLoading, PasteNotFound, PasteBadRequest);
|
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
struct PasteDecrypting(Dispatcher<DecryptionAgent>);
|
|
|
|
|
|
|
|
impl PasteState for PasteDecrypting {}
|
|
|
|
|
2021-10-19 02:18:33 -07:00
|
|
|
impl PastePartial {
|
|
|
|
fn new(
|
2021-10-19 23:53:22 -07:00
|
|
|
data: Bytes,
|
|
|
|
expires: Option<Expiration>,
|
2021-10-21 18:35:54 -07:00
|
|
|
partial_parsed_url: &PartialParsedUrl,
|
2021-10-19 02:18:33 -07:00
|
|
|
parent: ComponentLink<Paste>,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
parent,
|
2021-10-23 10:10:55 -07:00
|
|
|
dispatcher: DecryptionAgent::dispatcher(),
|
2021-10-19 23:53:22 -07:00
|
|
|
data,
|
|
|
|
expires,
|
2021-10-19 02:18:33 -07:00
|
|
|
key: partial_parsed_url.decryption_key,
|
|
|
|
nonce: partial_parsed_url.nonce,
|
|
|
|
password: None,
|
|
|
|
needs_pw: partial_parsed_url.needs_password,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum PartialPasteMessage {
|
|
|
|
DecryptionKey(Key),
|
|
|
|
Nonce(Nonce),
|
|
|
|
Password(Key),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Component for PastePartial {
|
|
|
|
type Message = PartialPasteMessage;
|
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
type Properties = ();
|
2021-10-19 02:18:33 -07:00
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
fn create(_: Self::Properties, _: ComponentLink<Self>) -> Self {
|
|
|
|
unimplemented!()
|
2021-10-19 02:18:33 -07:00
|
|
|
}
|
|
|
|
|
2021-10-16 09:50:11 -07:00
|
|
|
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
|
|
|
match msg {
|
2021-10-19 02:18:33 -07:00
|
|
|
PartialPasteMessage::DecryptionKey(key) => self.key = Some(key),
|
|
|
|
PartialPasteMessage::Nonce(nonce) => self.nonce = Some(nonce),
|
|
|
|
PartialPasteMessage::Password(password) => self.password = Some(password),
|
2021-10-16 09:50:11 -07:00
|
|
|
}
|
2021-10-19 02:18:33 -07:00
|
|
|
|
2021-10-19 23:53:22 -07:00
|
|
|
match (self.key, self.nonce, self.password) {
|
|
|
|
(Some(key), Some(nonce), maybe_password)
|
|
|
|
if (self.needs_pw && maybe_password.is_some())
|
|
|
|
|| (!self.needs_pw && maybe_password.is_none()) =>
|
|
|
|
{
|
2021-10-22 19:15:23 -07:00
|
|
|
let parent = self.parent.clone();
|
2021-10-23 10:10:55 -07:00
|
|
|
let mut data = self.data.to_vec();
|
2021-10-21 18:35:54 -07:00
|
|
|
let expires = self.expires;
|
2021-10-22 19:15:23 -07:00
|
|
|
|
2021-10-23 10:10:55 -07:00
|
|
|
// self.dispatcher.send((data, key, nonce, maybe_password));
|
|
|
|
todo!()
|
2021-10-19 02:18:33 -07:00
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:35:54 -07:00
|
|
|
// parent should re-render so this element should be dropped; no point
|
|
|
|
// in saying this needs to be re-rendered.
|
2021-10-19 02:18:33 -07:00
|
|
|
false
|
2021-10-16 09:50:11 -07:00
|
|
|
}
|
2021-10-17 14:15:29 -07:00
|
|
|
|
2021-10-16 09:50:11 -07:00
|
|
|
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
|
|
|
|
false
|
|
|
|
}
|
2021-10-17 14:15:29 -07:00
|
|
|
|
2021-10-16 09:50:11 -07:00
|
|
|
fn view(&self) -> Html {
|
2021-10-19 02:18:33 -07:00
|
|
|
html! {
|
|
|
|
"got partial data"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-22 19:15:23 -07:00
|
|
|
#[derive(Debug)]
|
2021-10-23 10:10:55 -07:00
|
|
|
pub enum PasteCompleteConstructionError {
|
2021-10-22 19:15:23 -07:00
|
|
|
StageOneFailure,
|
|
|
|
StageTwoFailure,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for PasteCompleteConstructionError {}
|
|
|
|
|
|
|
|
impl Display for PasteCompleteConstructionError {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match self {
|
|
|
|
PasteCompleteConstructionError::StageOneFailure => {
|
|
|
|
write!(f, "Failed to decrypt stage one.")
|
|
|
|
}
|
|
|
|
PasteCompleteConstructionError::StageTwoFailure => {
|
|
|
|
write!(f, "Failed to decrypt stage two.")
|
|
|
|
}
|
2021-10-19 02:18:33 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PasteComplete {
|
2021-10-19 23:53:22 -07:00
|
|
|
fn new(
|
2021-10-22 19:15:23 -07:00
|
|
|
parent: ComponentLink<Paste>,
|
|
|
|
decrypted: DecryptedData,
|
2021-10-19 23:53:22 -07:00
|
|
|
expires: Option<Expiration>,
|
|
|
|
) -> Self {
|
2021-10-19 02:18:33 -07:00
|
|
|
Self {
|
2021-10-22 19:15:23 -07:00
|
|
|
parent,
|
|
|
|
decrypted,
|
2021-10-19 23:53:22 -07:00
|
|
|
expires,
|
2021-10-19 02:18:33 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn view(&self) -> Html {
|
2021-10-22 19:15:23 -07:00
|
|
|
match &self.decrypted {
|
|
|
|
DecryptedData::String(decrypted) => html! {
|
|
|
|
html! {
|
|
|
|
<>
|
2021-10-23 10:10:55 -07:00
|
|
|
<pre class="paste">
|
|
|
|
<header class="unselectable">
|
|
|
|
{
|
|
|
|
self.expires.as_ref().map(ToString::to_string).unwrap_or_else(||
|
|
|
|
"This paste will not expire.".to_string()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
</header>
|
|
|
|
<hr />
|
|
|
|
<code>{decrypted}</code>
|
|
|
|
</pre>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
{"
|
|
|
|
hljs.highlightAll();
|
|
|
|
hljs.initLineNumbersOnLoad();
|
|
|
|
"}
|
|
|
|
</script>
|
2021-10-22 19:15:23 -07:00
|
|
|
</>
|
|
|
|
}
|
|
|
|
},
|
|
|
|
DecryptedData::Blob(decrypted) => {
|
|
|
|
let object_url = Url::create_object_url_with_blob(decrypted);
|
|
|
|
if let Ok(object_url) = object_url {
|
|
|
|
let file_name = window().location().pathname().unwrap_or("file".to_string());
|
|
|
|
let mut cloned = self.clone();
|
2021-10-23 10:10:55 -07:00
|
|
|
let decrypted_ref = Arc::clone(&decrypted);
|
2021-10-22 19:15:23 -07:00
|
|
|
let display_anyways_callback =
|
|
|
|
self.parent.callback_future_once(|_| async move {
|
|
|
|
let array_buffer: ArrayBuffer =
|
2021-10-23 10:10:55 -07:00
|
|
|
JsFuture::from(decrypted_ref.array_buffer())
|
2021-10-22 19:15:23 -07:00
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.dyn_into()
|
|
|
|
.unwrap();
|
|
|
|
let decoder = TextDecoder::new().unwrap();
|
|
|
|
cloned.decrypted = decoder
|
|
|
|
.decode_with_buffer_source(&array_buffer)
|
2021-10-23 10:10:55 -07:00
|
|
|
.map(Arc::new)
|
2021-10-22 19:15:23 -07:00
|
|
|
.map(DecryptedData::String)
|
|
|
|
.unwrap();
|
|
|
|
Box::new(cloned) as Box<dyn PasteState>
|
|
|
|
});
|
|
|
|
html! {
|
2021-10-23 10:10:55 -07:00
|
|
|
<section class="hljs fullscreen centered">
|
2021-10-22 19:15:23 -07:00
|
|
|
<div class="centered">
|
|
|
|
<p>{ "Found a binary file." }</p>
|
2021-10-23 10:10:55 -07:00
|
|
|
<a href=object_url download=file_name class="hljs-meta">{"Download"}</a>
|
2021-10-22 19:15:23 -07:00
|
|
|
</div>
|
|
|
|
<p onclick=display_anyways_callback class="display-anyways hljs-meta">{ "Display anyways?" }</p>
|
|
|
|
</section>
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// This branch really shouldn't happen, but might as well
|
|
|
|
// try and give a user-friendly error message.
|
|
|
|
html! {
|
|
|
|
<section class="hljs centered">
|
|
|
|
<p>{ "Failed to create an object URL for the decrypted file. Try reloading the page?" }</p>
|
|
|
|
</section>
|
2021-10-19 23:53:22 -07:00
|
|
|
}
|
2021-10-22 19:15:23 -07:00
|
|
|
}
|
|
|
|
}
|
2021-10-23 10:10:55 -07:00
|
|
|
DecryptedData::Image(decrypted, (width, height), size) => {
|
2021-10-22 19:15:23 -07:00
|
|
|
let object_url = Url::create_object_url_with_blob(decrypted);
|
|
|
|
if let Ok(object_url) = object_url {
|
|
|
|
let file_name = window().location().pathname().unwrap_or("file".to_string());
|
|
|
|
html! {
|
2021-10-23 10:10:55 -07:00
|
|
|
<section class="hljs fullscreen centered">
|
|
|
|
<img src=object_url.clone() />
|
|
|
|
<a href=object_url download=file_name class="hljs-meta">
|
|
|
|
{
|
|
|
|
format!(
|
|
|
|
"Download {} \u{2014} {} by {}",
|
|
|
|
Byte::from_bytes(*size as u128).get_appropriate_unit(true),
|
|
|
|
width, height,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
</a>
|
2021-10-22 19:15:23 -07:00
|
|
|
</section>
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// This branch really shouldn't happen, but might as well
|
|
|
|
// try and give a user-friendly error message.
|
|
|
|
html! {
|
2021-10-23 10:10:55 -07:00
|
|
|
<section class="hljs fullscreen centered">
|
2021-10-22 19:15:23 -07:00
|
|
|
<p>{ "Failed to create an object URL for the decrypted file. Try reloading the page?" }</p>
|
|
|
|
</section>
|
|
|
|
}
|
|
|
|
}
|
2021-10-17 14:15:29 -07:00
|
|
|
}
|
2021-10-16 09:50:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|