Compare commits

...

4 commits

Author SHA1 Message Date
xenofem 827c96daf5 update readme 2022-04-28 00:31:17 -04:00
xenofem 7d8c4f00fa maximum file count 2022-04-28 00:27:22 -04:00
xenofem c78844d8b1 fix page title 2022-04-28 00:12:40 -04:00
xenofem db233b57c1 rename upload.js 2022-04-28 00:12:20 -04:00
4 changed files with 23 additions and 14 deletions

View file

@ -14,7 +14,8 @@
sender has gone offline sender has gone offline
- Easy to send multiple files at once - they're bundled into a zip - Easy to send multiple files at once - they're bundled into a zip
file for receivers, with zero compression so extraction is quick file for receivers, with zero compression so extraction is quick
- Sanitizes filenames - Sanitises filenames, using sensible non-obnoxious defaults that
should be safe across platforms
- Fires a laser beam that turns you trans - Fires a laser beam that turns you trans
## configuration ## configuration
@ -28,9 +29,7 @@ transbeam is configured with the following environment variables:
## todo ## todo
- [ ] file downloading - uploader auth
- [ ] upload progress bar - downloader auth
- [ ] uploader auth - delete uploads after a while
- [ ] downloader auth - configurable maximum file size
- [ ] delete uploads after a while
- [ ] configurable maximum file size

View file

@ -11,6 +11,7 @@ use time::OffsetDateTime;
use crate::{file::LiveWriter, DownloadableFile, UploadedFile}; use crate::{file::LiveWriter, DownloadableFile, UploadedFile};
const MAX_FILES: usize = 256;
const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] = const FILENAME_DATE_FORMAT: &[time::format_description::FormatItem] =
time::macros::format_description!("[year]-[month]-[day]-[hour][minute][second]"); time::macros::format_description!("[year]-[month]-[day]-[hour][minute][second]");
@ -28,10 +29,12 @@ enum Error {
UnexpectedMessageType, UnexpectedMessageType,
#[error("Metadata contained an empty list of files")] #[error("Metadata contained an empty list of files")]
NoFiles, NoFiles,
#[error("Number of files submitted by client exceeded the maximum limit")]
TooManyFiles,
#[error("Websocket was closed by client before completing transfer")] #[error("Websocket was closed by client before completing transfer")]
ClosedEarly(Option<CloseReason>), ClosedEarly(Option<CloseReason>),
#[error("Client sent more data than they were supposed to")] #[error("Client sent more data than they were supposed to")]
TooMuchData, UnexpectedExtraData,
} }
impl Error { impl Error {
@ -43,8 +46,9 @@ impl Error {
Self::DuplicateFilename => CloseCode::Policy, Self::DuplicateFilename => CloseCode::Policy,
Self::UnexpectedMessageType => CloseCode::Invalid, Self::UnexpectedMessageType => CloseCode::Invalid,
Self::NoFiles => CloseCode::Policy, Self::NoFiles => CloseCode::Policy,
Self::TooManyFiles => CloseCode::Policy,
Self::ClosedEarly(_) => CloseCode::Invalid, Self::ClosedEarly(_) => CloseCode::Invalid,
Self::TooMuchData => CloseCode::Invalid, Self::UnexpectedExtraData => CloseCode::Invalid,
} }
} }
} }
@ -105,7 +109,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Uploader {
let msg = match msg { let msg = match msg {
Ok(m) => m, Ok(m) => m,
Err(e) => { Err(e) => {
error!("Websocket error: {:?}", e); error!("Websocket error: {}", e);
ctx.stop(); ctx.stop();
return; return;
} }
@ -113,7 +117,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Uploader {
match self.handle_message(msg, ctx) { match self.handle_message(msg, ctx) {
Err(e) => { Err(e) => {
error!("{:?}", e); error!("{}", e);
ctx.close(Some(ws::CloseReason { ctx.close(Some(ws::CloseReason {
code: e.close_code(), code: e.close_code(),
description: Some(e.to_string()), description: Some(e.to_string()),
@ -160,6 +164,9 @@ impl Uploader {
let raw_files: Vec<RawUploadedFile> = serde_json::from_slice(text.as_bytes())?; let raw_files: Vec<RawUploadedFile> = serde_json::from_slice(text.as_bytes())?;
info!("Received file list: {} files", raw_files.len()); info!("Received file list: {} files", raw_files.len());
debug!("{:?}", raw_files); debug!("{:?}", raw_files);
if raw_files.len() > MAX_FILES {
return Err(Error::TooManyFiles);
}
let mut filenames: HashSet<String> = HashSet::new(); let mut filenames: HashSet<String> = HashSet::new();
let mut files = Vec::new(); let mut files = Vec::new();
for raw_file in raw_files.iter() { for raw_file in raw_files.iter() {
@ -251,7 +258,7 @@ impl Uploader {
fn handle_data(&mut self, data: Bytes) -> Result<bool, Error> { fn handle_data(&mut self, data: Bytes) -> Result<bool, Error> {
if let Some(ref mut writer) = self.writer { if let Some(ref mut writer) = self.writer {
if (data.len() as u64) > self.bytes_remaining { if (data.len() as u64) > self.bytes_remaining {
return Err(Error::TooMuchData); return Err(Error::UnexpectedExtraData);
} }
self.bytes_remaining -= data.len() as u64; self.bytes_remaining -= data.len() as u64;
writer.write_all(&data)?; writer.write_all(&data)?;

View file

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="stylesheet" type="text/css" href="transbeam.css"/> <link rel="stylesheet" type="text/css" href="transbeam.css"/>
<script src="util.js"></script> <script src="util.js"></script>
<title>Upload Test</title> <title>transbeam</title>
</head> </head>
<body> <body>
<div id="header"> <div id="header">
@ -39,6 +39,6 @@
<h5>(c) 2022 xenofem, MIT licensed</h5> <h5>(c) 2022 xenofem, MIT licensed</h5>
<h5><a target="_blank" href="https://git.xeno.science/xenofem/transbeam">source</a></h5> <h5><a target="_blank" href="https://git.xeno.science/xenofem/transbeam">source</a></h5>
</div> </div>
<script src="upload.js"></script> <script src="transbeam.js"></script>
</body> </body>
</html> </html>

View file

@ -1,4 +1,5 @@
const FILE_CHUNK_SIZE = 16384; const FILE_CHUNK_SIZE = 16384;
const MAX_FILES = 256;
const fileInputContainer = document.getElementById('file_input_container'); const fileInputContainer = document.getElementById('file_input_container');
const fileInput = document.getElementById('file_input'); const fileInput = document.getElementById('file_input');
@ -86,6 +87,7 @@ function updateFiles() {
uploadButton.textContent = `Upload ${files.length} file${files.length > 1 ? 's' : ''} (${displaySize(totalBytes)})`; uploadButton.textContent = `Upload ${files.length} file${files.length > 1 ? 's' : ''} (${displaySize(totalBytes)})`;
uploadButton.style.display = ''; uploadButton.style.display = '';
} }
fileInput.disabled = (files.length >= MAX_FILES);
} }
updateFiles(); updateFiles();
@ -93,6 +95,7 @@ downloadLinkContainer.style.display = 'none';
progressContainer.style.display = 'none'; progressContainer.style.display = 'none';
function addFile(newFile) { function addFile(newFile) {
if (files.length >= MAX_FILES) { return; }
if (files.some((oldFile) => newFile.name === oldFile.name)) { return; } if (files.some((oldFile) => newFile.name === oldFile.name)) { return; }
files.push(newFile); files.push(newFile);