2024-10-31 19:50:50 -07:00
#![ allow(clippy::cmp_owned) ]
2021-03-17 15:30:33 -07:00
use std ::collections ::HashMap ;
2021-01-05 18:04:49 -08:00
// CRATES
2021-03-17 15:30:33 -07:00
use crate ::server ::ResponseExt ;
2025-02-02 21:48:46 -05:00
use crate ::subreddit ::join_until_size_limit ;
2021-02-24 21:29:23 -08:00
use crate ::utils ::{ redirect , template , Preferences } ;
2021-03-17 15:30:33 -07:00
use cookie ::Cookie ;
use futures_lite ::StreamExt ;
use hyper ::{ Body , Request , Response } ;
2024-10-02 23:43:13 +02:00
use rinja ::Template ;
2021-01-05 18:04:49 -08:00
use time ::{ Duration , OffsetDateTime } ;
// STRUCTS
#[ derive(Template) ]
2021-01-07 08:38:05 -08:00
#[ template(path = " settings.html " ) ]
2021-01-05 18:04:49 -08:00
struct SettingsTemplate {
2021-01-08 17:50:03 -08:00
prefs : Preferences ,
2021-11-15 03:39:33 +01:00
url : String ,
2021-01-05 18:04:49 -08:00
}
2021-05-10 01:25:52 +00:00
// CONSTANTS
2024-11-01 12:28:52 -04:00
const PREFS : [ & str ; 18 ] = [
2021-05-10 01:25:52 +00:00
" theme " ,
" front_page " ,
" layout " ,
" wide " ,
" comment_sort " ,
" post_sort " ,
2024-06-22 12:16:12 +02:00
" blur_spoiler " ,
2021-05-10 01:25:52 +00:00
" show_nsfw " ,
2022-09-26 19:35:23 -06:00
" blur_nsfw " ,
2021-05-10 01:25:52 +00:00
" use_hls " ,
" hide_hls_notification " ,
2021-10-25 21:27:55 -07:00
" autoplay_videos " ,
2024-05-13 23:49:59 +02:00
" hide_sidebar_and_summary " ,
2022-06-18 22:53:30 +01:00
" fixed_navbar " ,
2023-01-01 21:39:38 -05:00
" hide_awards " ,
2023-03-23 11:29:28 -07:00
" hide_score " ,
2023-01-12 09:46:56 +01:00
" disable_visit_reddit_confirmation " ,
2024-11-01 12:28:52 -04:00
" video_quality " ,
2021-05-10 01:25:52 +00:00
] ;
2021-01-05 18:04:49 -08:00
// FUNCTIONS
// Retrieve cookies from request "Cookie" header
2021-03-17 15:30:33 -07:00
pub async fn get ( req : Request < Body > ) -> Result < Response < Body > , String > {
2021-11-15 03:39:33 +01:00
let url = req . uri ( ) . to_string ( ) ;
2024-01-19 20:16:17 -05:00
Ok ( template ( & SettingsTemplate {
2023-01-01 21:39:38 -05:00
prefs : Preferences ::new ( & req ) ,
2021-11-14 18:51:36 -08:00
url ,
2024-01-19 20:16:17 -05:00
} ) )
2021-01-05 18:04:49 -08:00
}
// Set cookies using response "Set-Cookie" header
2021-03-17 15:30:33 -07:00
pub async fn set ( req : Request < Body > ) -> Result < Response < Body > , String > {
// Split the body into parts
let ( parts , mut body ) = req . into_parts ( ) ;
// Grab existing cookies
2024-01-19 20:16:17 -05:00
let _cookies : Vec < Cookie < '_ > > = parts
2021-05-20 12:24:06 -07:00
. headers
. get_all ( " Cookie " )
. iter ( )
. filter_map ( | header | Cookie ::parse ( header . to_str ( ) . unwrap_or_default ( ) ) . ok ( ) )
. collect ( ) ;
2021-03-17 15:30:33 -07:00
// Aggregate the body...
// let whole_body = hyper::body::aggregate(req).await.map_err(|e| e.to_string())?;
let body_bytes = body
. try_fold ( Vec ::new ( ) , | mut data , chunk | {
data . extend_from_slice ( & chunk ) ;
Ok ( data )
} )
. await
. map_err ( | e | e . to_string ( ) ) ? ;
let form = url ::form_urlencoded ::parse ( & body_bytes ) . collect ::< HashMap < _ , _ > > ( ) ;
2021-02-09 09:38:52 -08:00
2024-01-19 20:16:17 -05:00
let mut response = redirect ( " /settings " ) ;
2021-01-05 18:04:49 -08:00
2021-05-10 01:25:52 +00:00
for & name in & PREFS {
2021-03-17 15:30:33 -07:00
match form . get ( name ) {
2021-05-20 12:24:06 -07:00
Some ( value ) = > response . insert_cookie (
2023-12-26 16:24:53 -05:00
Cookie ::build ( ( name . to_owned ( ) , value . clone ( ) ) )
2021-01-08 17:35:04 -08:00
. path ( " / " )
. http_only ( true )
. expires ( OffsetDateTime ::now_utc ( ) + Duration ::weeks ( 52 ) )
2023-12-26 16:24:53 -05:00
. into ( ) ,
2021-01-08 17:35:04 -08:00
) ,
2021-05-20 12:24:06 -07:00
None = > response . remove_cookie ( name . to_string ( ) ) ,
2021-01-08 17:35:04 -08:00
} ;
}
2021-01-07 08:38:05 -08:00
2021-05-20 12:24:06 -07:00
Ok ( response )
2021-01-05 18:04:49 -08:00
}
2021-02-13 21:55:23 +01:00
2021-05-10 01:25:52 +00:00
fn set_cookies_method ( req : Request < Body > , remove_cookies : bool ) -> Response < Body > {
2021-03-17 15:30:33 -07:00
// Split the body into parts
let ( parts , _ ) = req . into_parts ( ) ;
// Grab existing cookies
2024-01-19 20:16:17 -05:00
let _cookies : Vec < Cookie < '_ > > = parts
2021-05-20 12:24:06 -07:00
. headers
. get_all ( " Cookie " )
. iter ( )
. filter_map ( | header | Cookie ::parse ( header . to_str ( ) . unwrap_or_default ( ) ) . ok ( ) )
. collect ( ) ;
2021-03-17 15:30:33 -07:00
let query = parts . uri . query ( ) . unwrap_or_default ( ) . as_bytes ( ) ;
2021-02-13 21:55:23 +01:00
2021-03-17 15:30:33 -07:00
let form = url ::form_urlencoded ::parse ( query ) . collect ::< HashMap < _ , _ > > ( ) ;
let path = match form . get ( " redirect " ) {
2021-05-10 01:25:52 +00:00
Some ( value ) = > format! ( " / {} " , value . replace ( " %26 " , " & " ) . replace ( " %23 " , " # " ) ) ,
2021-02-13 15:02:38 -08:00
None = > " / " . to_string ( ) ,
2021-02-13 21:55:23 +01:00
} ;
2024-01-19 20:16:17 -05:00
let mut response = redirect ( & path ) ;
2021-02-13 21:55:23 +01:00
2025-02-02 21:48:46 -05:00
for name in PREFS {
2021-03-17 15:30:33 -07:00
match form . get ( name ) {
2021-05-20 12:24:06 -07:00
Some ( value ) = > response . insert_cookie (
2023-12-26 16:24:53 -05:00
Cookie ::build ( ( name . to_owned ( ) , value . clone ( ) ) )
2021-02-13 21:55:23 +01:00
. path ( " / " )
. http_only ( true )
. expires ( OffsetDateTime ::now_utc ( ) + Duration ::weeks ( 52 ) )
2023-12-26 16:24:53 -05:00
. into ( ) ,
2021-02-13 21:55:23 +01:00
) ,
2021-05-10 01:25:52 +00:00
None = > {
if remove_cookies {
2021-09-09 17:28:55 -07:00
response . remove_cookie ( name . to_string ( ) ) ;
2021-05-10 01:25:52 +00:00
}
}
2021-02-13 21:55:23 +01:00
} ;
}
2025-02-02 21:48:46 -05:00
// Get subscriptions/filters to restore from query string
let subscriptions = form . get ( " subscriptions " ) ;
let filters = form . get ( " filters " ) ;
// We can't search through the cookies directly like in subreddit.rs, so instead we have to make a string out of the request's headers to search through
let cookies_string = parts
. headers
. get ( " cookie " )
. map ( | hv | hv . to_str ( ) . unwrap_or ( " " ) . to_string ( ) ) // Return String
. unwrap_or_else ( String ::new ) ; // Return an empty string if None
// If there are subscriptions to restore set them and delete any old subscriptions cookies, otherwise delete them all
if subscriptions . is_some ( ) {
let sub_list : Vec < String > = subscriptions . expect ( " Subscriptions " ) . split ( '+' ) . map ( str ::to_string ) . collect ( ) ;
// Start at 0 to keep track of what number we need to start deleting old subscription cookies from
let mut subscriptions_number_to_delete_from = 0 ;
// Starting at 0 so we handle the subscription cookie without a number first
for ( subscriptions_number , list ) in join_until_size_limit ( & sub_list ) . into_iter ( ) . enumerate ( ) {
let subscriptions_cookie = if subscriptions_number = = 0 {
" subscriptions " . to_string ( )
} else {
format! ( " subscriptions {} " , subscriptions_number )
} ;
response . insert_cookie (
Cookie ::build ( ( subscriptions_cookie , list ) )
. path ( " / " )
. http_only ( true )
. expires ( OffsetDateTime ::now_utc ( ) + Duration ::weeks ( 52 ) )
. into ( ) ,
) ;
subscriptions_number_to_delete_from + = 1 ;
}
// While subscriptionsNUMBER= is in the string of cookies add a response removing that cookie
while cookies_string . contains ( & format! ( " subscriptions {subscriptions_number_to_delete_from} = " ) ) {
// Remove that subscriptions cookie
response . remove_cookie ( format! ( " subscriptions {subscriptions_number_to_delete_from} " ) ) ;
// Increment subscriptions cookie number
subscriptions_number_to_delete_from + = 1 ;
}
} else {
// Remove unnumbered subscriptions cookie
response . remove_cookie ( " subscriptions " . to_string ( ) ) ;
// Starts at one to deal with the first numbered subscription cookie and onwards
let mut subscriptions_number_to_delete_from = 1 ;
// While subscriptionsNUMBER= is in the string of cookies add a response removing that cookie
while cookies_string . contains ( & format! ( " subscriptions {subscriptions_number_to_delete_from} = " ) ) {
// Remove that subscriptions cookie
response . remove_cookie ( format! ( " subscriptions {subscriptions_number_to_delete_from} " ) ) ;
// Increment subscriptions cookie number
subscriptions_number_to_delete_from + = 1 ;
}
}
// If there are filters to restore set them and delete any old filters cookies, otherwise delete them all
if filters . is_some ( ) {
let filters_list : Vec < String > = filters . expect ( " Filters " ) . split ( '+' ) . map ( str ::to_string ) . collect ( ) ;
// Start at 0 to keep track of what number we need to start deleting old subscription cookies from
let mut filters_number_to_delete_from = 0 ;
// Starting at 0 so we handle the subscription cookie without a number first
for ( filters_number , list ) in join_until_size_limit ( & filters_list ) . into_iter ( ) . enumerate ( ) {
let filters_cookie = if filters_number = = 0 {
" filters " . to_string ( )
} else {
format! ( " filters {} " , filters_number )
} ;
response . insert_cookie (
Cookie ::build ( ( filters_cookie , list ) )
. path ( " / " )
. http_only ( true )
. expires ( OffsetDateTime ::now_utc ( ) + Duration ::weeks ( 52 ) )
. into ( ) ,
) ;
filters_number_to_delete_from + = 1 ;
}
// While filtersNUMBER= is in the string of cookies add a response removing that cookie
while cookies_string . contains ( & format! ( " filters {filters_number_to_delete_from} = " ) ) {
// Remove that filters cookie
response . remove_cookie ( format! ( " filters {filters_number_to_delete_from} " ) ) ;
// Increment filters cookie number
filters_number_to_delete_from + = 1 ;
}
} else {
// Remove unnumbered filters cookie
response . remove_cookie ( " filters " . to_string ( ) ) ;
// Starts at one to deal with the first numbered subscription cookie and onwards
let mut filters_number_to_delete_from = 1 ;
// While filtersNUMBER= is in the string of cookies add a response removing that cookie
while cookies_string . contains ( & format! ( " filters {filters_number_to_delete_from} = " ) ) {
// Remove that sfilters cookie
response . remove_cookie ( format! ( " filters {filters_number_to_delete_from} " ) ) ;
// Increment filters cookie number
filters_number_to_delete_from + = 1 ;
}
}
2021-05-20 12:24:06 -07:00
response
2021-05-10 01:25:52 +00:00
}
// Set cookies using response "Set-Cookie" header
pub async fn restore ( req : Request < Body > ) -> Result < Response < Body > , String > {
Ok ( set_cookies_method ( req , true ) )
}
pub async fn update ( req : Request < Body > ) -> Result < Response < Body > , String > {
Ok ( set_cookies_method ( req , false ) )
2021-02-13 21:55:23 +01:00
}