This commit is contained in:
spikecodes 2021-03-11 20:15:26 -08:00
parent b2ae5e486f
commit 4173362ce1
No known key found for this signature in database
GPG Key ID: 004CECFF9B463BCB
5 changed files with 41 additions and 16 deletions

View File

@ -3,7 +3,7 @@ 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.4.0" version = "0.4.1"
authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"] authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"]
edition = "2018" edition = "2018"
@ -14,7 +14,7 @@ async-std = { version = "1.9.0", features = ["attributes"] }
async-tls = { version = "0.11.0", default-features = false, features = ["client"] } async-tls = { version = "0.11.0", default-features = false, features = ["client"] }
cached = "0.23.0" cached = "0.23.0"
clap = { version = "2.33.3", default-features = false } clap = { version = "2.33.3", default-features = false }
regex = "1.4.3" regex = "1.4.4"
serde = { version = "1.0.124", features = ["derive"] } serde = { version = "1.0.124", features = ["derive"] }
serde_json = "1.0.64" serde_json = "1.0.64"
tide = { version = "0.16.0", default-features = false, features = ["h1-server", "cookies"] } tide = { version = "0.16.0", default-features = false, features = ["h1-server", "cookies"] }

View File

@ -1,4 +1,5 @@
// CRATES // CRATES
use crate::esc;
use crate::utils::{ use crate::utils::{
cookie, error, format_num, format_url, param, request, rewrite_urls, template, time, val, Author, Comment, Flags, Flair, FlairPart, Media, Post, Preferences, cookie, error, format_num, format_url, param, request, rewrite_urls, template, time, val, Author, Comment, Flags, Flair, FlairPart, Media, Post, Preferences,
}; };
@ -81,7 +82,7 @@ async fn parse_post(json: &serde_json::Value) -> Post {
// Build a post using data parsed from Reddit post API // Build a post using data parsed from Reddit post API
Post { Post {
id: val(post, "id"), id: val(post, "id"),
title: val(post, "title"), title: esc!(post, "title"),
community: val(post, "subreddit"), community: val(post, "subreddit"),
body: rewrite_urls(&val(post, "selftext_html")).replace("\\", ""), body: rewrite_urls(&val(post, "selftext_html")).replace("\\", ""),
author: Author { author: Author {
@ -92,7 +93,7 @@ async fn parse_post(json: &serde_json::Value) -> Post {
post["data"]["author_flair_richtext"].as_array(), post["data"]["author_flair_richtext"].as_array(),
post["data"]["author_flair_text"].as_str(), post["data"]["author_flair_text"].as_str(),
), ),
text: val(post, "link_flair_text"), text: esc!(post, "link_flair_text"),
background_color: val(post, "author_flair_background_color"), background_color: val(post, "author_flair_background_color"),
foreground_color: val(post, "author_flair_text_color"), foreground_color: val(post, "author_flair_text_color"),
}, },
@ -115,7 +116,7 @@ async fn parse_post(json: &serde_json::Value) -> Post {
post["data"]["link_flair_richtext"].as_array(), post["data"]["link_flair_richtext"].as_array(),
post["data"]["link_flair_text"].as_str(), post["data"]["link_flair_text"].as_str(),
), ),
text: val(post, "link_flair_text"), text: esc!(post, "link_flair_text"),
background_color: val(post, "link_flair_background_color"), background_color: val(post, "link_flair_background_color"),
foreground_color: if val(post, "link_flair_text_color") == "dark" { foreground_color: if val(post, "link_flair_text_color") == "dark" {
"black".to_string() "black".to_string()
@ -191,7 +192,7 @@ async fn parse_comments(json: &serde_json::Value, post_link: &str, post_author:
data["author_flair_richtext"].as_array(), data["author_flair_richtext"].as_array(),
data["author_flair_text"].as_str(), data["author_flair_text"].as_str(),
), ),
text: val(&comment, "link_flair_text"), text: esc!(&comment, "link_flair_text"),
background_color: val(&comment, "author_flair_background_color"), background_color: val(&comment, "author_flair_background_color"),
foreground_color: val(&comment, "author_flair_text_color"), foreground_color: val(&comment, "author_flair_text_color"),
}, },

View File

@ -1,4 +1,5 @@
// CRATES // CRATES
use crate::esc;
use crate::utils::{cookie, error, format_num, format_url, param, redirect, request, rewrite_urls, template, val, Post, Preferences, Subreddit}; use crate::utils::{cookie, error, format_num, format_url, param, redirect, request, rewrite_urls, template, val, Post, Preferences, Subreddit};
use askama::Template; use askama::Template;
use tide::{http::Cookie, Request}; use tide::{http::Cookie, Request};
@ -167,9 +168,9 @@ async fn subreddit(sub: &str) -> Result<Subreddit, String> {
let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() }; let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() };
let sub = Subreddit { let sub = Subreddit {
name: val(&res, "display_name"), name: esc!(&res, "display_name"),
title: val(&res, "title"), title: esc!(&res, "title"),
description: val(&res, "public_description"), description: esc!(&res, "public_description"),
info: rewrite_urls(&val(&res, "description_html").replace("\\", "")), info: rewrite_urls(&val(&res, "description_html").replace("\\", "")),
icon: format_url(&icon), icon: format_url(&icon),
members: format_num(members), members: format_num(members),

View File

@ -1,4 +1,5 @@
// CRATES // CRATES
use crate::esc;
use crate::utils::{error, format_url, param, request, template, Post, Preferences, User}; use crate::utils::{error, format_url, param, request, template, Post, Preferences, User};
use askama::Template; use askama::Template;
use tide::Request; use tide::Request;
@ -57,17 +58,17 @@ async fn user(name: &str) -> Result<User, String> {
// Grab creation date as unix timestamp // Grab creation date as unix timestamp
let created: i64 = res["data"]["created"].as_f64().unwrap_or(0.0).round() as i64; let created: i64 = res["data"]["created"].as_f64().unwrap_or(0.0).round() as i64;
// nested_val function used to parse JSON from Reddit APIs // Closure used to parse JSON from Reddit APIs
let about = |item| res["data"]["subreddit"][item].as_str().unwrap_or_default().to_string(); let about = |item| res["data"]["subreddit"][item].as_str().unwrap_or_default().to_string();
// Parse the JSON output into a User struct // Parse the JSON output into a User struct
Ok(User { Ok(User {
name: name.to_string(), name: name.to_string(),
title: about("title"), title: esc!(about("title")),
icon: format_url(&about("icon_img")), icon: format_url(&about("icon_img")),
karma: res["data"]["total_karma"].as_i64().unwrap_or(0), karma: res["data"]["total_karma"].as_i64().unwrap_or(0),
created: OffsetDateTime::from_unix_timestamp(created).format("%b %d '%y"), created: OffsetDateTime::from_unix_timestamp(created).format("%b %d '%y"),
banner: about("banner_img"), banner: esc!(about("banner_img")),
description: about("public_description"), description: about("public_description"),
}) })
} }

View File

@ -1,6 +1,7 @@
// //
// CRATES // CRATES
// //
use crate::esc;
use askama::Template; use askama::Template;
use async_recursion::async_recursion; use async_recursion::async_recursion;
use async_std::{io, net::TcpStream, prelude::*}; use async_std::{io, net::TcpStream, prelude::*};
@ -227,14 +228,14 @@ impl Post {
let (rel_time, created) = time(data["created_utc"].as_f64().unwrap_or_default()); let (rel_time, created) = time(data["created_utc"].as_f64().unwrap_or_default());
let score = data["score"].as_i64().unwrap_or_default(); let score = data["score"].as_i64().unwrap_or_default();
let ratio: f64 = data["upvote_ratio"].as_f64().unwrap_or(1.0) * 100.0; let ratio: f64 = data["upvote_ratio"].as_f64().unwrap_or(1.0) * 100.0;
let title = val(post, "title"); let title = esc!(post, "title");
// Determine the type of media along with the media URL // Determine the type of media along with the media URL
let (post_type, media, gallery) = Media::parse(&data).await; let (post_type, media, gallery) = Media::parse(&data).await;
posts.push(Self { posts.push(Self {
id: val(post, "id"), id: val(post, "id"),
title: if title.is_empty() { fallback_title.to_owned() } else { title }, title: esc!(if title.is_empty() { fallback_title.to_owned() } else { title }),
community: val(post, "subreddit"), community: val(post, "subreddit"),
body: rewrite_urls(&val(post, "body_html")), body: rewrite_urls(&val(post, "body_html")),
author: Author { author: Author {
@ -245,7 +246,7 @@ impl Post {
data["author_flair_richtext"].as_array(), data["author_flair_richtext"].as_array(),
data["author_flair_text"].as_str(), data["author_flair_text"].as_str(),
), ),
text: val(post, "link_flair_text"), text: esc!(post, "link_flair_text"),
background_color: val(post, "author_flair_background_color"), background_color: val(post, "author_flair_background_color"),
foreground_color: val(post, "author_flair_text_color"), foreground_color: val(post, "author_flair_text_color"),
}, },
@ -272,7 +273,7 @@ impl Post {
data["link_flair_richtext"].as_array(), data["link_flair_richtext"].as_array(),
data["link_flair_text"].as_str(), data["link_flair_text"].as_str(),
), ),
text: val(post, "link_flair_text"), text: esc!(post, "link_flair_text"),
background_color: val(post, "link_flair_background_color"), background_color: val(post, "link_flair_background_color"),
foreground_color: if val(post, "link_flair_text_color") == "dark" { foreground_color: if val(post, "link_flair_text_color") == "dark" {
"black".to_string() "black".to_string()
@ -486,6 +487,27 @@ pub fn val(j: &Value, k: &str) -> String {
j["data"][k].as_str().unwrap_or_default().to_string() j["data"][k].as_str().unwrap_or_default().to_string()
} }
#[macro_export]
macro_rules! esc {
($f:expr) => {
$f.replace('<', "&lt;").replace('>', "&gt;")
};
($j:expr, $k:expr) => {
$j["data"][$k].as_str().unwrap_or_default().to_string().replace('<', "&lt;").replace('>', "&gt;")
};
}
// Escape < and > to accurately render HTML
// pub fn esc(j: &Value, k: &str) -> String {
// val(j,k)
// // .replace('&', "&amp;")
// .replace('<', "&lt;")
// .replace('>', "&gt;")
// // .replace('"', "&quot;")
// // .replace('\'', "&#x27;")
// // .replace('/', "&#x2f;")
// }
// //
// NETWORKING // NETWORKING
// //