From 940dc73f280742e553395d6a56eaca015a234b3a Mon Sep 17 00:00:00 2001 From: Dessalines Date: Fri, 15 May 2020 12:36:11 -0400 Subject: [PATCH] Federated mentions. Fixes #681 (#717) * Federated mentions. Fixes #681 * Changing some todos, adding comments. --- server/Cargo.lock | 352 ++++++++++++----------- server/Cargo.toml | 18 +- server/src/api/comment.rs | 256 +++++++---------- server/src/api/mod.rs | 4 +- server/src/apub/activities.rs | 4 +- server/src/apub/comment.rs | 114 ++++++-- server/src/apub/community.rs | 36 ++- server/src/apub/extensions/signatures.rs | 2 +- server/src/apub/mod.rs | 29 +- server/src/apub/post.rs | 32 ++- server/src/apub/shared_inbox.rs | 18 +- server/src/db/community.rs | 4 - server/src/lib.rs | 66 +++-- server/src/routes/feeds.rs | 3 +- server/src/routes/mod.rs | 5 +- server/src/routes/webfinger.rs | 101 ++++--- server/src/routes/websocket.rs | 2 +- ui/src/api_tests/api.spec.ts | 45 ++- ui/src/utils.ts | 21 +- 19 files changed, 660 insertions(+), 452 deletions(-) diff --git a/server/Cargo.lock b/server/Cargo.lock index 714422b64..c71b81905 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -2,24 +2,24 @@ # It is not intended for manual editing. [[package]] name = "activitystreams" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5b29a0f2c64cc56f2b79ec29cab68a9dab3b714d811a55668d072f18a8638e" +checksum = "edf0082191df1d6d39577b28b5daa7b8318b812038d0f7a83f05331f17580934" dependencies = [ "activitystreams-derive", "chrono", "mime", - "serde 1.0.106", - "serde_json 1.0.51", + "serde 1.0.110", + "serde_json 1.0.53", "thiserror", "url", ] [[package]] name = "activitystreams-derive" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985d3ca1ee226e83f4118e0235bc11d9fce39c4eec8d53739a21b01dd0b3f30f" +checksum = "c39ba5929399e9f921055bac76dd8f47419fa5b6b6da1ac4c1e82b94ed0ac7b4" dependencies = [ "proc-macro2", "quote", @@ -144,8 +144,8 @@ dependencies = [ "pin-project", "rand 0.7.3", "regex 1.3.7", - "serde 1.0.106", - "serde_json 1.0.51", + "serde 1.0.110", + "serde_json 1.0.53", "serde_urlencoded", "sha1", "slab", @@ -172,20 +172,21 @@ dependencies = [ "http", "log", "regex 1.3.7", - "serde 1.0.106", + "serde 1.0.110", ] [[package]] name = "actix-rt" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20066d9200ef8d441ac156c76dd36c3f1e9a15976c34e69ae97f7f570b331882" +checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227" dependencies = [ "actix-macros", "actix-threadpool", "copyless", "futures-channel", "futures-util", + "smallvec", "tokio", ] @@ -311,8 +312,8 @@ dependencies = [ "net2", "pin-project", "regex 1.3.7", - "serde 1.0.106", - "serde_json 1.0.51", + "serde 1.0.110", + "serde_json 1.0.53", "serde_urlencoded", "time", "url", @@ -355,6 +356,15 @@ dependencies = [ "syn", ] +[[package]] +name = "addr2line" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456d75cbb82da1ad150c8a9d97285ffcd21c9931dcb11e995903e7d75141b38b" +dependencies = [ + "gimli", +] + [[package]] name = "adler32" version = "1.0.4" @@ -411,9 +421,9 @@ checksum = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" [[package]] name = "async-trait" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da71fef07bc806586090247e971229289f64c210a278ee5ae419314eb386b31d" +checksum = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b" dependencies = [ "proc-macro2", "quote", @@ -461,33 +471,24 @@ dependencies = [ "mime", "percent-encoding", "rand 0.7.3", - "serde 1.0.106", - "serde_json 1.0.51", + "serde 1.0.110", + "serde_json 1.0.53", "serde_urlencoded", ] [[package]] name = "backtrace" -version = "0.3.46" +version = "0.3.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" +checksum = "0df2f85c8a2abbe3b7d7e748052fdd9b76a0458fdeb16ad4223f5eca78c7c130" dependencies = [ - "backtrace-sys", + "addr2line", "cfg-if", "libc", + "object", "rustc-demangle", ] -[[package]] -name = "backtrace-sys" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78848718ee1255a2485d1309ad9cdecfc2e7d0362dd11c6829364c6b35ae1bc7" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "base64" version = "0.9.3" @@ -515,21 +516,20 @@ checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" [[package]] name = "base64" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3" +checksum = "53d1ccbaf7d9ec9537465a97bf19edc1a4e158ecb49fc16178202238c569cc42" [[package]] name = "bcrypt" -version = "0.6.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f055c8591efe08e03f534ee632672ea3643c3932e485f422107ec6cc1157b0ea" +checksum = "41b70db86f3c560199b0dada79a22b9a924622384abb2a756a9707ffcce077f2" dependencies = [ - "base64 0.12.0", + "base64 0.12.1", "blowfish", "byteorder", - "lazy_static 1.4.0", - "rand 0.7.3", + "getrandom", ] [[package]] @@ -607,9 +607,9 @@ checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" [[package]] name = "bumpalo" -version = "3.2.1" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" +checksum = "5356f1d23ee24a1f785a56d1d1a5f0fd5b0f6a0c0fb2412ce11da71649ab78f6" [[package]] name = "byte-tools" @@ -640,9 +640,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.52" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" +checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c" [[package]] name = "cfg-if" @@ -658,15 +658,15 @@ checksum = "80094f509cf8b5ae86a4966a39b3ff66cd7e2a3e594accec3743ff3fabeab5b2" dependencies = [ "num-integer", "num-traits 0.2.11", - "serde 1.0.106", + "serde 1.0.110", "time", ] [[package]] name = "clap" -version = "2.33.0" +version = "2.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" dependencies = [ "ansi_term", "atty", @@ -712,9 +712,9 @@ dependencies = [ "lazy_static 1.4.0", "nom 5.1.1", "rust-ini", - "serde 1.0.106", + "serde 1.0.110", "serde-hjson 0.9.1", - "serde_json 1.0.51", + "serde_json 1.0.53", "toml", "yaml-rust", ] @@ -773,9 +773,9 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda1c0c03cacf3365d84818a40293f0e3f3953db8759c9c565a3b434edf0b52e" +checksum = "762e34611d2d5233a506a79072be944fddd057db2f18e04c0d6fa79e3fd466fd" dependencies = [ "curl-sys", "libc", @@ -788,9 +788,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.30+curl-7.69.1" +version = "0.4.31+curl-7.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923b38e423a8f47a4058e96f2a1fa2865a6231097ee860debd678d244277d50c" +checksum = "dcd62757cc4f5ab9404bc6ca9f0ae447e729a1403948ce5106bd588ceac6a3b0" dependencies = [ "cc", "libc", @@ -864,9 +864,9 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.5" +version = "0.99.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7" +checksum = "46b046a346c374c6c3c84d2070bfe33904504686bdf949c2d8eb22edad3f270c" dependencies = [ "proc-macro2", "quote", @@ -885,7 +885,7 @@ dependencies = [ "diesel_derives", "pq-sys", "r2d2", - "serde_json 1.0.51", + "serde_json 1.0.53", ] [[package]] @@ -1035,9 +1035,9 @@ checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" [[package]] name = "encoding_rs" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" +checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171" dependencies = [ "cfg-if", ] @@ -1075,9 +1075,9 @@ dependencies = [ [[package]] name = "failure" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" dependencies = [ "backtrace", "failure_derive", @@ -1085,9 +1085,9 @@ dependencies = [ [[package]] name = "failure_derive" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2", "quote", @@ -1124,9 +1124,9 @@ dependencies = [ [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" @@ -1167,9 +1167,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futures" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" +checksum = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" dependencies = [ "futures-channel", "futures-core", @@ -1182,9 +1182,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" +checksum = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" dependencies = [ "futures-core", "futures-sink", @@ -1192,15 +1192,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" +checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" [[package]] name = "futures-executor" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" +checksum = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" dependencies = [ "futures-core", "futures-task", @@ -1209,15 +1209,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" +checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" [[package]] name = "futures-macro" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" +checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -1227,21 +1227,24 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" +checksum = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" [[package]] name = "futures-task" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" +checksum = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" +dependencies = [ + "once_cell", +] [[package]] name = "futures-util" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +checksum = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" dependencies = [ "futures-channel", "futures-core", @@ -1250,6 +1253,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr 2.3.3", + "pin-project", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -1286,10 +1290,16 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.2.4" +name = "gimli" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42" +checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" + +[[package]] +name = "h2" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" dependencies = [ "bytes", "fnv", @@ -1374,9 +1384,9 @@ dependencies = [ [[package]] name = "http-signature-normalization" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde6b0321d465ea2cdc18b5d5ec73eee2ff20177ecee126cd8dd997f6c49e088" +checksum = "648233553603e7bb55bc1ea08a514661e212c09c10f6434507894273d8b5e773" dependencies = [ "chrono", "thiserror", @@ -1434,9 +1444,9 @@ dependencies = [ [[package]] name = "ipconfig" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa79fa216fbe60834a9c0737d7fcd30425b32d1c58854663e24d4c4b328ed83f" +checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" dependencies = [ "socket2", "widestring", @@ -1446,9 +1456,9 @@ dependencies = [ [[package]] name = "isahc" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bdb3bdcbf6d534daaad1a686eda0d0dc1946818fa71e3edd3124d001adfdc2" +checksum = "ee16417863ac2b4869ac774af7ef0322b08c53d952665edc5749ccaa37d68588" dependencies = [ "bytes", "crossbeam-channel", @@ -1490,9 +1500,9 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "js-sys" -version = "0.3.37" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055" +checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7" dependencies = [ "wasm-bindgen", ] @@ -1506,8 +1516,8 @@ dependencies = [ "base64 0.11.0", "pem", "ring", - "serde 1.0.106", - "serde_json 1.0.51", + "serde 1.0.110", + "serde_json 1.0.53", "simple_asn1", ] @@ -1549,7 +1559,7 @@ dependencies = [ "actix-rt", "actix-web", "actix-web-actors", - "base64 0.12.0", + "base64 0.12.1", "bcrypt", "chrono", "comrak", @@ -1576,8 +1586,8 @@ dependencies = [ "rand 0.7.3", "regex 1.3.7", "rss", - "serde 1.0.106", - "serde_json 1.0.51", + "serde 1.0.110", + "serde_json 1.0.53", "sha2", "strum", "strum_macros", @@ -1599,9 +1609,9 @@ dependencies = [ "log", "native-tls", "nom 4.2.3", - "serde 1.0.106", + "serde 1.0.110", "serde_derive", - "serde_json 1.0.51", + "serde_json 1.0.53", ] [[package]] @@ -1633,9 +1643,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" +checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" [[package]] name = "libnghttp2-sys" @@ -1671,9 +1681,9 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "lock_api" @@ -1699,7 +1709,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" dependencies = [ - "linked-hash-map 0.5.2", + "linked-hash-map 0.5.3", ] [[package]] @@ -1789,9 +1799,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" +checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" dependencies = [ "cfg-if", "fuchsia-zircon", @@ -1808,9 +1818,9 @@ dependencies = [ [[package]] name = "mio-uds" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" dependencies = [ "iovec", "libc", @@ -1849,9 +1859,9 @@ dependencies = [ [[package]] name = "net2" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" dependencies = [ "cfg-if", "libc", @@ -1934,6 +1944,18 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" + +[[package]] +name = "once_cell" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" + [[package]] name = "opaque-debug" version = "0.2.3" @@ -1962,9 +1984,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.55" +version = "0.9.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7717097d810a0f2e2323f9e5d11e71608355e24828410b55b9d4f18aa5f9a5d8" +checksum = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e" dependencies = [ "autocfg 1.0.0", "cc", @@ -2059,18 +2081,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.9" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6a7f5eee6292c559c793430c55c00aea9d3b3d1905e855806ca4d7253426a2" +checksum = "81d480cb4e89522ccda96d0eed9af94180b7a5f93fb28f66e1fd7d68431663d1" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.9" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8988430ce790d8682672117bc06dda364c0be32d3abd738234f19f3240bad99a" +checksum = "a82996f11efccb19b685b14b5df818de31c1edcee3daa256ab5775dd98e72feb" dependencies = [ "proc-macro2", "quote", @@ -2079,9 +2101,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" +checksum = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f" [[package]] name = "pin-utils" @@ -2124,9 +2146,9 @@ checksum = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" [[package]] name = "proc-macro2" -version = "1.0.10" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" dependencies = [ "unicode-xid", ] @@ -2149,9 +2171,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +checksum = "42934bc9c8ab0d3b273a16d8551c8f0fcff46be73276ca083ec2414c15c4ba5e" dependencies = [ "proc-macro2", ] @@ -2400,13 +2422,13 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.12" +version = "0.16.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba5a8ec64ee89a76c98c549af81ff14813df09c3e6dc4766c3856da48597a0c" +checksum = "703516ae74571f24b465b4a1431e81e2ad51336cb0ded733a55a1aa3eccac196" dependencies = [ "cc", - "lazy_static 1.4.0", "libc", + "once_cell", "spin", "untrusted", "web-sys", @@ -2464,9 +2486,9 @@ checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" [[package]] name = "schannel" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" dependencies = [ "lazy_static 1.4.0", "winapi 0.3.8", @@ -2489,9 +2511,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f331b9025654145cd425b9ded0caf8f5ae0df80d418b326e2dc1c3dc5eb0620" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" dependencies = [ "bitflags", "core-foundation", @@ -2533,9 +2555,9 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" [[package]] name = "serde" -version = "1.0.106" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +checksum = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" dependencies = [ "serde_derive", ] @@ -2568,9 +2590,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.106" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" +checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" dependencies = [ "proc-macro2", "quote", @@ -2591,14 +2613,14 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.51" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" +checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" dependencies = [ "indexmap", "itoa 0.4.5", "ryu", - "serde 1.0.106", + "serde 1.0.110", ] [[package]] @@ -2618,7 +2640,7 @@ checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" dependencies = [ "dtoa 0.4.5", "itoa 0.4.5", - "serde 1.0.106", + "serde 1.0.110", "url", ] @@ -2759,9 +2781,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "410a7488c0a728c7ceb4ad59b9567eb4053d02e8cc7f5c0e0eeeb39518369213" +checksum = "4696caa4048ac7ce2bcd2e484b3cef88c1004e41b8e945a277e2c25dc0b72060" dependencies = [ "proc-macro2", "quote", @@ -2814,18 +2836,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d12a1dae4add0f0d568eebc7bf142f145ba1aa2544cafb195c76f0f409091b60" +checksum = "467e5ff447618a916519a4e0d62772ab14f434897f3d63f05d8700ef1e9b22c1" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f34e0c1caaa462fd840ec6b768946ea1e7842620d94fe29d5b847138f521269" +checksum = "e63c1091225b9834089b429bc4a2e01223470e3183e891582909e9d1c4cb55d9" dependencies = [ "proc-macro2", "quote", @@ -2862,9 +2884,9 @@ dependencies = [ [[package]] name = "threadpool" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dae184447c15d5a6916d973c642aec485105a13cd238192a6927ae3e077d66" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" dependencies = [ "num_cpus", ] @@ -2881,9 +2903,9 @@ dependencies = [ [[package]] name = "tokio" -version = "0.2.19" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d9c43f1bb96970e153bcbae39a65e249ccb942bd9d36dbdf086024920417c9c" +checksum = "d099fa27b9702bed751524694adbe393e18b36b204da91eb1cbbbbb4a5ee2d58" dependencies = [ "bytes", "fnv", @@ -2934,7 +2956,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" dependencies = [ - "serde 1.0.106", + "serde 1.0.110", ] [[package]] @@ -3063,9 +3085,9 @@ checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" [[package]] name = "untrusted" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" @@ -3076,7 +3098,7 @@ dependencies = [ "idna", "matches", "percent-encoding", - "serde 1.0.106", + "serde 1.0.110", ] [[package]] @@ -3101,7 +3123,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" dependencies = [ "rand 0.7.3", - "serde 1.0.106", + "serde 1.0.110", ] [[package]] @@ -3143,9 +3165,9 @@ checksum = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" [[package]] name = "vec_map" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" @@ -3167,9 +3189,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasm-bindgen" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc57ce05287f8376e998cbddfb4c8cb43b84a7ec55cf4551d7c00eef317a47f" +checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3177,9 +3199,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d967d37bf6c16cca2973ca3af071d0a2523392e4a594548155d89a678f4237cd" +checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94" dependencies = [ "bumpalo", "lazy_static 1.4.0", @@ -3192,9 +3214,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd151b63e1ea881bb742cd20e1d6127cef28399558f3b5d415289bc41eee3a4" +checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3202,9 +3224,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68a5b36eef1be7868f668632863292e37739656a80fc4b9acec7b0bd35a4931" +checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a" dependencies = [ "proc-macro2", "quote", @@ -3215,15 +3237,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf76fe7d25ac79748a37538b7daeed1c7a6867c92d3245c12c6222e4a20d639" +checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad" [[package]] name = "web-sys" -version = "0.3.37" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6f51648d8c56c366144378a33290049eafdd784071077f6fe37dae64c1c4cb" +checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642" dependencies = [ "js-sys", "wasm-bindgen", @@ -3312,5 +3334,5 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" dependencies = [ - "linked-hash-map 0.5.2", + "linked-hash-map 0.5.3", ] diff --git a/server/Cargo.toml b/server/Cargo.toml index ab76d06fa..cf680b669 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -8,17 +8,17 @@ edition = "2018" diesel = { version = "1.4.4", features = ["postgres","chrono","r2d2","64-column-tables","serde_json"] } diesel_migrations = "1.4.0" dotenv = "0.15.0" -activitystreams = "0.6.0" -bcrypt = "0.6.2" +activitystreams = "0.6.1" +bcrypt = "0.8.0" chrono = { version = "0.4.7", features = ["serde"] } -failure = "0.1.5" +failure = "0.1.8" serde_json = { version = "1.0.48", features = ["preserve_order"]} serde = { version = "1.0.105", features = ["derive"] } actix = "0.9.0" actix-web = "2.0.0" actix-files = "0.2.1" actix-web-actors = "2.0.0" -actix-rt = "1.0.0" +actix-rt = "1.1.1" log = "0.4.0" env_logger = "0.7.1" rand = "0.7.3" @@ -36,13 +36,13 @@ config = "0.10.1" hjson = "0.8.2" url = { version = "2.1.1", features = ["serde"] } percent-encoding = "2.1.0" -isahc = "0.9" +isahc = "0.9.2" comrak = "0.7" openssl = "0.10" http = "0.2.1" -http-signature-normalization = "0.4.1" -base64 = "0.12.0" -tokio = "0.2.18" -futures = "0.3.4" +http-signature-normalization = "0.5.1" +base64 = "0.12.1" +tokio = "0.2.21" +futures = "0.3.5" itertools = "0.9.0" uuid = { version = "0.8", features = ["serde", "v4"] } diff --git a/server/src/api/comment.rs b/server/src/api/comment.rs index 0660a52c8..f469b4622 100644 --- a/server/src/api/comment.rs +++ b/server/src/api/comment.rs @@ -76,8 +76,6 @@ impl Perform for Oper { let user_id = claims.id; - let hostname = &format!("https://{}", Settings::get().hostname); - let conn = pool.get()?; // Check for a community ban @@ -120,107 +118,9 @@ impl Perform for Oper { updated_comment.send_create(&user, &conn)?; - let mut recipient_ids = Vec::new(); - // Scan the comment for user mentions, add those rows - let extracted_usernames = extract_usernames(&comment_form.content); - - for username_mention in &extracted_usernames { - if let Ok(mention_user) = User_::read_from_name(&conn, username_mention) { - // You can't mention yourself - // At some point, make it so you can't tag the parent creator either - // This can cause two notifications, one for reply and the other for mention - if mention_user.id != user_id { - recipient_ids.push(mention_user.id); - - let user_mention_form = UserMentionForm { - recipient_id: mention_user.id, - comment_id: inserted_comment.id, - read: None, - }; - - // Allow this to fail softly, since comment edits might re-update or replace it - // Let the uniqueness handle this fail - match UserMention::create(&conn, &user_mention_form) { - Ok(_mention) => (), - Err(_e) => error!("{}", &_e), - }; - - // Send an email to those users that have notifications on - if mention_user.send_notifications_to_email { - if let Some(mention_email) = mention_user.email { - let subject = &format!( - "{} - Mentioned by {}", - Settings::get().hostname, - claims.username - ); - let html = &format!( - "

User Mention


{} - {}

inbox", - claims.username, comment_form.content, hostname - ); - match send_email(subject, &mention_email, &mention_user.name, html) { - Ok(_o) => _o, - Err(e) => error!("{}", e), - }; - } - } - } - } - } - - // Send notifs to the parent commenter / poster - match data.parent_id { - Some(parent_id) => { - let parent_comment = Comment::read(&conn, parent_id)?; - if parent_comment.creator_id != user_id { - let parent_user = User_::read(&conn, parent_comment.creator_id)?; - recipient_ids.push(parent_user.id); - - if parent_user.send_notifications_to_email { - if let Some(comment_reply_email) = parent_user.email { - let subject = &format!( - "{} - Reply from {}", - Settings::get().hostname, - claims.username - ); - let html = &format!( - "

Comment Reply


{} - {}

inbox", - claims.username, comment_form.content, hostname - ); - match send_email(subject, &comment_reply_email, &parent_user.name, html) { - Ok(_o) => _o, - Err(e) => error!("{}", e), - }; - } - } - } - } - // Its a post - None => { - if post.creator_id != user_id { - let parent_user = User_::read(&conn, post.creator_id)?; - recipient_ids.push(parent_user.id); - - if parent_user.send_notifications_to_email { - if let Some(post_reply_email) = parent_user.email { - let subject = &format!( - "{} - Reply from {}", - Settings::get().hostname, - claims.username - ); - let html = &format!( - "

Post Reply


{} - {}

inbox", - claims.username, comment_form.content, hostname - ); - match send_email(subject, &post_reply_email, &parent_user.name, html) { - Ok(_o) => _o, - Err(e) => error!("{}", e), - }; - } - } - } - } - }; + let mentions = scrape_text_for_mentions(&comment_form.content); + let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post); // You like your own comment by default let like_form = CommentLikeForm { @@ -353,53 +253,10 @@ impl Perform for Oper { updated_comment.send_update(&user, &conn)?; } - let mut recipient_ids = Vec::new(); + let post = Post::read(&conn, data.post_id)?; - // Scan the comment for user mentions, add those rows - let extracted_usernames = extract_usernames(&comment_form.content); - - for username_mention in &extracted_usernames { - let mention_user = User_::read_from_name(&conn, username_mention); - - if mention_user.is_ok() { - let mention_user_id = mention_user?.id; - - // You can't mention yourself - // At some point, make it so you can't tag the parent creator either - // This can cause two notifications, one for reply and the other for mention - if mention_user_id != user_id { - recipient_ids.push(mention_user_id); - - let user_mention_form = UserMentionForm { - recipient_id: mention_user_id, - comment_id: data.edit_id, - read: None, - }; - - // Allow this to fail softly, since comment edits might re-update or replace it - // Let the uniqueness handle this fail - match UserMention::create(&conn, &user_mention_form) { - Ok(_mention) => (), - Err(_e) => error!("{}", &_e), - } - } - } - } - - // Add to recipient ids - match data.parent_id { - Some(parent_id) => { - let parent_comment = Comment::read(&conn, parent_id)?; - if parent_comment.creator_id != user_id { - let parent_user = User_::read(&conn, parent_comment.creator_id)?; - recipient_ids.push(parent_user.id); - } - } - None => { - let post = Post::read(&conn, data.post_id)?; - recipient_ids.push(post.creator_id); - } - } + let mentions = scrape_text_for_mentions(&comment_form.content); + let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post); // Mod tables if let Some(removed) = data.removed.to_owned() { @@ -646,3 +503,106 @@ impl Perform for Oper { Ok(GetCommentsResponse { comments }) } } + +pub fn send_local_notifs( + conn: &PgConnection, + mentions: &[MentionData], + comment: &Comment, + user: &User_, + post: &Post, +) -> Vec { + let mut recipient_ids = Vec::new(); + let hostname = &format!("https://{}", Settings::get().hostname); + + // Send the local mentions + for mention in mentions + .iter() + .filter(|m| m.is_local() && m.name.ne(&user.name)) + .collect::>() + { + if let Ok(mention_user) = User_::read_from_name(&conn, &mention.name) { + // TODO + // At some point, make it so you can't tag the parent creator either + // This can cause two notifications, one for reply and the other for mention + recipient_ids.push(mention_user.id); + + let user_mention_form = UserMentionForm { + recipient_id: mention_user.id, + comment_id: comment.id, + read: None, + }; + + // Allow this to fail softly, since comment edits might re-update or replace it + // Let the uniqueness handle this fail + match UserMention::create(&conn, &user_mention_form) { + Ok(_mention) => (), + Err(_e) => error!("{}", &_e), + }; + + // Send an email to those users that have notifications on + if mention_user.send_notifications_to_email { + if let Some(mention_email) = mention_user.email { + let subject = &format!("{} - Mentioned by {}", Settings::get().hostname, user.name,); + let html = &format!( + "

User Mention


{} - {}

inbox", + user.name, comment.content, hostname + ); + match send_email(subject, &mention_email, &mention_user.name, html) { + Ok(_o) => _o, + Err(e) => error!("{}", e), + }; + } + } + } + } + + // Send notifs to the parent commenter / poster + match comment.parent_id { + Some(parent_id) => { + if let Ok(parent_comment) = Comment::read(&conn, parent_id) { + if parent_comment.creator_id != user.id { + if let Ok(parent_user) = User_::read(&conn, parent_comment.creator_id) { + recipient_ids.push(parent_user.id); + + if parent_user.send_notifications_to_email { + if let Some(comment_reply_email) = parent_user.email { + let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,); + let html = &format!( + "

Comment Reply


{} - {}

inbox", + user.name, comment.content, hostname + ); + match send_email(subject, &comment_reply_email, &parent_user.name, html) { + Ok(_o) => _o, + Err(e) => error!("{}", e), + }; + } + } + } + } + } + } + // Its a post + None => { + if post.creator_id != user.id { + if let Ok(parent_user) = User_::read(&conn, post.creator_id) { + recipient_ids.push(parent_user.id); + + if parent_user.send_notifications_to_email { + if let Some(post_reply_email) = parent_user.email { + let subject = &format!("{} - Reply from {}", Settings::get().hostname, user.name,); + let html = &format!( + "

Post Reply


{} - {}

inbox", + user.name, comment.content, hostname + ); + match send_email(subject, &post_reply_email, &parent_user.name, html) { + Ok(_o) => _o, + Err(e) => error!("{}", e), + }; + } + } + } + } + } + }; + recipient_ids +} diff --git a/server/src/api/mod.rs b/server/src/api/mod.rs index 9112a3be3..fca7c0357 100644 --- a/server/src/api/mod.rs +++ b/server/src/api/mod.rs @@ -18,8 +18,8 @@ use crate::db::user_mention_view::*; use crate::db::user_view::*; use crate::db::*; use crate::{ - extract_usernames, fetch_iframely_and_pictshare_data, generate_random_string, naive_from_unix, - naive_now, remove_slurs, send_email, slur_check, slurs_vec_to_str, + fetch_iframely_and_pictshare_data, generate_random_string, naive_from_unix, naive_now, + remove_slurs, scrape_text_for_mentions, send_email, slur_check, slurs_vec_to_str, MentionData, }; use crate::apub::{ diff --git a/server/src/apub/activities.rs b/server/src/apub/activities.rs index 23ae96987..9fc1fa1f4 100644 --- a/server/src/apub/activities.rs +++ b/server/src/apub/activities.rs @@ -2,7 +2,7 @@ use super::*; pub fn populate_object_props( props: &mut ObjectProperties, - addressed_to: &str, + addressed_ccs: Vec, object_id: &str, ) -> Result<(), Error> { props @@ -12,7 +12,7 @@ pub fn populate_object_props( // TODO: should to/cc go on the Create, or on the Post? or on both? // TODO: handle privacy on the receiving side (at least ignore anything thats not public) .set_to_xsd_any_uri(public())? - .set_cc_xsd_any_uri(addressed_to)?; + .set_many_cc_xsd_any_uris(addressed_ccs)?; Ok(()) } diff --git a/server/src/apub/comment.rs b/server/src/apub/comment.rs index df41f67be..f746fc0ff 100644 --- a/server/src/apub/comment.rs +++ b/server/src/apub/comment.rs @@ -88,6 +88,8 @@ impl FromApub for CommentForm { None => None, }; + // TODO this failed because a mention on a post that wasn't on this server yet. Has to do with + // fetching replytos let post = Post::read_from_apub_id(&conn, &post_ap_id)?; Ok(CommentForm { @@ -121,12 +123,15 @@ impl ApubObjectType for Comment { let community = Community::read(conn, post.community_id)?; let id = format!("{}/create/{}", self.ap_id, uuid::Uuid::new_v4()); + let maa: MentionsAndAddresses = + collect_non_local_mentions_and_addresses(&conn, &self.content, &community)?; + let mut create = Create::new(); - populate_object_props( - &mut create.object_props, - &community.get_followers_url(), - &id, - )?; + populate_object_props(&mut create.object_props, maa.addressed_ccs, &id)?; + + // Set the mention tags + create.object_props.set_many_tag_base_boxes(maa.tags)?; + create .create_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? @@ -134,7 +139,7 @@ impl ApubObjectType for Comment { insert_activity(&conn, creator.id, &create, true)?; - send_activity(&create, creator, community.get_follower_inboxes(&conn)?)?; + send_activity(&create, creator, maa.inboxes)?; Ok(()) } @@ -145,12 +150,15 @@ impl ApubObjectType for Comment { let community = Community::read(&conn, post.community_id)?; let id = format!("{}/update/{}", self.ap_id, uuid::Uuid::new_v4()); + let maa: MentionsAndAddresses = + collect_non_local_mentions_and_addresses(&conn, &self.content, &community)?; + let mut update = Update::new(); - populate_object_props( - &mut update.object_props, - &community.get_followers_url(), - &id, - )?; + populate_object_props(&mut update.object_props, maa.addressed_ccs, &id)?; + + // Set the mention tags + update.object_props.set_many_tag_base_boxes(maa.tags)?; + update .update_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? @@ -158,7 +166,7 @@ impl ApubObjectType for Comment { insert_activity(&conn, creator.id, &update, true)?; - send_activity(&update, creator, community.get_follower_inboxes(&conn)?)?; + send_activity(&update, creator, maa.inboxes)?; Ok(()) } @@ -171,7 +179,7 @@ impl ApubObjectType for Comment { populate_object_props( &mut delete.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; @@ -197,7 +205,7 @@ impl ApubObjectType for Comment { populate_object_props( &mut delete.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; @@ -213,7 +221,7 @@ impl ApubObjectType for Comment { populate_object_props( &mut undo.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &undo_id, )?; @@ -237,7 +245,7 @@ impl ApubObjectType for Comment { populate_object_props( &mut remove.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; @@ -263,7 +271,7 @@ impl ApubObjectType for Comment { populate_object_props( &mut remove.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; @@ -278,7 +286,7 @@ impl ApubObjectType for Comment { populate_object_props( &mut undo.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &undo_id, )?; @@ -302,7 +310,11 @@ impl ApubLikeableType for Comment { let id = format!("{}/like/{}", self.ap_id, uuid::Uuid::new_v4()); let mut like = Like::new(); - populate_object_props(&mut like.object_props, &community.get_followers_url(), &id)?; + populate_object_props( + &mut like.object_props, + vec![community.get_followers_url()], + &id, + )?; like .like_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? @@ -323,7 +335,7 @@ impl ApubLikeableType for Comment { let mut dislike = Dislike::new(); populate_object_props( &mut dislike.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; dislike @@ -344,7 +356,11 @@ impl ApubLikeableType for Comment { let id = format!("{}/dislike/{}", self.ap_id, uuid::Uuid::new_v4()); let mut like = Like::new(); - populate_object_props(&mut like.object_props, &community.get_followers_url(), &id)?; + populate_object_props( + &mut like.object_props, + vec![community.get_followers_url()], + &id, + )?; like .like_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? @@ -357,7 +373,7 @@ impl ApubLikeableType for Comment { populate_object_props( &mut undo.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &undo_id, )?; @@ -372,3 +388,57 @@ impl ApubLikeableType for Comment { Ok(()) } } + +struct MentionsAndAddresses { + addressed_ccs: Vec, + inboxes: Vec, + tags: Vec, +} + +/// This takes a comment, and builds a list of to_addresses, inboxes, +/// and mention tags, so they know where to be sent to. +/// Addresses are the users / addresses that go in the cc field. +fn collect_non_local_mentions_and_addresses( + conn: &PgConnection, + content: &str, + community: &Community, +) -> Result { + let mut addressed_ccs = vec![community.get_followers_url()]; + + // Add the mention tag + let mut tags = Vec::new(); + + // Get the inboxes for any mentions + let mentions = scrape_text_for_mentions(&content) + .into_iter() + // Filter only the non-local ones + .filter(|m| !m.is_local()) + .collect::>(); + let mut mention_inboxes = Vec::new(); + for mention in &mentions { + // TODO should it be fetching it every time? + if let Ok(actor_id) = fetch_webfinger_url(mention) { + debug!("mention actor_id: {}", actor_id); + addressed_ccs.push(actor_id.to_owned()); + let mention_user = get_or_fetch_and_upsert_remote_user(&actor_id, &conn)?; + let shared_inbox = mention_user.get_shared_inbox_url(); + mention_inboxes.push(shared_inbox); + let mut mention_tag = Mention::new(); + mention_tag + .link_props + .set_href(actor_id)? + .set_name_xsd_string(mention.full_name())?; + tags.push(mention_tag); + } + } + + let mut inboxes = community.get_follower_inboxes(&conn)?; + inboxes.extend(mention_inboxes); + inboxes = inboxes.into_iter().unique().collect(); + + Ok(MentionsAndAddresses { + addressed_ccs, + inboxes, + tags, + }) +} diff --git a/server/src/apub/community.rs b/server/src/apub/community.rs index f4e8848f2..d8ea86c7a 100644 --- a/server/src/apub/community.rs +++ b/server/src/apub/community.rs @@ -114,7 +114,11 @@ impl ActorType for Community { let id = format!("{}/delete/{}", self.actor_id, uuid::Uuid::new_v4()); let mut delete = Delete::default(); - populate_object_props(&mut delete.object_props, &self.get_followers_url(), &id)?; + populate_object_props( + &mut delete.object_props, + vec![self.get_followers_url()], + &id, + )?; delete .delete_props @@ -135,7 +139,11 @@ impl ActorType for Community { let id = format!("{}/delete/{}", self.actor_id, uuid::Uuid::new_v4()); let mut delete = Delete::default(); - populate_object_props(&mut delete.object_props, &self.get_followers_url(), &id)?; + populate_object_props( + &mut delete.object_props, + vec![self.get_followers_url()], + &id, + )?; delete .delete_props @@ -147,7 +155,11 @@ impl ActorType for Community { let undo_id = format!("{}/undo/delete/{}", self.actor_id, uuid::Uuid::new_v4()); let mut undo = Undo::default(); - populate_object_props(&mut undo.object_props, &self.get_followers_url(), &undo_id)?; + populate_object_props( + &mut undo.object_props, + vec![self.get_followers_url()], + &undo_id, + )?; undo .undo_props @@ -168,7 +180,11 @@ impl ActorType for Community { let id = format!("{}/remove/{}", self.actor_id, uuid::Uuid::new_v4()); let mut remove = Remove::default(); - populate_object_props(&mut remove.object_props, &self.get_followers_url(), &id)?; + populate_object_props( + &mut remove.object_props, + vec![self.get_followers_url()], + &id, + )?; remove .remove_props @@ -189,7 +205,11 @@ impl ActorType for Community { let id = format!("{}/remove/{}", self.actor_id, uuid::Uuid::new_v4()); let mut remove = Remove::default(); - populate_object_props(&mut remove.object_props, &self.get_followers_url(), &id)?; + populate_object_props( + &mut remove.object_props, + vec![self.get_followers_url()], + &id, + )?; remove .remove_props @@ -200,7 +220,11 @@ impl ActorType for Community { let undo_id = format!("{}/undo/remove/{}", self.actor_id, uuid::Uuid::new_v4()); let mut undo = Undo::default(); - populate_object_props(&mut undo.object_props, &self.get_followers_url(), &undo_id)?; + populate_object_props( + &mut undo.object_props, + vec![self.get_followers_url()], + &undo_id, + )?; undo .undo_props diff --git a/server/src/apub/extensions/signatures.rs b/server/src/apub/extensions/signatures.rs index e8630f214..e79e845de 100644 --- a/server/src/apub/extensions/signatures.rs +++ b/server/src/apub/extensions/signatures.rs @@ -55,7 +55,7 @@ pub fn sign(request: &Builder, actor: &dyn ActorType) -> Result { .unwrap() .as_str(), headers, - ) + )? .sign(signing_key_id, |signing_string| { let private_key = PKey::private_key_from_pem(actor.private_key().as_bytes())?; let mut signer = Signer::new(MessageDigest::sha256(), &private_key).unwrap(); diff --git a/server/src/apub/mod.rs b/server/src/apub/mod.rs index 6ff6a0f8f..9d4a60106 100644 --- a/server/src/apub/mod.rs +++ b/server/src/apub/mod.rs @@ -9,7 +9,6 @@ pub mod private_message; pub mod shared_inbox; pub mod user; pub mod user_inbox; - use crate::api::community::CommunityResponse; use crate::db::activity::insert_activity; use crate::websocket::server::SendCommunityRoomMessage; @@ -21,6 +20,7 @@ use activitystreams::{ context, endpoint::EndpointProperties, ext::{Ext, Extensible}, + link::Mention, object::{properties::ObjectProperties, Note, Page, Tombstone}, public, BaseBox, }; @@ -38,7 +38,7 @@ use serde::{Deserialize, Serialize}; use std::time::Duration; use url::Url; -use crate::api::comment::CommentResponse; +use crate::api::comment::{send_local_notifs, CommentResponse}; use crate::api::post::PostResponse; use crate::api::site::SearchResponse; use crate::api::user::PrivateMessageResponse; @@ -57,12 +57,13 @@ use crate::db::user::{UserForm, User_}; use crate::db::user_view::UserView; use crate::db::{Crud, Followable, Joinable, Likeable, SearchType}; use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown}; +use crate::routes::webfinger::WebFingerResponse; use crate::routes::{ChatServerParam, DbPoolParam}; use crate::websocket::{ server::{SendComment, SendPost, SendUserRoomMessage}, UserOperation, }; -use crate::{convert_datetime, naive_now, Settings}; +use crate::{convert_datetime, naive_now, scrape_text_for_mentions, MentionData, Settings}; use crate::apub::extensions::group_extensions::GroupExtension; use crate::apub::extensions::page_extension::PageExtension; @@ -280,3 +281,25 @@ pub trait ActorType { .to_ext() } } + +pub fn fetch_webfinger_url(mention: &MentionData) -> Result { + let fetch_url = format!( + "{}://{}/.well-known/webfinger?resource=acct:{}@{}", + get_apub_protocol_string(), + mention.domain, + mention.name, + mention.domain + ); + debug!("Fetching webfinger url: {}", &fetch_url); + let text = isahc::get(&fetch_url)?.text()?; + let res: WebFingerResponse = serde_json::from_str(&text)?; + let link = res + .links + .iter() + .find(|l| l.type_.eq(&Some("application/activity+json".to_string()))) + .ok_or_else(|| format_err!("No application/activity+json link found."))?; + link + .href + .to_owned() + .ok_or_else(|| format_err!("No href found.")) +} diff --git a/server/src/apub/post.rs b/server/src/apub/post.rs index b2374ffd7..3ea2040dc 100644 --- a/server/src/apub/post.rs +++ b/server/src/apub/post.rs @@ -124,7 +124,7 @@ impl ApubObjectType for Post { let mut create = Create::new(); populate_object_props( &mut create.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; create @@ -147,7 +147,7 @@ impl ApubObjectType for Post { let mut update = Update::new(); populate_object_props( &mut update.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; update @@ -169,7 +169,7 @@ impl ApubObjectType for Post { populate_object_props( &mut delete.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; @@ -193,7 +193,7 @@ impl ApubObjectType for Post { populate_object_props( &mut delete.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; @@ -209,7 +209,7 @@ impl ApubObjectType for Post { populate_object_props( &mut undo.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &undo_id, )?; @@ -233,7 +233,7 @@ impl ApubObjectType for Post { populate_object_props( &mut remove.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; @@ -256,7 +256,7 @@ impl ApubObjectType for Post { populate_object_props( &mut remove.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; @@ -271,7 +271,7 @@ impl ApubObjectType for Post { populate_object_props( &mut undo.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &undo_id, )?; @@ -295,7 +295,11 @@ impl ApubLikeableType for Post { let id = format!("{}/like/{}", self.ap_id, uuid::Uuid::new_v4()); let mut like = Like::new(); - populate_object_props(&mut like.object_props, &community.get_followers_url(), &id)?; + populate_object_props( + &mut like.object_props, + vec![community.get_followers_url()], + &id, + )?; like .like_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? @@ -315,7 +319,7 @@ impl ApubLikeableType for Post { let mut dislike = Dislike::new(); populate_object_props( &mut dislike.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &id, )?; dislike @@ -335,7 +339,11 @@ impl ApubLikeableType for Post { let id = format!("{}/like/{}", self.ap_id, uuid::Uuid::new_v4()); let mut like = Like::new(); - populate_object_props(&mut like.object_props, &community.get_followers_url(), &id)?; + populate_object_props( + &mut like.object_props, + vec![community.get_followers_url()], + &id, + )?; like .like_props .set_actor_xsd_any_uri(creator.actor_id.to_owned())? @@ -348,7 +356,7 @@ impl ApubLikeableType for Post { populate_object_props( &mut undo.object_props, - &community.get_followers_url(), + vec![community.get_followers_url()], &undo_id, )?; diff --git a/server/src/apub/shared_inbox.rs b/server/src/apub/shared_inbox.rs index 6bbe97251..b65f3a8a3 100644 --- a/server/src/apub/shared_inbox.rs +++ b/server/src/apub/shared_inbox.rs @@ -167,12 +167,18 @@ fn receive_create_comment( let comment = CommentForm::from_apub(¬e, &conn)?; let inserted_comment = Comment::create(conn, &comment)?; + let post = Post::read(&conn, inserted_comment.post_id)?; + + // Note: + // Although mentions could be gotten from the post tags (they are included there), or the ccs, + // Its much easier to scrape them from the comment body, since the API has to do that + // anyway. + let mentions = scrape_text_for_mentions(&inserted_comment.content); + let recipient_ids = send_local_notifs(&conn, &mentions, &inserted_comment, &user, &post); // Refetch the view let comment_view = CommentView::read(&conn, inserted_comment.id, None)?; - // TODO get those recipient actor ids from somewhere - let recipient_ids = vec![]; let res = CommentResponse { comment: comment_view, recipient_ids, @@ -353,13 +359,15 @@ fn receive_update_comment( let comment = CommentForm::from_apub(¬e, &conn)?; let comment_id = Comment::read_from_apub_id(conn, &comment.ap_id)?.id; - Comment::update(conn, comment_id, &comment)?; + let updated_comment = Comment::update(conn, comment_id, &comment)?; + let post = Post::read(&conn, updated_comment.post_id)?; + + let mentions = scrape_text_for_mentions(&updated_comment.content); + let recipient_ids = send_local_notifs(&conn, &mentions, &updated_comment, &user, &post); // Refetch the view let comment_view = CommentView::read(&conn, comment_id, None)?; - // TODO get those recipient actor ids from somewhere - let recipient_ids = vec![]; let res = CommentResponse { comment: comment_view, recipient_ids, diff --git a/server/src/db/community.rs b/server/src/db/community.rs index 9b858fde3..fad19c4c5 100644 --- a/server/src/db/community.rs +++ b/server/src/db/community.rs @@ -92,10 +92,6 @@ impl Community { use crate::schema::community::dsl::*; community.filter(local.eq(true)).load::(conn) } - - pub fn get_url(&self) -> String { - format!("https://{}/c/{}", Settings::get().hostname, self.name) - } } #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] diff --git a/server/src/lib.rs b/server/src/lib.rs index ce886754e..b68e2af58 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -40,6 +40,7 @@ use crate::settings::Settings; use actix_web::dev::ConnectionInfo; use chrono::{DateTime, FixedOffset, Local, NaiveDateTime, Utc}; use isahc::prelude::*; +use itertools::Itertools; use lettre::smtp::authentication::{Credentials, Mechanism}; use lettre::smtp::extension::ClientId; use lettre::smtp::ConnectionReuseParameters; @@ -103,20 +104,6 @@ pub fn slurs_vec_to_str(slurs: Vec<&str>) -> String { [start, combined].concat() } -pub fn extract_usernames(test: &str) -> Vec<&str> { - let mut matches: Vec<&str> = USERNAME_MATCHES_REGEX - .find_iter(test) - .map(|mat| mat.as_str()) - .collect(); - - // Unique - matches.sort_unstable(); - matches.dedup(); - - // Remove /u/ - matches.iter().map(|t| &t[3..]).collect() -} - pub fn generate_random_string() -> String { thread_rng().sample_iter(&Alphanumeric).take(30).collect() } @@ -249,9 +236,48 @@ pub fn get_ip(conn_info: &ConnectionInfo) -> String { .to_string() } +// TODO nothing is done with community / group webfingers yet, so just ignore those for now +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MentionData { + pub name: String, + pub domain: String, +} + +impl MentionData { + pub fn is_local(&self) -> bool { + Settings::get().hostname.eq(&self.domain) + } + pub fn full_name(&self) -> String { + format!("@{}@{}", &self.name, &self.domain) + } +} + +pub fn scrape_text_for_mentions(text: &str) -> Vec { + let mut out: Vec = Vec::new(); + for caps in WEBFINGER_USER_REGEX.captures_iter(text) { + out.push(MentionData { + name: caps["name"].to_string(), + domain: caps["domain"].to_string(), + }); + } + out.into_iter().unique().collect() +} + #[cfg(test)] mod tests { - use crate::{extract_usernames, is_email_regex, remove_slurs, slur_check, slurs_vec_to_str}; + use crate::{ + is_email_regex, remove_slurs, scrape_text_for_mentions, slur_check, slurs_vec_to_str, + }; + + #[test] + fn test_mentions_regex() { + let text = "Just read a great blog post by [@tedu@honk.teduangst.com](/u/test). And another by !test_community@fish.teduangst.com . Another [@lemmy@lemmy_alpha:8540](/u/fish)"; + let mentions = scrape_text_for_mentions(text); + + assert_eq!(mentions[0].name, "tedu".to_string()); + assert_eq!(mentions[0].domain, "honk.teduangst.com".to_string()); + assert_eq!(mentions[1].domain, "lemmy_alpha:8540".to_string()); + } #[test] fn test_email() { @@ -287,13 +313,6 @@ mod tests { } } - #[test] - fn test_extract_usernames() { - let usernames = extract_usernames("this is a user mention for [/u/testme](/u/testme) and thats all. Oh [/u/another](/u/another) user. And the first again [/u/testme](/u/testme) okay"); - let expected = vec!["another", "testme"]; - assert_eq!(usernames, expected); - } - // These helped with testing // #[test] // fn test_iframely() { @@ -320,4 +339,7 @@ lazy_static! { static ref EMAIL_REGEX: Regex = Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap(); static ref SLUR_REGEX: Regex = RegexBuilder::new(r"(fag(g|got|tard)?|maricos?|cock\s?sucker(s|ing)?|nig(\b|g?(a|er)?(s|z)?)\b|dindu(s?)|mudslime?s?|kikes?|mongoloids?|towel\s*heads?|\bspi(c|k)s?\b|\bchinks?|niglets?|beaners?|\bnips?\b|\bcoons?\b|jungle\s*bunn(y|ies?)|jigg?aboo?s?|\bpakis?\b|rag\s*heads?|gooks?|cunts?|bitch(es|ing|y)?|puss(y|ies?)|twats?|feminazis?|whor(es?|ing)|\bslut(s|t?y)?|\btrann?(y|ies?)|ladyboy(s?)|\b(b|re|r)tard(ed)?s?)").case_insensitive(true).build().unwrap(); static ref USERNAME_MATCHES_REGEX: Regex = Regex::new(r"/u/[a-zA-Z][0-9a-zA-Z_]*").unwrap(); + // TODO keep this old one, it didn't work with port well tho + // static ref WEBFINGER_USER_REGEX: Regex = Regex::new(r"@(?P[\w.]+)@(?P[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)").unwrap(); + static ref WEBFINGER_USER_REGEX: Regex = Regex::new(r"@(?P[\w.]+)@(?P[a-zA-Z0-9._:-]+)").unwrap(); } diff --git a/server/src/routes/feeds.rs b/server/src/routes/feeds.rs index 6bb70b931..999252c47 100644 --- a/server/src/routes/feeds.rs +++ b/server/src/routes/feeds.rs @@ -142,7 +142,6 @@ fn get_feed_community( ) -> Result { let site_view = SiteView::read(&conn)?; let community = Community::read_from_name(&conn, &community_name)?; - let community_url = community.get_url(); let posts = PostQueryBuilder::create(&conn) .listing_type(ListingType::All) @@ -155,7 +154,7 @@ fn get_feed_community( let mut channel_builder = ChannelBuilder::default(); channel_builder .title(&format!("{} - {}", site_view.name, community.name)) - .link(community_url) + .link(community.actor_id) .items(items); if let Some(community_desc) = community.description { diff --git a/server/src/routes/mod.rs b/server/src/routes/mod.rs index 109dbd76d..957bf1ba8 100644 --- a/server/src/routes/mod.rs +++ b/server/src/routes/mod.rs @@ -1,6 +1,8 @@ use crate::api::{Oper, Perform}; use crate::apub::get_apub_protocol_string; +use crate::db::community::Community; use crate::db::site_view::SiteView; +use crate::db::user::User_; use crate::rate_limit::rate_limiter::RateLimiter; use crate::websocket::{server::ChatServer, WebsocketInfo}; use crate::{get_ip, markdown_to_html, version, Settings}; @@ -13,11 +15,10 @@ use diesel::{ r2d2::{ConnectionManager, Pool}, PgConnection, }; -use log::{error, info}; +use log::{debug, error, info}; use regex::Regex; use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder}; use serde::{Deserialize, Serialize}; -use serde_json::json; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; diff --git a/server/src/routes/webfinger.rs b/server/src/routes/webfinger.rs index 7bdce2067..439064ad7 100644 --- a/server/src/routes/webfinger.rs +++ b/server/src/routes/webfinger.rs @@ -1,11 +1,27 @@ use super::*; -use crate::db::community::Community; #[derive(Deserialize)] pub struct Params { resource: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct WebFingerResponse { + pub subject: String, + pub aliases: Vec, + pub links: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct WebFingerLink { + pub rel: Option, + #[serde(rename(serialize = "type", deserialize = "type"))] + pub type_: Option, + pub href: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub template: Option, +} + pub fn config(cfg: &mut web::ServiceConfig) { if Settings::get().federation.enabled { cfg.route( @@ -21,6 +37,11 @@ lazy_static! { Settings::get().hostname )) .unwrap(); + static ref WEBFINGER_USER_REGEX: Regex = Regex::new(&format!( + "^acct:([a-z0-9_]{{3, 20}})@{}$", + Settings::get().hostname + )) + .unwrap(); } /// Responds to webfinger requests of the following format. There isn't any real documentation for @@ -36,46 +57,58 @@ async fn get_webfinger_response( let res = web::block(move || { let conn = db.get()?; - let regex_parsed = WEBFINGER_COMMUNITY_REGEX + let community_regex_parsed = WEBFINGER_COMMUNITY_REGEX .captures(&info.resource) .map(|c| c.get(1)) .flatten(); - let community_name = match regex_parsed { - Some(c) => c.as_str(), - None => return Err(format_err!("not_found")), + + let user_regex_parsed = WEBFINGER_USER_REGEX + .captures(&info.resource) + .map(|c| c.get(1)) + .flatten(); + + let url = if let Some(community_name) = community_regex_parsed { + // Make sure the requested community exists. + let community = match Community::read_from_name(&conn, &community_name.as_str()) { + Ok(o) => o, + Err(_) => return Err(format_err!("not_found")), + }; + community.actor_id + } else if let Some(user_name) = user_regex_parsed { + // Make sure the requested user exists. + let user = match User_::read_from_name(&conn, &user_name.as_str()) { + Ok(o) => o, + Err(_) => return Err(format_err!("not_found")), + }; + user.actor_id + } else { + return Err(format_err!("not_found")); }; - // Make sure the requested community exists. - let community = match Community::read_from_name(&conn, &community_name) { - Ok(o) => o, - Err(_) => return Err(format_err!("not_found")), + let wf_res = WebFingerResponse { + subject: info.resource.to_owned(), + aliases: vec![url.to_owned()], + links: vec![ + WebFingerLink { + rel: Some("http://webfinger.net/rel/profile-page".to_string()), + type_: Some("text/html".to_string()), + href: Some(url.to_owned()), + template: None, + }, + WebFingerLink { + rel: Some("self".to_string()), + type_: Some("application/activity+json".to_string()), + href: Some(url), + template: None, + }, // TODO: this also needs to return the subscribe link once that's implemented + //{ + // "rel": "http://ostatus.org/schema/1.0/subscribe", + // "template": "https://my_instance.com/authorize_interaction?uri={uri}" + //} + ], }; - let community_url = community.get_url(); - - Ok(json!({ - "subject": info.resource, - "aliases": [ - community_url, - ], - "links": [ - { - "rel": "http://webfinger.net/rel/profile-page", - "type": "text/html", - "href": community.get_url(), - }, - { - "rel": "self", - "type": "application/activity+json", - "href": community_url - } - // TODO: this also needs to return the subscribe link once that's implemented - //{ - // "rel": "http://ostatus.org/schema/1.0/subscribe", - // "template": "https://my_instance.com/authorize_interaction?uri={uri}" - //} - ] - })) + Ok(wf_res) }) .await .map(|json| HttpResponse::Ok().json(json)) diff --git a/server/src/routes/websocket.rs b/server/src/routes/websocket.rs index a95ff9ee8..8b94965a4 100644 --- a/server/src/routes/websocket.rs +++ b/server/src/routes/websocket.rs @@ -143,7 +143,7 @@ impl WSSession { // check client heartbeats if Instant::now().duration_since(act.hb) > CLIENT_TIMEOUT { // heartbeat timed out - error!("Websocket Client heartbeat failed, disconnecting!"); + debug!("Websocket Client heartbeat failed, disconnecting!"); // notify chat server act.cs_addr.do_send(Disconnect { diff --git a/ui/src/api_tests/api.spec.ts b/ui/src/api_tests/api.spec.ts index dcfb5e628..d5da17c7f 100644 --- a/ui/src/api_tests/api.spec.ts +++ b/ui/src/api_tests/api.spec.ts @@ -22,6 +22,7 @@ import { EditPrivateMessageForm, PrivateMessageResponse, PrivateMessagesResponse, + GetUserMentionsResponse, } from '../interfaces'; let lemmyAlphaUrl = 'http://localhost:8540'; @@ -379,6 +380,44 @@ describe('main', () => { expect(getPostResAlpha.comments[0].community_local).toBe(false); expect(getPostResAlpha.comments[0].creator_local).toBe(false); expect(getPostResAlpha.comments[0].score).toBe(1); + + // Lemmy alpha responds to their own comment, but mentions lemmy beta. + // Make sure lemmy beta gets that in their inbox. + let mentionContent = 'A test mention of @lemmy_beta@lemmy_beta:8550'; + let mentionCommentForm: CommentForm = { + content: mentionContent, + post_id: 2, + parent_id: createResponse.comment.id, + auth: lemmyAlphaAuth, + }; + + let createMentionRes: CommentResponse = await fetch( + `${lemmyAlphaApiUrl}/comment`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: wrapper(mentionCommentForm), + } + ).then(d => d.json()); + + expect(createMentionRes.comment.content).toBe(mentionContent); + expect(createMentionRes.comment.community_local).toBe(false); + expect(createMentionRes.comment.creator_local).toBe(true); + expect(createMentionRes.comment.score).toBe(1); + + // Make sure lemmy beta sees that new mention + let getMentionUrl = `${lemmyBetaApiUrl}/user/mention?sort=New&unread_only=false&auth=${lemmyBetaAuth}`; + let getMentionsRes: GetUserMentionsResponse = await fetch(getMentionUrl, { + method: 'GET', + }).then(d => d.json()); + + // The newest show up first + expect(getMentionsRes.mentions[0].content).toBe(mentionContent); + expect(getMentionsRes.mentions[0].community_local).toBe(true); + expect(getMentionsRes.mentions[0].creator_local).toBe(false); + expect(getMentionsRes.mentions[0].score).toBe(1); }); }); @@ -413,9 +452,9 @@ describe('main', () => { method: 'GET', }).then(d => d.json()); - expect(getPostRes.comments[1].content).toBe(content); - expect(getPostRes.comments[1].community_local).toBe(true); - expect(getPostRes.comments[1].creator_local).toBe(false); + expect(getPostRes.comments[2].content).toBe(content); + expect(getPostRes.comments[2].community_local).toBe(true); + expect(getPostRes.comments[2].creator_local).toBe(false); }); }); diff --git a/ui/src/utils.ts b/ui/src/utils.ts index 277da9874..54322c8b2 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -502,8 +502,8 @@ export function setupTribute(): Tribute { trigger: '@', selectTemplate: (item: any) => { let link = item.original.local - ? `[@${item.original.key}](/u/${item.original.key})` - : `[@${item.original.key}](/user/${item.original.id})`; + ? `[${item.original.key}](/u/${item.original.name})` + : `[${item.original.key}](/user/${item.original.id})`; return link; }, values: (text: string, cb: any) => { @@ -520,8 +520,8 @@ export function setupTribute(): Tribute { trigger: '!', selectTemplate: (item: any) => { let link = item.original.local - ? `[!${item.original.key}](/c/${item.original.key})` - : `[!${item.original.key}](/community/${item.original.id})`; + ? `[${item.original.key}](/c/${item.original.name})` + : `[${item.original.key}](/community/${item.original.id})`; return link; }, values: (text: string, cb: any) => { @@ -565,9 +565,9 @@ function userSearch(text: string, cb: any) { if (res.op == UserOperation.Search) { let data = res.data as SearchResponse; let users = data.users.map(u => { - let name_ = u.local ? u.name : `${u.name}@${hostname(u.actor_id)}`; return { - key: name_, + key: `@${u.name}@${hostname(u.actor_id)}`, + name: u.name, local: u.local, id: u.id, }; @@ -602,9 +602,9 @@ function communitySearch(text: string, cb: any) { if (res.op == UserOperation.Search) { let data = res.data as SearchResponse; let communities = data.communities.map(c => { - let name_ = c.local ? c.name : `${c.name}@${hostname(c.actor_id)}`; return { - key: name_, + key: `!${c.name}@${hostname(c.actor_id)}`, + name: c.name, local: c.local, id: c.id, }; @@ -859,5 +859,8 @@ export function previewLines(text: string, lines: number = 3): string { } export function hostname(url: string): string { - return new URL(url).hostname; + let cUrl = new URL(url); + return window.location.port + ? `${cUrl.hostname}:${cUrl.port}` + : `${cUrl.hostname}`; }