From 2bfdf0ef4923dc767088e98a62163e67f15ec092 Mon Sep 17 00:00:00 2001 From: Emi Simpson Date: Tue, 2 Nov 2021 19:08:50 -0400 Subject: [PATCH] Improve URL encoding and decoding --- web/Cargo.toml | 2 ++ web/src/main.rs | 40 ++++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/web/Cargo.toml b/web/Cargo.toml index 6391345..f9acfcb 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -15,6 +15,8 @@ askama = "0.10" argh = "0.1.6" serde = "1.0" serde_yaml = "0.8" +form_urlencoded = "1.0.1" +percent-encoding = "2.1.0" rusttype = { version = "0.9.2", optional = true } image = { version = "0.23.14", optional = true } lazy_static = { version = "1.4.0", optional = true } diff --git a/web/src/main.rs b/web/src/main.rs index 7a8e4ea..349a778 100644 --- a/web/src/main.rs +++ b/web/src/main.rs @@ -3,13 +3,14 @@ pub mod contrast; pub mod statics; pub mod configuration; -use std::collections::HashMap; use crate::configuration::Conf; use std::io::IoSlice; use crate::statics::StaticAsset; use smol::io::AsyncWriteExt; use smol::stream::StreamExt; use std::borrow::Cow; +use form_urlencoded; +use percent_encoding::{percent_decode_str, percent_encode, NON_ALPHANUMERIC}; use pronouns_today::user_preferences::ParseError; use pronouns_today::UserPreferences; use configuration::ConfigError; @@ -167,7 +168,7 @@ fn route_request(req: &ScgiRequest) -> Route { "GET" => (), // All POST requests are met with link generation - "POST" => return Route::GenerateLink(String::from_utf8_lossy(&req.body).into_owned()), + "POST" => return Route::GenerateLink(req.body.clone()), // No other methods are supported _ => return Route::BadRequest(BadRequest::MethodNotAllowed), @@ -267,7 +268,7 @@ enum Route { SendThumbnail(Option, Option), /// Generate a link to the appropriate pronoun page based on the user's inputs. - GenerateLink(String), + GenerateLink(Vec), /// There was a problem with the user's request, and an error page should be sent BadRequest(BadRequest), @@ -325,7 +326,11 @@ impl Route { let pronoun = Route::get_pronoun(name.as_ref(), prefs, settings)?; let body = IndexTemplate { - name, + name: name.map( + |name| percent_decode_str(&name) + .decode_utf8_lossy() + .into_owned() + ), pronoun, pronouns: settings.pronoun_list.iter().enumerate().collect(), url: format!("{}{}", &conf.base_url, uri), @@ -369,32 +374,31 @@ impl Route { /// The method for the [`Route::GenerateLink`] route fn generate_link( - body: String, + body: Vec, settings: &InstanceSettings, ) -> Result { - let form = body.split('&') - .filter_map(|entry| { - let mut split = entry.split('='); - split.next().map(|key| ( - key, - split.next().unwrap_or("") - )) - }) - .collect::>(); + let form = form_urlencoded::parse(&body); let mut weights = vec![0; settings.pronoun_list.len()]; - for (k, v) in form.iter() { + let mut name = None; + for (k, v) in form { if let Ok(i) = k.parse::() { let w = v.parse::().map_err(|_| BadRequest::BadFormData((*v).into()))?; if i < weights.len() - 1 { weights[i] = w; } - } + } else if k == "name" { + name = Some(v); + } } let prefs = InstanceSettings::create_preferences(&weights); let pref_string = prefs.as_prefstring(); - let url = match form.get("name") { - Some(name) if !name.is_empty() => format!("/{}/{}", name, pref_string), + let url = match name { + Some(name) if !name.is_empty() => format!( + "/{}/{}", + percent_encode(name.as_bytes(), NON_ALPHANUMERIC), + pref_string + ), _ => format!("/{}", pref_string), };