# 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",
"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",
"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",
]
"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",
"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"
[[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",
"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"
[[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]]
[[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"
[[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"
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",
"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",
]
[[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",
[[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",
[[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",
"diesel_derives",
"pq-sys",
"r2d2",
- "serde_json 1.0.51",
+ "serde_json 1.0.53",
]
[[package]]
[[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",
]
[[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",
[[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",
[[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"
[[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",
[[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",
[[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",
[[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",
[[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",
"futures-sink",
"futures-task",
"memchr 2.3.3",
+ "pin-project",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
"wasi",
]
+[[package]]
+name = "gimli"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c"
+
[[package]]
name = "h2"
-version = "0.2.4"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42"
+checksum = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff"
dependencies = [
"bytes",
"fnv",
[[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",
[[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",
[[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",
[[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",
]
"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",
]
"actix-rt",
"actix-web",
"actix-web-actors",
- "base64 0.12.0",
+ "base64 0.12.1",
"bcrypt",
"chrono",
"comrak",
"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",
"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]]
[[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"
[[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"
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]]
[[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",
[[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",
[[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",
"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"
[[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",
[[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",
[[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"
[[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",
]
[[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",
]
[[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",
[[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",
[[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",
[[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",
]
[[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",
[[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]]
dependencies = [
"dtoa 0.4.5",
"itoa 0.4.5",
- "serde 1.0.106",
+ "serde 1.0.110",
"url",
]
[[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",
[[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",
[[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",
]
[[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",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
dependencies = [
- "serde 1.0.106",
+ "serde 1.0.110",
]
[[package]]
[[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"
"idna",
"matches",
"percent-encoding",
- "serde 1.0.106",
+ "serde 1.0.110",
]
[[package]]
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
dependencies = [
"rand 0.7.3",
- "serde 1.0.106",
+ "serde 1.0.110",
]
[[package]]
[[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"
[[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",
[[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",
[[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",
[[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",
[[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",
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",
]
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"
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"] }
let user_id = claims.id;
- let hostname = &format!("https://{}", Settings::get().hostname);
-
let conn = pool.get()?;
// Check for a community ban
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!(
- "<h1>User Mention</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
- 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!(
- "<h1>Comment Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
- 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!(
- "<h1>Post Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
- 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 {
updated_comment.send_update(&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 {
- 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),
- }
- }
- }
- }
+ let post = Post::read(&conn, data.post_id)?;
- // 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() {
Ok(GetCommentsResponse { comments })
}
}
+
+pub fn send_local_notifs(
+ conn: &PgConnection,
+ mentions: &[MentionData],
+ comment: &Comment,
+ user: &User_,
+ post: &Post,
+) -> Vec<i32> {
+ 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::<Vec<&MentionData>>()
+ {
+ 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!(
+ "<h1>User Mention</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
+ 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!(
+ "<h1>Comment Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
+ 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!(
+ "<h1>Post Reply</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
+ 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
+}
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::{
pub fn populate_object_props(
props: &mut ObjectProperties,
- addressed_to: &str,
+ addressed_ccs: Vec<String>,
object_id: &str,
) -> Result<(), Error> {
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(())
}
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 {
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())?
insert_activity(&conn, creator.id, &create, true)?;
- send_activity(&create, creator, community.get_follower_inboxes(&conn)?)?;
+ send_activity(&create, creator, maa.inboxes)?;
Ok(())
}
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())?
insert_activity(&conn, creator.id, &update, true)?;
- send_activity(&update, creator, community.get_follower_inboxes(&conn)?)?;
+ send_activity(&update, creator, maa.inboxes)?;
Ok(())
}
populate_object_props(
&mut delete.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
populate_object_props(
&mut delete.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
populate_object_props(
&mut undo.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&undo_id,
)?;
populate_object_props(
&mut remove.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
populate_object_props(
&mut remove.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
populate_object_props(
&mut undo.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&undo_id,
)?;
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())?
let mut dislike = Dislike::new();
populate_object_props(
&mut dislike.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
dislike
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())?
populate_object_props(
&mut undo.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&undo_id,
)?;
Ok(())
}
}
+
+struct MentionsAndAddresses {
+ addressed_ccs: Vec<String>,
+ inboxes: Vec<String>,
+ tags: Vec<Mention>,
+}
+
+/// 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<MentionsAndAddresses, Error> {
+ 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::<Vec<MentionData>>();
+ 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,
+ })
+}
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
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
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
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
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
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
.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();
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;
context,
endpoint::EndpointProperties,
ext::{Ext, Extensible},
+ link::Mention,
object::{properties::ObjectProperties, Note, Page, Tombstone},
public, BaseBox,
};
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;
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;
.to_ext()
}
}
+
+pub fn fetch_webfinger_url(mention: &MentionData) -> Result<String, Error> {
+ 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."))
+}
let mut create = Create::new();
populate_object_props(
&mut create.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
create
let mut update = Update::new();
populate_object_props(
&mut update.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
update
populate_object_props(
&mut delete.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
populate_object_props(
&mut delete.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
populate_object_props(
&mut undo.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&undo_id,
)?;
populate_object_props(
&mut remove.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
populate_object_props(
&mut remove.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
populate_object_props(
&mut undo.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&undo_id,
)?;
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())?
let mut dislike = Dislike::new();
populate_object_props(
&mut dislike.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&id,
)?;
dislike
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())?
populate_object_props(
&mut undo.object_props,
- &community.get_followers_url(),
+ vec![community.get_followers_url()],
&undo_id,
)?;
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,
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,
use crate::schema::community::dsl::*;
community.filter(local.eq(true)).load::<Community>(conn)
}
-
- pub fn get_url(&self) -> String {
- format!("https://{}/c/{}", Settings::get().hostname, self.name)
- }
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
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;
[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()
}
.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<MentionData> {
+ let mut out: Vec<MentionData> = 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() {
}
}
- #[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() {
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<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)").unwrap();
+ static ref WEBFINGER_USER_REGEX: Regex = Regex::new(r"@(?P<name>[\w.]+)@(?P<domain>[a-zA-Z0-9._:-]+)").unwrap();
}
) -> Result<ChannelBuilder, failure::Error> {
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)
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 {
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};
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};
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<String>,
+ pub links: Vec<WebFingerLink>,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct WebFingerLink {
+ pub rel: Option<String>,
+ #[serde(rename(serialize = "type", deserialize = "type"))]
+ pub type_: Option<String>,
+ pub href: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub template: Option<String>,
+}
+
pub fn config(cfg: &mut web::ServiceConfig) {
if Settings::get().federation.enabled {
cfg.route(
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
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 user_regex_parsed = WEBFINGER_USER_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")),
- };
- // 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 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"));
};
- let community_url = community.get_url();
+ 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}"
+ //}
+ ],
+ };
- 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))
// 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 {
EditPrivateMessageForm,
PrivateMessageResponse,
PrivateMessagesResponse,
+ GetUserMentionsResponse,
} from '../interfaces';
let lemmyAlphaUrl = 'http://localhost:8540';
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);
});
});
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);
});
});
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) => {
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) => {
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,
};
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,
};
}
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}`;
}