diff --git a/Cargo.lock b/Cargo.lock index 3c603262..cb86b4ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,10 +1,14 @@ [[package]] -name = "activitystreams" +name = "activitypub" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "activitystreams-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "activitystreams-traits 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "activitystreams-types 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "activitystreams-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -29,7 +33,7 @@ dependencies = [ [[package]] name = "activitystreams-types" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "activitystreams-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -54,6 +58,27 @@ dependencies = [ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ammonia" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "antidote" version = "1.0.0" @@ -72,6 +97,16 @@ dependencies = [ "nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "atty" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "backtrace" version = "0.3.6" @@ -196,6 +231,36 @@ dependencies = [ "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "clap" +version = "2.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "comrak" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", + "entities 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "pest_derive 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "typed-arena 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cookie" version = "0.9.2" @@ -350,6 +415,11 @@ dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "entities" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "error-chain" version = "0.10.0" @@ -412,6 +482,15 @@ name = "fuchsia-zircon-sys" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "futf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "futures" version = "0.1.21" @@ -457,6 +536,19 @@ name = "hex" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "html5ever" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "markup5ever 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "httparse" version = "1.2.4" @@ -623,6 +715,31 @@ dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "maplit" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "markup5ever" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "matches" version = "0.1.6" @@ -729,6 +846,14 @@ dependencies = [ "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nodrop" version = "0.1.12" @@ -824,6 +949,21 @@ name = "pest" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "pest" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "pest_derive" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "phf" version = "0.7.22" @@ -868,14 +1008,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "plume" version = "0.1.0" dependencies = [ - "activitystreams 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "activitystreams-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "activitystreams-traits 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "activitystreams-types 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "activitypub 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ammonia 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "array_tool 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "bcrypt 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "comrak 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "diesel 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -903,6 +1042,11 @@ dependencies = [ "vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "proc-macro2" version = "0.2.3" @@ -995,6 +1139,14 @@ name = "redox_syscall" version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "regex" version = "0.2.10" @@ -1256,6 +1408,42 @@ name = "state" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "string_cache" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "string_cache_codegen" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "string_cache_shared" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "syn" version = "0.11.11" @@ -1325,6 +1513,16 @@ dependencies = [ "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tendril" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "utf-8 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tera" version = "0.10.10" @@ -1343,6 +1541,24 @@ dependencies = [ "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "thread_local" version = "0.3.5" @@ -1524,11 +1740,24 @@ name = "traitobject" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "twoway" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typeable" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "typed-arena" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "typenum" version = "1.10.0" @@ -1573,6 +1802,11 @@ name = "unicode-segmentation" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-xid" version = "0.0.4" @@ -1583,6 +1817,11 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unidecode" version = "0.3.0" @@ -1611,6 +1850,11 @@ dependencies = [ "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "utf-8" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "utf8-ranges" version = "1.0.0" @@ -1629,6 +1873,11 @@ name = "vcpkg" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "vec_map" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "version_check" version = "0.1.3" @@ -1683,15 +1932,18 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] -"checksum activitystreams 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "638541e5169c839f6581302c50e38876312389475cd911ecc7c446c7491004cc" +"checksum activitypub 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab73fd715ad1e74fb44e4b2356db91a158793a2472a0c19e9002ab3184773d87" "checksum activitystreams-derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48db826c588a009960d74530e7c215e21fae130f585362504dc6b6357e5ce86b" "checksum activitystreams-traits 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "670ef03168e704b0cae242e7a5d8b40506772b339687e01a3496fc4afe2e8542" -"checksum activitystreams-types 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aff9aa0c3412fe4da72a1f6e4b1c2e9792bfdf1308b709389192f17aa8e2b3cd" +"checksum activitystreams-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "14806b3c88c439e1670fdc99d9b18bf1a47d4fa7152fe8a3bd7da08b6ced3e95" "checksum adler32 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6cbd0b9af8587c72beadc9f72d35b9fbb070982c9e6203e46e93f10df25f8f45" "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" +"checksum ammonia 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd4c682378117e4186a492b2252b9537990e1617f44aed9788b9a1149de45477" +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum antidote 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34fde25430d87a9388dadbe6e34d7f72a462c8b43ac8d309b42b0a8505d7e2a5" "checksum array_tool 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" +"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" "checksum backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe525f66f42d207968308ee86bc2dd60aa5fab535b22e616323a173d097d8e" "checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" "checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9" @@ -1708,6 +1960,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8b9d2900f78631a5876dc5d6c9033ede027253efcd33dd36b1309fc6cab97ee0" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum chrono 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce36c92cb605414e9b824f866f5babe0a0368e39ea07393b9b63cf3844c0e6" +"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" +"checksum comrak 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "053b26c8ce23b4c505a9479beace98f95899e0bf5c5255cf0219e9b0f48cf6ea" "checksum cookie 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "477eb650753e319be2ae77ec368a58c638f9f0c4d941c39bad95e950fb1d1d0d" "checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67" "checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d" @@ -1724,6 +1978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum dotenv 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a70de3c590ce18df70743cace1cf12565637a0b26fd8b04ef10c7d33fdc66cdc" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" "checksum encoding_rs 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "98fd0f24d1fb71a4a6b9330c8ca04cbd4e7cc5d846b54ca74ff376bc7c9f798d" +"checksum entities 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" "checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" @@ -1732,6 +1987,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futf 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" "checksum futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "1a70b146671de62ec8c8ed572219ca5d594d9b06c0b364d5e67b722fc559b48c" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" @@ -1739,6 +1995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b04478cf718862650a0bf66acaf8f2f8c906fbc703f35c916c1f4211b069a364" "checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" "checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2" @@ -1758,6 +2015,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum libflate 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1a429b86418868c7ea91ee50e9170683f47fd9d94f5375438ec86ec3adb74e8e" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" +"checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +"checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" +"checksum markup5ever 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfedc97d5a503e96816d10fedcd5b42f760b2e525ce2f7ec71f6a41780548475" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" @@ -1769,6 +2029,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum native-tls 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f74dbadc8b43df7864539cedb7bc91345e532fdd913cfdc23ad94f4d2d40fbc0" "checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" +"checksum new_debug_unreachable 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cdc457076c78ab54d5e0d6fa7c47981757f1e34dc39ff92787f217dede586c4" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" "checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" "checksum num-traits 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dee092fcdf725aee04dd7da1d21debff559237d49ef1cb3e69bcb8ece44c7364" @@ -1782,12 +2043,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pear_codegen 0.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ca34109829349aeefe22772916da5404b3f5cd0e63a72c5d91209fc809342265" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pest 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3e2e823a5967bb4cdc6d3e46f47baaf4ecfeae44413a642b74ad44e59e49c7f6" +"checksum pest 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0fce5d8b5cc33983fc74f78ad552b5522ab41442c4ca91606e4236eb4b5ceefc" +"checksum pest_derive 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ab94faafeb93f4c5e3ce81ca0e5a779529a602ad5d09ae6d21996bfb8b6a52bf" "checksum phf 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "7d37a244c75a9748e049225155f56dbcb98fe71b192fd25fd23cb914b5ad62f2" "checksum phf_codegen 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "4e4048fe7dd7a06b8127ecd6d3803149126e9b33c7558879846da3a63f734f2b" "checksum phf_generator 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "05a079dd052e7b674d21cb31cbb6c05efd56a2cd2827db7692e2f1a507ebd998" "checksum phf_shared 0.7.22 (registry+https://github.com/rust-lang/crates.io-index)" = "c2261d544c2bb6aa3b10022b0be371b9c7c64f762ef28c6f5d4f1ef6d97b5930" "checksum pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "110d5ee3593dbb73f56294327fe5668bcc997897097cbc76b51e7aed3f52452f" "checksum pq-sys 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4dfb5e575ef93a1b7b2a381d47ba7c5d4e4f73bff37cee932195de769aad9a54" +"checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" "checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0" "checksum proc-macro2 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "49b6a521dc81b643e9a51e0d1cf05df46d5a2f3c0280ea72bcb68276ba64a118" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" @@ -1799,6 +2063,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a77c51c07654ddd93f6cb543c7a849863b03abc7e82591afda6dc8ad4ac3ac4a" "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "aec3f58d903a7d2a9dc2bf0e41a746f4530e0cab6b615494e058f67a3ef947fb" "checksum regex-syntax 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bd90079345f4a4c3409214734ae220fd773c6f2e8a543d07370c6c1c369cfbfb" "checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" @@ -1828,6 +2093,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum smallvec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4f357e8cd37bf8822e1b964e96fd39e2cb5a0424f8aaa284ccaccc2162411c" "checksum state 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5562ac59585fe3d9a1ccf6b4e298ce773f5063db80d59f783776b410c1714c2" +"checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" +"checksum string_cache_codegen 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "35293b05cf1494e8ddd042a7df6756bf18d07f42d234f32e71dce8a7aabb0191" +"checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" +"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5" "checksum syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "91b52877572087400e83d24b9178488541e3d535259e04ff17a63df1e5ceff59" @@ -1836,7 +2105,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum syntex_fmt_macros 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5386bdc48758d136af85b3880548e1f3a9fad8d7dc2b38bdb48c36a9cdefc0" "checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +"checksum tendril 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9de21546595a0873061940d994bbbc5c35f024ae4fd61ec5c5b159115684f508" "checksum tera 0.10.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d706c3bec8103f346fc7b8a3887a2ff4195cf704bdbc6307069f32ea8a2b3af5" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" "checksum tokio 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "be15ef40f675c9fe66e354d74c73f3ed012ca1aa14d65846a33ee48f1ae8d922" @@ -1853,7 +2125,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum tokio-udp 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "137bda266504893ac4774e0ec4c2108f7ccdbcb7ac8dced6305fe9e4e0b5041a" "checksum toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a0263c6c02c4db6c8f7681f9fd35e90de799ebd4cfdeab77a38f4ff6b3d8c0d9" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" +"checksum twoway 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" +"checksum typed-arena 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5934776c3ac1bea4a9d56620d6bf2d483b20d394e49581db40f187e1118ff667" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" @@ -1861,15 +2135,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" "checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" "checksum unidecode 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "402bb19d8e03f1d1a7450e2bd613980869438e0666331be3e073089124aa1adc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" "checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" +"checksum utf-8 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1262dfab4c30d5cb7c07026be00ee343a6cf5027fdc0104a9160f354e5db75c" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc7e3b898aa6f6c08e5295b6c89258d1331e9ac578cc992fb818759951bdc22" "checksum vcpkg 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7ed0f6789c8a85ca41bbc1c9d175422116a9869bd1cf31bb08e1493ecce60380" +"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" diff --git a/Cargo.toml b/Cargo.toml index d5b416c9..819d8a4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,13 +3,11 @@ authors = ["Bat' "] name = "plume" version = "0.1.0" [dependencies] -activitystreams = "0.1" -activitystreams-derive = "0.1" -activitystreams-traits = "0.1" -activitystreams-types = "0.1" +activitypub = "0.1.1" array_tool = "1.0" base64 = "0.9" bcrypt = "0.2" +comrak = "0.2" dotenv = "*" failure = "0.1" failure_derive = "0.1" @@ -25,6 +23,7 @@ serde = "*" serde_derive = "1.0" serde_json = "1.0" url = "1.7" +ammonia = "1.1.0" [dependencies.chrono] features = ["serde"] diff --git a/migrations/2018-05-24-100613_add_notifications_creation_date/down.sql b/migrations/2018-05-24-100613_add_notifications_creation_date/down.sql new file mode 100644 index 00000000..ca04e11e --- /dev/null +++ b/migrations/2018-05-24-100613_add_notifications_creation_date/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE notifications DROP COLUMN creation_date; diff --git a/migrations/2018-05-24-100613_add_notifications_creation_date/up.sql b/migrations/2018-05-24-100613_add_notifications_creation_date/up.sql new file mode 100644 index 00000000..fddcc387 --- /dev/null +++ b/migrations/2018-05-24-100613_add_notifications_creation_date/up.sql @@ -0,0 +1,2 @@ +-- Your SQL goes here +ALTER TABLE notifications ADD COLUMN creation_date TIMESTAMP NOT NULL DEFAULT now(); diff --git a/src/activity_pub/inbox.rs b/src/activity_pub/inbox.rs index e6629119..789ab130 100644 --- a/src/activity_pub/inbox.rs +++ b/src/activity_pub/inbox.rs @@ -1,7 +1,6 @@ -use activitystreams_traits::Actor; -use activitystreams_types::{ - actor::Person, - activity::{Accept, Create, Follow, Like, Undo}, +use activitypub::{ + Actor, + activity::{Accept, Announce, Create, Follow, Like, Undo}, object::{Article, Note} }; use diesel::PgConnection; @@ -19,8 +18,10 @@ use models::{ follows, likes, posts::*, + reshares::*, users::User }; +use safe_string::SafeString; #[derive(Fail, Debug)] enum InboxError { @@ -40,7 +41,7 @@ pub trait Inbox { blog_id: 0, // TODO slug: String::from(""), // TODO title: article.object_props.name_string().unwrap(), - content: article.object_props.content_string().unwrap(), + content: SafeString::new(&article.object_props.content_string().unwrap()), published: true, license: String::from("CC-0"), ap_url: article.object_props.url_string()? @@ -52,8 +53,8 @@ pub trait Inbox { let previous_url = note.object_props.in_reply_to.clone().unwrap().as_str().unwrap().to_string(); let previous_comment = Comment::find_by_ap_url(conn, previous_url.clone()); Comment::insert(conn, NewComment { - content: note.object_props.content_string().unwrap(), - spoiler_text: note.object_props.summary_string().unwrap(), + content: SafeString::new(¬e.object_props.content_string().unwrap()), + spoiler_text: note.object_props.summary_string().unwrap_or(String::from("")), ap_url: note.object_props.id_string().ok(), in_response_to_id: previous_comment.clone().map(|c| c.id), post_id: previous_comment @@ -66,11 +67,11 @@ pub trait Inbox { } fn follow(&self, conn: &PgConnection, follow: Follow) -> Result<(), Error> { - let from = User::from_url(conn, follow.actor.as_str().unwrap().to_string()).unwrap(); - match User::from_url(conn, follow.object.as_str().unwrap().to_string()) { + let from = User::from_url(conn, follow.follow_props.actor.as_str().unwrap().to_string()).unwrap(); + match User::from_url(conn, follow.follow_props.object.as_str().unwrap().to_string()) { Some(u) => self.accept_follow(conn, &from, &u, follow, from.id, u.id), None => { - let blog = Blog::from_url(conn, follow.object.as_str().unwrap().to_string()).unwrap(); + let blog = Blog::from_url(conn, follow.follow_props.object.as_str().unwrap().to_string()).unwrap(); self.accept_follow(conn, &from, &blog, follow, from.id, blog.id) } }; @@ -78,8 +79,8 @@ pub trait Inbox { } fn like(&self, conn: &PgConnection, like: Like) -> Result<(), Error> { - let liker = User::from_url(conn, like.actor.as_str().unwrap().to_string()); - let post = Post::find_by_ap_url(conn, like.object.as_str().unwrap().to_string()); + let liker = User::from_url(conn, like.like_props.actor.as_str().unwrap().to_string()); + let post = Post::find_by_ap_url(conn, like.like_props.object.as_str().unwrap().to_string()); likes::Like::insert(conn, likes::NewLike { post_id: post.unwrap().id, user_id: liker.unwrap().id, @@ -89,20 +90,32 @@ pub trait Inbox { } fn unlike(&self, conn: &PgConnection, undo: Undo) -> Result<(), Error> { - let like = likes::Like::find_by_ap_url(conn, undo.object_object::()?.object_props.id_string()?).unwrap(); + let like = likes::Like::find_by_ap_url(conn, undo.undo_props.object_object::()?.object_props.id_string()?).unwrap(); like.delete(conn); Ok(()) } + fn announce(&self, conn: &PgConnection, announce: Announce) -> Result<(), Error> { + let user = User::from_url(conn, announce.announce_props.actor.as_str().unwrap().to_string()); + let post = Post::find_by_ap_url(conn, announce.announce_props.object.as_str().unwrap().to_string()); + Reshare::insert(conn, NewReshare { + post_id: post.unwrap().id, + user_id: user.unwrap().id, + ap_url: announce.object_props.id_string()? + }); + Ok(()) + } + fn save(&self, conn: &PgConnection, act: serde_json::Value) -> Result<(), Error> { match act["type"].as_str() { Some(t) => { match t { + "Announce" => self.announce(conn, serde_json::from_value(act.clone())?), "Create" => { let act: Create = serde_json::from_value(act.clone())?; - match act.object["type"].as_str().unwrap() { - "Article" => self.new_article(conn, act.object_object().unwrap()), - "Note" => self.new_comment(conn, act.object_object().unwrap(), act.actor_object::()?.object_props.id_string()?), + match act.create_props.object["type"].as_str().unwrap() { + "Article" => self.new_article(conn, act.create_props.object_object()?), + "Note" => self.new_comment(conn, act.create_props.object_object()?, act.create_props.actor_link::()?.0), _ => Err(InboxError::InvalidType)? } }, @@ -110,7 +123,7 @@ pub trait Inbox { "Like" => self.like(conn, serde_json::from_value(act.clone())?), "Undo" => { let act: Undo = serde_json::from_value(act.clone())?; - match act.object["type"].as_str().unwrap() { + match act.undo_props.object["type"].as_str().unwrap() { "Like" => self.unlike(conn, act), _ => Err(InboxError::CantUndo)? } @@ -137,8 +150,8 @@ pub trait Inbox { }); let mut accept = Accept::default(); - accept.set_actor_link::(from.clone().into_id()).unwrap(); - accept.set_object_object(follow).unwrap(); + accept.accept_props.set_actor_link::(from.clone().into_id()).unwrap(); + accept.accept_props.set_object_object(follow).unwrap(); broadcast(conn, &*from, accept, vec![target.clone()]); } } diff --git a/src/activity_pub/mod.rs b/src/activity_pub/mod.rs index 35e5ee5e..febd9591 100644 --- a/src/activity_pub/mod.rs +++ b/src/activity_pub/mod.rs @@ -1,4 +1,4 @@ -use activitystreams_traits::{Activity, Actor, Object, Link}; +use activitypub::{Activity, Actor, Object, Link}; use array_tool::vec::Uniq; use diesel::PgConnection; use reqwest::Client; @@ -103,16 +103,11 @@ pub fn broadcast>(id: T) -> Id { - Id { - id: id.into() - } + Id(id.into()) } } diff --git a/src/main.rs b/src/main.rs index cbd27b47..ce8f78d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,12 @@ #![feature(plugin, custom_derive, iterator_find_map)] #![plugin(rocket_codegen)] -extern crate activitystreams; -#[macro_use] -extern crate activitystreams_derive; -extern crate activitystreams_traits; -extern crate activitystreams_types; +extern crate activitypub; extern crate array_tool; extern crate base64; extern crate bcrypt; extern crate chrono; +extern crate comrak; extern crate failure; #[macro_use] extern crate failure_derive; @@ -32,6 +29,7 @@ extern crate serde_derive; #[macro_use] extern crate serde_json; extern crate url; +extern crate ammonia; use diesel::{pg::PgConnection, r2d2::{ConnectionManager, Pool}}; use dotenv::dotenv; @@ -44,6 +42,7 @@ mod models; mod schema; mod routes; mod utils; +mod safe_string; lazy_static! { pub static ref BASE_URL: String = env::var("BASE_URL") @@ -70,19 +69,24 @@ fn main() { routes::blogs::activity_details, routes::blogs::outbox, routes::blogs::new, + routes::blogs::new_auth, routes::blogs::create, routes::comments::new, + routes::comments::new_auth, routes::comments::create, routes::instance::index, routes::instance::configure, routes::instance::post_config, routes::instance::shared_inbox, + routes::instance::nodeinfo, routes::likes::create, + routes::likes::create_auth, routes::notifications::notifications, + routes::notifications::notifications_auth, routes::posts::details, routes::posts::activity_details, @@ -90,7 +94,11 @@ fn main() { routes::posts::new_auth, routes::posts::create, + routes::reshares::create, + routes::reshares::create_auth, + routes::session::new, + routes::session::new_message, routes::session::create, routes::session::delete, @@ -98,10 +106,14 @@ fn main() { routes::user::me, routes::user::details, + routes::user::dashboard, + routes::user::dashboard_auth, routes::user::followers, routes::user::edit, + routes::user::edit_auth, routes::user::update, routes::user::follow, + routes::user::follow_auth, routes::user::activity_details, routes::user::outbox, routes::user::inbox, @@ -110,6 +122,7 @@ fn main() { routes::user::create, routes::well_known::host_meta, + routes::well_known::nodeinfo, routes::well_known::webfinger ]) .manage(init_pool()) diff --git a/src/models/blogs.rs b/src/models/blogs.rs index 46765486..cf8bd070 100644 --- a/src/models/blogs.rs +++ b/src/models/blogs.rs @@ -1,5 +1,4 @@ -use activitystreams_traits::{Actor, Object}; -use activitystreams_types::collection::OrderedCollection; +use activitypub::{Actor, Object, collection::OrderedCollection}; use reqwest::{ Client, header::{Accept, qitem}, @@ -8,7 +7,7 @@ use reqwest::{ use serde_json; use url::Url; use chrono::NaiveDateTime; -use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection}; +use diesel::{self, QueryDsl, RunQueryDsl, ExpressionMethods, PgConnection, dsl::any}; use openssl::{ hash::MessageDigest, pkey::{PKey, Private}, @@ -72,6 +71,14 @@ impl Blog { .into_iter().nth(0) } + pub fn find_for_author(conn: &PgConnection, author_id: i32) -> Vec { + use schema::blog_authors; + let author_ids = blog_authors::table.filter(blog_authors::author_id.eq(author_id)).select(blog_authors::blog_id); + blogs::table.filter(blogs::id.eq(any(author_ids))) + .load::(conn) + .expect("Couldn't load blogs ") + } + pub fn find_by_name(conn: &PgConnection, name: String, instance_id: i32) -> Option { blogs::table.filter(blogs::actor_id.eq(name)) .filter(blogs::instance_id.eq(instance_id)) diff --git a/src/models/comments.rs b/src/models/comments.rs index 8a24195e..e763e906 100644 --- a/src/models/comments.rs +++ b/src/models/comments.rs @@ -1,9 +1,9 @@ -use activitystreams_types::{ +use activitypub::{ activity::Create, object::{Note, properties::ObjectProperties} }; use chrono; -use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods}; +use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, dsl::any}; use serde_json; use activity_pub::{ @@ -12,15 +12,17 @@ use activity_pub::{ object::Object }; use models::{ + instance::Instance, posts::Post, users::User }; use schema::comments; +use safe_string::SafeString; #[derive(Queryable, Identifiable, Serialize, Clone)] pub struct Comment { pub id: i32, - pub content: String, + pub content: SafeString, pub in_response_to_id: Option, pub post_id: i32, pub author_id: i32, @@ -33,7 +35,7 @@ pub struct Comment { #[derive(Insertable)] #[table_name = "comments"] pub struct NewComment { - pub content: String, + pub content: SafeString, pub in_response_to_id: Option, pub post_id: i32, pub author_id: i32, @@ -105,11 +107,20 @@ impl Comment { pub fn create_activity(&self, conn: &PgConnection) -> Create { let mut act = Create::default(); - act.set_actor_link(self.get_author(conn).into_id()).unwrap(); - act.set_object_object(self.into_activity(conn)).unwrap(); + act.create_props.set_actor_link(self.get_author(conn).into_id()).unwrap(); + act.create_props.set_object_object(self.into_activity(conn)).unwrap(); act.object_props.set_id_string(format!("{}/activity", self.ap_url.clone().unwrap())).unwrap(); act } + + pub fn count_local(conn: &PgConnection) -> usize { + use schema::users; + let local_authors = users::table.filter(users::instance_id.eq(Instance::local_id(conn))).select(users::id); + comments::table.filter(comments::author_id.eq(any(local_authors))) + .load::(conn) + .expect("Couldn't load local comments") + .len() + } } impl Object for Comment { diff --git a/src/models/likes.rs b/src/models/likes.rs index acf505dc..65b24ff8 100644 --- a/src/models/likes.rs +++ b/src/models/likes.rs @@ -1,4 +1,4 @@ -use activitystreams_types::activity; +use activitypub::activity; use chrono; use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; use serde_json; @@ -76,15 +76,15 @@ impl Like { diesel::delete(self).execute(conn).unwrap(); let mut act = activity::Undo::default(); - act.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); - act.set_object_object(self.into_activity(conn)).unwrap(); + act.undo_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); + act.undo_props.set_object_object(self.into_activity(conn)).unwrap(); act } pub fn into_activity(&self, conn: &PgConnection) -> activity::Like { let mut act = activity::Like::default(); - act.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); - act.set_object_link(Post::get(conn, self.post_id).unwrap().into_id()).unwrap(); + act.like_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); + act.like_props.set_object_link(Post::get(conn, self.post_id).unwrap().into_id()).unwrap(); act.object_props.set_id_string(format!("{}/like/{}", User::get(conn, self.user_id).unwrap().ap_url, Post::get(conn, self.post_id).unwrap().ap_url diff --git a/src/models/notifications.rs b/src/models/notifications.rs index 70ffde89..1087d5cb 100644 --- a/src/models/notifications.rs +++ b/src/models/notifications.rs @@ -1,3 +1,4 @@ +use chrono::NaiveDateTime; use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods}; use models::users::User; @@ -9,7 +10,8 @@ pub struct Notification { pub title: String, pub content: Option, pub link: Option, - pub user_id: i32 + pub user_id: i32, + pub creation_date: NaiveDateTime } #[derive(Insertable)] @@ -39,6 +41,7 @@ impl Notification { pub fn find_for_user(conn: &PgConnection, user: &User) -> Vec { notifications::table.filter(notifications::user_id.eq(user.id)) + .order_by(notifications::creation_date.desc()) .load::(conn) .expect("Couldn't load user notifications") } diff --git a/src/models/posts.rs b/src/models/posts.rs index dcb94837..be2ce429 100644 --- a/src/models/posts.rs +++ b/src/models/posts.rs @@ -1,10 +1,9 @@ -use activitystreams_types::{ +use activitypub::{ activity::Create, object::{Article, properties::ObjectProperties} }; use chrono::NaiveDateTime; -use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl}; -use diesel::dsl::any; +use diesel::{self, PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, BelongingToDsl, dsl::any}; use serde_json; use BASE_URL; @@ -15,11 +14,14 @@ use activity_pub::{ }; use models::{ blogs::Blog, + instance::Instance, likes::Like, post_authors::PostAuthor, + reshares::Reshare, users::User }; use schema::posts; +use safe_string::SafeString; #[derive(Queryable, Identifiable, Serialize)] pub struct Post { @@ -27,7 +29,7 @@ pub struct Post { pub blog_id: i32, pub slug: String, pub title: String, - pub content: String, + pub content: SafeString, pub published: bool, pub license: String, pub creation_date: NaiveDateTime, @@ -40,7 +42,7 @@ pub struct NewPost { pub blog_id: i32, pub slug: String, pub title: String, - pub content: String, + pub content: SafeString, pub published: bool, pub license: String, pub ap_url: String @@ -62,6 +64,17 @@ impl Post { .into_iter().nth(0) } + pub fn count_local(conn: &PgConnection) -> usize { + use schema::post_authors; + use schema::users; + let local_authors = users::table.filter(users::instance_id.eq(Instance::local_id(conn))).select(users::id); + let local_posts_id = post_authors::table.filter(post_authors::author_id.eq(any(local_authors))).select(post_authors::post_id); + posts::table.filter(posts::id.eq(any(local_posts_id))) + .load::(conn) + .expect("Couldn't load local posts") + .len() + } + pub fn find_by_slug(conn: &PgConnection, slug: String) -> Option { posts::table.filter(posts::slug.eq(slug)) .limit(1) @@ -127,6 +140,13 @@ impl Post { .expect("Couldn't load likes associted to post") } + pub fn get_reshares(&self, conn: &PgConnection) -> Vec { + use schema::reshares; + reshares::table.filter(reshares::post_id.eq(self.id)) + .load::(conn) + .expect("Couldn't load reshares associted to post") + } + pub fn update_ap_url(&self, conn: &PgConnection) { if self.ap_url.len() == 0 { diesel::update(self) @@ -169,8 +189,8 @@ impl Post { pub fn create_activity(&self, conn: &PgConnection) -> Create { let mut act = Create::default(); act.object_props.set_id_string(format!("{}/activity", self.ap_url)).unwrap(); - act.set_actor_link(Id::new(self.get_authors(conn)[0].clone().ap_url)).unwrap(); - act.set_object_object(self.into_activity(conn)).unwrap(); + act.create_props.set_actor_link(Id::new(self.get_authors(conn)[0].clone().ap_url)).unwrap(); + act.create_props.set_object_object(self.into_activity(conn)).unwrap(); act } } @@ -183,7 +203,7 @@ impl IntoId for Post { impl Object for Post { fn compute_id(&self, conn: &PgConnection) -> String { - ap_url(format!("{}/~/{}/{}", BASE_URL.as_str(), self.get_blog(conn).actor_id, self.slug)) + ap_url(format!("{}/~/{}/{}/", BASE_URL.as_str(), self.get_blog(conn).actor_id, self.slug)) } fn serialize(&self, conn: &PgConnection) -> serde_json::Value { diff --git a/src/models/reshares.rs b/src/models/reshares.rs index 0ad8f464..65c4739e 100644 --- a/src/models/reshares.rs +++ b/src/models/reshares.rs @@ -1,23 +1,26 @@ +use activitypub::activity; use chrono::NaiveDateTime; use diesel::{self, PgConnection, QueryDsl, RunQueryDsl, ExpressionMethods}; +use activity_pub::{IntoId, actor::Actor, object::Object}; +use models::{posts::Post, users::User}; use schema::reshares; #[derive(Serialize, Deserialize, Queryable, Identifiable)] pub struct Reshare { - id: i32, - user_id: i32, - post_id: i32, - ap_url: String, - creation_date: NaiveDateTime + pub id: i32, + pub user_id: i32, + pub post_id: i32, + pub ap_url: String, + pub creation_date: NaiveDateTime } #[derive(Insertable)] #[table_name = "reshares"] pub struct NewReshare { - user_id: i32, - post_id: i32, - ap_url: String + pub user_id: i32, + pub post_id: i32, + pub ap_url: String } impl Reshare { @@ -35,4 +38,63 @@ impl Reshare { .expect("Could'nt load reshare") .into_iter().nth(0) } + + pub fn update_ap_url(&self, conn: &PgConnection) { + if self.ap_url.len() == 0 { + diesel::update(self) + .set(reshares::ap_url.eq(format!( + "{}/reshare/{}", + User::get(conn, self.user_id).unwrap().compute_id(conn), + Post::get(conn, self.post_id).unwrap().compute_id(conn) + ))) + .get_result::(conn).expect("Couldn't update AP URL"); + } + } + + pub fn find_by_ap_url(conn: &PgConnection, ap_url: String) -> Option { + reshares::table.filter(reshares::ap_url.eq(ap_url)) + .limit(1) + .load::(conn) + .expect("Error loading reshare by AP URL") + .into_iter().nth(0) + } + + pub fn find_by_user_on_post(conn: &PgConnection, user: &User, post: &Post) -> Option { + reshares::table.filter(reshares::post_id.eq(post.id)) + .filter(reshares::user_id.eq(user.id)) + .limit(1) + .load::(conn) + .expect("Error loading reshare for user and post") + .into_iter().nth(0) + } + + pub fn get_recents_for_author(conn: &PgConnection, user: &User, limit: i64) -> Vec { + reshares::table.filter(reshares::user_id.eq(user.id)) + .order(reshares::creation_date.desc()) + .limit(limit) + .load::(conn) + .expect("Error loading recent reshares for user") + } + + pub fn get_post(&self, conn: &PgConnection) -> Option { + Post::get(conn, self.post_id) + } + + pub fn delete(&self, conn: &PgConnection) -> activity::Undo { + diesel::delete(self).execute(conn).unwrap(); + + let mut act = activity::Undo::default(); + act.undo_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); + act.undo_props.set_object_object(self.into_activity(conn)).unwrap(); + act + } + + pub fn into_activity(&self, conn: &PgConnection) -> activity::Announce { + let mut act = activity::Announce::default(); + act.announce_props.set_actor_link(User::get(conn, self.user_id).unwrap().into_id()).unwrap(); + act.announce_props.set_object_link(Post::get(conn, self.post_id).unwrap().into_id()).unwrap(); + act.object_props.set_id_string(self.ap_url.clone()).unwrap(); + + act + } } diff --git a/src/models/users.rs b/src/models/users.rs index 92b85715..86771548 100644 --- a/src/models/users.rs +++ b/src/models/users.rs @@ -1,9 +1,8 @@ -use activitystreams_traits::{Actor, Object, Link}; -use activitystreams_types::{ - actor::Person, +use activitypub::{ + Actor, Object, + actor::{Person, properties::ApActorProperties}, collection::OrderedCollection, - object::properties::ObjectProperties, - CustomObject + object::properties::ObjectProperties }; use bcrypt; use chrono::NaiveDateTime; @@ -36,6 +35,8 @@ use activity_pub::{ }; use db_conn::DbConn; use models::{ + blogs::Blog, + blog_authors::BlogAuthor, comments::Comment, follows::Follow, instance::Instance, @@ -44,6 +45,7 @@ use models::{ posts::Post }; use schema::users; +use safe_string::SafeString; pub const AUTH_COOKIE: &'static str = "user_id"; @@ -55,7 +57,7 @@ pub struct User { pub outbox_url: String, pub inbox_url: String, pub is_admin: bool, - pub summary: String, + pub summary: SafeString, pub email: Option, pub hashed_password: Option, pub instance_id: i32, @@ -74,7 +76,7 @@ pub struct NewUser { pub outbox_url: String, pub inbox_url: String, pub is_admin: bool, - pub summary: String, + pub summary: SafeString, pub email: Option, pub hashed_password: Option, pub instance_id: i32, @@ -118,6 +120,13 @@ impl User { .into_iter().nth(0) } + pub fn count_local(conn: &PgConnection) -> usize { + users::table.filter(users::instance_id.eq(Instance::local_id(conn))) + .load::(conn) + .expect("Couldn't load local users") + .len() + } + pub fn find_by_email(conn: &PgConnection, email: String) -> Option { users::table.filter(users::email.eq(email)) .limit(1) @@ -192,7 +201,7 @@ impl User { outbox_url: acct["outbox"].as_str().unwrap().to_string(), inbox_url: acct["inbox"].as_str().unwrap().to_string(), is_admin: false, - summary: acct["summary"].as_str().unwrap().to_string(), + summary: SafeString::new(&acct["summary"].as_str().unwrap().to_string()), email: None, hashed_password: None, instance_id: instance.id, @@ -287,11 +296,31 @@ impl User { .len() > 0 } + pub fn has_reshared(&self, conn: &PgConnection, post: &Post) -> bool { + use schema::reshares; + use models::reshares::Reshare; + reshares::table + .filter(reshares::post_id.eq(post.id)) + .filter(reshares::user_id.eq(self.id)) + .load::(conn) + .expect("Couldn't load reshares") + .len() > 0 + } + + pub fn is_author_in(&self, conn: &PgConnection, blog: Blog) -> bool { + use schema::blog_authors; + blog_authors::table.filter(blog_authors::author_id.eq(self.id)) + .filter(blog_authors::blog_id.eq(blog.id)) + .load::(conn) + .expect("Couldn't load blog/author relationship") + .len() > 0 + } + pub fn get_keypair(&self) -> PKey { PKey::from_rsa(Rsa::private_key_from_pem(self.private_key.clone().unwrap().as_ref()).unwrap()).unwrap() } - pub fn into_activity(&self, conn: &PgConnection) -> CustomObject { + pub fn into_activity(&self, conn: &PgConnection) -> Person { let mut actor = Person::default(); actor.object_props = ObjectProperties { id: Some(serde_json::to_value(self.compute_id(conn)).unwrap()), @@ -300,34 +329,22 @@ impl User { url: Some(serde_json::to_value(self.compute_id(conn)).unwrap()), ..ObjectProperties::default() }; - - CustomObject::new(actor, ApProps { - inbox: Some(serde_json::to_value(self.compute_inbox(conn)).unwrap()), - outbox: Some(serde_json::to_value(self.compute_outbox(conn)).unwrap()), + actor.ap_actor_props = ApActorProperties { + inbox: serde_json::to_value(self.compute_inbox(conn)).unwrap(), + outbox: serde_json::to_value(self.compute_outbox(conn)).unwrap(), preferred_username: Some(serde_json::to_value(self.get_actor_id()).unwrap()), endpoints: Some(json!({ "sharedInbox": ap_url(format!("{}/inbox", BASE_URL.as_str())) - })) - }) + })), + followers: None, + following: None, + liked: None, + streams: None + }; + actor } } -#[derive(Serialize, Deserialize, Default, Properties)] -#[serde(rename_all = "camelCase")] -pub struct ApProps { - #[activitystreams(ab(Object, Link))] - inbox: Option, - - #[activitystreams(ab(Object, Link))] - outbox: Option, - - #[activitystreams(ab(Object, Link))] - preferred_username: Option, - - #[activitystreams(ab(Object))] - endpoints: Option -} - impl<'a, 'r> FromRequest<'a, 'r> for User { type Error = (); @@ -355,7 +372,7 @@ impl APActor for User { } fn get_summary(&self) -> String { - self.summary.clone() + self.summary.get().clone() } fn get_instance(&self, conn: &PgConnection) -> Instance { @@ -427,10 +444,22 @@ impl WithInbox for User { impl Inbox for User { fn received(&self, conn: &PgConnection, act: serde_json::Value) { - self.save(conn, act.clone()).unwrap(); + if let Err(err) = self.save(conn, act.clone()) { + println!("Inbox error:\n{}\n{}\n\nActivity was: {}", err.cause(), err.backtrace(), act.to_string()); + } // Notifications match act["type"].as_str().unwrap() { + "Announce" => { + let actor = User::from_url(conn, act["actor"].as_str().unwrap().to_string()).unwrap(); + let post = Post::find_by_ap_url(conn, act["object"].as_str().unwrap().to_string()).unwrap(); + Notification::insert(conn, NewNotification { + title: format!("{} reshared your article", actor.display_name.clone()), + content: Some(post.title), + link: Some(post.ap_url), + user_id: self.id + }); + }, "Follow" => { let follower = User::from_url(conn, act["actor"].as_str().unwrap().to_string()).unwrap(); Notification::insert(conn, NewNotification { @@ -453,13 +482,17 @@ impl Inbox for User { "Create" => { match act["object"]["type"].as_str().unwrap() { "Note" => { - let comment = Comment::find_by_ap_url(conn, act["object"]["id"].as_str().unwrap().to_string()).unwrap(); - Notification::insert(conn, NewNotification { - title: format!("{} commented your article", comment.get_author(conn).display_name.clone()), - content: Some(comment.get_post(conn).title), - link: comment.ap_url, - user_id: self.id - }); + match Comment::find_by_ap_url(conn, act["object"]["id"].as_str().unwrap().to_string()) { + Some(comment) => { + Notification::insert(conn, NewNotification { + title: format!("{} commented your article", comment.get_author(conn).display_name.clone()), + content: Some(comment.get_post(conn).title), + link: comment.ap_url, + user_id: self.id + }); + }, + None => println!("Couldn't find comment by AP id, to create a new notification") + }; } _ => {} } @@ -529,7 +562,7 @@ impl NewUser { outbox_url: String::from(""), inbox_url: String::from(""), is_admin: is_admin, - summary: summary, + summary: SafeString::new(&summary), email: Some(email), hashed_password: Some(password), instance_id: instance_id, diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 08fea83b..1131a06b 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -1,7 +1,7 @@ -use activitystreams_types::collection::OrderedCollection; +use activitypub::collection::OrderedCollection; use rocket::{ request::Form, - response::Redirect + response::{Redirect, Flash} }; use rocket_contrib::Template; use serde_json; @@ -24,6 +24,7 @@ fn details(name: String, conn: DbConn, user: Option) -> Template { Template::render("blogs/details", json!({ "blog": blog, "account": user, + "is_author": user.map(|x| x.is_author_in(&*conn, blog)), "recents": recents.into_iter().map(|p| { json!({ "post": p, @@ -53,6 +54,11 @@ fn new(user: User) -> Template { })) } +#[get("/blogs/new", rank = 2)] +fn new_auth() -> Flash{ + utils::requires_login("You need to be logged in order to create a new blog", "/blogs/new") +} + #[derive(FromForm)] struct NewBlogForm { pub title: String @@ -77,7 +83,7 @@ fn create(conn: DbConn, data: Form, user: User) -> Redirect { is_owner: true }); - Redirect::to(format!("/~/{}", slug).as_str()) + Redirect::to(format!("/~/{}/", slug).as_str()) } #[get("/~//outbox")] diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 2a47b87f..86542a11 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -1,4 +1,7 @@ -use rocket::{ request::Form, response::Redirect}; +use rocket::{ + request::Form, + response::{Redirect, Flash} +}; use rocket_contrib::Template; use activity_pub::broadcast; @@ -9,6 +12,9 @@ use models::{ users::User }; +use utils; +use safe_string::SafeString; + #[get("/~/<_blog>//comment")] fn new(_blog: String, slug: String, user: User, conn: DbConn) -> Template { let post = Post::find_by_slug(&*conn, slug).unwrap(); @@ -18,6 +24,11 @@ fn new(_blog: String, slug: String, user: User, conn: DbConn) -> Template { })) } +#[get("/~///comment", rank=2)] +fn new_auth(blog: String, slug: String) -> Flash{ + utils::requires_login("You need to be logged in order to post a comment", &format!("~/{}/{}/comment", blog, slug)) +} + #[derive(FromForm)] struct CommentQuery { responding_to: Option @@ -33,7 +44,7 @@ fn create(blog: String, slug: String, query: CommentQuery, data: Form) -> Template { match Instance::get_local(&*conn) { Some(inst) => { - let recents = Post::get_recents(&*conn, 5); + let recents = Post::get_recents(&*conn, 6); Template::render("instance/index", json!({ "instance": inst, @@ -75,3 +76,28 @@ fn shared_inbox(conn: DbConn, data: String) -> String { instance.received(&*conn, act); String::from("") } + +#[get("/nodeinfo")] +fn nodeinfo(conn: DbConn) -> Json { + Json(json!({ + "version": "2.0", + "software": { + "name": "Plume", + "version": "0.1.0" + }, + "protocols": ["activitypub"], + "services": { + "inbound": [], + "outbound": [] + }, + "openRegistrations": true, + "usage": { + "users": { + "total": User::count_local(&*conn) + }, + "localPosts": Post::count_local(&*conn), + "localComments": Comment::count_local(&*conn) + }, + "metadata": {} + })) +} diff --git a/src/routes/likes.rs b/src/routes/likes.rs index 11c578bb..55dce944 100644 --- a/src/routes/likes.rs +++ b/src/routes/likes.rs @@ -1,4 +1,4 @@ -use rocket::response::Redirect; +use rocket::response::{Redirect, Flash}; use activity_pub::broadcast; use db_conn::DbConn; @@ -8,6 +8,8 @@ use models::{ users::User }; +use utils; + #[get("/~///like")] fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { let post = Post::find_by_slug(&*conn, slug.clone()).unwrap(); @@ -29,3 +31,8 @@ fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { Redirect::to(format!("/~/{}/{}/", blog, slug).as_ref()) } + +#[get("/~///like", rank = 2)] +fn create_auth(blog: String, slug: String) -> Flash{ + utils::requires_login("You need to be logged in order to like a post", &format!("/~/{}/{}/like", blog, slug)) +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 60946b8a..501c774c 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -7,6 +7,7 @@ pub mod instance; pub mod likes; pub mod notifications; pub mod posts; +pub mod reshares; pub mod session; pub mod user; pub mod well_known; diff --git a/src/routes/notifications.rs b/src/routes/notifications.rs index ae8c0d0b..6b096148 100644 --- a/src/routes/notifications.rs +++ b/src/routes/notifications.rs @@ -1,8 +1,11 @@ +use rocket::response::{Redirect, Flash}; use rocket_contrib::Template; use db_conn::DbConn; use models::{notifications::Notification, users::User}; +use utils; + #[get("/notifications")] fn notifications(conn: DbConn, user: User) -> Template { Template::render("notifications/index", json!({ @@ -10,3 +13,8 @@ fn notifications(conn: DbConn, user: User) -> Template { "notifications": Notification::find_for_user(&*conn, &user) })) } + +#[get("/notifications", rank = 2)] +fn notifications_auth() -> Flash{ + utils::requires_login("You need to be logged in order to see your notifications", "/notifications") +} diff --git a/src/routes/posts.rs b/src/routes/posts.rs index e213c869..893670e2 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -1,6 +1,7 @@ +use comrak::{markdown_to_html, ComrakOptions}; use heck::KebabCase; use rocket::request::Form; -use rocket::response::Redirect; +use rocket::response::{Redirect, Flash}; use rocket_contrib::Template; use serde_json; @@ -14,6 +15,7 @@ use models::{ users::User }; use utils; +use safe_string::SafeString; #[get("/~//", rank = 4)] fn details(blog: String, slug: String, conn: DbConn, user: Option) -> Template { @@ -39,6 +41,8 @@ fn details(blog: String, slug: String, conn: DbConn, user: Option) -> Temp }).collect::>(), "n_likes": post.get_likes(&*conn).len(), "has_liked": user.clone().map(|u| u.has_liked(&*conn, &post)).unwrap_or(false), + "n_reshares": post.get_reshares(&*conn).len(), + "has_reshared": user.clone().map(|u| u.has_reshared(&*conn, &post)).unwrap_or(false), "account": user, "date": &post.creation_date.timestamp() })) @@ -54,9 +58,9 @@ fn activity_details(_blog: String, slug: String, conn: DbConn) -> ActivityPub { activity_pub(act) } -#[get("/~/<_blog>/new", rank = 2)] -fn new_auth(_blog: String) -> Redirect { - utils::requires_login() +#[get("/~//new", rank = 2)] +fn new_auth(blog: String) -> Flash { + utils::requires_login("You need to be logged in order to write a new post", &format!("/~/{}/new",blog)) } #[get("/~/<_blog>/new", rank = 1)] @@ -78,11 +82,26 @@ fn create(blog_name: String, data: Form, user: User, conn: DbConn) let blog = Blog::find_by_fqn(&*conn, blog_name.to_string()).unwrap(); let form = data.get(); let slug = form.title.to_string().to_kebab_case(); + + let content = markdown_to_html(form.content.to_string().as_ref(), &ComrakOptions{ + smart: true, + safe: true, + ext_strikethrough: true, + ext_tagfilter: true, + ext_table: true, + ext_autolink: true, + ext_tasklist: true, + ext_superscript: true, + ext_header_ids: Some("title".to_string()), + ext_footnotes: true, + ..ComrakOptions::default() + }); + let post = Post::insert(&*conn, NewPost { blog_id: blog.id, slug: slug.to_string(), title: form.title.to_string(), - content: form.content.to_string(), + content: SafeString::new(&content), published: true, license: form.license.to_string(), ap_url: "".to_string() @@ -96,5 +115,5 @@ fn create(blog_name: String, data: Form, user: User, conn: DbConn) let act = post.create_activity(&*conn); broadcast(&*conn, &user, act, user.get_followers(&*conn)); - Redirect::to(format!("/~/{}/{}", blog_name, slug).as_str()) + Redirect::to(format!("/~/{}/{}/", blog_name, slug).as_str()) } diff --git a/src/routes/reshares.rs b/src/routes/reshares.rs new file mode 100644 index 00000000..19f69332 --- /dev/null +++ b/src/routes/reshares.rs @@ -0,0 +1,38 @@ +use rocket::response::{Redirect, Flash}; + +use activity_pub::broadcast; +use db_conn::DbConn; +use models::{ + posts::Post, + reshares::*, + users::User +}; + +use utils; + +#[get("/~///reshare")] +fn create(blog: String, slug: String, user: User, conn: DbConn) -> Redirect { + let post = Post::find_by_slug(&*conn, slug.clone()).unwrap(); + + if !user.has_reshared(&*conn, &post) { + let reshare = Reshare::insert(&*conn, NewReshare { + post_id: post.id, + user_id: user.id, + ap_url: "".to_string() + }); + reshare.update_ap_url(&*conn); + + broadcast(&*conn, &user, reshare.into_activity(&*conn), user.get_followers(&*conn)); + } else { + let reshare = Reshare::find_by_user_on_post(&*conn, &user, &post).unwrap(); + let delete_act = reshare.delete(&*conn); + broadcast(&*conn, &user, delete_act, user.get_followers(&*conn)); + } + + Redirect::to(format!("/~/{}/{}/", blog, slug).as_ref()) +} + +#[get("/~///reshare", rank=1)] +fn create_auth(blog: String, slug: String) -> Flash { + utils::requires_login("You need to be logged in order to reshare a post", &format!("/~/{}/{}/reshare",blog, slug)) +} diff --git a/src/routes/session.rs b/src/routes/session.rs index f8a1ed94..bf7d5e6b 100644 --- a/src/routes/session.rs +++ b/src/routes/session.rs @@ -1,7 +1,7 @@ use rocket::{ http::{Cookie, Cookies}, response::{Redirect, status::NotFound}, - request::Form + request::{Form,FlashMessage} }; use rocket_contrib::Template; @@ -15,6 +15,20 @@ fn new(user: Option) -> Template { })) } +#[derive(FromForm)] +struct Message { + m: String +} + +#[get("/login?")] +fn new_message(user: Option, message: Message) -> Template { + Template::render("session/login", json!({ + "account": user, + "message": message.m + })) +} + + #[derive(FromForm)] struct LoginForm { email_or_name: String, @@ -22,7 +36,7 @@ struct LoginForm { } #[post("/login", data = "")] -fn create(conn: DbConn, data: Form, mut cookies: Cookies) -> Result> { +fn create(conn: DbConn, data: Form, flash: Option, mut cookies: Cookies) -> Result> { let form = data.get(); let user = match User::find_by_email(&*conn, form.email_or_name.to_string()) { Some(usr) => Ok(usr), @@ -31,12 +45,14 @@ fn create(conn: DbConn, data: Form, mut cookies: Cookies) -> Result Err("Invalid username or password") } }; - match user { Ok(usr) => { if usr.auth(form.password.to_string()) { cookies.add_private(Cookie::new(AUTH_COOKIE, usr.id.to_string())); - Ok(Redirect::to("/")) + Ok(Redirect::to(&flash + .and_then(|f| if f.name()=="callback" { Some(f.msg().to_owned()) } else { None }) + .unwrap_or("/".to_owned())) + ) } else { Err(NotFound(String::from("Invalid username or password"))) } diff --git a/src/routes/user.rs b/src/routes/user.rs index 807168a8..49023c6a 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -1,8 +1,10 @@ -use activitystreams_types::{ +use activitypub::{ activity::Follow, collection::OrderedCollection }; -use rocket::{request::Form, response::Redirect}; +use rocket::{request::Form, + response::{Redirect, Flash} +}; use rocket_contrib::Template; use serde_json; @@ -13,21 +15,28 @@ use activity_pub::{ }; use db_conn::DbConn; use models::{ + blogs::Blog, follows, instance::Instance, posts::Post, + reshares::Reshare, users::* }; +use utils; #[get("/me")] -fn me(user: User) -> Redirect { - Redirect::to(format!("/@/{}", user.username).as_ref()) +fn me(user: Option) -> Result> { + match user { + Some(user) => Ok(Redirect::to(format!("/@/{}/", user.username).as_ref())), + None => Err(utils::requires_login("", "/me")) + } } #[get("/@/", rank = 2)] fn details(name: String, conn: DbConn, account: Option) -> Template { let user = User::find_by_fqn(&*conn, name).unwrap(); - let recents = Post::get_recents_for_author(&*conn, &user, 5); + let recents = Post::get_recents_for_author(&*conn, &user, 6); + let reshares = Reshare::get_recents_for_author(&*conn, &user, 6); let user_id = user.id.clone(); let n_followers = user.get_followers(&*conn).len(); @@ -47,11 +56,39 @@ fn details(name: String, conn: DbConn, account: Option) -> Template { "date": p.creation_date.timestamp() }) }).collect::>(), + "reshares": reshares.into_iter().map(|r| { + let p = r.get_post(&*conn).unwrap(); + json!({ + "post": p, + "author": ({ + let author = &p.get_authors(&*conn)[0]; + let mut json = serde_json::to_value(author).unwrap(); + json["fqn"] = serde_json::Value::String(author.get_fqn(&*conn)); + json + }), + "url": format!("/~/{}/{}/", p.get_blog(&*conn).actor_id, p.slug), + "date": p.creation_date.timestamp() + }) + }).collect::>(), "is_self": account.map(|a| a.id == user_id).unwrap_or(false), "n_followers": n_followers })) } +#[get("/dashboard")] +fn dashboard(user: User, conn: DbConn) -> Template { + let blogs = Blog::find_for_author(&*conn, user.id); + Template::render("users/dashboard", json!({ + "account": user, + "blogs": blogs + })) +} + +#[get("/dashboard", rank = 2)] +fn dashboard_auth() -> Flash { + utils::requires_login("You need to be logged in order to access your dashboard", "/dashboard") +} + #[get("/@//follow")] fn follow(name: String, conn: DbConn, user: User) -> Redirect { let target = User::find_by_fqn(&*conn, name.clone()).unwrap(); @@ -60,11 +97,16 @@ fn follow(name: String, conn: DbConn, user: User) -> Redirect { following_id: target.id }); let mut act = Follow::default(); - act.set_actor_link::(user.clone().into_id()).unwrap(); - act.set_object_object(user.into_activity(&*conn)).unwrap(); + act.follow_props.set_actor_link::(user.clone().into_id()).unwrap(); + act.follow_props.set_object_object(user.into_activity(&*conn)).unwrap(); act.object_props.set_id_string(format!("{}/follow/{}", user.ap_url, target.ap_url)).unwrap(); broadcast(&*conn, &user, act, vec![target]); - Redirect::to(format!("/@/{}", name).as_ref()) + Redirect::to(format!("/@/{}/", name).as_ref()) +} + +#[get("/@//follow", rank = 2)] +fn follow_auth(name: String) -> Flash { + utils::requires_login("You need to be logged in order to follow someone", &format!("/@/{}/follow", name)) } #[get("/@//followers", rank = 2)] @@ -109,6 +151,11 @@ fn edit(name: String, user: User) -> Option