aboutsummaryrefslogtreecommitdiffstats
path: root/src/listing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/listing.rs')
-rw-r--r--src/listing.rs68
1 files changed, 42 insertions, 26 deletions
diff --git a/src/listing.rs b/src/listing.rs
index 43cfb0e..33a0342 100644
--- a/src/listing.rs
+++ b/src/listing.rs
@@ -2,7 +2,7 @@ use actix_web::body::Body;
use actix_web::dev::ServiceResponse;
use actix_web::http::StatusCode;
use actix_web::web::Query;
-use actix_web::{HttpRequest, HttpResponse, Result};
+use actix_web::{HttpRequest, HttpResponse};
use bytesize::ByteSize;
use percent_encoding::{percent_decode_str, utf8_percent_encode};
use qrcodegen::{QrCode, QrCodeEcc};
@@ -24,7 +24,7 @@ mod percent_encode_sets {
const BASE: &AsciiSet = &CONTROLS.add(b'%');
pub const QUERY: &AsciiSet = &BASE.add(b' ').add(b'"').add(b'#').add(b'<').add(b'>');
pub const PATH: &AsciiSet = &QUERY.add(b'?').add(b'`').add(b'{').add(b'}');
- pub const PATH_SEGMENT: &AsciiSet = &PATH.add(b'/');
+ pub const PATH_SEGMENT: &AsciiSet = &PATH.add(b'/').add(b'\\');
}
/// Query parameters
@@ -142,7 +142,7 @@ impl Breadcrumb {
}
}
-pub async fn file_handler(req: HttpRequest) -> Result<actix_files::NamedFile> {
+pub async fn file_handler(req: HttpRequest) -> actix_web::Result<actix_files::NamedFile> {
let path = &req.app_data::<crate::MiniserveConfig>().unwrap().path;
actix_files::NamedFile::open(path).map_err(Into::into)
}
@@ -169,25 +169,10 @@ pub fn directory_listing(
dirs_first: bool,
hide_version_footer: bool,
title: Option<String>,
-) -> Result<ServiceResponse, io::Error> {
+) -> io::Result<ServiceResponse> {
use actix_web::dev::BodyEncoding;
let serve_path = req.path();
- // In case the current path is a directory, we want to make sure that the current URL ends
- // on a slash ("/").
- if !serve_path.ends_with('/') {
- let query = match req.query_string() {
- "" => String::new(),
- _ => format!("?{}", req.query_string()),
- };
- return Ok(ServiceResponse::new(
- req.clone(),
- HttpResponse::MovedPermanently()
- .header("Location", format!("{}/{}", serve_path, query))
- .body("301"),
- ));
- }
-
let base = Path::new(serve_path);
let random_route_abs = format!("/{}", random_route.clone().unwrap_or_default());
let is_root = base.parent().is_none() || Path::new(&req.path()) == Path::new(&random_route_abs);
@@ -243,10 +228,10 @@ pub fn directory_listing(
if let Some(url) = query_params.qrcode {
let res = match QrCode::encode_text(&url, QrCodeEcc::Medium) {
Ok(qr) => HttpResponse::Ok()
- .header("Content-Type", "image/svg+xml")
- .body(qr.to_svg_string(2)),
+ .append_header(("Content-Type", "image/svg+xml"))
+ .body(qr_to_svg_string(&qr, 2)),
Err(err) => {
- log::error!("URL is too long: {:?}", err);
+ log::error!("URL is invalid (too long?): {:?}", err);
HttpResponse::UriTooLong().body(Body::Empty)
}
};
@@ -376,7 +361,7 @@ pub fn directory_listing(
// We will create the archive in a separate thread, and stream the content using a pipe.
// The pipe is made of a futures channel, and an adapter to implement the `Write` trait.
// Include 10 messages of buffer for erratic connection speeds.
- let (tx, rx) = futures::channel::mpsc::channel::<Result<actix_web::web::Bytes, ()>>(10);
+ let (tx, rx) = futures::channel::mpsc::channel::<io::Result<actix_web::web::Bytes>>(10);
let pipe = crate::pipe::Pipe::new(tx);
// Start the actual archive creation in a separate thread.
@@ -392,11 +377,11 @@ pub fn directory_listing(
HttpResponse::Ok()
.content_type(archive_method.content_type())
.encoding(archive_method.content_encoding())
- .header("Content-Transfer-Encoding", "binary")
- .header(
+ .append_header(("Content-Transfer-Encoding", "binary"))
+ .append_header((
"Content-Disposition",
format!("attachment; filename={:?}", file_name),
- )
+ ))
.body(actix_web::body::BodyStream::new(rx)),
))
} else {
@@ -452,3 +437,34 @@ pub fn extract_query_parameters(req: &HttpRequest) -> QueryParameters {
}
}
}
+
+// Returns a string of SVG code for an image depicting
+// the given QR Code, with the given number of border modules.
+// The string always uses Unix newlines (\n), regardless of the platform.
+fn qr_to_svg_string(qr: &QrCode, border: i32) -> String {
+ assert!(border >= 0, "Border must be non-negative");
+ let mut result = String::new();
+ result += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ result += "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n";
+ let dimension = qr
+ .size()
+ .checked_add(border.checked_mul(2).unwrap())
+ .unwrap();
+ result += &format!(
+ "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 {0} {0}\" stroke=\"none\">\n", dimension);
+ result += "\t<rect width=\"100%\" height=\"100%\" fill=\"#FFFFFF\"/>\n";
+ result += "\t<path d=\"";
+ for y in 0..qr.size() {
+ for x in 0..qr.size() {
+ if qr.get_module(x, y) {
+ if x != 0 || y != 0 {
+ result += " ";
+ }
+ result += &format!("M{},{}h1v1h-1z", x + border, y + border);
+ }
+ }
+ }
+ result += "\" fill=\"#000000\"/>\n";
+ result += "</svg>\n";
+ result
+}