diff --git a/Cargo.lock b/Cargo.lock index 58439fa..2c5da4d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,6 +100,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "bumpalo" version = "3.9.1" @@ -207,6 +216,15 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "cpufeatures" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +dependencies = [ + "libc", +] + [[package]] name = "darling" version = "0.13.4" @@ -242,6 +260,15 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "fastrand" version = "1.7.0" @@ -371,6 +398,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "h2" version = "0.3.13" @@ -424,9 +461,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", @@ -447,9 +484,9 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" [[package]] name = "hyper" -version = "0.14.18" +version = "0.14.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" dependencies = [ "bytes", "futures-channel", @@ -503,9 +540,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" dependencies = [ "autocfg", "hashbrown 0.11.2", @@ -522,9 +559,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "js-sys" @@ -543,13 +580,13 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libreddit" -version = "0.22.7" +version = "0.22.9" dependencies = [ "askama", "async-recursion", @@ -559,8 +596,10 @@ dependencies = [ "futures-lite", "hyper", "hyper-rustls", + "percent-encoding", "regex", "route-recognizer", + "rust-embed", "serde", "serde_json", "time", @@ -664,9 +703,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl-probe" @@ -676,9 +721,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "os_str_bytes" -version = "6.0.1" +version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" +checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" [[package]] name = "parking" @@ -729,11 +774,11 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "proc-macro2" -version = "1.0.38" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -756,9 +801,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", @@ -767,9 +812,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "ring" @@ -793,10 +838,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" [[package]] -name = "rustls" -version = "0.20.5" +name = "rust-embed" +version = "6.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a024a432ae760ab3bff924ad91ce1cfa52cb57ed16e1ef32d0d249cfee1a6c13" +checksum = "9a17e5ac65b318f397182ae94e532da0ba56b88dd1200b774715d36c4943b1c3" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e763e24ba2bf0c72bc6be883f967f794a019fafd1b86ba1daff9c91a7edd30" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756feca3afcbb1487a1d01f4ecd94cf8ec98ea074c55a69e7136d29fb6166029" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustls" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" dependencies = [ "log", "ring", @@ -827,18 +906,27 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", - "winapi", + "windows-sys", ] [[package]] @@ -911,6 +999,19 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -956,13 +1057,13 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1124,6 +1225,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "typenum" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" + [[package]] name = "unicase" version = "2.6.0" @@ -1139,6 +1246,12 @@ version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" + [[package]] name = "unicode-normalization" version = "0.1.19" @@ -1148,12 +1261,6 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-xid" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" - [[package]] name = "untrusted" version = "0.7.1" @@ -1184,6 +1291,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "want" version = "0.3.0" @@ -1290,6 +1408,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index b55c1d1..484962c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "libreddit" description = " Alternative private front-end to Reddit" license = "AGPL-3.0" repository = "https://github.com/spikecodes/libreddit" -version = "0.22.7" +version = "0.22.9" authors = ["spikecodes <19519553+spikecodes@users.noreply.github.com>"] edition = "2021" @@ -12,14 +12,16 @@ askama = { version = "0.11.1", default-features = false } async-recursion = "1.0.0" cached = "0.34.0" clap = { version = "3.1.18", default-features = false, features = ["std"] } -regex = "1.5.5" +regex = "1.5.6" serde = { version = "1.0.137", features = ["derive"] } cookie = "0.16.0" futures-lite = "1.12.0" -hyper = { version = "0.14.18", features = ["full"] } +hyper = { version = "0.14.19", features = ["full"] } hyper-rustls = "0.23.0" +percent-encoding = "2.1.0" route-recognizer = "0.3.1" serde_json = "1.0.81" tokio = { version = "1.18.2", features = ["full"] } time = "0.3.9" url = "2.2.2" +rust-embed = "6.4.0" diff --git a/README.md b/README.md index adfd157..08b624d 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,9 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new) | [libreddit.spike.codes](https://libreddit.spike.codes) (official) | 🇺🇸 US | | | [libreddit.dothq.co](https://libreddit.dothq.co) | 🇩🇪 DE | ✅ | | [libreddit.kavin.rocks](https://libreddit.kavin.rocks) | 🇮🇳 IN | | -| [libreddit.40two.app](https://libreddit.40two.app) | 🇳🇱 NL | | | [reddit.invak.id](https://reddit.invak.id) | 🇧🇬 BG | | | [reddit.phii.me](https://reddit.phii.me) | 🇺🇸 US | | | [lr.riverside.rocks](https://lr.riverside.rocks) | 🇺🇸 US | | -| [libreddit.silkky.cloud](https://libreddit.silkky.cloud) | 🇫🇮 FI | ✅ | | [libreddit.strongthany.cc](https://libreddit.strongthany.cc) | 🇺🇸 US | | | [libreddit.database.red](https://libreddit.database.red) | 🇺🇸 US | ✅ | | [libreddit.privacy.com.de](https://libreddit.privacy.com.de) | 🇩🇪 DE | | @@ -56,7 +54,6 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new) | [libreddit.some-things.org](https://libreddit.some-things.org) | 🇨🇭 CH | | | [reddit.stuehieyr.com](https://reddit.stuehieyr.com) | 🇩🇪 DE | | | [lr.mint.lgbt](https://lr.mint.lgbt) | 🇨🇦 CA | | -| [libreddit.alefvanoon.xyz](https://libreddit.alefvanoon.xyz) | 🇺🇸 US | ✅ | | [libreddit.igna.rocks](https://libreddit.igna.rocks) | 🇺🇸 US | | | [libreddit.autarkic.org](https://libreddit.autarkic.org) | 🇺🇸 US | | | [libreddit.flux.industries](https://libreddit.flux.industries) | 🇩🇪 DE | ✅ | @@ -72,7 +69,7 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new) | [libreddit.hu](https://libreddit.hu) | 🇫🇮 FI | ✅ | | [libreddit.totaldarkness.net](https://libreddit.totaldarkness.net) | 🇨🇦 CA | | | [libreddit.esmailelbob.xyz](https://libreddit.esmailelbob.xyz) | 🇨🇦 CA | | -| [lr.vern.cc](https://lr.vern.cc) | 🇵🇱 PL | | +| [lr.vern.cc](https://lr.vern.cc) | 🇨🇦 CA | | | [libreddit.nl](https://libreddit.nl) | 🇳🇱 NL | | | [lr.stilic.ml](https://lr.stilic.ml) | 🇫🇷 FR | ✅ | | [reddi.tk](https://reddi.tk) | 🇺🇸 US | ✅ | @@ -93,6 +90,9 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new) | [reddit.beparanoid.de](https://reddit.beparanoid.de) | 🇨🇭 CH | | | [libreddit.dcs0.hu](https://libreddit.dcs0.hu) | 🇭🇺 HU | | | [reddit.dr460nf1r3.org](https://reddit.dr460nf1r3.org) | 🇩🇪 DE | ✅ | +| [rd.jae.su](https://rd.jae.su) | 🇫🇮 FI | | +| [libreddit.mha.fi](https://libreddit.mha.fi) | 🇫🇮 FI | | +| [libreddit.foss.wtf](https://libreddit.foss.wtf) | 🇩🇪 DE | | | [spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion](http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion) | 🇮🇳 IN | | | [fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion](http://fwhhsbrbltmrct5hshrnqlqygqvcgmnek3cnka55zj4y7nuus5muwyyd.onion) | 🇩🇪 DE | | | [kphht2jcflojtqte4b4kyx7p2ahagv4debjj32nre67dxz7y57seqwyd.onion](http://kphht2jcflojtqte4b4kyx7p2ahagv4debjj32nre67dxz7y57seqwyd.onion) | 🇳🇱 NL | | @@ -106,7 +106,9 @@ Feel free to [open an issue](https://github.com/spikecodes/libreddit/issues/new) | [ol5begilptoou34emq2sshf3may3hlblvipdjtybbovpb7c7zodxmtqd.onion](http://ol5begilptoou34emq2sshf3may3hlblvipdjtybbovpb7c7zodxmtqd.onion) | 🇩🇪 DE | | | [lbrdtjaj7567ptdd4rv74lv27qhxfkraabnyphgcvptl64ijx2tijwid.onion](http://lbrdtjaj7567ptdd4rv74lv27qhxfkraabnyphgcvptl64ijx2tijwid.onion) | 🇨🇦 CA | | | [libreddit.lqs5fjmajyp7rvp4qvyubwofzi6d4imua7vs237rkc4m5qogitqwrgyd.onion](http://libreddit.lqs5fjmajyp7rvp4qvyubwofzi6d4imua7vs237rkc4m5qogitqwrgyd.onion) | 🇨🇦 CA | | -| [reddit.prnoid54e44a4bduq5due64jkk7wcnkxcp5kv3juncm7veptjcqudgyd.onion](reddit.prnoid54e44a4bduq5due64jkk7wcnkxcp5kv3juncm7veptjcqudgyd.onion) | 🇨🇭 CH | | +| [reddit.prnoid54e44a4bduq5due64jkk7wcnkxcp5kv3juncm7veptjcqudgyd.onion](http://reddit.prnoid54e44a4bduq5due64jkk7wcnkxcp5kv3juncm7veptjcqudgyd.onion) | 🇨🇭 CH | | +| [inz6tbezfwzexva6dize4cqraj2tjdhygxabmcgysccesvw2pybzhbyd.onion](http://inz6tbezfwzexva6dize4cqraj2tjdhygxabmcgysccesvw2pybzhbyd.onion) | 🇫🇮 FI | | +| [libreddit.micohauwkjbyw5meacrb4ipicwvwg4xtzl7y7viv53kig2mdcsvwkyyd.onion](http://libreddit.micohauwkjbyw5meacrb4ipicwvwg4xtzl7y7viv53kig2mdcsvwkyyd.onion/)| 🇫🇮 FI | | A checkmark in the "Cloudflare" category here refers to the use of the reverse proxy, [Cloudflare](https://cloudflare.com). The checkmark will not be listed for a site that uses Cloudflare DNS but rather the proxying service which grants Cloudflare the ability to monitor traffic to the website. @@ -267,8 +269,8 @@ Assign a default value for each setting by passing environment variables to Libr | `FRONT_PAGE` | `["default", "popular", "all"]` | `default` | | `LAYOUT` | `["card", "clean", "compact"]` | `card` | | `WIDE` | `["on", "off"]` | `off` | -| `COMMENT_SORT` | `["hot", "new", "top", "rising", "controversial"]` | `hot` | -| `POST_SORT` | `["confidence", "top", "new", "controversial", "old"]` | `confidence` | +| `POST_SORT` | `["hot", "new", "top", "rising", "controversial"]` | `hot` | +| `COMMENT_SORT` | `["confidence", "top", "new", "controversial", "old"]` | `confidence` | | `SHOW_NSFW` | `["on", "off"]` | `off` | | `USE_HLS` | `["on", "off"]` | `off` | | `HIDE_HLS_NOTIFICATION` | `["on", "off"]` | `off` | diff --git a/contrib/libreddit.conf b/contrib/libreddit.conf index 18b8193..dd91e3b 100644 --- a/contrib/libreddit.conf +++ b/contrib/libreddit.conf @@ -1,2 +1,2 @@ -ADDRESS=localhost +ADDRESS=0.0.0.0 PORT=12345 diff --git a/contrib/libreddit.service b/contrib/libreddit.service index b6e6fef..8ed5da7 100644 --- a/contrib/libreddit.service +++ b/contrib/libreddit.service @@ -11,5 +11,27 @@ Environment=PORT=8080 EnvironmentFile=-/etc/libreddit.conf ExecStart=/usr/bin/libreddit -a ${ADDRESS} -p ${PORT} +# Hardening +DeviceAllow= +LockPersonality=yes +MemoryDenyWriteExecute=yes +PrivateDevices=yes +ProcSubset=pid +ProtectClock=yes +ProtectControlGroups=yes +ProtectHome=yes +ProtectHostname=yes +ProtectKernelLogs=yes +ProtectKernelModules=yes +ProtectKernelTunables=yes +ProtectProc=invisible +RestrictAddressFamilies=AF_INET AF_INET6 +RestrictNamespaces=yes +RestrictRealtime=yes +RestrictSUIDSGID=yes +SystemCallArchitectures=native +SystemCallFilter=@system-service ~@privileged ~@resources +UMask=0077 + [Install] WantedBy=default.target diff --git a/src/client.rs b/src/client.rs index 864a6c6..da271dd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,6 +1,7 @@ use cached::proc_macro::cached; use futures_lite::{future::Boxed, FutureExt}; use hyper::{body::Buf, client, Body, Request, Response, Uri}; +use percent_encoding::{percent_encode, CONTROLS}; use serde_json::Value; use std::result::Result; @@ -90,7 +91,7 @@ fn request(url: String, quarantine: bool) -> Boxed, String .headers() .get("Location") .map(|val| { - let new_url = val.to_str().unwrap_or_default(); + let new_url = percent_encode(val.as_bytes(), CONTROLS).to_string(); format!("{}{}raw_json=1", new_url, if new_url.contains('?') { "&" } else { "?" }) }) .unwrap_or_default() diff --git a/src/main.rs b/src/main.rs index 7efa558..b6f7889 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ mod user; mod utils; // Import Crates -use clap::{Command, Arg}; +use clap::{Arg, Command}; use futures_lite::FutureExt; use hyper::{header::HeaderValue, Body, Request, Response}; @@ -19,7 +19,7 @@ use hyper::{header::HeaderValue, Body, Request, Response}; mod client; use client::proxy; use server::RequestExt; -use utils::{error, redirect}; +use utils::{error, redirect, ThemeAssets}; mod server; @@ -85,6 +85,23 @@ async fn resource(body: &str, content_type: &str, cache: bool) -> Result Result, String> { + let mut res = include_str!("../static/style.css").to_string(); + for file in ThemeAssets::iter() { + res.push('\n'); + let theme = ThemeAssets::get(file.as_ref()).unwrap(); + res.push_str(std::str::from_utf8(theme.data.as_ref()).unwrap()); + } + Ok( + Response::builder() + .status(200) + .header("content-type", "text/css") + .header("Cache-Control", "public, max-age=1209600, s-maxage=86400") + .body(res.to_string().into()) + .unwrap_or_default(), + ) +} + #[tokio::main] async fn main() { let matches = Command::new("Libreddit") @@ -152,7 +169,7 @@ async fn main() { } // Read static files - app.at("/style.css").get(|_| resource(include_str!("../static/style.css"), "text/css", false).boxed()); + app.at("/style.css").get(|_| style().boxed()); app .at("/manifest.json") .get(|_| resource(include_str!("../static/manifest.json"), "application/json", false).boxed()); diff --git a/src/post.rs b/src/post.rs index 2ebc313..02ca380 100644 --- a/src/post.rs +++ b/src/post.rs @@ -1,6 +1,5 @@ // CRATES use crate::client::json; -use crate::esc; use crate::server::RequestExt; use crate::subreddit::{can_access_quarantine, quarantine}; use crate::utils::{ @@ -13,7 +12,7 @@ use std::collections::HashSet; // STRUCTS #[derive(Template)] -#[template(path = "post.html", escape = "none")] +#[template(path = "post.html")] struct PostTemplate { comments: Vec, post: Post, @@ -100,7 +99,10 @@ async fn parse_post(json: &serde_json::Value) -> Post { let permalink = val(post, "permalink"); let body = if val(post, "removed_by_category") == "moderator" { - format!("

[removed] — view removed post

", permalink) + format!( + "

[removed] — view removed post

", + permalink + ) } else { rewrite_urls(&val(post, "selftext_html")) }; @@ -108,7 +110,7 @@ async fn parse_post(json: &serde_json::Value) -> Post { // Build a post using data parsed from Reddit post API Post { id: val(post, "id"), - title: esc!(post, "title"), + title: val(post, "title"), community: val(post, "subreddit"), body, author: Author { @@ -119,7 +121,7 @@ async fn parse_post(json: &serde_json::Value) -> Post { post["data"]["author_flair_richtext"].as_array(), post["data"]["author_flair_text"].as_str(), ), - text: esc!(post, "link_flair_text"), + text: val(post, "link_flair_text"), background_color: val(post, "author_flair_background_color"), foreground_color: val(post, "author_flair_text_color"), }, @@ -143,7 +145,7 @@ async fn parse_post(json: &serde_json::Value) -> Post { post["data"]["link_flair_richtext"].as_array(), post["data"]["link_flair_text"].as_str(), ), - text: esc!(post, "link_flair_text"), + text: val(post, "link_flair_text"), background_color: val(post, "link_flair_background_color"), foreground_color: if val(post, "link_flair_text_color") == "dark" { "black".to_string() @@ -199,7 +201,10 @@ fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str, let highlighted = id == highlighted_comment; let body = if val(&comment, "author") == "[deleted]" && val(&comment, "body") == "[removed]" { - format!("

[removed] — view removed comment

", post_link, id) + format!( + "

[removed] — view removed comment

", + post_link, id + ) } else { rewrite_urls(&val(&comment, "body_html")) }; @@ -212,7 +217,7 @@ fn parse_comments(json: &serde_json::Value, post_link: &str, post_author: &str, data["author_flair_richtext"].as_array(), data["author_flair_text"].as_str(), ), - text: esc!(&comment, "link_flair_text"), + text: val(&comment, "link_flair_text"), background_color: val(&comment, "author_flair_background_color"), foreground_color: val(&comment, "author_flair_text_color"), }, diff --git a/src/search.rs b/src/search.rs index d096bfe..4b13594 100644 --- a/src/search.rs +++ b/src/search.rs @@ -29,7 +29,7 @@ struct Subreddit { } #[derive(Template)] -#[template(path = "search.html", escape = "none")] +#[template(path = "search.html")] struct SearchTemplate { posts: Vec, subreddits: Vec, diff --git a/src/server.rs b/src/server.rs index ce20860..979dbd7 100644 --- a/src/server.rs +++ b/src/server.rs @@ -158,8 +158,8 @@ impl Server { Ok::<_, String>(service_fn(move |req: Request| { let headers = default_headers.clone(); - // Remove double slashes - let mut path = req.uri().path().replace("//", "/"); + // Remove double slashes and decode encoded slashes + let mut path = req.uri().path().replace("//", "/").replace("%2F","/"); // Remove trailing slashes if path != "/" && path.ends_with('/') { diff --git a/src/subreddit.rs b/src/subreddit.rs index ed4dde4..75e271a 100644 --- a/src/subreddit.rs +++ b/src/subreddit.rs @@ -1,5 +1,4 @@ // CRATES -use crate::esc; use crate::utils::{ catch_random, error, filter_posts, format_num, format_url, get_filters, param, redirect, rewrite_urls, setting, template, val, Post, Preferences, Subreddit, }; @@ -11,7 +10,7 @@ use time::{Duration, OffsetDateTime}; // STRUCTS #[derive(Template)] -#[template(path = "subreddit.html", escape = "none")] +#[template(path = "subreddit.html")] struct SubredditTemplate { sub: Subreddit, posts: Vec, @@ -30,7 +29,7 @@ struct SubredditTemplate { } #[derive(Template)] -#[template(path = "wiki.html", escape = "none")] +#[template(path = "wiki.html")] struct WikiTemplate { sub: String, wiki: String, @@ -40,7 +39,7 @@ struct WikiTemplate { } #[derive(Template)] -#[template(path = "wall.html", escape = "none")] +#[template(path = "wall.html")] struct WallTemplate { title: String, sub: String, @@ -412,9 +411,9 @@ async fn subreddit(sub: &str, quarantined: bool) -> Result { let icon = if community_icon.is_empty() { val(&res, "icon_img") } else { community_icon.to_string() }; Ok(Subreddit { - name: esc!(&res, "display_name"), - title: esc!(&res, "title"), - description: esc!(&res, "public_description"), + name: val(&res, "display_name"), + title: val(&res, "title"), + description: val(&res, "public_description"), info: rewrite_urls(&val(&res, "description_html")), // moderators: moderators_list(sub, quarantined).await.unwrap_or_default(), icon: format_url(&icon), diff --git a/src/user.rs b/src/user.rs index 25d4d86..0d03f2e 100644 --- a/src/user.rs +++ b/src/user.rs @@ -1,6 +1,5 @@ // CRATES use crate::client::json; -use crate::esc; use crate::server::RequestExt; use crate::utils::{error, filter_posts, format_url, get_filters, param, setting, template, Post, Preferences, User}; use askama::Template; @@ -9,7 +8,7 @@ use time::{macros::format_description, OffsetDateTime}; // STRUCTS #[derive(Template)] -#[template(path = "user.html", escape = "none")] +#[template(path = "user.html")] struct UserTemplate { user: User, posts: Vec, @@ -106,11 +105,11 @@ async fn user(name: &str) -> Result { // Parse the JSON output into a User struct User { name: res["data"]["name"].as_str().unwrap_or(name).to_owned(), - title: esc!(about("title")), + title: about("title"), icon: format_url(&about("icon_img")), karma: res["data"]["total_karma"].as_i64().unwrap_or(0), created: created.format(format_description!("[month repr:short] [day] '[year repr:last_two]")).unwrap_or_default(), - banner: esc!(about("banner_img")), + banner: about("banner_img"), description: about("public_description"), } }) diff --git a/src/utils.rs b/src/utils.rs index 56a7de7..2691d16 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,15 +1,16 @@ // // CRATES // -use crate::{client::json, esc, server::RequestExt}; +use crate::{client::json, server::RequestExt}; use askama::Template; use cookie::Cookie; use hyper::{Body, Request, Response}; use regex::Regex; +use rust_embed::RustEmbed; use serde_json::Value; use std::collections::{HashMap, HashSet}; use std::str::FromStr; -use time::{Duration, OffsetDateTime, macros::format_description}; +use time::{macros::format_description, Duration, OffsetDateTime}; use url::Url; // Post flair with content, background color and foreground color @@ -41,7 +42,7 @@ impl FlairPart { Self { flair_part_type: value("e").to_string(), value: match value("e") { - "text" => esc!(value("t")), + "text" => value("t").to_string(), "emoji" => format_url(value("u")), _ => String::new(), }, @@ -54,7 +55,7 @@ impl FlairPart { "text" => match text_flair { Some(text) => vec![Self { flair_part_type: "text".to_string(), - value: esc!(text), + value: text.to_string(), }], None => Vec::new(), }, @@ -217,24 +218,19 @@ pub struct Post { impl Post { // Fetch posts of a user or subreddit and return a vector of posts and the "after" value pub async fn fetch(path: &str, quarantine: bool) -> Result<(Vec, String), String> { - let res; - let post_list; - // Send a request to the url - match json(path.to_string(), quarantine).await { + let res = match json(path.to_string(), quarantine).await { // If success, receive JSON in response - Ok(response) => { - res = response; - } + Ok(response) => response, // If the Reddit API returns an error, exit this function Err(msg) => return Err(msg), - } + }; // Fetch the list of posts from the JSON response - match res["data"]["children"].as_array() { - Some(list) => post_list = list, + let post_list = match res["data"]["children"].as_array() { + Some(list) => list, None => return Err("No posts found".to_string()), - } + }; let mut posts: Vec = Vec::new(); @@ -245,7 +241,7 @@ impl Post { let (rel_time, created) = time(data["created_utc"].as_f64().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 title = esc!(post, "title"); + let title = val(post, "title"); // Determine the type of media along with the media URL let (post_type, media, gallery) = Media::parse(data).await; @@ -270,7 +266,7 @@ impl Post { data["author_flair_richtext"].as_array(), data["author_flair_text"].as_str(), ), - text: esc!(post, "link_flair_text"), + text: val(post, "link_flair_text"), background_color: val(post, "author_flair_background_color"), foreground_color: val(post, "author_flair_text_color"), }, @@ -298,7 +294,7 @@ impl Post { data["link_flair_richtext"].as_array(), data["link_flair_text"].as_str(), ), - text: esc!(post, "link_flair_text"), + text: val(post, "link_flair_text"), background_color: val(post, "link_flair_background_color"), foreground_color: if val(post, "link_flair_text_color") == "dark" { "black".to_string() @@ -324,7 +320,7 @@ impl Post { } #[derive(Template)] -#[template(path = "comment.html", escape = "none")] +#[template(path = "comment.html")] // Comment with content, post, score and data/time that it was posted pub struct Comment { pub id: String, @@ -400,7 +396,7 @@ impl Awards { } #[derive(Template)] -#[template(path = "error.html", escape = "none")] +#[template(path = "error.html")] pub struct ErrorTemplate { pub msg: String, pub prefs: Preferences, @@ -445,6 +441,7 @@ pub struct Params { #[derive(Default)] pub struct Preferences { + pub available_themes: Vec, pub theme: String, pub front_page: String, pub layout: String, @@ -459,10 +456,23 @@ pub struct Preferences { pub filters: Vec, } +#[derive(RustEmbed)] +#[folder = "static/themes/"] +#[include = "*.css"] +pub struct ThemeAssets; + impl Preferences { // Build preferences from cookies pub fn new(req: Request) -> Self { + // Read available theme names from embedded css files. + // Always make the default "system" theme available. + let mut themes = vec!["system".to_string()]; + for file in ThemeAssets::iter() { + let chunks: Vec<&str> = file.as_ref().split(".css").collect(); + themes.push(chunks[0].to_owned()) + } Self { + available_themes: themes, theme: setting(&req, "theme"), front_page: setting(&req, "front_page"), layout: setting(&req, "layout"), @@ -607,12 +617,11 @@ pub fn format_url(url: &str) -> String { // Rewrite Reddit links to Libreddit in body of text pub fn rewrite_urls(input_text: &str) -> String { - - let text1 = - Regex::new(r#"href="(https|http|)://(www\.|old\.|np\.|amp\.|)(reddit\.com|redd\.it)/"#) - .map_or(String::new(), |re| re.replace_all(input_text, r#"href="/"#).to_string()) - // Remove (html-encoded) "\" from URLs. - .replace("%5C", "").replace(r"\", ""); + let text1 = Regex::new(r#"href="(https|http|)://(www\.|old\.|np\.|amp\.|)(reddit\.com|redd\.it)/"#) + .map_or(String::new(), |re| re.replace_all(input_text, r#"href="/"#).to_string()) + // Remove (html-encoded) "\" from URLs. + .replace("%5C", "") + .replace('\\', ""); // Rewrite external media previews to Libreddit Regex::new(r"https://external-preview\.redd\.it(.*)[^?]").map_or(String::new(), |re| { @@ -656,7 +665,12 @@ pub fn time(created: f64) -> (String, String) { format!("{}m ago", time_delta.whole_minutes()) }; - (rel_time, time.format(format_description!("[month repr:short] [day] [year], [hour]:[minute]:[second] UTC")).unwrap_or_default()) + ( + rel_time, + time + .format(format_description!("[month repr:short] [day] [year], [hour]:[minute]:[second] UTC")) + .unwrap_or_default(), + ) } // val() function used to parse JSON from Reddit APIs @@ -664,17 +678,6 @@ pub fn val(j: &Value, k: &str) -> String { j["data"][k].as_str().unwrap_or_default().to_string() } -// Escape < and > to accurately render HTML -#[macro_export] -macro_rules! esc { - ($f:expr) => { - $f.replace('&', "&").replace('<', "<").replace('>', ">") - }; - ($j:expr, $k:expr) => { - $j["data"][$k].as_str().unwrap_or_default().to_string().replace('<', "<").replace('>', ">") - }; -} - // // NETWORKING // @@ -727,7 +730,8 @@ mod tests { #[test] fn rewrite_urls_removes_backslashes() { - let comment_body_html = r#"https://www.reddit.com/r/linux\\_gaming/comments/x/just\\_a\\_test/"#; + let comment_body_html = + r#"https://www.reddit.com/r/linux\\_gaming/comments/x/just\\_a\\_test/"#; assert_eq!( rewrite_urls(comment_body_html), r#"https://www.reddit.com/r/linux_gaming/comments/x/just_a_test/"# diff --git a/static/style.css b/static/style.css index dc2a6de..3e0d64d 100644 --- a/static/style.css +++ b/static/style.css @@ -45,124 +45,7 @@ } } -/* Light theme setting */ -.light { - --accent: #009a9a; - --green: #00a229; - --text: black; - --foreground: #f5f5f5; - --background: #ddd; - --outside: #ececec; - --post: #eee; - --panel-border: 1px solid #ccc; - --highlighted: white; - --visited: #555; - --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} - -/* Black theme setting */ -.black { - --accent: #009a9a; - --green: #00a229; - --text: white; - --foreground: #0f0f0f; - --background: black; - --outside: black; - --post: black; - --panel-border: 2px solid #0f0f0f; - --highlighted: #0f0f0f; - --visited: #aaa; - --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} - -/* Dracula theme setting */ -.dracula { - --accent: #bd93f9; - --green: #50fa7b; - --text: #f8f8f2; - --foreground: #3d4051; - --background: #282a36; - --outside: #393c4d; - --post: #333544; - --panel-border: 2px solid #44475a; - --highlighted: #4e5267; - --visited: #969692; - --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} - -/* Nord theme setting */ -.nord { - --accent: #8fbcbb; - --green: #a3be8c; - --text: #eceff4; - --foreground: #3b4252; - --background: #2e3440; - --outside: #434c5e; - --post: #434c5e; - --panel-border: 2px solid #4c566a; - --highlighted: #3b4252; - --visited: #a3a5aa; - --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} - -/* Laserwave theme setting */ -.laserwave { - --accent: #eb64b9; - --green: #74dfc4; - --text: #e0dfe1; - --foreground: #302a36; - --background: #27212e; - --outside: #3e3647; - --post: #3e3647; - --panel-border: 2px solid #2f2738; - --highlighted: #302a36; - --visited: #91889b; - --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); -} - -/* Violet theme setting */ -.violet { - --accent: #7c71dd; - --green: #5cff85; - --text: white; - --foreground: #1F2347; - --background: #12152b; - --outside: #181c3a; - --post: #181c3a; - --panel-border: 1px solid #1F2347; - --highlighted: #1F2347; - --visited: #aaa; - --shadow: 0 2px 5px rgba(0, 0, 0, 0.5); -} - -/* Gold theme setting */ -.gold { - --accent: #f2aa4c; - --green: #5cff85; - --text: white; - --foreground: #234; - --background: #101820; - --outside: #1b2936; - --post: #1b2936; - --panel-border: 0px solid black; - --highlighted: #234; - --visited: #aaa; - --shadow: 0 2px 5px rgba(0, 0, 0, 0.5); -} - -/* Rosebox theme setting */ -.rosebox { - --accent: #a57562; - --green: #a3be8c; - --text: white; - --foreground: #222; - --background: #262626; - --outside: #222; - --post: #222; - --panel-border: 1px solid #222; - --highlighted: #262626; - --shadow: 0 1px 3px rgba(0, 0, 0, 0.5); -} +/* Other themes are located in the "themes" folder */ /* General */ @@ -894,6 +777,7 @@ a.search_subreddit:hover { padding: 5px 15px 5px 12px; grid-area: post_body; width: calc(100% - 30px); + overflow-wrap: anywhere; } /* Used only for text post preview */ diff --git a/static/themes/black.css b/static/themes/black.css new file mode 100644 index 0000000..5906b5b --- /dev/null +++ b/static/themes/black.css @@ -0,0 +1,14 @@ +/* Black theme setting */ +.black { + --accent: #009a9a; + --green: #00a229; + --text: white; + --foreground: #0f0f0f; + --background: black; + --outside: black; + --post: black; + --panel-border: 2px solid #0f0f0f; + --highlighted: #0f0f0f; + --visited: #aaa; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} \ No newline at end of file diff --git a/static/themes/dark.css b/static/themes/dark.css new file mode 100644 index 0000000..ef7b32c --- /dev/null +++ b/static/themes/dark.css @@ -0,0 +1,14 @@ +/* Dark theme setting */ +.dark{ + --accent: aqua; + --green: #5cff85; + --text: white; + --foreground: #222; + --background: #0f0f0f; + --outside: #1f1f1f; + --post: #161616; + --panel-border: 1px solid #333; + --highlighted: #333; + --visited: #aaa; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.5); +} \ No newline at end of file diff --git a/static/themes/dracula.css b/static/themes/dracula.css new file mode 100644 index 0000000..0e36e2b --- /dev/null +++ b/static/themes/dracula.css @@ -0,0 +1,14 @@ +/* Dracula theme setting */ +.dracula { + --accent: #bd93f9; + --green: #50fa7b; + --text: #f8f8f2; + --foreground: #3d4051; + --background: #282a36; + --outside: #393c4d; + --post: #333544; + --panel-border: 2px solid #44475a; + --highlighted: #4e5267; + --visited: #969692; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} \ No newline at end of file diff --git a/static/themes/gold.css b/static/themes/gold.css new file mode 100644 index 0000000..4ff7d6e --- /dev/null +++ b/static/themes/gold.css @@ -0,0 +1,14 @@ +/* Gold theme setting */ +.gold { + --accent: #f2aa4c; + --green: #5cff85; + --text: white; + --foreground: #234; + --background: #101820; + --outside: #1b2936; + --post: #1b2936; + --panel-border: 0px solid black; + --highlighted: #234; + --visited: #aaa; + --shadow: 0 2px 5px rgba(0, 0, 0, 0.5); +} \ No newline at end of file diff --git a/static/themes/laserwave.css b/static/themes/laserwave.css new file mode 100644 index 0000000..dde21f3 --- /dev/null +++ b/static/themes/laserwave.css @@ -0,0 +1,14 @@ +/* Laserwave theme setting */ +.laserwave { + --accent: #eb64b9; + --green: #74dfc4; + --text: #e0dfe1; + --foreground: #302a36; + --background: #27212e; + --outside: #3e3647; + --post: #3e3647; + --panel-border: 2px solid #2f2738; + --highlighted: #302a36; + --visited: #91889b; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} \ No newline at end of file diff --git a/static/themes/light.css b/static/themes/light.css new file mode 100644 index 0000000..1fe0387 --- /dev/null +++ b/static/themes/light.css @@ -0,0 +1,14 @@ +/* Light theme setting */ +.light { + --accent: #009a9a; + --green: #00a229; + --text: black; + --foreground: #f5f5f5; + --background: #ddd; + --outside: #ececec; + --post: #eee; + --panel-border: 1px solid #ccc; + --highlighted: white; + --visited: #555; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} \ No newline at end of file diff --git a/static/themes/nord.css b/static/themes/nord.css new file mode 100644 index 0000000..d75fd11 --- /dev/null +++ b/static/themes/nord.css @@ -0,0 +1,14 @@ +/* Nord theme setting */ +.nord { + --accent: #8fbcbb; + --green: #a3be8c; + --text: #eceff4; + --foreground: #3b4252; + --background: #2e3440; + --outside: #434c5e; + --post: #434c5e; + --panel-border: 2px solid #4c566a; + --highlighted: #3b4252; + --visited: #a3a5aa; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} diff --git a/static/themes/rosebox.css b/static/themes/rosebox.css new file mode 100644 index 0000000..903931d --- /dev/null +++ b/static/themes/rosebox.css @@ -0,0 +1,13 @@ +/* Rosebox theme setting */ +.rosebox { + --accent: #a57562; + --green: #a3be8c; + --text: white; + --foreground: #222; + --background: #262626; + --outside: #222; + --post: #222; + --panel-border: 1px solid #222; + --highlighted: #262626; + --shadow: 0 1px 3px rgba(0, 0, 0, 0.5); +} \ No newline at end of file diff --git a/static/themes/violet.css b/static/themes/violet.css new file mode 100644 index 0000000..0d836ee --- /dev/null +++ b/static/themes/violet.css @@ -0,0 +1,14 @@ +/* Violet theme setting */ +.violet { + --accent: #7c71dd; + --green: #5cff85; + --text: white; + --foreground: #1F2347; + --background: #12152b; + --outside: #181c3a; + --post: #181c3a; + --panel-border: 1px solid #1F2347; + --highlighted: #1F2347; + --visited: #aaa; + --shadow: 0 2px 5px rgba(0, 0, 0, 0.5); +} \ No newline at end of file diff --git a/templates/comment.html b/templates/comment.html index 3014192..61c44f5 100644 --- a/templates/comment.html +++ b/templates/comment.html @@ -32,9 +32,9 @@ {% if is_filtered %}
(Filtered content)
{% else %} -
{{ body }}
+
{{ body|safe }}
{% endif %} -
{% for c in replies -%}{{ c.render().unwrap() }}{%- endfor %} +
{% for c in replies -%}{{ c.render().unwrap()|safe }}{%- endfor %}
diff --git a/templates/post.html b/templates/post.html index add5881..227d7fc 100644 --- a/templates/post.html +++ b/templates/post.html @@ -55,7 +55,7 @@ {% endif %}

-

+

{{ post.title }} {% if post.flair.flair_parts.len() > 0 %} {% call utils::render_flair(post.flair.flair_parts) %} {% endif %} {% if post.flags.nsfw %} NSFW{% endif %} -

+

@@ -110,7 +110,7 @@ {% endif %} -
{{ post.body }}
+
{{ post.body|safe }}
{{ post.score.0 }} Upvotes
{%- endfor %} diff --git a/templates/search.html b/templates/search.html index 4b04717..b53e279 100644 --- a/templates/search.html +++ b/templates/search.html @@ -39,7 +39,7 @@ {% endif %} {% for subreddit in subreddits %} -
{% if subreddit.icon != "" %}r/{{ subreddit.name }} icon{% endif %}
+
{% if subreddit.icon != "" %}r/{{ subreddit.name }} icon{% endif %}

r/{{ subreddit.name }} @@ -97,13 +97,13 @@ {% if params.before != "" %} PREV + &before={{ params.before }}" accesskey="P">PREV {% endif %} {% if params.after != "" %} NEXT + &after={{ params.after }}" accesskey="N">NEXT {% endif %} {% endif %} diff --git a/templates/settings.html b/templates/settings.html index 8a70912..60ee109 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -15,7 +15,7 @@

Interface @@ -110,7 +110,7 @@

Note: settings and subscriptions are saved in browser cookies. Clearing your cookies will reset them.


-

You can restore your current settings and subscriptions after clearing your cookies using this link.

+

You can restore your current settings and subscriptions after clearing your cookies using this link.

diff --git a/templates/subreddit.html b/templates/subreddit.html index 1a5e43e..4fdad65 100644 --- a/templates/subreddit.html +++ b/templates/subreddit.html @@ -69,11 +69,11 @@
{% if !ends.0.is_empty() %} - PREV + PREV {% endif %} {% if !ends.1.is_empty() %} - NEXT + NEXT {% endif %}
@@ -93,7 +93,7 @@ {% endif %}
Icon for r/{{ sub.name }} -

{{ sub.title }}

+

{{ sub.title }}

r/{{ sub.name }}

{{ sub.description }}

@@ -131,7 +131,7 @@
{% endif %} @@ -70,11 +70,11 @@
{% if ends.0 != "" %} - PREV + PREV {% endif %} {% if ends.1 != "" %} - NEXT + NEXT {% endif %}
@@ -85,7 +85,7 @@ {% endif %}
User icon -

{{ user.title }}

+

{{ user.title }}

u/{{ user.name }}

{{ user.description }}
diff --git a/templates/utils.html b/templates/utils.html index a11583f..07e1ce8 100644 --- a/templates/utils.html +++ b/templates/utils.html @@ -38,7 +38,6 @@ {%- endmacro %} {% macro sub_list(current) -%} -{% if prefs.subscriptions.len() > 0 %}
Feeds
@@ -46,13 +45,14 @@ Home Popular All -

REDDIT FEEDS

- {% for sub in prefs.subscriptions %} - {{ sub }} - {% endfor %} + {% if prefs.subscriptions.len() > 0 %} +

REDDIT FEEDS

+ {% for sub in prefs.subscriptions %} + {{ sub }} + {% endfor %} + {% endif %}
-{% endif %} {%- endmacro %} {% macro render_hls_notification(redirect_url) -%} @@ -83,7 +83,7 @@ {% endfor %} {% endif %}

-

+

{% if post.flair.flair_parts.len() > 0 %} {% call render_flair(post.flair.flair_parts) %} {% endif %} {{ post.title }}{% if post.flags.nsfw %} NSFW{% endif %} -

+

{% if (prefs.layout.is_empty() || prefs.layout == "card") && post.post_type == "image" %} @@ -138,7 +138,7 @@
{{ post.score.0 }} Upvotes
- {{ post.body }} + {{ post.body|safe }}
- {{ wiki }} + {{ wiki|safe }}
-{% endblock %} \ No newline at end of file +{% endblock %}