Added Percent Encoding Support

This commit is contained in:
spikecodes 2020-11-29 18:50:29 -08:00
parent 8f157c0b40
commit 9a6430656d
11 changed files with 63 additions and 16570 deletions

3
Cargo.lock generated
View File

@ -1394,11 +1394,12 @@ dependencies = [
[[package]] [[package]]
name = "libreddit" name = "libreddit"
version = "0.1.7" version = "0.1.8"
dependencies = [ dependencies = [
"actix-web", "actix-web",
"askama", "askama",
"chrono", "chrono",
"percent-encoding",
"pulldown-cmark", "pulldown-cmark",
"serde", "serde",
"serde_json", "serde_json",

View File

@ -3,15 +3,16 @@ name = "libreddit"
description = " Alternative private front-end to Reddit" description = " Alternative private front-end to Reddit"
license = "AGPL-3.0" license = "AGPL-3.0"
repository = "https://github.com/spikecodes/libreddit" repository = "https://github.com/spikecodes/libreddit"
version = "0.1.7" version = "0.1.8"
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"] authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
edition = "2018" edition = "2018"
[features] [features]
default = ["proxy"] default = ["proxy"]
proxy = ["actix-web/rustls"] proxy = ["actix-web/rustls", "percent-encoding"]
[dependencies] [dependencies]
percent-encoding = { version = "2.1.0", optional = true }
actix-web = "3.2.0" actix-web = "3.2.0"
surf = "2.1.0" surf = "2.1.0"
askama = "0.8.0" askama = "0.8.0"

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
edition = "2018" edition = "2018"
tab_spaces = 2 tab_spaces = 2
hard_tabs = true hard_tabs = true
max_width = 200 max_width = 175

View File

@ -4,9 +4,9 @@ use actix_web::{get, App, HttpResponse, HttpServer};
// Reference local files // Reference local files
mod popular; mod popular;
mod post; mod post;
mod proxy;
mod subreddit; mod subreddit;
mod user; mod user;
mod proxy;
mod utils; mod utils;
// Create Services // Create Services

View File

@ -1,7 +1,7 @@
// CRATES // CRATES
use actix_web::{get, web, HttpResponse, Result, http::StatusCode};
use askama::Template;
use crate::utils::{fetch_posts, ErrorTemplate, Params, Post}; use crate::utils::{fetch_posts, ErrorTemplate, Params, Post};
use actix_web::{get, http::StatusCode, web, HttpResponse, Result};
use askama::Template;
// STRUCTS // STRUCTS
#[derive(Template)] #[derive(Template)]

View File

@ -1,9 +1,12 @@
// CRATES // CRATES
use actix_web::{get, web, HttpResponse, Result, http::StatusCode}; use crate::utils::{request, val, Comment, ErrorTemplate, Flair, Params, Post};
use actix_web::{get, http::StatusCode, web, HttpResponse, Result};
use askama::Template; use askama::Template;
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
use pulldown_cmark::{html, Options, Parser}; use pulldown_cmark::{html, Options, Parser};
use crate::utils::{request, val, Comment, ErrorTemplate, Flair, Params, Post};
#[cfg(feature = "proxy")]
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
// STRUCTS // STRUCTS
#[derive(Template)] #[derive(Template)]
@ -66,6 +69,14 @@ async fn page(web::Path((_sub, id)): web::Path<(String, String)>, params: web::Q
} }
} }
async fn format_url(url: &str) -> String {
#[cfg(feature = "proxy")]
return utf8_percent_encode(url, NON_ALPHANUMERIC).to_string();
#[cfg(not(feature = "proxy"))]
return url.to_string();
}
// UTILITIES // UTILITIES
async fn media(data: &serde_json::Value) -> String { async fn media(data: &serde_json::Value) -> String {
let post_hint: &str = data["data"]["post_hint"].as_str().unwrap_or(""); let post_hint: &str = data["data"]["post_hint"].as_str().unwrap_or("");
@ -76,15 +87,20 @@ async fn media(data: &serde_json::Value) -> String {
let media: String = if !has_media { let media: String = if !has_media {
format!(r#"<h4 class="post_body"><a href="{u}">{u}</a></h4>"#, u = data["data"]["url"].as_str().unwrap()) format!(r#"<h4 class="post_body"><a href="{u}">{u}</a></h4>"#, u = data["data"]["url"].as_str().unwrap())
} else { } else {
format!(r#"<img class="post_image" src="{}{}.png"/>"#, prefix, data["data"]["url"].as_str().unwrap()) format!(
r#"<img class="post_image" src="{}{}.png"/>"#,
prefix,
format_url(data["data"]["url"].as_str().unwrap()).await
)
}; };
match post_hint { match post_hint {
"hosted:video" => format!( "hosted:video" => format!(
r#"<video class="post_image" src="{}{}" controls/>"#, r#"<video class="post_image" src="{}{}" controls/>"#,
prefix, data["data"]["media"]["reddit_video"]["fallback_url"].as_str().unwrap() prefix,
format_url(data["data"]["media"]["reddit_video"]["fallback_url"].as_str().unwrap()).await
), ),
"image" => format!(r#"<img class="post_image" src="{}{}"/>"#, prefix, data["data"]["url"].as_str().unwrap()), "image" => format!(r#"<img class="post_image" src="{}{}"/>"#, prefix, format_url(data["data"]["url"].as_str().unwrap()).await),
"self" => String::from(""), "self" => String::from(""),
_ => media, _ => media,
} }

View File

@ -1,18 +1,27 @@
use actix_web::{get, web, HttpResponse, Result, client::Client, Error}; use actix_web::{client::Client, get, web, Error, HttpResponse, Result};
#[cfg(feature = "proxy")]
use percent_encoding::percent_decode_str;
#[get("/imageproxy/{url:.*}")] #[get("/imageproxy/{url:.*}")]
async fn handler(web::Path(url): web::Path<String>) -> Result<HttpResponse> { async fn handler(web::Path(url): web::Path<String>) -> Result<HttpResponse> {
if cfg!(feature = "proxy") { if cfg!(feature = "proxy") {
dbg!(&url); #[cfg(feature = "proxy")]
let media: String = percent_decode_str(url.as_str()).decode_utf8()?.to_string();
#[cfg(not(feature = "proxy"))]
let media: String = url;
dbg!(&media);
let client = Client::default(); let client = Client::default();
client.get(url) client
.get(media)
.send() .send()
.await .await
.map_err(Error::from) .map_err(Error::from)
.and_then(|res| { .and_then(|res| Ok(HttpResponse::build(res.status()).streaming(res)))
Ok(HttpResponse::build(res.status()).streaming(res))
})
} else { } else {
Ok(HttpResponse::Ok().body("")) Ok(HttpResponse::Ok().body(""))
} }
} }

View File

@ -1,7 +1,7 @@
// CRATES // CRATES
use actix_web::{get, web, HttpResponse, Result, http::StatusCode}; use crate::utils::{fetch_posts, request, val, ErrorTemplate, Params, Post, Subreddit};
use actix_web::{get, http::StatusCode, web, HttpResponse, Result};
use askama::Template; use askama::Template;
use crate::utils::{request, val, fetch_posts, ErrorTemplate, Params, Post, Subreddit};
// STRUCTS // STRUCTS
#[derive(Template)] #[derive(Template)]
@ -10,7 +10,7 @@ struct SubredditTemplate {
sub: Subreddit, sub: Subreddit,
posts: Vec<Post>, posts: Vec<Post>,
sort: String, sort: String,
ends: (String, String) ends: (String, String),
} }
// SERVICES // SERVICES
@ -57,7 +57,7 @@ pub async fn render(sub_name: String, sort: Option<String>, ends: (Option<String
sub: sub, sub: sub,
posts: items.0, posts: items.0,
sort: sorting, sort: sorting,
ends: (before, items.1) ends: (before, items.1),
} }
.render() .render()
.unwrap(); .unwrap();
@ -82,7 +82,7 @@ async fn subreddit(sub: &String) -> Result<Subreddit, &'static str> {
let res = req.unwrap(); let res = req.unwrap();
let members = res["data"]["subscribers"].as_u64().unwrap_or(0); let members = res["data"]["subscribers"].as_u64().unwrap_or(0);
let active = res["data"]["accounts_active"].as_u64().unwrap_or(0); let active = res["data"]["accounts_active"].as_u64().unwrap_or(0);
let sub = Subreddit { let sub = Subreddit {
name: val(&res, "display_name").await, name: val(&res, "display_name").await,
@ -94,4 +94,4 @@ async fn subreddit(sub: &String) -> Result<Subreddit, &'static str> {
}; };
Ok(sub) Ok(sub)
} }

View File

@ -1,7 +1,7 @@
// CRATES // CRATES
use actix_web::{get, web, HttpResponse, Result, http::StatusCode}; use crate::utils::{fetch_posts, nested_val, request, ErrorTemplate, Params, Post, User};
use actix_web::{get, http::StatusCode, web, HttpResponse, Result};
use askama::Template; use askama::Template;
use crate::utils::{nested_val, request, fetch_posts, ErrorTemplate, Params, Post, User};
// STRUCTS // STRUCTS
#[derive(Template)] #[derive(Template)]
@ -71,4 +71,4 @@ async fn user(name: &String) -> Result<User, &'static str> {
banner: nested_val(&res, "subreddit", "banner_img").await, banner: nested_val(&res, "subreddit", "banner_img").await,
description: nested_val(&res, "subreddit", "public_description").await, description: nested_val(&res, "subreddit", "public_description").await,
}) })
} }

View File

@ -2,8 +2,8 @@
// CRATES // CRATES
// //
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
use surf::{get, client, middleware::Redirect}; use serde_json::{from_str, Value};
use serde_json::{Value, from_str}; use surf::{client, get, middleware::Redirect};
// //
// STRUCTS // STRUCTS
@ -23,7 +23,7 @@ pub struct Post {
pub score: String, pub score: String,
pub media: String, pub media: String,
pub time: String, pub time: String,
pub flair: Flair pub flair: Flair,
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -32,7 +32,7 @@ pub struct Comment {
pub body: String, pub body: String,
pub author: String, pub author: String,
pub score: String, pub score: String,
pub time: String pub time: String,
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -42,7 +42,7 @@ pub struct User {
pub icon: String, pub icon: String,
pub karma: i64, pub karma: i64,
pub banner: String, pub banner: String,
pub description: String pub description: String,
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -53,7 +53,7 @@ pub struct Subreddit {
pub description: String, pub description: String,
pub icon: String, pub icon: String,
pub members: String, pub members: String,
pub active: String pub active: String,
} }
// Parser for query params, used in sorting (eg. /r/rust/?sort=hot) // Parser for query params, used in sorting (eg. /r/rust/?sort=hot)
@ -61,14 +61,14 @@ pub struct Subreddit {
pub struct Params { pub struct Params {
pub sort: Option<String>, pub sort: Option<String>,
pub after: Option<String>, pub after: Option<String>,
pub before: Option<String> pub before: Option<String>,
} }
// Error template // Error template
#[derive(askama::Template)] #[derive(askama::Template)]
#[template(path = "error.html", escape = "none")] #[template(path = "error.html", escape = "none")]
pub struct ErrorTemplate { pub struct ErrorTemplate {
pub message: String pub message: String,
} }
// //