diff --git a/CHANGELOG.md b/CHANGELOG.md index 12b8298c..e8e842c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Bump Rust to nightly 2022-01-26 (#1015) - Remove "Latest articles" timeline (#1069) - Change order of timeline tabs (#1069, #1070, #1072) +- Migrate ActivityPub-related crates from activitypub 0.1 to activitystreams 0.7 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 6a510d93..16d066c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,61 +3,45 @@ version = 3 [[package]] -name = "activitypub" -version = "0.1.6" +name = "activitystreams" +version = "0.7.0-alpha.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bfd311e7b4102971757a2a6f143a93b1a8e6b5afc2c46936af827fd9eab403f" +checksum = "459a89e7d449cf49e57044d59dcf637d2927362b7a75737c90bd3679ba476a78" dependencies = [ - "activitystreams-derive", - "activitystreams-traits", - "activitystreams-types", - "serde 1.0.137", - "serde_derive", - "serde_json", -] - -[[package]] -name = "activitystreams-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176bdecfca82b1980e4769e3d54b6a392284b724083e0bff68272e290f17458f" -dependencies = [ - "proc-macro2 0.3.8", - "quote 0.5.2", - "syn 0.13.11", -] - -[[package]] -name = "activitystreams-traits" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ef03168e704b0cae242e7a5d8b40506772b339687e01a3496fc4afe2e8542" -dependencies = [ - "failure", - "serde 1.0.137", - "serde_json", -] - -[[package]] -name = "activitystreams-types" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff74c5765278614a009f97b9ec12f9a7c732bbcc5e0337fcfcab619b784860ec" -dependencies = [ - "activitystreams-derive", - "activitystreams-traits", - "chrono", + "activitystreams-kinds", + "iri-string", "mime 0.3.16", "serde 1.0.137", - "serde_derive", "serde_json", + "time 0.3.9", +] + +[[package]] +name = "activitystreams-ext" +version = "0.1.0-alpha.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb8e19a0810cc25df3535061a08b7d8f8a734d309ea4411c57a9767e4a2ffa0e" +dependencies = [ + "activitystreams", + "serde 1.0.137", + "serde_json", +] + +[[package]] +name = "activitystreams-kinds" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d014a4fb8828870b7b46bee6257b9a89d06188ae8d435381ba94f14c8c697d8" +dependencies = [ + "iri-string", + "serde 1.0.137", ] [[package]] name = "addr2line" -version = "0.15.2" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a2e47a1fbe209ee101dd6d61285226744c6c8d3c21c8dc878ba6cb9f467f3a" +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ "gimli", ] @@ -215,12 +199,12 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.52" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" +checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -261,9 +245,12 @@ dependencies = [ [[package]] name = "autocfg" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] [[package]] name = "autocfg" @@ -288,9 +275,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.59" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4717cfcbfaa661a0fd48f8453951837ae7e8f81e481fbb136e3202d72805a744" +checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" dependencies = [ "addr2line", "cc", @@ -462,9 +449,9 @@ checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" [[package]] name = "cc" -version = "1.0.72" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "census" @@ -504,7 +491,7 @@ checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ "libc", "num-integer", - "num-traits 0.2.14", + "num-traits 0.2.15", "serde 1.0.137", "time 0.1.43", "winapi 0.3.9", @@ -555,9 +542,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.3" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062" +checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" dependencies = [ "memchr", ] @@ -574,7 +561,7 @@ dependencies = [ "serde 1.0.137", "serde-hjson", "serde_json", - "toml 0.5.8", + "toml 0.5.9", "yaml-rust", ] @@ -630,7 +617,7 @@ dependencies = [ "hkdf", "hmac", "percent-encoding 2.1.0", - "rand 0.8.4", + "rand 0.8.5", "sha2", "time 0.1.43", ] @@ -654,7 +641,7 @@ dependencies = [ "cookie 0.12.0", "failure", "idna 0.1.5", - "log 0.4.14", + "log 0.4.17", "publicsuffix", "serde 1.0.137", "serde_json", @@ -665,9 +652,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" dependencies = [ "core-foundation-sys", "libc", @@ -681,9 +668,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" dependencies = [ "libc", ] @@ -696,9 +683,9 @@ checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" [[package]] name = "crc32fast" -version = "1.3.0" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if 1.0.0", ] @@ -724,11 +711,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" dependencies = [ "cfg-if 1.0.0", - "crossbeam-channel 0.5.2", + "crossbeam-channel 0.5.4", "crossbeam-deque 0.8.1", - "crossbeam-epoch 0.9.6", - "crossbeam-queue 0.3.3", - "crossbeam-utils 0.8.6", + "crossbeam-epoch 0.9.8", + "crossbeam-queue 0.3.5", + "crossbeam-utils 0.8.8", ] [[package]] @@ -743,12 +730,12 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.6", + "crossbeam-utils 0.8.8", ] [[package]] @@ -769,8 +756,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ "cfg-if 1.0.0", - "crossbeam-epoch 0.9.6", - "crossbeam-utils 0.8.6", + "crossbeam-epoch 0.9.8", + "crossbeam-utils 0.8.8", ] [[package]] @@ -790,12 +777,13 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.6" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" dependencies = [ + "autocfg 1.1.0", "cfg-if 1.0.0", - "crossbeam-utils 0.8.6", + "crossbeam-utils 0.8.8", "lazy_static", "memoffset 0.6.5", "scopeguard", @@ -814,12 +802,12 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b979d76c9fcb84dffc80a73f7290da0f83e4c95773494674cb44b76d13a7a110" +checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" dependencies = [ "cfg-if 1.0.0", - "crossbeam-utils 0.8.6", + "crossbeam-utils 0.8.8", ] [[package]] @@ -835,9 +823,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ "cfg-if 1.0.0", "lazy_static", @@ -912,8 +900,8 @@ checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "strsim 0.10.0", "syn 1.0.92", ] @@ -925,7 +913,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a" dependencies = [ "darling_core", - "quote 1.0.15", + "quote 1.0.18", "syn 1.0.92", ] @@ -987,8 +975,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5" dependencies = [ "darling", - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -1067,8 +1055,8 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -1211,9 +1199,9 @@ checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" [[package]] name = "encoding_rs" -version = "0.8.30" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ "cfg-if 1.0.0", ] @@ -1225,7 +1213,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be3c61c59fdc91f5dbc3ea31ee8623122ce80057058be560654c5d410d181a6" dependencies = [ "lazy_static", - "log 0.4.14", + "log 0.4.17", "rand 0.7.3", ] @@ -1235,7 +1223,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" dependencies = [ - "backtrace 0.3.59", + "backtrace 0.3.65", "failure_derive", ] @@ -1245,8 +1233,8 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", "synstructure", ] @@ -1262,30 +1250,30 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2" +checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" dependencies = [ "instant", ] [[package]] name = "filetime" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.10", + "redox_syscall 0.2.13", "winapi 0.3.9", ] [[package]] name = "flate2" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af" dependencies = [ "cfg-if 1.0.0", "crc32fast", @@ -1390,9 +1378,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "futf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c9c1ce3fa9336301af935ab852c437817d14cd33690446569392e65170aac3b" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" dependencies = [ "mac", "new_debug_unreachable", @@ -1469,8 +1457,8 @@ version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -1499,7 +1487,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "pin-utils", "slab", ] @@ -1556,8 +1544,8 @@ checksum = "649db3b5cda06091ea6aacb9f66f7002dfe885505b324b8ed795261253ffc2b3" dependencies = [ "gettext", "gettext-utils", - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -1579,9 +1567,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.24.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4075386626662786ddb0ec9081e7c7eeb1ba31951f447ca780ef9f5d568189" +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" [[package]] name = "glob" @@ -1614,7 +1602,7 @@ dependencies = [ "chomp", "guid", "guid-parser", - "rand 0.8.4", + "rand 0.8.5", "winapi 0.3.9", ] @@ -1653,7 +1641,7 @@ dependencies = [ "futures 0.1.31", "http 0.1.21", "indexmap", - "log 0.4.14", + "log 0.4.17", "slab", "string", "tokio-io", @@ -1670,7 +1658,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 0.2.6", + "http 0.2.7", "indexmap", "slab", "tokio 0.2.25", @@ -1681,20 +1669,20 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" dependencies = [ "bytes 1.1.0", "fnv", "futures-core", "futures-sink", "futures-util", - "http 0.2.6", + "http 0.2.7", "indexmap", "slab", "tokio 1.18.1", - "tokio-util 0.6.9", + "tokio-util 0.7.1", "tracing", ] @@ -1709,9 +1697,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" dependencies = [ "ahash 0.7.6", ] @@ -1773,11 +1761,11 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" dependencies = [ - "log 0.4.14", + "log 0.4.17", "mac", "markup5ever", - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -1800,9 +1788,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" +checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" dependencies = [ "bytes 1.1.0", "fnv", @@ -1828,7 +1816,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" dependencies = [ "bytes 0.5.6", - "http 0.2.6", + "http 0.2.7", ] [[package]] @@ -1838,8 +1826,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ "bytes 1.1.0", - "http 0.2.6", - "pin-project-lite 0.2.8", + "http 0.2.7", + "pin-project-lite 0.2.9", ] [[package]] @@ -1894,7 +1882,7 @@ dependencies = [ "httparse", "iovec", "itoa 0.4.8", - "log 0.4.14", + "log 0.4.17", "net2", "rustc_version", "time 0.1.43", @@ -1920,7 +1908,7 @@ dependencies = [ "futures-core", "futures-util", "h2 0.2.7", - "http 0.2.6", + "http 0.2.7", "http-body 0.3.1", "httparse", "httpdate 0.3.2", @@ -1943,13 +1931,13 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.12", - "http 0.2.6", + "h2 0.3.13", + "http 0.2.7", "http-body 0.4.4", "httparse", "httpdate 1.0.2", "itoa 1.0.1", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "socket2 0.4.4", "tokio 1.18.1", "tower-service", @@ -2032,9 +2020,9 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" [[package]] name = "indexmap" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg 1.1.0", "hashbrown 0.11.2", @@ -2062,9 +2050,9 @@ dependencies = [ [[package]] name = "inout" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1f03d4ab4d5dc9ec2d219f86c15d2a15fc08239d1cd3b2d6a19717c0a2f443" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ "generic-array", ] @@ -2089,9 +2077,18 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" + +[[package]] +name = "iri-string" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e76a2a488a2bce15f9e696e3b9b66a4e1412c13339344bba8a24b7eae30f91b" +dependencies = [ + "serde 1.0.137", +] [[package]] name = "itertools" @@ -2183,7 +2180,7 @@ dependencies = [ "futures-util", "lazy_static", "lber", - "log 0.4.14", + "log 0.4.17", "native-tls", "nom 2.2.1", "percent-encoding 2.1.0", @@ -2191,7 +2188,7 @@ dependencies = [ "tokio 1.18.1", "tokio-native-tls", "tokio-stream", - "tokio-util 0.7.0", + "tokio-util 0.7.1", "url 2.2.2", ] @@ -2205,7 +2202,7 @@ dependencies = [ "bufstream", "fast_chemail", "hostname", - "log 0.4.14", + "log 0.4.17", "native-tls", "nom 4.2.3", "serde 1.0.137", @@ -2248,9 +2245,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.124" +version = "0.2.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50" +checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" [[package]] name = "libsqlite3-sys" @@ -2395,14 +2392,14 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" dependencies = [ - "log 0.4.14", + "log 0.4.17", ] [[package]] name = "log" -version = "0.4.14" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if 1.0.0", ] @@ -2434,7 +2431,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" dependencies = [ - "log 0.4.14", + "log 0.4.17", "phf", "phf_codegen", "string_cache", @@ -2510,8 +2507,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" dependencies = [ "migrations_internals", - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -2532,9 +2529,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "mime_guess" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" dependencies = [ "mime 0.3.16", "unicase 2.6.0", @@ -2548,12 +2545,11 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.4.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" dependencies = [ "adler", - "autocfg 1.1.0", ] [[package]] @@ -2568,7 +2564,7 @@ dependencies = [ "iovec", "kernel32-sys", "libc", - "log 0.4.14", + "log 0.4.17", "miow 0.2.2", "net2", "slab", @@ -2582,7 +2578,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" dependencies = [ "libc", - "log 0.4.14", + "log 0.4.17", "miow 0.3.7", "ntapi", "wasi 0.11.0+wasi-snapshot-preview1", @@ -2596,7 +2592,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" dependencies = [ "lazycell", - "log 0.4.14", + "log 0.4.17", "mio 0.6.23", "slab", ] @@ -2607,7 +2603,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" dependencies = [ - "log 0.4.14", + "log 0.4.17", "mio 0.6.23", "miow 0.3.7", "winapi 0.3.9", @@ -2653,11 +2649,11 @@ checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" dependencies = [ "buf_redux", "httparse", - "log 0.4.14", + "log 0.4.17", "mime 0.3.16", "mime_guess", "quick-error", - "rand 0.8.4", + "rand 0.8.5", "safemem", "tempfile", "twoway", @@ -2689,7 +2685,7 @@ checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" dependencies = [ "lazy_static", "libc", - "log 0.4.14", + "log 0.4.17", "openssl", "openssl-probe", "openssl-sys", @@ -2775,13 +2771,12 @@ dependencies = [ [[package]] name = "nom" -version = "7.1.0" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" dependencies = [ "memchr", "minimal-lexical", - "version_check 0.9.4", ] [[package]] @@ -2792,7 +2787,7 @@ checksum = "37794436ca3029a3089e0b95d42da1f0b565ad271e4d3bb4bad0c7bb70b10605" dependencies = [ "bytecount", "memchr", - "nom 7.1.0", + "nom 7.1.1", ] [[package]] @@ -2815,9 +2810,9 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" dependencies = [ "winapi 0.3.9", ] @@ -2830,17 +2825,17 @@ checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" dependencies = [ "autocfg 1.1.0", "num-integer", - "num-traits 0.2.14", + "num-traits 0.2.15", ] [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ "autocfg 1.1.0", - "num-traits 0.2.14", + "num-traits 0.2.15", ] [[package]] @@ -2851,7 +2846,7 @@ checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" dependencies = [ "autocfg 1.1.0", "num-integer", - "num-traits 0.2.14", + "num-traits 0.2.15", ] [[package]] @@ -2860,14 +2855,14 @@ version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" dependencies = [ - "num-traits 0.2.14", + "num-traits 0.2.15", ] [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg 1.1.0", ] @@ -2883,10 +2878,22 @@ dependencies = [ ] [[package]] -name = "object" -version = "0.24.0" +name = "num_threads" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5b3dd1c072ee7963717671d1ca129f1048fda25edea6b752bfc71ac8854170" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "object" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456" +dependencies = [ + "memchr", +] [[package]] name = "once_cell" @@ -2943,8 +2950,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -3041,7 +3048,7 @@ dependencies = [ "cfg-if 1.0.0", "instant", "libc", - "redox_syscall 0.2.10", + "redox_syscall 0.2.13", "smallvec 1.8.0", "winapi 0.3.9", ] @@ -3054,7 +3061,7 @@ checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if 1.0.0", "libc", - "redox_syscall 0.2.10", + "redox_syscall 0.2.13", "smallvec 1.8.0", "windows-sys", ] @@ -3099,7 +3106,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ - "phf_shared 0.10.0", + "phf_shared", ] [[package]] @@ -3108,18 +3115,8 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", + "phf_generator", + "phf_shared", ] [[package]] @@ -3128,17 +3125,8 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ - "phf_shared 0.10.0", - "rand 0.8.4", -] - -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher", + "phf_shared", + "rand 0.8.5", ] [[package]] @@ -3165,8 +3153,8 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -3178,9 +3166,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -3190,9 +3178,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "plist" @@ -3204,7 +3192,7 @@ dependencies = [ "indexmap", "line-wrap", "serde 1.0.137", - "time 0.3.5", + "time 0.3.9", "xml-rs", ] @@ -3212,7 +3200,7 @@ dependencies = [ name = "plume" version = "0.7.1" dependencies = [ - "activitypub", + "activitystreams", "atom_syndication", "chrono", "clap", @@ -3269,11 +3257,11 @@ dependencies = [ name = "plume-common" version = "0.7.1" dependencies = [ - "activitypub", - "activitystreams-derive", - "activitystreams-traits", + "activitystreams", + "activitystreams-ext", "array_tool", "askama_escape", + "assert-json-diff", "base64 0.13.0", "chrono", "flume", @@ -3326,7 +3314,7 @@ dependencies = [ name = "plume-models" version = "0.7.1" dependencies = [ - "activitypub", + "activitystreams", "ammonia", "assert-json-diff", "bcrypt", @@ -3404,8 +3392,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", "version_check 0.9.4", ] @@ -3416,8 +3404,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "version_check 0.9.4", ] @@ -3451,15 +3439,6 @@ dependencies = [ "unicode-xid 0.1.0", ] -[[package]] -name = "proc-macro2" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" -dependencies = [ - "unicode-xid 0.1.0", -] - [[package]] name = "proc-macro2" version = "0.4.30" @@ -3471,11 +3450,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" dependencies = [ - "unicode-xid 0.2.2", + "unicode-xid 0.2.3", ] [[package]] @@ -3523,15 +3502,6 @@ dependencies = [ "proc-macro2 0.2.3", ] -[[package]] -name = "quote" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" -dependencies = [ - "proc-macro2 0.3.8", -] - [[package]] name = "quote" version = "0.6.13" @@ -3543,11 +3513,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.37", ] [[package]] @@ -3556,7 +3526,7 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" dependencies = [ - "log 0.4.14", + "log 0.4.17", "parking_lot 0.11.2", "scheduled-thread-pool", ] @@ -3580,7 +3550,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" dependencies = [ - "autocfg 0.1.7", + "autocfg 0.1.8", "libc", "rand_chacha 0.1.1", "rand_core 0.4.2", @@ -3588,7 +3558,7 @@ dependencies = [ "rand_isaac", "rand_jitter", "rand_os", - "rand_pcg 0.1.2", + "rand_pcg", "rand_xorshift", "winapi 0.3.9", ] @@ -3604,19 +3574,17 @@ dependencies = [ "rand_chacha 0.2.2", "rand_core 0.5.1", "rand_hc 0.2.0", - "rand_pcg 0.2.1", ] [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.3", - "rand_hc 0.3.1", ] [[package]] @@ -3625,7 +3593,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "autocfg 0.1.7", + "autocfg 0.1.8", "rand_core 0.3.1", ] @@ -3700,15 +3668,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core 0.6.3", -] - [[package]] name = "rand_isaac" version = "0.1.1" @@ -3749,19 +3708,10 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" dependencies = [ - "autocfg 0.1.7", + "autocfg 0.1.8", "rand_core 0.4.2", ] -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "rand_xorshift" version = "0.1.1" @@ -3773,9 +3723,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221" dependencies = [ "autocfg 1.1.0", "crossbeam-deque 0.8.1", @@ -3785,14 +3735,13 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4" dependencies = [ - "crossbeam-channel 0.5.2", + "crossbeam-channel 0.5.4", "crossbeam-deque 0.8.1", - "crossbeam-utils 0.8.6", - "lazy_static", + "crossbeam-utils 0.8.8", "num_cpus", ] @@ -3813,9 +3762,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ "bitflags 1.3.2", ] @@ -3868,7 +3817,7 @@ dependencies = [ "http 0.1.21", "hyper 0.12.36", "hyper-tls 0.3.2", - "log 0.4.14", + "log 0.4.17", "mime 0.3.16", "mime_guess", "native-tls", @@ -3897,19 +3846,19 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "http 0.2.6", + "http 0.2.7", "http-body 0.3.1", "hyper 0.13.10", "hyper-tls 0.4.3", "ipnet", "js-sys", "lazy_static", - "log 0.4.14", + "log 0.4.17", "mime 0.3.16", "mime_guess", "native-tls", "percent-encoding 2.1.0", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "serde 1.0.137", "serde_urlencoded 0.7.1", "tokio 0.2.25", @@ -3932,19 +3881,19 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.3.12", - "http 0.2.6", + "h2 0.3.13", + "http 0.2.7", "http-body 0.4.4", "hyper 0.14.18", "hyper-tls 0.5.0", "ipnet", "js-sys", "lazy_static", - "log 0.4.14", + "log 0.4.17", "mime 0.3.16", "native-tls", "percent-encoding 2.1.0", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "serde 1.0.137", "serde_json", "serde_urlencoded 0.7.1", @@ -4010,7 +3959,7 @@ checksum = "4a7ab1dfdc75bb8bd2be381f37796b1b300c45a3c9145b34d86715e8dd90bf28" dependencies = [ "atty", "base64 0.13.0", - "log 0.4.14", + "log 0.4.17", "memchr", "num_cpus", "pear", @@ -4044,7 +3993,7 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b6303dccab46dce6c7ac26c9b9d8d8cde1b19614b027c3f913be6611bff6d9b" dependencies = [ - "log 0.4.14", + "log 0.4.17", "notify", "rocket", "serde 1.0.137", @@ -4110,12 +4059,12 @@ checksum = "d74a71f39f2d0e35ada983c76aeaa3a58b6c5735c8865073a87d190219c64eb1" dependencies = [ "fastrand", "lazy_static", - "nom 7.1.0", + "nom 7.1.1", "nom_locate", "num-bigint", "num-integer", "num-rational", - "num-traits 0.2.14", + "num-traits 0.2.15", ] [[package]] @@ -4128,7 +4077,7 @@ dependencies = [ "bytecount", "itertools 0.10.3", "md5", - "nom 7.1.0", + "nom 7.1.1", ] [[package]] @@ -4210,9 +4159,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "2.4.2" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -4223,9 +4172,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.4.2" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" dependencies = [ "core-foundation-sys", "libc", @@ -4280,8 +4229,8 @@ version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -4359,8 +4308,8 @@ checksum = "e63e6744142336dfb606fe2b068afa2e1cca1ee6a5d8377277a92945d81fa331" dependencies = [ "bitflags 1.3.2", "itertools 0.8.2", - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -4375,15 +4324,15 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.7" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" [[package]] name = "slab" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] name = "slog" @@ -4404,11 +4353,11 @@ dependencies = [ [[package]] name = "slog-stdlog" -version = "4.1.0" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8228ab7302adbf4fcb37e66f3cda78003feb521e7fd9e3847ec117a7784d0f5a" +checksum = "6706b2ace5bbae7291d3f8d2473e2bfab073ccd7d03670946197aec98471fa3e" dependencies = [ - "log 0.4.14", + "log 0.4.17", "slog", "slog-scope", ] @@ -4493,28 +4442,28 @@ dependencies = [ [[package]] name = "string_cache" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "923f0f39b6267d37d23ce71ae7235602134b250ace715dd2c90421998ddac0c6" +checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08" dependencies = [ - "lazy_static", "new_debug_unreachable", - "parking_lot 0.11.2", - "phf_shared 0.8.0", + "once_cell", + "parking_lot 0.12.0", + "phf_shared", "precomputed-hash", "serde 1.0.137", ] [[package]] name = "string_cache_codegen" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97" +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro2 1.0.36", - "quote 1.0.15", + "phf_generator", + "phf_shared", + "proc-macro2 1.0.37", + "quote 1.0.18", ] [[package]] @@ -4546,17 +4495,6 @@ dependencies = [ "unicode-xid 0.1.0", ] -[[package]] -name = "syn" -version = "0.13.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14f9bf6292f3a61d2c716723fdb789a41bbe104168e6f496dc6497e531ea1b9b" -dependencies = [ - "proc-macro2 0.3.8", - "quote 0.5.2", - "unicode-xid 0.1.0", -] - [[package]] name = "syn" version = "0.14.9" @@ -4585,9 +4523,9 @@ version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", - "unicode-xid 0.2.2", + "proc-macro2 1.0.37", + "quote 1.0.18", + "unicode-xid 0.2.3", ] [[package]] @@ -4596,10 +4534,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", - "unicode-xid 0.2.2", + "unicode-xid 0.2.3", ] [[package]] @@ -4646,7 +4584,7 @@ dependencies = [ "futures 0.3.21", "htmlescape", "levenshtein_automata", - "log 0.4.14", + "log 0.4.17", "memmap", "murmurhash32", "notify", @@ -4689,7 +4627,7 @@ dependencies = [ "futures 0.3.21", "htmlescape", "levenshtein_automata", - "log 0.4.14", + "log 0.4.17", "lru", "memmap", "murmurhash32", @@ -4770,16 +4708,16 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "libc", - "redox_syscall 0.2.10", + "redox_syscall 0.2.13", "remove_dir_all", "winapi 0.3.9", ] [[package]] name = "tendril" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9ef557cb397a4f0a5a3a628f06515f78563f2209e64d47055d9dc6052bf5e33" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" dependencies = [ "futf", "mac", @@ -4797,21 +4735,21 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -4836,12 +4774,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.5" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" +checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" dependencies = [ - "itoa 0.4.8", + "itoa 1.0.1", "libc", + "num_threads", ] [[package]] @@ -4855,9 +4794,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -4924,7 +4863,7 @@ dependencies = [ "num_cpus", "once_cell", "parking_lot 0.12.0", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "signal-hook-registry", "socket2 0.4.4", "tokio-macros 1.7.0", @@ -4970,7 +4909,7 @@ checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674" dependencies = [ "bytes 0.4.12", "futures 0.1.31", - "log 0.4.14", + "log 0.4.17", ] [[package]] @@ -4979,8 +4918,8 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -4990,8 +4929,8 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] @@ -5014,7 +4953,7 @@ dependencies = [ "crossbeam-utils 0.7.2", "futures 0.1.31", "lazy_static", - "log 0.4.14", + "log 0.4.17", "mio 0.6.23", "num_cpus", "parking_lot 0.9.0", @@ -5043,7 +4982,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" dependencies = [ "futures-core", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tokio 1.18.1", ] @@ -5082,7 +5021,7 @@ dependencies = [ "crossbeam-utils 0.7.2", "futures 0.1.31", "lazy_static", - "log 0.4.14", + "log 0.4.17", "num_cpus", "slab", "tokio-executor", @@ -5119,37 +5058,23 @@ dependencies = [ "bytes 0.5.6", "futures-core", "futures-sink", - "log 0.4.14", + "log 0.4.17", "pin-project-lite 0.1.12", "tokio 0.2.25", ] [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" dependencies = [ "bytes 1.1.0", "futures-core", "futures-sink", - "log 0.4.14", - "pin-project-lite 0.2.8", - "tokio 1.18.1", -] - -[[package]] -name = "tokio-util" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64910e1b9c1901aaf5375561e35b9c057d95ff41a44ede043a03e09279eabaf1" -dependencies = [ - "bytes 1.1.0", - "futures-core", - "futures-sink", - "log 0.4.14", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tokio 1.18.1", + "tracing", ] [[package]] @@ -5163,9 +5088,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" dependencies = [ "serde 1.0.137", ] @@ -5183,28 +5108,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" dependencies = [ "cfg-if 1.0.0", - "log 0.4.14", - "pin-project-lite 0.2.8", + "log 0.4.17", + "pin-project-lite 0.2.9", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", ] [[package]] name = "tracing-core" -version = "0.1.22" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" +checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" dependencies = [ "lazy_static", "valuable", @@ -5222,20 +5147,20 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ "lazy_static", - "log 0.4.14", + "log 0.4.17", "tracing-core", ] [[package]] name = "tracing-subscriber" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9df98b037d039d03400d9dd06b0f8ce05486b5f25e9a2d7d36196e142ebbc52" +checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596" dependencies = [ "ansi_term", "sharded-slab", @@ -5307,9 +5232,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-normalization" @@ -5334,9 +5259,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "universal-hash" @@ -5385,9 +5310,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "utf8-ranges" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ae116fef2b7fea257ed6440d3cfcff7f190865f170cdad00bb6465bf18ecba" +checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" [[package]] name = "uuid" @@ -5433,8 +5358,8 @@ dependencies = [ "if_chain", "lazy_static", "proc-macro-error", - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "regex", "syn 1.0.92", "validator_types", @@ -5446,7 +5371,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2ddf34293296847abfc1493b15c6e2f5d3cd19f57ad7d22673bf4c6278da329" dependencies = [ - "proc-macro2 1.0.36", + "proc-macro2 1.0.37", "syn 1.0.92", ] @@ -5504,7 +5429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6395efa4784b027708f7451087e647ec73cc74f5d9bc2e418404248d679a230" dependencies = [ "futures 0.1.31", - "log 0.4.14", + "log 0.4.17", "try-lock", ] @@ -5514,7 +5439,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" dependencies = [ - "log 0.4.14", + "log 0.4.17", "try-lock", ] @@ -5556,18 +5481,18 @@ checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" dependencies = [ "bumpalo", "lazy_static", - "log 0.4.14", - "proc-macro2 1.0.36", - "quote 1.0.15", + "log 0.4.17", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.28" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5581,7 +5506,7 @@ version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" dependencies = [ - "quote 1.0.15", + "quote 1.0.18", "wasm-bindgen-macro-support", ] @@ -5591,8 +5516,8 @@ version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" dependencies = [ - "proc-macro2 1.0.36", - "quote 1.0.15", + "proc-macro2 1.0.37", + "quote 1.0.18", "syn 1.0.92", "wasm-bindgen-backend", "wasm-bindgen-shared", @@ -5631,7 +5556,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace22f62983d7c2ca0512ef319de0b52f837a1e8e79d81107825a9e8699c5bd8" dependencies = [ - "hashbrown 0.12.0", + "hashbrown 0.12.1", "once_cell", ] @@ -5769,9 +5694,9 @@ dependencies = [ [[package]] name = "xattr" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ "libc", ] @@ -5799,12 +5724,12 @@ dependencies = [ [[package]] name = "yansi" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zeroize" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb5728b8afd3f280a869ce1d4c554ffaed35f45c231fc41bfbd0381bef50317" +checksum = "94693807d016b2f2d2e14420eb3bfcca689311ff775dcf113d74ea624b7cdf07" diff --git a/Cargo.toml b/Cargo.toml index 3244c1d4..198f3671 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,6 @@ repository = "https://github.com/Plume-org/Plume" edition = "2018" [dependencies] -activitypub = "0.1.3" atom_syndication = "0.11.0" clap = "2.33" dotenv = "0.15.0" @@ -28,6 +27,7 @@ webfinger = "0.4.1" tracing = "0.1.34" tracing-subscriber = "0.3.10" riker = "0.4.2" +activitystreams = "0.7.0-alpha.18" [[bin]] name = "plume" diff --git a/plume-common/Cargo.toml b/plume-common/Cargo.toml index 03a7e1c2..65934a7c 100644 --- a/plume-common/Cargo.toml +++ b/plume-common/Cargo.toml @@ -5,9 +5,6 @@ authors = ["Plume contributors"] edition = "2018" [dependencies] -activitypub = "0.1.1" -activitystreams-derive = "0.1.1" -activitystreams-traits = "0.1.0" array_tool = "1.0" base64 = "0.13" heck = "0.4.0" @@ -23,6 +20,8 @@ syntect = "4.5.0" regex-syntax = { version = "0.6.17", default-features = false, features = ["unicode-perl"] } tracing = "0.1.34" askama_escape = "0.10.3" +activitystreams = "0.7.0-alpha.18" +activitystreams-ext = "0.1.0-alpha.2" url = "2.2.2" flume = "0.10.12" tokio = { version = "1.18.1", features = ["full"] } @@ -38,6 +37,7 @@ git = "https://git.joinplu.me/Plume/pulldown-cmark" branch = "bidi-plume" [dev-dependencies] +assert-json-diff = "2.0.1" once_cell = "1.10.0" [features] diff --git a/plume-common/src/activity_pub/inbox.rs b/plume-common/src/activity_pub/inbox.rs index a0c3b75c..e2a769ee 100644 --- a/plume-common/src/activity_pub/inbox.rs +++ b/plume-common/src/activity_pub/inbox.rs @@ -10,8 +10,7 @@ use super::{request, sign::Signer}; /// # Example /// /// ```rust -/// # extern crate activitypub; -/// # use activitypub::{actor::Person, activity::{Announce, Create}, object::Note}; +/// # use activitystreams::{prelude::*, base::Base, actor::Person, activity::{Announce, Create}, object::Note, iri_string::types::IriString}; /// # use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa}; /// # use once_cell::sync::Lazy; /// # use plume_common::activity_pub::inbox::*; @@ -113,12 +112,13 @@ use super::{request, sign::Signer}; /// # } /// # } /// # -/// # let mut act = Create::default(); -/// # act.object_props.set_id_string(String::from("https://test.ap/activity")).unwrap(); -/// # let mut person = Person::default(); -/// # person.object_props.set_id_string(String::from("https://test.ap/actor")).unwrap(); -/// # act.create_props.set_actor_object(person).unwrap(); -/// # act.create_props.set_object_object(Note::default()).unwrap(); +/// # let mut person = Person::new(); +/// # person.set_id("https://test.ap/actor".parse::().unwrap()); +/// # let mut act = Create::new( +/// # Base::retract(person).unwrap().into_generic().unwrap(), +/// # Base::retract(Note::new()).unwrap().into_generic().unwrap() +/// # ); +/// # act.set_id("https://test.ap/activity".parse::().unwrap()); /// # let activity_json = serde_json::to_value(act).unwrap(); /// # /// # let conn = (); @@ -197,29 +197,29 @@ where } /// Registers an handler on this Inbox. - pub fn with(self, proxy: Option<&reqwest::Proxy>) -> Inbox<'a, C, E, R> + pub fn with(self, proxy: Option<&reqwest::Proxy>) -> Self where A: AsActor<&'a C> + FromId, - V: activitypub::Activity, + V: activitystreams::markers::Activity + serde::de::DeserializeOwned, M: AsObject + FromId, M::Output: Into, { - if let Inbox::NotHandled(ctx, mut act, e) = self { + if let Self::NotHandled(ctx, mut act, e) = self { if serde_json::from_value::(act.clone()).is_ok() { let act_clone = act.clone(); let act_id = match act_clone["id"].as_str() { Some(x) => x, - None => return Inbox::NotHandled(ctx, act, InboxError::InvalidID), + None => return Self::NotHandled(ctx, act, InboxError::InvalidID), }; // Get the actor ID let actor_id = match get_id(act["actor"].clone()) { Some(x) => x, - None => return Inbox::NotHandled(ctx, act, InboxError::InvalidActor(None)), + None => return Self::NotHandled(ctx, act, InboxError::InvalidActor(None)), }; if Self::is_spoofed_activity(&actor_id, &act) { - return Inbox::NotHandled(ctx, act, InboxError::InvalidObject(None)); + return Self::NotHandled(ctx, act, InboxError::InvalidObject(None)); } // Transform this actor to a model (see FromId for details about the from_id function) @@ -235,14 +235,14 @@ where if let Some(json) = json { act["actor"] = json; } - return Inbox::NotHandled(ctx, act, InboxError::InvalidActor(Some(e))); + return Self::NotHandled(ctx, act, InboxError::InvalidActor(Some(e))); } }; // Same logic for "object" let obj_id = match get_id(act["object"].clone()) { Some(x) => x, - None => return Inbox::NotHandled(ctx, act, InboxError::InvalidObject(None)), + None => return Self::NotHandled(ctx, act, InboxError::InvalidObject(None)), }; let obj = match M::from_id( ctx, @@ -255,19 +255,19 @@ where if let Some(json) = json { act["object"] = json; } - return Inbox::NotHandled(ctx, act, InboxError::InvalidObject(Some(e))); + return Self::NotHandled(ctx, act, InboxError::InvalidObject(Some(e))); } }; // Handle the activity match obj.activity(ctx, actor, act_id) { - Ok(res) => Inbox::Handled(res.into()), - Err(e) => Inbox::Failed(e), + Ok(res) => Self::Handled(res.into()), + Err(e) => Self::Failed(e), } } else { // If the Activity type is not matching the expected one for // this handler, try with the next one. - Inbox::NotHandled(ctx, act, e) + Self::NotHandled(ctx, act, e) } } else { self @@ -333,7 +333,7 @@ pub trait FromId: Sized { type Error: From> + Debug; /// The ActivityPub object type representing Self - type Object: activitypub::Object; + type Object: activitystreams::markers::Object + serde::de::DeserializeOwned; /// Tries to get an instance of `Self` from an ActivityPub ID. /// @@ -418,8 +418,7 @@ pub trait AsActor { /// representing the Note by a Message type, without any specific context. /// /// ```rust -/// # extern crate activitypub; -/// # use activitypub::{activity::Create, actor::Person, object::Note}; +/// # use activitystreams::{prelude::*, activity::Create, actor::Person, object::Note}; /// # use plume_common::activity_pub::inbox::{AsActor, AsObject, FromId}; /// # use plume_common::activity_pub::sign::{gen_keypair, Error as SignError, Result as SignResult, Signer}; /// # use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa}; @@ -501,7 +500,10 @@ pub trait AsActor { /// } /// /// fn from_activity(_: &(), obj: Note) -> Result { -/// Ok(Message { text: obj.object_props.content_string().map_err(|_| ())? }) +/// Ok(Message { +/// text: obj.content() +/// .and_then(|content| content.to_owned().single_xsd_string()).ok_or(())? +/// }) /// } /// /// fn get_sender() -> &'static dyn Signer { @@ -521,7 +523,7 @@ pub trait AsActor { /// ``` pub trait AsObject where - V: activitypub::Activity, + V: activitystreams::markers::Activity, { /// What kind of error is returned when something fails type Error; @@ -549,7 +551,13 @@ mod tests { use crate::activity_pub::sign::{ gen_keypair, Error as SignError, Result as SignResult, Signer, }; - use activitypub::{activity::*, actor::Person, object::Note}; + use activitystreams::{ + activity::{Announce, Create, Delete, Like}, + actor::Person, + base::Base, + object::Note, + prelude::*, + }; use once_cell::sync::Lazy; use openssl::{hash::MessageDigest, pkey::PKey, rsa::Rsa}; @@ -598,11 +606,11 @@ mod tests { type Object = Person; fn from_db(_: &(), _id: &str) -> Result { - Ok(MyActor) + Ok(Self) } fn from_activity(_: &(), _obj: Person) -> Result { - Ok(MyActor) + Ok(Self) } fn get_sender() -> &'static dyn Signer { @@ -626,11 +634,11 @@ mod tests { type Object = Note; fn from_db(_: &(), _id: &str) -> Result { - Ok(MyObject) + Ok(Self) } fn from_activity(_: &(), _obj: Note) -> Result { - Ok(MyObject) + Ok(Self) } fn get_sender() -> &'static dyn Signer { @@ -678,21 +686,15 @@ mod tests { } fn build_create() -> Create { - let mut act = Create::default(); - act.object_props - .set_id_string(String::from("https://test.ap/activity")) - .unwrap(); - let mut person = Person::default(); - person - .object_props - .set_id_string(String::from("https://test.ap/actor")) - .unwrap(); - act.create_props.set_actor_object(person).unwrap(); - let mut note = Note::default(); - note.object_props - .set_id_string(String::from("https://test.ap/note")) - .unwrap(); - act.create_props.set_object_object(note).unwrap(); + let mut person = Person::new(); + person.set_id("https://test.ap/actor".parse().unwrap()); + let mut note = Note::new(); + note.set_id("https://test.ap/note".parse().unwrap()); + let mut act = Create::new( + Base::retract(person).unwrap().into_generic().unwrap(), + Base::retract(note).unwrap().into_generic().unwrap(), + ); + act.set_id("https://test.ap/activity".parse().unwrap()); act } @@ -729,6 +731,16 @@ mod tests { } struct FailingActor; + impl AsActor<&()> for FailingActor { + fn get_inbox_url(&self) -> String { + String::from("https://test.ap/failing-actor/inbox") + } + + fn is_local(&self) -> bool { + false + } + } + impl FromId<()> for FailingActor { type Error = (); type Object = Person; @@ -737,7 +749,7 @@ mod tests { Err(()) } - fn from_activity(_: &(), _obj: Person) -> Result { + fn from_activity(_: &(), _obj: Self::Object) -> Result { Err(()) } @@ -745,15 +757,6 @@ mod tests { &*MY_SIGNER } } - impl AsActor<&()> for FailingActor { - fn get_inbox_url(&self) -> String { - String::from("https://test.ap/failing-actor/inbox") - } - - fn is_local(&self) -> bool { - false - } - } impl AsObject for MyObject { type Error = (); diff --git a/plume-common/src/activity_pub/mod.rs b/plume-common/src/activity_pub/mod.rs index 74fb1300..fc5c7bd7 100644 --- a/plume-common/src/activity_pub/mod.rs +++ b/plume-common/src/activity_pub/mod.rs @@ -1,4 +1,14 @@ -use activitypub::{Activity, Link, Object}; +use activitystreams::{ + actor::{ApActor, Group, Person}, + base::{AnyBase, Base, Extends}, + iri_string::types::IriString, + kind, + markers::{self, Activity}, + object::{ApObject, Article, Object}, + primitives::{AnyString, OneOrMany}, + unparsed::UnparsedMutExt, +}; +use activitystreams_ext::{Ext1, Ext2, UnparsedExtension}; use array_tool::vec::Uniq; use futures::future::join_all; use reqwest::{header::HeaderValue, ClientBuilder, RequestBuilder, Url}; @@ -67,7 +77,7 @@ impl ActivityStream { } } -impl<'r, O: Object> Responder<'r> for ActivityStream { +impl<'r, O: serde::Serialize> Responder<'r> for ActivityStream { fn respond_to(self, request: &Request<'_>) -> Result, Status> { let mut json = serde_json::to_value(&self.0).map_err(|_| Status::InternalServerError)?; json["@context"] = context(); @@ -114,10 +124,11 @@ impl<'a, 'r> FromRequest<'a, 'r> for ApRequest { .unwrap_or(Outcome::Forward(())) } } + pub fn broadcast(sender: &S, act: A, to: Vec, proxy: Option) where S: sign::Signer, - A: Activity, + A: Activity + serde::Serialize, T: inbox::AsActor, { let boxes = to @@ -198,15 +209,12 @@ where } headers.insert("Host", host_header_value.unwrap()); headers.insert("Digest", request::Digest::digest(&body)); - let request_builder = client - .post(&inbox) - .headers(headers.clone()) - .header( - "Signature", - request::signature(sender, &headers, ("post", url.path(), url.query())) - .expect("activity_pub::broadcast: request signature error"), - ) - .body(body); + headers.insert( + "Signature", + request::signature(sender, &headers, ("post", url.path(), url.query())) + .expect("activity_pub::broadcast: request signature error"), + ); + let request_builder = client.post(&inbox).headers(headers.clone()).body(body); let _ = tx.send_async(request_builder).await; } drop(tx); @@ -233,46 +241,193 @@ pub trait IntoId { fn into_id(self) -> Id; } -impl Link for Id {} - -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct ApSignature { - #[activitystreams(concrete(PublicKey), functional)] - pub public_key: Option, + pub public_key: PublicKey, } -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct PublicKey { - #[activitystreams(concrete(String), functional)] - pub id: Option, - - #[activitystreams(concrete(String), functional)] - pub owner: Option, - - #[activitystreams(concrete(String), functional)] - pub public_key_pem: Option, + pub id: IriString, + pub owner: IriString, + pub public_key_pem: String, } -#[derive(Clone, Debug, Default, UnitString)] -#[activitystreams(Hashtag)] -pub struct HashtagType; +impl UnparsedExtension for ApSignature +where + U: UnparsedMutExt, +{ + type Error = serde_json::Error; -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] + fn try_from_unparsed(unparsed_mut: &mut U) -> Result { + Ok(ApSignature { + public_key: unparsed_mut.remove("publicKey")?, + }) + } + + fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> { + unparsed_mut.insert("publicKey", self.public_key)?; + Ok(()) + } +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -pub struct Hashtag { - #[serde(rename = "type")] - kind: HashtagType, - - #[activitystreams(concrete(String), functional)] - pub href: Option, - - #[activitystreams(concrete(String), functional)] - pub name: Option, +pub struct SourceProperty { + pub source: Source, } -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +impl UnparsedExtension for SourceProperty +where + U: UnparsedMutExt, +{ + type Error = serde_json::Error; + + fn try_from_unparsed(unparsed_mut: &mut U) -> Result { + Ok(SourceProperty { + source: unparsed_mut.remove("source")?, + }) + } + + fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> { + unparsed_mut.insert("source", self.source)?; + Ok(()) + } +} + +pub type CustomPerson = Ext1, ApSignature>; +pub type CustomGroup = Ext2, ApSignature, SourceProperty>; + +kind!(HashtagType, Hashtag); + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct Hashtag { + #[serde(skip_serializing_if = "Option::is_none")] + pub href: Option, + + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + + #[serde(flatten)] + inner: Object, +} + +impl Hashtag { + pub fn new() -> Self { + Self { + href: None, + name: None, + inner: Object::new(), + } + } + + pub fn extending(mut inner: Object) -> Result { + let href = inner.remove("href")?; + let name = inner.remove("name")?; + + Ok(Self { href, name, inner }) + } + + pub fn retracting(self) -> Result, serde_json::Error> { + let Self { + href, + name, + mut inner, + } = self; + + inner.insert("href", href)?; + inner.insert("name", name)?; + Ok(inner) + } +} + +pub trait AsHashtag: markers::Object { + fn hashtag_ref(&self) -> &Hashtag; + + fn hashtag_mut(&mut self) -> &mut Hashtag; +} + +pub trait HashtagExt: AsHashtag { + fn href(&self) -> Option<&IriString> { + self.hashtag_ref().href.as_ref() + } + + fn set_href(&mut self, href: T) -> &mut Self + where + T: Into, + { + self.hashtag_mut().href = Some(href.into()); + self + } + + fn take_href(&mut self) -> Option { + self.hashtag_mut().href.take() + } + + fn delete_href(&mut self) -> &mut Self { + self.hashtag_mut().href = None; + self + } + + fn name(&self) -> Option<&AnyString> { + self.hashtag_ref().name.as_ref() + } + + fn set_name(&mut self, name: T) -> &mut Self + where + T: Into, + { + self.hashtag_mut().name = Some(name.into()); + self + } + + fn take_name(&mut self) -> Option { + self.hashtag_mut().name.take() + } + + fn delete_name(&mut self) -> &mut Self { + self.hashtag_mut().name = None; + self + } +} + +impl Default for Hashtag { + fn default() -> Self { + Self::new() + } +} + +impl AsHashtag for Hashtag { + fn hashtag_ref(&self) -> &Self { + self + } + + fn hashtag_mut(&mut self) -> &mut Self { + self + } +} + +impl Extends for Hashtag { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + let inner = Object::extends(base)?; + Self::extending(inner) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl markers::Base for Hashtag {} +impl markers::Object for Hashtag {} +impl HashtagExt for T where T: AsHashtag {} + +#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct Source { pub media_type: String, @@ -280,13 +435,300 @@ pub struct Source { pub content: String, } -impl Object for Source {} +impl UnparsedExtension for Source +where + U: UnparsedMutExt, +{ + type Error = serde_json::Error; -#[derive(Clone, Debug, Default, Deserialize, Serialize, Properties)] -#[serde(rename_all = "camelCase")] -pub struct Licensed { - #[activitystreams(concrete(String), functional)] - pub license: Option, + fn try_from_unparsed(unparsed_mut: &mut U) -> Result { + Ok(Source { + content: unparsed_mut.remove("content")?, + media_type: unparsed_mut.remove("mediaType")?, + }) + } + + fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> { + unparsed_mut.insert("content", self.content)?; + unparsed_mut.insert("mediaType", self.media_type)?; + Ok(()) + } } -impl Object for Licensed {} +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct Licensed { + pub license: Option, +} + +impl UnparsedExtension for Licensed +where + U: UnparsedMutExt, +{ + type Error = serde_json::Error; + + fn try_from_unparsed(unparsed_mut: &mut U) -> Result { + Ok(Licensed { + license: unparsed_mut.remove("license")?, + }) + } + + fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> { + unparsed_mut.insert("license", self.license)?; + Ok(()) + } +} + +pub type LicensedArticle = Ext1, Licensed>; + +pub trait ToAsString { + fn to_as_string(&self) -> Option; +} + +impl ToAsString for OneOrMany<&AnyString> { + fn to_as_string(&self) -> Option { + self.as_as_str().map(|s| s.to_string()) + } +} + +trait AsAsStr { + fn as_as_str(&self) -> Option<&str>; +} + +impl AsAsStr for OneOrMany<&AnyString> { + fn as_as_str(&self) -> Option<&str> { + self.iter().next().map(|prop| prop.as_str()) + } +} + +pub trait ToAsUri { + fn to_as_uri(&self) -> Option; +} + +impl ToAsUri for OneOrMany { + fn to_as_uri(&self) -> Option { + self.iter() + .next() + .and_then(|prop| prop.as_xsd_any_uri().map(|uri| uri.to_string())) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use activitystreams::{ + activity::{ActorAndObjectRef, Create}, + object::kind::ArticleType, + }; + use assert_json_diff::assert_json_eq; + use serde_json::{from_str, json, to_value}; + + #[test] + fn se_ap_signature() { + let ap_signature = ApSignature { + public_key: PublicKey { + id: "https://example.com/pubkey".parse().unwrap(), + owner: "https://example.com/owner".parse().unwrap(), + public_key_pem: "pubKeyPem".into(), + }, + }; + let expected = json!({ + "publicKey": { + "id": "https://example.com/pubkey", + "owner": "https://example.com/owner", + "publicKeyPem": "pubKeyPem" + } + }); + assert_json_eq!(to_value(ap_signature).unwrap(), expected); + } + + #[test] + fn de_ap_signature() { + let value: ApSignature = from_str( + r#" + { + "publicKey": { + "id": "https://example.com/", + "owner": "https://example.com/", + "publicKeyPem": "" + } + } + "#, + ) + .unwrap(); + let expected = ApSignature { + public_key: PublicKey { + id: "https://example.com/".parse().unwrap(), + owner: "https://example.com/".parse().unwrap(), + public_key_pem: "".into(), + }, + }; + assert_eq!(value, expected); + } + + #[test] + fn se_custom_person() { + let actor = ApActor::new("https://example.com/inbox".parse().unwrap(), Person::new()); + let person = CustomPerson::new( + actor, + ApSignature { + public_key: PublicKey { + id: "https://example.com/pubkey".parse().unwrap(), + owner: "https://example.com/owner".parse().unwrap(), + public_key_pem: "pubKeyPem".into(), + }, + }, + ); + let expected = json!({ + "inbox": "https://example.com/inbox", + "type": "Person", + "publicKey": { + "id": "https://example.com/pubkey", + "owner": "https://example.com/owner", + "publicKeyPem": "pubKeyPem" + } + }); + assert_eq!(to_value(person).unwrap(), expected); + } + + #[test] + fn de_custom_group() { + let group = CustomGroup::new( + ApActor::new("https://example.com/inbox".parse().unwrap(), Group::new()), + ApSignature { + public_key: PublicKey { + id: "https://example.com/pubkey".parse().unwrap(), + owner: "https://example.com/owner".parse().unwrap(), + public_key_pem: "pubKeyPem".into(), + }, + }, + SourceProperty { + source: Source { + content: String::from("This is a *custom* group."), + media_type: String::from("text/markdown"), + }, + }, + ); + let expected = json!({ + "inbox": "https://example.com/inbox", + "type": "Group", + "publicKey": { + "id": "https://example.com/pubkey", + "owner": "https://example.com/owner", + "publicKeyPem": "pubKeyPem" + }, + "source": { + "content": "This is a *custom* group.", + "mediaType": "text/markdown" + } + }); + assert_eq!(to_value(group).unwrap(), expected); + } + + #[test] + fn se_licensed_article() { + let object = ApObject::new(Article::new()); + let licensed_article = LicensedArticle::new( + object, + Licensed { + license: Some("CC-0".into()), + }, + ); + let expected = json!({ + "type": "Article", + "license": "CC-0", + }); + assert_json_eq!(to_value(licensed_article).unwrap(), expected); + } + + #[test] + fn de_licensed_article() { + let value: LicensedArticle = from_str( + r#" + { + "type": "Article", + "id": "https://plu.me/~/Blog/my-article", + "attributedTo": ["https://plu.me/@/Admin", "https://plu.me/~/Blog"], + "content": "Hello.", + "name": "My Article", + "summary": "Bye.", + "source": { + "content": "Hello.", + "mediaType": "text/markdown" + }, + "published": "2014-12-12T12:12:12Z", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "license": "CC-0" + } + "#, + ) + .unwrap(); + let expected = json!({ + "type": "Article", + "id": "https://plu.me/~/Blog/my-article", + "attributedTo": ["https://plu.me/@/Admin", "https://plu.me/~/Blog"], + "content": "Hello.", + "name": "My Article", + "summary": "Bye.", + "source": { + "content": "Hello.", + "mediaType": "text/markdown" + }, + "published": "2014-12-12T12:12:12Z", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "license": "CC-0" + }); + + assert_eq!(to_value(value).unwrap(), expected); + } + + #[test] + fn de_create_with_licensed_article() { + let create: Create = from_str( + r#" + { + "id": "https://plu.me/~/Blog/my-article", + "type": "Create", + "actor": "https://plu.me/@/Admin", + "to": "https://www.w3.org/ns/activitystreams#Public", + "object": { + "type": "Article", + "id": "https://plu.me/~/Blog/my-article", + "attributedTo": ["https://plu.me/@/Admin", "https://plu.me/~/Blog"], + "content": "Hello.", + "name": "My Article", + "summary": "Bye.", + "source": { + "content": "Hello.", + "mediaType": "text/markdown" + }, + "published": "2014-12-12T12:12:12Z", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "license": "CC-0" + } + } + "#, + ) + .unwrap(); + let base = create.object_field_ref().as_single_base().unwrap(); + let any_base = AnyBase::from_base(base.clone()); + let value = any_base.extend::().unwrap(); + let expected = json!({ + "type": "Article", + "id": "https://plu.me/~/Blog/my-article", + "attributedTo": ["https://plu.me/@/Admin", "https://plu.me/~/Blog"], + "content": "Hello.", + "name": "My Article", + "summary": "Bye.", + "source": { + "content": "Hello.", + "mediaType": "text/markdown" + }, + "published": "2014-12-12T12:12:12Z", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "license": "CC-0" + }); + + assert_eq!(to_value(value).unwrap(), expected); + } +} diff --git a/plume-common/src/lib.rs b/plume-common/src/lib.rs old mode 100755 new mode 100644 index 878923d4..80021762 --- a/plume-common/src/lib.rs +++ b/plume-common/src/lib.rs @@ -1,7 +1,5 @@ #![feature(associated_type_defaults)] -#[macro_use] -extern crate activitystreams_derive; #[macro_use] extern crate shrinkwraprs; #[macro_use] diff --git a/plume-models/Cargo.toml b/plume-models/Cargo.toml index 447f7dbc..9d968530 100644 --- a/plume-models/Cargo.toml +++ b/plume-models/Cargo.toml @@ -5,7 +5,6 @@ authors = ["Plume contributors"] edition = "2018" [dependencies] -activitypub = "0.1.1" ammonia = "3.2.0" bcrypt = "0.12.1" guid-create = "0.2" @@ -35,6 +34,7 @@ riker = "0.4.2" once_cell = "1.10.0" lettre = "0.9.6" native-tls = "0.2.10" +activitystreams = "0.7.0-alpha.18" [dependencies.chrono] features = ["serde"] diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 9d0839ae..ac20ee86 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -1,12 +1,14 @@ use crate::{ - ap_url, db_conn::DbConn, instance::*, medias::Media, posts::Post, safe_string::SafeString, + db_conn::DbConn, instance::*, medias::Media, posts::Post, safe_string::SafeString, schema::blogs, users::User, Connection, Error, PlumeRocket, Result, CONFIG, ITEMS_PER_PAGE, }; -use activitypub::{ - actor::Group, +use activitystreams::{ + actor::{ApActor, ApActorExt, AsApActor, Group}, + base::AnyBase, collection::{OrderedCollection, OrderedCollectionPage}, - object::Image, - CustomObject, + iri_string::types::IriString, + object::{kind::ImageType, ApObject, Image, ObjectExt}, + prelude::*, }; use chrono::NaiveDateTime; use diesel::{self, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl, SaveChangesDsl}; @@ -18,14 +20,12 @@ use openssl::{ }; use plume_common::activity_pub::{ inbox::{AsActor, FromId}, - sign, ActivityStream, ApSignature, Id, IntoId, PublicKey, Source, + sign, ActivityStream, ApSignature, CustomGroup, Id, IntoId, PublicKey, Source, SourceProperty, + ToAsString, ToAsUri, }; -use url::Url; use webfinger::*; -pub type CustomGroup = CustomObject; - -#[derive(Queryable, Identifiable, Clone, AsChangeset)] +#[derive(Queryable, Identifiable, Clone, AsChangeset, Debug)] #[changeset_options(treat_none_as_null = "true")] pub struct Blog { pub id: i32, @@ -161,104 +161,120 @@ impl Blog { } pub fn to_activity(&self, conn: &Connection) -> Result { - let mut blog = Group::default(); - blog.ap_actor_props - .set_preferred_username_string(self.actor_id.clone())?; - blog.object_props.set_name_string(self.title.clone())?; - blog.ap_actor_props - .set_outbox_string(self.outbox_url.clone())?; - blog.ap_actor_props - .set_inbox_string(self.inbox_url.clone())?; - blog.object_props - .set_summary_string(self.summary_html.to_string())?; - blog.ap_object_props.set_source_object(Source { - content: self.summary.clone(), - media_type: String::from("text/markdown"), - })?; + let mut blog = ApActor::new(self.inbox_url.parse()?, Group::new()); + blog.set_preferred_username(self.actor_id.clone()); + blog.set_name(self.title.clone()); + blog.set_outbox(self.outbox_url.parse()?); + blog.set_summary(self.summary_html.to_string()); + let source = SourceProperty { + source: Source { + content: self.summary.clone(), + media_type: String::from("text/markdown"), + }, + }; - let mut icon = Image::default(); - icon.object_props.set_url_string( - self.icon_id - .and_then(|id| Media::get(conn, id).and_then(|m| m.url()).ok()) - .unwrap_or_default(), - )?; - icon.object_props.set_attributed_to_link( - self.icon_id - .and_then(|id| { - Media::get(conn, id) - .and_then(|m| Ok(User::get(conn, m.owner_id)?.into_id())) - .ok() - }) - .unwrap_or_else(|| Id::new(String::new())), - )?; - blog.object_props.set_icon_object(icon)?; + let mut icon = Image::new(); + let _ = self.icon_id.map(|id| { + Media::get(conn, id).and_then(|m| { + let _ = m + .url() + .and_then(|url| url.parse::().map_err(|_| Error::Url)) + .map(|url| icon.set_url(url)); + icon.set_attributed_to( + User::get(conn, m.owner_id)? + .into_id() + .parse::()?, + ); + Ok(()) + }) + }); + blog.set_icon(icon.into_any_base()?); - let mut banner = Image::default(); - banner.object_props.set_url_string( - self.banner_id - .and_then(|id| Media::get(conn, id).and_then(|m| m.url()).ok()) - .unwrap_or_default(), - )?; - banner.object_props.set_attributed_to_link( - self.banner_id - .and_then(|id| { - Media::get(conn, id) - .and_then(|m| Ok(User::get(conn, m.owner_id)?.into_id())) - .ok() - }) - .unwrap_or_else(|| Id::new(String::new())), - )?; - blog.object_props.set_image_object(banner)?; + let mut banner = Image::new(); + let _ = self.banner_id.map(|id| { + Media::get(conn, id).and_then(|m| { + let _ = m + .url() + .and_then(|url| url.parse::().map_err(|_| Error::Url)) + .map(|url| banner.set_url(url)); + banner.set_attributed_to( + User::get(conn, m.owner_id)? + .into_id() + .parse::()?, + ); + Ok(()) + }) + }); + blog.set_image(banner.into_any_base()?); - blog.object_props.set_id_string(self.ap_url.clone())?; + blog.set_id(self.ap_url.parse()?); - let mut public_key = PublicKey::default(); - public_key.set_id_string(format!("{}#main-key", self.ap_url))?; - public_key.set_owner_string(self.ap_url.clone())?; - public_key.set_public_key_pem_string(self.public_key.clone())?; - let mut ap_signature = ApSignature::default(); - ap_signature.set_public_key_publickey(public_key)?; + let pub_key = PublicKey { + id: format!("{}#main-key", self.ap_url).parse()?, + owner: self.ap_url.parse()?, + public_key_pem: self.public_key.clone(), + }; + let ap_signature = ApSignature { + public_key: pub_key, + }; - Ok(CustomGroup::new(blog, ap_signature)) + Ok(CustomGroup::new(blog, ap_signature, source)) } pub fn outbox(&self, conn: &Connection) -> Result> { - let mut coll = OrderedCollection::default(); - coll.collection_props.items = serde_json::to_value(self.get_activities(conn))?; - coll.collection_props - .set_total_items_u64(self.get_activities(conn).len() as u64)?; - coll.collection_props - .set_first_link(Id::new(ap_url(&format!("{}?page=1", &self.outbox_url))))?; - coll.collection_props - .set_last_link(Id::new(ap_url(&format!( + self.outbox_collection(conn).map(ActivityStream::new) + } + pub fn outbox_collection(&self, conn: &Connection) -> Result { + let acts = self.get_activities(conn); + let acts = acts + .iter() + .filter_map(|value| AnyBase::from_arbitrary_json(value).ok()) + .collect::>(); + let n_acts = acts.len(); + let mut coll = OrderedCollection::new(); + coll.set_many_items(acts); + coll.set_total_items(n_acts as u64); + coll.set_first(format!("{}?page=1", &self.outbox_url).parse::()?); + coll.set_last( + format!( "{}?page={}", &self.outbox_url, - (self.get_activities(conn).len() as u64 + ITEMS_PER_PAGE as u64 - 1) as u64 - / ITEMS_PER_PAGE as u64 - ))))?; - Ok(ActivityStream::new(coll)) + (n_acts as u64 + ITEMS_PER_PAGE as u64 - 1) as u64 / ITEMS_PER_PAGE as u64 + ) + .parse::()?, + ); + Ok(coll) } pub fn outbox_page( &self, conn: &Connection, (min, max): (i32, i32), ) -> Result> { - let mut coll = OrderedCollectionPage::default(); + self.outbox_collection_page(conn, (min, max)) + .map(ActivityStream::new) + } + pub fn outbox_collection_page( + &self, + conn: &Connection, + (min, max): (i32, i32), + ) -> Result { + let mut coll = OrderedCollectionPage::new(); let acts = self.get_activity_page(conn, (min, max)); //This still doesn't do anything because the outbox //doesn't do anything yet - coll.collection_page_props.set_next_link(Id::new(&format!( - "{}?page={}", - &self.outbox_url, - min / ITEMS_PER_PAGE + 1 - )))?; - coll.collection_page_props.set_prev_link(Id::new(&format!( - "{}?page={}", - &self.outbox_url, - min / ITEMS_PER_PAGE - 1 - )))?; - coll.collection_props.items = serde_json::to_value(acts)?; - Ok(ActivityStream::new(coll)) + coll.set_next( + format!("{}?page={}", &self.outbox_url, min / ITEMS_PER_PAGE + 1) + .parse::()?, + ); + coll.set_prev( + format!("{}?page={}", &self.outbox_url, min / ITEMS_PER_PAGE - 1) + .parse::()?, + ); + coll.set_many_items( + acts.iter() + .filter_map(|value| AnyBase::from_arbitrary_json(value).ok()), + ); + Ok(coll) } fn get_activities(&self, _conn: &Connection) -> Vec { vec![] @@ -354,9 +370,90 @@ impl FromId for Blog { } fn from_activity(conn: &DbConn, acct: CustomGroup) -> Result { - let url = Url::parse(&acct.object.object_props.id_string()?)?; - let inst = url.host_str().ok_or(Error::Url)?; - let instance = Instance::find_by_domain(conn, inst).or_else(|_| { + let (name, outbox_url, inbox_url) = { + let actor = acct.ap_actor_ref(); + let name = actor + .preferred_username() + .ok_or(Error::MissingApProperty)? + .to_string(); + if name.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) { + return Err(Error::InvalidValue); + } + ( + name, + actor.outbox()?.ok_or(Error::MissingApProperty)?.to_string(), + actor.inbox()?.to_string(), + ) + }; + + let mut new_blog = NewBlog { + actor_id: name.to_string(), + outbox_url, + inbox_url, + public_key: acct.ext_one.public_key.public_key_pem.to_string(), + private_key: None, + theme: None, + ..NewBlog::default() + }; + + let object = ApObject::new(acct.inner); + new_blog.title = object + .name() + .and_then(|name| name.to_as_string()) + .unwrap_or(name); + new_blog.summary_html = SafeString::new( + &object + .summary() + .and_then(|summary| summary.to_as_string()) + .unwrap_or_default(), + ); + + let icon_id = object + .icon() + .and_then(|icons| { + icons.iter().next().and_then(|icon| { + let icon = icon.to_owned().extend::().ok()??; + let owner = icon.attributed_to()?.to_as_uri()?; + Media::save_remote( + conn, + icon.url()?.to_as_uri()?, + &User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?, + ) + .ok() + }) + }) + .map(|m| m.id); + new_blog.icon_id = icon_id; + + let banner_id = object + .image() + .and_then(|banners| { + banners.iter().next().and_then(|banner| { + let banner = banner.to_owned().extend::().ok()??; + let owner = banner.attributed_to()?.to_as_uri()?; + Media::save_remote( + conn, + banner.url()?.to_as_uri()?, + &User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?, + ) + .ok() + }) + }) + .map(|m| m.id); + new_blog.banner_id = banner_id; + + new_blog.summary = acct.ext_two.source.content; + + let any_base = AnyBase::from_extended(object)?; + let id = any_base.id().ok_or(Error::MissingApProperty)?; + new_blog.ap_url = id.to_string(); + + let inst = id + .authority_components() + .ok_or(Error::Url)? + .host() + .to_string(); + let instance = Instance::find_by_domain(conn, &inst).or_else(|_| { Instance::insert( conn, NewInstance { @@ -373,75 +470,9 @@ impl FromId for Blog { }, ) })?; - let icon_id = acct - .object - .object_props - .icon_image() - .ok() - .and_then(|icon| { - let owner = icon.object_props.attributed_to_link::().ok()?; - Media::save_remote( - conn, - icon.object_props.url_string().ok()?, - &User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?, - ) - .ok() - }) - .map(|m| m.id); + new_blog.instance_id = instance.id; - let banner_id = acct - .object - .object_props - .image_image() - .ok() - .and_then(|banner| { - let owner = banner.object_props.attributed_to_link::().ok()?; - Media::save_remote( - conn, - banner.object_props.url_string().ok()?, - &User::from_id(conn, &owner, None, CONFIG.proxy()).ok()?, - ) - .ok() - }) - .map(|m| m.id); - - let name = acct.object.ap_actor_props.preferred_username_string()?; - if name.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) { - return Err(Error::InvalidValue); - } - - Blog::insert( - conn, - NewBlog { - actor_id: name.clone(), - title: acct.object.object_props.name_string().unwrap_or(name), - outbox_url: acct.object.ap_actor_props.outbox_string()?, - inbox_url: acct.object.ap_actor_props.inbox_string()?, - summary: acct - .object - .ap_object_props - .source_object::() - .map(|s| s.content) - .unwrap_or_default(), - instance_id: instance.id, - ap_url: acct.object.object_props.id_string()?, - public_key: acct - .custom_props - .public_key_publickey()? - .public_key_pem_string()?, - private_key: None, - banner_id, - icon_id, - summary_html: SafeString::new( - &acct - .object - .object_props - .summary_string() - .unwrap_or_default(), - ), - theme: None, - }, - ) + Blog::insert(conn, new_blog) } fn get_sender() -> &'static dyn sign::Signer { @@ -512,12 +543,14 @@ pub(crate) mod tests { blog_authors::*, instance::tests as instance_tests, medias::NewMedia, tests::db, users::tests as usersTests, Connection as Conn, }; + use assert_json_diff::assert_json_eq; use diesel::Connection; + use serde_json::to_value; pub(crate) fn fill_database(conn: &Conn) -> (Vec, Vec) { instance_tests::fill_database(conn); let users = usersTests::fill_database(conn); - let blog1 = Blog::insert( + let mut blog1 = Blog::insert( conn, NewBlog::new_local( "BlogName".to_owned(), @@ -590,6 +623,41 @@ pub(crate) mod tests { }, ) .unwrap(); + + blog1.icon_id = Some( + Media::insert( + conn, + NewMedia { + file_path: "aaa.png".into(), + alt_text: String::new(), + is_remote: false, + remote_url: None, + sensitive: false, + content_warning: None, + owner_id: users[0].id, + }, + ) + .unwrap() + .id, + ); + blog1.banner_id = Some( + Media::insert( + conn, + NewMedia { + file_path: "bbb.png".into(), + alt_text: String::new(), + is_remote: false, + remote_url: None, + sensitive: false, + content_warning: None, + owner_id: users[0].id, + }, + ) + .unwrap() + .id, + ); + let _: Blog = blog1.save_changes(&*conn).unwrap(); + (users, vec![blog1, blog2, blog3]) } @@ -886,7 +954,6 @@ pub(crate) mod tests { .id, ); let _: Blog = blogs[0].save_changes(&**conn).unwrap(); - let ap_repr = blogs[0].to_activity(&conn).unwrap(); blogs[0].delete(&conn).unwrap(); let blog = Blog::from_activity(&conn, ap_repr).unwrap(); @@ -907,4 +974,90 @@ pub(crate) mod tests { Ok(()) }) } + + #[test] + fn to_activity() { + let conn = &db(); + conn.test_transaction::<_, Error, _>(|| { + let (_users, blogs) = fill_database(&conn); + let blog = &blogs[0]; + let act = blog.to_activity(conn)?; + + let expected = json!({ + "icon": { + "attributedTo": "https://plu.me/@/admin/", + "type": "Image", + "url": "https://plu.me/aaa.png" + }, + "id": "https://plu.me/~/BlogName/", + "image": { + "attributedTo": "https://plu.me/@/admin/", + "type": "Image", + "url": "https://plu.me/bbb.png" + }, + "inbox": "https://plu.me/~/BlogName/inbox", + "name": "Blog name", + "outbox": "https://plu.me/~/BlogName/outbox", + "preferredUsername": "BlogName", + "publicKey": { + "id": "https://plu.me/~/BlogName/#main-key", + "owner": "https://plu.me/~/BlogName/", + "publicKeyPem": blog.public_key + }, + "source": { + "content": "This is a small blog", + "mediaType": "text/markdown" + }, + "summary": "", + "type": "Group" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } + + #[test] + fn outbox_collection() { + let conn = &db(); + conn.test_transaction::<_, Error, _>(|| { + let (_users, blogs) = fill_database(conn); + let blog = &blogs[0]; + let act = blog.outbox_collection(conn)?; + + let expected = json!({ + "items": [], + "totalItems": 0, + "first": "https://plu.me/~/BlogName/outbox?page=1", + "last": "https://plu.me/~/BlogName/outbox?page=0", + "type": "OrderedCollection" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } + + #[test] + fn outbox_collection_page() { + let conn = &db(); + conn.test_transaction::<_, Error, _>(|| { + let (_users, blogs) = fill_database(conn); + let blog = &blogs[0]; + let act = blog.outbox_collection_page(conn, (33, 36))?; + + let expected = json!({ + "next": "https://plu.me/~/BlogName/outbox?page=3", + "prev": "https://plu.me/~/BlogName/outbox?page=1", + "items": [], + "type": "OrderedCollectionPage" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } } diff --git a/plume-models/src/comments.rs b/plume-models/src/comments.rs index 1da82a36..d928b9a3 100644 --- a/plume-models/src/comments.rs +++ b/plume-models/src/comments.rs @@ -11,18 +11,23 @@ use crate::{ users::User, Connection, Error, Result, CONFIG, }; -use activitypub::{ +use activitystreams::{ activity::{Create, Delete}, - link, + base::{AnyBase, Base}, + iri_string::types::IriString, + link::{self, kind::MentionType}, object::{Note, Tombstone}, + prelude::*, + primitives::OneOrMany, + time::OffsetDateTime, }; -use chrono::{self, NaiveDateTime, TimeZone, Utc}; +use chrono::{self, NaiveDateTime}; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl}; use plume_common::{ activity_pub::{ inbox::{AsActor, AsObject, FromId}, sign::Signer, - Id, IntoId, PUBLIC_VISIBILITY, + IntoId, ToAsString, ToAsUri, PUBLIC_VISIBILITY, }, utils, }; @@ -115,29 +120,32 @@ impl Comment { Some(Media::get_media_processor(conn, vec![&author])), ); - let mut note = Note::default(); - let to = vec![Id::new(PUBLIC_VISIBILITY.to_string())]; + let mut note = Note::new(); + let to = vec![PUBLIC_VISIBILITY.parse::()?]; - note.object_props - .set_id_string(self.ap_url.clone().unwrap_or_default())?; - note.object_props - .set_summary_string(self.spoiler_text.clone())?; - note.object_props.set_content_string(html)?; - note.object_props - .set_in_reply_to_link(Id::new(self.in_response_to_id.map_or_else( - || Ok(Post::get(conn, self.post_id)?.ap_url), - |id| Ok(Comment::get(conn, id)?.ap_url.unwrap_or_default()) as Result, - )?))?; - note.object_props - .set_published_utctime(Utc.from_utc_datetime(&self.creation_date))?; - note.object_props.set_attributed_to_link(author.into_id())?; - note.object_props.set_to_link_vec(to)?; - note.object_props.set_tag_link_vec( - mentions - .into_iter() - .filter_map(|m| Mention::build_activity(conn, &m).ok()) - .collect::>(), - )?; + note.set_id( + self.ap_url + .clone() + .unwrap_or_default() + .parse::()?, + ); + note.set_summary(self.spoiler_text.clone()); + note.set_content(html); + note.set_in_reply_to(self.in_response_to_id.map_or_else( + || Post::get(conn, self.post_id).map(|post| post.ap_url), + |id| Comment::get(conn, id).map(|comment| comment.ap_url.unwrap_or_default()), + )?); + note.set_published( + OffsetDateTime::from_unix_timestamp_nanos(self.creation_date.timestamp_nanos().into()) + .expect("OffsetDateTime"), + ); + note.set_attributed_to(author.into_id().parse::()?); + note.set_many_tos(to); + note.set_many_tags(mentions.into_iter().filter_map(|m| { + Mention::build_activity(conn, &m) + .map(|mention| mention.into_any_base().expect("Can convert")) + .ok() + })); Ok(note) } @@ -145,17 +153,26 @@ impl Comment { let author = User::get(conn, self.author_id)?; let note = self.to_activity(conn)?; - let mut act = Create::default(); - act.create_props.set_actor_link(author.into_id())?; - act.create_props.set_object_object(note.clone())?; - act.object_props.set_id_string(format!( - "{}/activity", - self.ap_url.clone().ok_or(Error::MissingApProperty)?, - ))?; - act.object_props - .set_to_link_vec(note.object_props.to_link_vec::()?)?; - act.object_props - .set_cc_link_vec(vec![Id::new(self.get_author(conn)?.followers_endpoint)])?; + let note_clone = note.clone(); + + let mut act = Create::new( + author.into_id().parse::()?, + Base::retract(note)?.into_generic()?, + ); + act.set_id( + format!( + "{}/activity", + self.ap_url.clone().ok_or(Error::MissingApProperty)?, + ) + .parse::()?, + ); + act.set_many_tos( + note_clone + .to() + .iter() + .flat_map(|tos| tos.iter().map(|to| to.to_owned())), + ); + act.set_many_ccs(vec![self.get_author(conn)?.followers_endpoint]); Ok(act) } @@ -180,20 +197,21 @@ impl Comment { } pub fn build_delete(&self, conn: &Connection) -> Result { - let mut act = Delete::default(); - act.delete_props - .set_actor_link(self.get_author(conn)?.into_id())?; + let mut tombstone = Tombstone::new(); + tombstone.set_id( + self.ap_url + .as_ref() + .ok_or(Error::MissingApProperty)? + .parse::()?, + ); - let mut tombstone = Tombstone::default(); - tombstone - .object_props - .set_id_string(self.ap_url.clone().ok_or(Error::MissingApProperty)?)?; - act.delete_props.set_object_object(tombstone)?; + let mut act = Delete::new( + self.get_author(conn)?.into_id().parse::()?, + Base::retract(tombstone)?.into_generic()?, + ); - act.object_props - .set_id_string(format!("{}#delete", self.ap_url.clone().unwrap()))?; - act.object_props - .set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY)])?; + act.set_id(format!("{}#delete", self.ap_url.clone().unwrap()).parse::()?); + act.set_many_tos(vec![PUBLIC_VISIBILITY.parse::()?]); Ok(act) } @@ -210,102 +228,104 @@ impl FromId for Comment { fn from_activity(conn: &DbConn, note: Note) -> Result { let comm = { let previous_url = note - .object_props - .in_reply_to - .as_ref() + .in_reply_to() .ok_or(Error::MissingApProperty)? - .as_str() + .iter() + .next() + .ok_or(Error::MissingApProperty)? + .id() .ok_or(Error::MissingApProperty)?; - let previous_comment = Comment::find_by_ap_url(conn, previous_url); + let previous_comment = Comment::find_by_ap_url(conn, previous_url.as_str()); - let is_public = |v: &Option| match v - .as_ref() - .unwrap_or(&serde_json::Value::Null) - { - serde_json::Value::Array(v) => v - .iter() - .filter_map(serde_json::Value::as_str) - .any(|s| s == PUBLIC_VISIBILITY), - serde_json::Value::String(s) => s == PUBLIC_VISIBILITY, - _ => false, + let is_public = |v: &Option<&OneOrMany>| match v { + Some(one_or_many) => one_or_many.iter().any(|any_base| { + let id = any_base.id(); + id.is_some() && id.unwrap() == PUBLIC_VISIBILITY + }), + None => false, }; - let public_visibility = is_public(¬e.object_props.to) - || is_public(¬e.object_props.bto) - || is_public(¬e.object_props.cc) - || is_public(¬e.object_props.bcc); + let public_visibility = is_public(¬e.to()) + || is_public(¬e.bto()) + || is_public(¬e.cc()) + || is_public(¬e.bcc()); + let summary = note.summary().and_then(|summary| summary.to_as_string()); + let sensitive = summary.is_some(); let comm = Comment::insert( conn, NewComment { - content: SafeString::new(¬e.object_props.content_string()?), - spoiler_text: note.object_props.summary_string().unwrap_or_default(), - ap_url: note.object_props.id_string().ok(), + content: SafeString::new( + ¬e + .content() + .ok_or(Error::MissingApProperty)? + .to_as_string() + .ok_or(Error::InvalidValue)?, + ), + spoiler_text: summary.unwrap_or_default(), + ap_url: Some( + note.id_unchecked() + .ok_or(Error::MissingApProperty)? + .to_string(), + ), in_response_to_id: previous_comment.iter().map(|c| c.id).next(), post_id: previous_comment.map(|c| c.post_id).or_else(|_| { - Ok(Post::find_by_ap_url(conn, previous_url)?.id) as Result + Ok(Post::find_by_ap_url(conn, previous_url.as_str())?.id) as Result })?, author_id: User::from_id( conn, - ¬e.object_props.attributed_to_link::()?, + ¬e + .attributed_to() + .ok_or(Error::MissingApProperty)? + .to_as_uri() + .ok_or(Error::MissingApProperty)?, None, CONFIG.proxy(), ) .map_err(|(_, e)| e)? .id, - sensitive: note.object_props.summary_string().is_ok(), + sensitive, public_visibility, }, )?; // save mentions - if let Some(serde_json::Value::Array(tags)) = note.object_props.tag.clone() { - for tag in tags { - serde_json::from_value::(tag) - .map_err(Error::from) - .and_then(|m| { - let author = &Post::get(conn, comm.post_id)?.get_authors(conn)?[0]; - let not_author = m.link_props.href_string()? != author.ap_url.clone(); - Mention::from_activity(conn, &m, comm.id, false, not_author) - }) - .ok(); + if let Some(tags) = note.tag() { + let author_url = &Post::get(conn, comm.post_id)?.get_authors(conn)?[0].ap_url; + for tag in tags.iter() { + let m = tag.clone().extend::()?; // FIXME: Don't clone + if m.is_none() { + continue; + } + let m = m.unwrap(); + let not_author = m.href().ok_or(Error::MissingApProperty)? != author_url; + let _ = Mention::from_activity(conn, &m, comm.id, false, not_author); } } comm }; if !comm.public_visibility { - let receivers_ap_url = |v: Option| { - let filter = |e: serde_json::Value| { - if let serde_json::Value::String(s) = e { - Some(s) - } else { - None + let mut receiver_ids = HashSet::new(); + let mut receivers_id = |v: Option<&'_ OneOrMany>| { + if let Some(one_or_many) = v { + for any_base in one_or_many.iter() { + if let Some(id) = any_base.id() { + receiver_ids.insert(id.to_string()); + } } - }; - match v.unwrap_or(serde_json::Value::Null) { - serde_json::Value::Array(v) => v, - v => vec![v], } - .into_iter() - .filter_map(filter) }; - let mut note = note; + receivers_id(note.to()); + receivers_id(note.cc()); + receivers_id(note.bto()); + receivers_id(note.bcc()); - let to = receivers_ap_url(note.object_props.to.take()); - let cc = receivers_ap_url(note.object_props.cc.take()); - let bto = receivers_ap_url(note.object_props.bto.take()); - let bcc = receivers_ap_url(note.object_props.bcc.take()); - - let receivers_ap_url = to - .chain(cc) - .chain(bto) - .chain(bcc) - .collect::>() // remove duplicates (don't do a query more than once) + let receivers_ap_url = receiver_ids .into_iter() .flat_map(|v| { - if let Ok(user) = User::from_id(conn, &v, None, CONFIG.proxy()) { + if let Ok(user) = User::from_id(conn, v.as_ref(), None, CONFIG.proxy()) { vec![user] } else { vec![] // TODO try to fetch collection diff --git a/plume-models/src/follows.rs b/plume-models/src/follows.rs index b1e6fdaf..c74b90a1 100644 --- a/plume-models/src/follows.rs +++ b/plume-models/src/follows.rs @@ -2,7 +2,12 @@ use crate::{ ap_url, db_conn::DbConn, instance::Instance, notifications::*, schema::follows, users::User, Connection, Error, Result, CONFIG, }; -use activitypub::activity::{Accept, Follow as FollowAct, Undo}; +use activitystreams::{ + activity::{Accept, ActorAndObjectRef, Follow as FollowAct, Undo}, + base::AnyBase, + iri_string::types::IriString, + prelude::*, +}; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl}; use plume_common::activity_pub::{ broadcast, @@ -53,15 +58,13 @@ impl Follow { pub fn to_activity(&self, conn: &Connection) -> Result { let user = User::get(conn, self.follower_id)?; let target = User::get(conn, self.following_id)?; + let target_id = target.ap_url.parse::()?; + + let mut act = FollowAct::new(user.ap_url.parse::()?, target_id.clone()); + act.set_id(self.ap_url.parse::()?); + act.set_many_tos(vec![target_id]); + act.set_many_ccs(vec![PUBLIC_VISIBILITY.parse::()?]); - let mut act = FollowAct::default(); - act.follow_props.set_actor_link::(user.into_id())?; - act.follow_props - .set_object_link::(target.clone().into_id())?; - act.object_props.set_id_string(self.ap_url.clone())?; - act.object_props.set_to_link_vec(vec![target.into_id()])?; - act.object_props - .set_cc_link_vec(vec![Id::new(PUBLIC_VISIBILITY.to_string())])?; Ok(act) } @@ -94,7 +97,11 @@ impl Follow { NewFollow { follower_id: from_id, following_id: target_id, - ap_url: follow.object_props.id_string()?, + ap_url: follow + .object_field_ref() + .as_single_id() + .ok_or(Error::MissingApProperty)? + .to_string(), }, )?; res.notify(conn)?; @@ -115,39 +122,35 @@ impl Follow { target: &A, follow: FollowAct, ) -> Result { - let mut accept = Accept::default(); + let mut accept = Accept::new( + target.clone().into_id().parse::()?, + AnyBase::from_extended(follow)?, + ); let accept_id = ap_url(&format!( "{}/follows/{}/accept", CONFIG.base_url.as_str(), self.id )); - accept.object_props.set_id_string(accept_id)?; - accept - .object_props - .set_to_link_vec(vec![from.clone().into_id()])?; - accept - .object_props - .set_cc_link_vec(vec![Id::new(PUBLIC_VISIBILITY.to_string())])?; - accept - .accept_props - .set_actor_link::(target.clone().into_id())?; - accept.accept_props.set_object_object(follow)?; + accept.set_id(accept_id.parse::()?); + accept.set_many_tos(vec![from.clone().into_id().parse::()?]); + accept.set_many_ccs(vec![PUBLIC_VISIBILITY.parse::()?]); Ok(accept) } pub fn build_undo(&self, conn: &Connection) -> Result { - let mut undo = Undo::default(); - undo.undo_props - .set_actor_link(User::get(conn, self.follower_id)?.into_id())?; - undo.object_props - .set_id_string(format!("{}/undo", self.ap_url))?; - undo.undo_props - .set_object_link::(self.clone().into_id())?; - undo.object_props - .set_to_link_vec(vec![User::get(conn, self.following_id)?.into_id()])?; - undo.object_props - .set_cc_link_vec(vec![Id::new(PUBLIC_VISIBILITY.to_string())])?; + let mut undo = Undo::new( + User::get(conn, self.follower_id)? + .ap_url + .parse::()?, + self.ap_url.parse::()?, + ); + undo.set_id(format!("{}/undo", self.ap_url).parse::()?); + undo.set_many_tos(vec![User::get(conn, self.following_id)? + .ap_url + .parse::()?]); + undo.set_many_ccs(vec![PUBLIC_VISIBILITY.parse::()?]); + Ok(undo) } } @@ -159,11 +162,7 @@ impl AsObject for User { fn activity(self, conn: &DbConn, actor: User, id: &str) -> Result { // Mastodon (at least) requires the full Follow object when accepting it, // so we rebuilt it here - let mut follow = FollowAct::default(); - follow.object_props.set_id_string(id.to_string())?; - follow - .follow_props - .set_actor_link::(actor.clone().into_id())?; + let follow = FollowAct::new(actor.ap_url.parse::()?, id.parse::()?); Follow::accept_follow(conn, &actor, &self, follow, actor.id, self.id) } } @@ -179,7 +178,11 @@ impl FromId for Follow { fn from_activity(conn: &DbConn, follow: FollowAct) -> Result { let actor = User::from_id( conn, - &follow.follow_props.actor_link::()?, + follow + .actor_field_ref() + .as_single_id() + .ok_or(Error::MissingApProperty)? + .as_str(), None, CONFIG.proxy(), ) @@ -187,7 +190,11 @@ impl FromId for Follow { let target = User::from_id( conn, - &follow.follow_props.object_link::()?, + follow + .object_field_ref() + .as_single_id() + .ok_or(Error::MissingApProperty)? + .as_str(), None, CONFIG.proxy(), ) diff --git a/plume-models/src/inbox.rs b/plume-models/src/inbox.rs index 21af9d88..fdf4056e 100644 --- a/plume-models/src/inbox.rs +++ b/plume-models/src/inbox.rs @@ -1,4 +1,4 @@ -use activitypub::activity::*; +use activitystreams::activity::{Announce, Create, Delete, Follow, Like, Undo, Update}; use crate::{ comments::Comment, @@ -94,8 +94,8 @@ pub(crate) mod tests { license: "WTFPL".to_owned(), creation_date: None, ap_url: format!("https://plu.me/~/{}/testing", blogs[0].actor_id), - subtitle: String::new(), - source: String::new(), + subtitle: "Bye".to_string(), + source: "Hello".to_string(), cover_id: None, }, ) @@ -268,7 +268,7 @@ pub(crate) mod tests { "actor": users[0].ap_url, "object": { "type": "Article", - "id": "https://plu.me/~/Blog/my-article", + "id": "https://plu.me/~/BlogName/testing", "attributedTo": [users[0].ap_url, blogs[0].ap_url], "content": "Hello.", "name": "My Article", @@ -286,11 +286,11 @@ pub(crate) mod tests { match super::inbox(&conn, act).unwrap() { super::InboxResult::Post(p) => { assert!(p.is_author(&conn, users[0].id).unwrap()); - assert_eq!(p.source, "Hello.".to_owned()); + assert_eq!(p.source, "Hello".to_owned()); assert_eq!(p.blog_id, blogs[0].id); - assert_eq!(p.content, SafeString::new("Hello.")); - assert_eq!(p.subtitle, "Bye.".to_owned()); - assert_eq!(p.title, "My Article".to_owned()); + assert_eq!(p.content, SafeString::new("Hello")); + assert_eq!(p.subtitle, "Bye".to_owned()); + assert_eq!(p.title, "Testing".to_owned()); } _ => panic!("Unexpected result"), }; diff --git a/plume-models/src/lib.rs b/plume-models/src/lib.rs index 546d5997..c9c991b4 100644 --- a/plume-models/src/lib.rs +++ b/plume-models/src/lib.rs @@ -16,6 +16,7 @@ extern crate serde_json; #[macro_use] extern crate tantivy; +use activitystreams::iri_string; pub use lettre; pub use lettre::smtp; use once_cell::sync::Lazy; @@ -100,6 +101,12 @@ impl From for Error { } } +impl From for Error { + fn from(_: iri_string::validate::Error) -> Self { + Error::Url + } +} + impl From for Error { fn from(_: serde_json::Error) -> Self { Error::SerDe @@ -118,12 +125,9 @@ impl From for Error { } } -impl From for Error { - fn from(err: activitypub::Error) -> Self { - match err { - activitypub::Error::NotFound => Error::MissingApProperty, - _ => Error::SerDe, - } +impl From for Error { + fn from(_: activitystreams::checked::CheckError) -> Error { + Error::MissingApProperty } } diff --git a/plume-models/src/likes.rs b/plume-models/src/likes.rs index 9c119177..b9dda167 100644 --- a/plume-models/src/likes.rs +++ b/plume-models/src/likes.rs @@ -2,13 +2,18 @@ use crate::{ db_conn::DbConn, instance::Instance, notifications::*, posts::Post, schema::likes, timeline::*, users::User, Connection, Error, Result, CONFIG, }; -use activitypub::activity; +use activitystreams::{ + activity::{ActorAndObjectRef, Like as LikeAct, Undo}, + base::AnyBase, + iri_string::types::IriString, + prelude::*, +}; use chrono::NaiveDateTime; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl}; use plume_common::activity_pub::{ inbox::{AsActor, AsObject, FromId}, sign::Signer, - Id, IntoId, PUBLIC_VISIBILITY, + PUBLIC_VISIBILITY, }; #[derive(Clone, Queryable, Identifiable)] @@ -34,18 +39,16 @@ impl Like { find_by!(likes, find_by_ap_url, ap_url as &str); find_by!(likes, find_by_user_on_post, user_id as i32, post_id as i32); - pub fn to_activity(&self, conn: &Connection) -> Result { - let mut act = activity::Like::default(); - act.like_props - .set_actor_link(User::get(conn, self.user_id)?.into_id())?; - act.like_props - .set_object_link(Post::get(conn, self.post_id)?.into_id())?; - act.object_props - .set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY.to_string())])?; - act.object_props.set_cc_link_vec(vec![Id::new( - User::get(conn, self.user_id)?.followers_endpoint, - )])?; - act.object_props.set_id_string(self.ap_url.clone())?; + pub fn to_activity(&self, conn: &Connection) -> Result { + let mut act = LikeAct::new( + User::get(conn, self.user_id)?.ap_url.parse::()?, + Post::get(conn, self.post_id)?.ap_url.parse::()?, + ); + act.set_many_tos(vec![PUBLIC_VISIBILITY.parse::()?]); + act.set_many_ccs(vec![User::get(conn, self.user_id)? + .followers_endpoint + .parse::()?]); + act.set_id(self.ap_url.parse::()?); Ok(act) } @@ -67,24 +70,22 @@ impl Like { Ok(()) } - pub fn build_undo(&self, conn: &Connection) -> Result { - let mut act = activity::Undo::default(); - act.undo_props - .set_actor_link(User::get(conn, self.user_id)?.into_id())?; - act.undo_props.set_object_object(self.to_activity(conn)?)?; - act.object_props - .set_id_string(format!("{}#delete", self.ap_url))?; - act.object_props - .set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY.to_string())])?; - act.object_props.set_cc_link_vec(vec![Id::new( - User::get(conn, self.user_id)?.followers_endpoint, - )])?; + pub fn build_undo(&self, conn: &Connection) -> Result { + let mut act = Undo::new( + User::get(conn, self.user_id)?.ap_url.parse::()?, + AnyBase::from_extended(self.to_activity(conn)?)?, + ); + act.set_id(format!("{}#delete", self.ap_url).parse::()?); + act.set_many_tos(vec![PUBLIC_VISIBILITY.parse::()?]); + act.set_many_ccs(vec![User::get(conn, self.user_id)? + .followers_endpoint + .parse::()?]); Ok(act) } } -impl AsObject for Post { +impl AsObject for Post { type Error = Error; type Output = Like; @@ -106,19 +107,22 @@ impl AsObject for Post { impl FromId for Like { type Error = Error; - type Object = activity::Like; + type Object = LikeAct; fn from_db(conn: &DbConn, id: &str) -> Result { Like::find_by_ap_url(conn, id) } - fn from_activity(conn: &DbConn, act: activity::Like) -> Result { + fn from_activity(conn: &DbConn, act: LikeAct) -> Result { let res = Like::insert( conn, NewLike { post_id: Post::from_id( conn, - &act.like_props.object_link::()?, + act.object_field_ref() + .as_single_id() + .ok_or(Error::MissingApProperty)? + .as_str(), None, CONFIG.proxy(), ) @@ -126,13 +130,19 @@ impl FromId for Like { .id, user_id: User::from_id( conn, - &act.like_props.actor_link::()?, + act.actor_field_ref() + .as_single_id() + .ok_or(Error::MissingApProperty)? + .as_str(), None, CONFIG.proxy(), ) .map_err(|(_, e)| e)? .id, - ap_url: act.object_props.id_string()?, + ap_url: act + .id_unchecked() + .ok_or(Error::MissingApProperty)? + .to_string(), }, )?; res.notify(conn)?; @@ -144,7 +154,7 @@ impl FromId for Like { } } -impl AsObject for Like { +impl AsObject for Like { type Error = Error; type Output = (); diff --git a/plume-models/src/medias.rs b/plume-models/src/medias.rs index 1af5f3b6..05a5278c 100644 --- a/plume-models/src/medias.rs +++ b/plume-models/src/medias.rs @@ -2,11 +2,11 @@ use crate::{ ap_url, db_conn::DbConn, instance::Instance, safe_string::SafeString, schema::medias, users::User, Connection, Error, Result, CONFIG, }; -use activitypub::object::Image; +use activitystreams::{object::Image, prelude::*}; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl}; use guid_create::GUID; use plume_common::{ - activity_pub::{inbox::FromId, request, Id}, + activity_pub::{inbox::FromId, request, ToAsString, ToAsUri}, utils::{escape, MediaProcessor}, }; use std::{ @@ -208,9 +208,9 @@ impl Media { // TODO: merge with save_remote? pub fn from_activity(conn: &DbConn, image: &Image) -> Result { let remote_url = image - .object_props - .url_string() - .or(Err(Error::MissingApProperty))?; + .url() + .and_then(|url| url.to_as_uri()) + .ok_or(Error::MissingApProperty)?; let path = determine_mirror_file_path(&remote_url); let parent = path.parent().ok_or(Error::InvalidValue)?; if !parent.is_dir() { @@ -231,11 +231,12 @@ impl Media { let mut updated = false; let alt_text = image - .object_props - .content_string() - .or(Err(Error::NotFound))?; - let sensitive = image.object_props.summary_string().is_ok(); - let content_warning = image.object_props.summary_string().ok(); + .content() + .and_then(|content| content.to_as_string()) + .ok_or(Error::NotFound)?; + let summary = image.summary().and_then(|summary| summary.to_as_string()); + let sensitive = summary.is_some(); + let content_warning = summary; if media.alt_text != alt_text { media.alt_text = alt_text; updated = true; @@ -262,28 +263,25 @@ impl Media { Ok(media) }) .or_else(|_| { + let summary = image.summary().and_then(|summary| summary.to_as_string()); Media::insert( conn, NewMedia { file_path: path.to_str().ok_or(Error::InvalidValue)?.to_string(), alt_text: image - .object_props - .content_string() - .or(Err(Error::NotFound))?, + .content() + .and_then(|content| content.to_as_string()) + .ok_or(Error::NotFound)?, is_remote: false, remote_url: None, - sensitive: image.object_props.summary_string().is_ok(), - content_warning: image.object_props.summary_string().ok(), + sensitive: summary.is_some(), + content_warning: summary, owner_id: User::from_id( conn, - image - .object_props - .attributed_to_link_vec::() - .or(Err(Error::NotFound))? - .into_iter() - .next() - .ok_or(Error::NotFound)? - .as_ref(), + &image + .attributed_to() + .and_then(|attributed_to| attributed_to.to_as_uri()) + .ok_or(Error::MissingApProperty)?, None, CONFIG.proxy(), ) diff --git a/plume-models/src/mentions.rs b/plume-models/src/mentions.rs index 16672781..dddb4250 100644 --- a/plume-models/src/mentions.rs +++ b/plume-models/src/mentions.rs @@ -2,7 +2,11 @@ use crate::{ comments::Comment, db_conn::DbConn, notifications::*, posts::Post, schema::mentions, users::User, Connection, Error, Result, }; -use activitypub::link; +use activitystreams::{ + base::BaseExt, + iri_string::types::IriString, + link::{self, LinkExt}, +}; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl}; use plume_common::activity_pub::inbox::AsActor; @@ -58,19 +62,17 @@ impl Mention { pub fn build_activity(conn: &DbConn, ment: &str) -> Result { let user = User::find_by_fqn(conn, ment)?; - let mut mention = link::Mention::default(); - mention.link_props.set_href_string(user.ap_url)?; - mention.link_props.set_name_string(format!("@{}", ment))?; + let mut mention = link::Mention::new(); + mention.set_href(user.ap_url.parse::()?); + mention.set_name(format!("@{}", ment)); Ok(mention) } pub fn to_activity(&self, conn: &Connection) -> Result { let user = self.get_mentioned(conn)?; - let mut mention = link::Mention::default(); - mention.link_props.set_href_string(user.ap_url.clone())?; - mention - .link_props - .set_name_string(format!("@{}", user.fqn))?; + let mut mention = link::Mention::new(); + mention.set_href(user.ap_url.parse::()?); + mention.set_name(format!("@{}", user.fqn)); Ok(mention) } @@ -81,8 +83,8 @@ impl Mention { in_post: bool, notify: bool, ) -> Result { - let ap_url = ment.link_props.href_string().or(Err(Error::NotFound))?; - let mentioned = User::find_by_ap_url(conn, &ap_url)?; + let ap_url = ment.href().ok_or(Error::NotFound)?.as_str(); + let mentioned = User::find_by_ap_url(conn, ap_url)?; if in_post { Post::get(conn, inside).and_then(|post| { diff --git a/plume-models/src/posts.rs b/plume-models/src/posts.rs index 65c98735..ef86ca97 100644 --- a/plume-models/src/posts.rs +++ b/plume-models/src/posts.rs @@ -3,20 +3,24 @@ use crate::{ post_authors::*, safe_string::SafeString, schema::posts, tags::*, timeline::*, users::User, Connection, Error, PostEvent::*, Result, CONFIG, POST_CHAN, }; -use activitypub::{ +use activitystreams::{ activity::{Create, Delete, Update}, - link, - object::{Article, Image, Tombstone}, - CustomObject, + base::{AnyBase, Base}, + iri_string::types::IriString, + link::{self, kind::MentionType}, + object::{kind::ImageType, ApObject, Article, AsApObject, Image, ObjectExt, Tombstone}, + prelude::*, + time::OffsetDateTime, }; -use chrono::{NaiveDateTime, TimeZone, Utc}; +use chrono::{NaiveDateTime, Utc}; use diesel::{self, BelongingToDsl, ExpressionMethods, QueryDsl, RunQueryDsl}; use once_cell::sync::Lazy; use plume_common::{ activity_pub::{ inbox::{AsActor, AsObject, FromId}, sign::Signer, - Hashtag, Id, IntoId, Licensed, Source, PUBLIC_VISIBILITY, + Hashtag, HashtagType, Id, IntoId, Licensed, LicensedArticle, ToAsString, ToAsUri, + PUBLIC_VISIBILITY, }, utils::{iri_percent_encode_seg, md_to_html}, }; @@ -24,8 +28,6 @@ use riker::actors::{Publish, Tell}; use std::collections::{HashMap, HashSet}; use std::sync::{Arc, Mutex}; -pub type LicensedArticle = CustomObject; - static BLOG_FQN_CACHE: Lazy>> = Lazy::new(|| Mutex::new(HashMap::new())); #[derive(Queryable, Identifiable, Clone, AsChangeset, Debug)] @@ -353,92 +355,92 @@ impl Post { .collect::>(); mentions_json.append(&mut tags_json); - let mut article = Article::default(); - article.object_props.set_name_string(self.title.clone())?; - article.object_props.set_id_string(self.ap_url.clone())?; + let mut article = ApObject::new(Article::new()); + article.set_name(self.title.clone()); + article.set_id(self.ap_url.parse::()?); let mut authors = self .get_authors(conn)? .into_iter() - .map(|x| Id::new(x.ap_url)) - .collect::>(); - authors.push(self.get_blog(conn)?.into_id()); // add the blog URL here too - article - .object_props - .set_attributed_to_link_vec::(authors)?; - article - .object_props - .set_content_string(self.content.get().clone())?; - article.ap_object_props.set_source_object(Source { - content: self.source.clone(), - media_type: String::from("text/markdown"), - })?; - article - .object_props - .set_published_utctime(Utc.from_utc_datetime(&self.creation_date))?; - article - .object_props - .set_summary_string(self.subtitle.clone())?; - article.object_props.tag = Some(json!(mentions_json)); + .filter_map(|x| x.ap_url.parse::().ok()) + .collect::>(); + authors.push(self.get_blog(conn)?.ap_url.parse::()?); // add the blog URL here too + article.set_many_attributed_tos(authors); + article.set_content(self.content.get().clone()); + let source = AnyBase::from_arbitrary_json(serde_json::json!({ + "content": self.source, + "mediaType": "text/markdown", + }))?; + article.set_source(source); + article.set_published( + OffsetDateTime::from_unix_timestamp_nanos(self.creation_date.timestamp_nanos().into()) + .expect("OffsetDateTime"), + ); + article.set_summary(&*self.subtitle); + article.set_many_tags( + mentions_json + .iter() + .filter_map(|mention_json| AnyBase::from_arbitrary_json(mention_json).ok()), + ); if let Some(media_id) = self.cover_id { let media = Media::get(conn, media_id)?; - let mut cover = Image::default(); - cover.object_props.set_url_string(media.url()?)?; + let mut cover = Image::new(); + cover.set_url(media.url()?); if media.sensitive { - cover - .object_props - .set_summary_string(media.content_warning.unwrap_or_default())?; + cover.set_summary(media.content_warning.unwrap_or_default()); } - cover.object_props.set_content_string(media.alt_text)?; - cover - .object_props - .set_attributed_to_link_vec(vec![User::get(conn, media.owner_id)?.into_id()])?; - article.object_props.set_icon_object(cover)?; + cover.set_content(media.alt_text); + cover.set_many_attributed_tos(vec![User::get(conn, media.owner_id)? + .ap_url + .parse::()?]); + article.set_icon(cover.into_any_base()?); } - article.object_props.set_url_string(self.ap_url.clone())?; - article - .object_props - .set_to_link_vec::(to.into_iter().map(Id::new).collect())?; - article - .object_props - .set_cc_link_vec::(cc.into_iter().map(Id::new).collect())?; - let mut license = Licensed::default(); - license.set_license_string(self.license.clone())?; + article.set_url(self.ap_url.parse::()?); + article.set_many_tos( + to.into_iter() + .filter_map(|to| to.parse::().ok()) + .collect::>(), + ); + article.set_many_ccs( + cc.into_iter() + .filter_map(|cc| cc.parse::().ok()) + .collect::>(), + ); + let license = Licensed { + license: Some(self.license.clone()), + }; Ok(LicensedArticle::new(article, license)) } pub fn create_activity(&self, conn: &Connection) -> Result { let article = self.to_activity(conn)?; - let mut act = Create::default(); - act.object_props - .set_id_string(format!("{}/activity", self.ap_url))?; - act.object_props - .set_to_link_vec::(article.object.object_props.to_link_vec()?)?; - act.object_props - .set_cc_link_vec::(article.object.object_props.cc_link_vec()?)?; - act.create_props - .set_actor_link(Id::new(self.get_authors(conn)?[0].clone().ap_url))?; - act.create_props.set_object_object(article)?; + let to = article.to().ok_or(Error::MissingApProperty)?.clone(); + let cc = article.cc().ok_or(Error::MissingApProperty)?.clone(); + let mut act = Create::new( + self.get_authors(conn)?[0].ap_url.parse::()?, + Base::retract(article)?.into_generic()?, + ); + act.set_id(format!("{}/activity", self.ap_url).parse::()?); + act.set_many_tos(to); + act.set_many_ccs(cc); Ok(act) } pub fn update_activity(&self, conn: &Connection) -> Result { let article = self.to_activity(conn)?; - let mut act = Update::default(); - act.object_props.set_id_string(format!( - "{}/update-{}", - self.ap_url, - Utc::now().timestamp() - ))?; - act.object_props - .set_to_link_vec::(article.object.object_props.to_link_vec()?)?; - act.object_props - .set_cc_link_vec::(article.object.object_props.cc_link_vec()?)?; - act.update_props - .set_actor_link(Id::new(self.get_authors(conn)?[0].clone().ap_url))?; - act.update_props.set_object_object(article)?; + let to = article.to().ok_or(Error::MissingApProperty)?.clone(); + let cc = article.cc().ok_or(Error::MissingApProperty)?.clone(); + let mut act = Update::new( + self.get_authors(conn)?[0].ap_url.parse::()?, + Base::retract(article)?.into_generic()?, + ); + act.set_id( + format!("{}/update-{}", self.ap_url, Utc::now().timestamp()).parse::()?, + ); + act.set_many_tos(to); + act.set_many_ccs(cc); Ok(act) } @@ -447,10 +449,8 @@ impl Post { .into_iter() .map(|m| { ( - m.link_props - .href_string() - .ok() - .and_then(|ap_url| User::find_by_ap_url(conn, &ap_url).ok()) + m.href() + .and_then(|ap_url| User::find_by_ap_url(conn, ap_url.as_ref()).ok()) .map(|u| u.id), m, ) @@ -485,7 +485,7 @@ impl Post { pub fn update_tags(&self, conn: &Connection, tags: Vec) -> Result<()> { let tags_name = tags .iter() - .filter_map(|t| t.name_string().ok()) + .filter_map(|t| t.name.as_ref().map(|name| name.as_str().to_string())) .collect::>(); let old_tags = Tag::for_post(&*conn, self.id)?; @@ -502,8 +502,9 @@ impl Post { for t in tags { if !t - .name_string() - .map(|n| old_tags_name.contains(&n)) + .name + .as_ref() + .map(|n| old_tags_name.contains(n.as_str())) .unwrap_or(true) { Tag::from_activity(conn, &t, self.id, false)?; @@ -521,7 +522,7 @@ impl Post { pub fn update_hashtags(&self, conn: &Connection, tags: Vec) -> Result<()> { let tags_name = tags .iter() - .filter_map(|t| t.name_string().ok()) + .filter_map(|t| t.name.as_ref().map(|name| name.as_str().to_string())) .collect::>(); let old_tags = Tag::for_post(&*conn, self.id)?; @@ -538,8 +539,9 @@ impl Post { for t in tags { if !t - .name_string() - .map(|n| old_tags_name.contains(&n)) + .name + .as_ref() + .map(|n| old_tags_name.contains(n.as_str())) .unwrap_or(true) { Tag::from_activity(conn, &t, self.id, true)?; @@ -566,18 +568,19 @@ impl Post { } pub fn build_delete(&self, conn: &Connection) -> Result { - let mut act = Delete::default(); - act.delete_props - .set_actor_link(self.get_authors(conn)?[0].clone().into_id())?; + let mut tombstone = Tombstone::new(); + tombstone.set_id(self.ap_url.parse()?); - let mut tombstone = Tombstone::default(); - tombstone.object_props.set_id_string(self.ap_url.clone())?; - act.delete_props.set_object_object(tombstone)?; + let mut act = Delete::new( + self.get_authors(conn)?[0] + .clone() + .into_id() + .parse::()?, + Base::retract(tombstone)?.into_generic()?, + ); - act.object_props - .set_id_string(format!("{}#delete", self.ap_url))?; - act.object_props - .set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY)])?; + act.set_id(format!("{}#delete", self.ap_url).parse()?); + act.set_many_tos(vec![PUBLIC_VISIBILITY.parse::()?]); Ok(act) } @@ -621,47 +624,82 @@ impl FromId for Post { } fn from_activity(conn: &DbConn, article: LicensedArticle) -> Result { - let conn = conn; - let license = article.custom_props.license_string().unwrap_or_default(); - let article = article.object; + let license = article.ext_one.license.unwrap_or_default(); + let article = article.inner; let (blog, authors) = article - .object_props - .attributed_to_link_vec::()? - .into_iter() + .ap_object_ref() + .attributed_to() + .ok_or(Error::MissingApProperty)? + .iter() .fold((None, vec![]), |(blog, mut authors), link| { - let url = link; - match User::from_id(conn, &url, None, CONFIG.proxy()) { - Ok(u) => { - authors.push(u); - (blog, authors) + if let Some(url) = link.id() { + match User::from_id(conn, url.as_str(), None, CONFIG.proxy()) { + Ok(u) => { + authors.push(u); + (blog, authors) + } + Err(_) => ( + blog.or_else(|| { + Blog::from_id(conn, url.as_str(), None, CONFIG.proxy()).ok() + }), + authors, + ), } - Err(_) => ( - blog.or_else(|| Blog::from_id(conn, &url, None, CONFIG.proxy()).ok()), - authors, - ), + } else { + // logically, url possible to be an object without id proprty like {"type":"Person", "name":"Sally"} but we ignore the case + (blog, authors) } }); - let cover = article - .object_props - .icon_object::() - .ok() - .and_then(|img| Media::from_activity(conn, &img).ok().map(|m| m.id)); + let cover = article.icon().and_then(|icon| { + icon.iter().next().and_then(|img| { + let image = img.to_owned().extend::().ok()??; + Media::from_activity(conn, &image).ok().map(|m| m.id) + }) + }); - let title = article.object_props.name_string()?; + let title = article + .name() + .and_then(|name| name.to_as_string()) + .ok_or(Error::MissingApProperty)?; + let id = AnyBase::from_extended(article.clone()) // FIXME: Don't clone + .ok() + .ok_or(Error::MissingApProperty)? + .id() + .map(|id| id.to_string()); let ap_url = article - .object_props - .url_string() - .or_else(|_| article.object_props.id_string())?; + .url() + .and_then(|url| url.to_as_uri().or(id)) + .ok_or(Error::MissingApProperty)?; + let source = article + .source() + .and_then(|s| { + serde_json::to_value(s).ok().and_then(|obj| { + if !obj.is_object() { + return None; + } + obj.get("content") + .and_then(|content| content.as_str().map(|c| c.to_string())) + }) + }) + .unwrap_or_default(); let post = Post::from_db(conn, &ap_url) .and_then(|mut post| { let mut updated = false; let slug = Self::slug(&title); - let content = SafeString::new(&article.object_props.content_string()?); - let subtitle = article.object_props.summary_string()?; - let source = article.ap_object_props.source_object::()?.content; + let content = SafeString::new( + &article + .content() + .and_then(|content| content.to_as_string()) + .ok_or(Error::MissingApProperty)?, + ); + let subtitle = article + .summary() + .and_then(|summary| summary.to_as_string()) + .ok_or(Error::MissingApProperty)?; + if post.slug != slug { post.slug = slug.to_string(); updated = true; @@ -683,7 +721,7 @@ impl FromId for Post { updated = true; } if post.source != source { - post.source = source; + post.source = source.clone(); updated = true; } if post.cover_id != cover { @@ -704,14 +742,27 @@ impl FromId for Post { blog_id: blog.ok_or(Error::NotFound)?.id, slug: Self::slug(&title).to_string(), title, - content: SafeString::new(&article.object_props.content_string()?), + content: SafeString::new( + &article + .content() + .and_then(|content| content.to_as_string()) + .ok_or(Error::MissingApProperty)?, + ), published: true, license, // FIXME: This is wrong: with this logic, we may use the display URL as the AP ID. We need two different fields ap_url, - creation_date: Some(article.object_props.published_utctime()?.naive_utc()), - subtitle: article.object_props.summary_string()?, - source: article.ap_object_props.source_object::()?.content, + creation_date: article.published().map(|published| { + let timestamp_secs = published.unix_timestamp(); + let timestamp_nanos = published.unix_timestamp_nanos() + - (timestamp_secs as i128) * 1000i128 * 1000i128 * 1000i128; + NaiveDateTime::from_timestamp(timestamp_secs, timestamp_nanos as u32) + }), + subtitle: article + .summary() + .and_then(|summary| summary.to_as_string()) + .ok_or(Error::MissingApProperty)?, + source, cover_id: cover, }, ) @@ -735,22 +786,22 @@ impl FromId for Post { .2 .into_iter() .collect::>(); - if let Some(serde_json::Value::Array(tags)) = article.object_props.tag { - for tag in tags { - serde_json::from_value::(tag.clone()) - .map(|m| Mention::from_activity(conn, &m, post.id, true, true)) + if let Some(tags) = article.tag() { + for tag in tags.iter() { + tag.clone() + .extend::() // FIXME: Don't clone + .map(|mention| { + mention.map(|m| Mention::from_activity(conn, &m, post.id, true, true)) + }) .ok(); - serde_json::from_value::(tag.clone()) - .map_err(Error::from) - .and_then(|t| { - let tag_name = t.name_string()?; - Ok(Tag::from_activity( - conn, - &t, - post.id, - hashtags.remove(&tag_name), - )) + tag.clone() + .extend::() // FIXME: Don't clone + .map(|hashtag| { + hashtag.and_then(|t| { + let tag_name = t.name.clone()?.as_str().to_string(); + Tag::from_activity(conn, &t, post.id, hashtags.remove(&tag_name)).ok() + }) }) .ok(); } @@ -762,15 +813,15 @@ impl FromId for Post { } fn get_sender() -> &'static dyn Signer { - Instance::get_local_instance_user().expect("Failed to local instance user") + Instance::get_local_instance_user().expect("Failed to get local instance user") } } impl AsObject for Post { type Error = Error; - type Output = Post; + type Output = Self; - fn activity(self, _conn: &DbConn, _actor: User, _id: &str) -> Result { + fn activity(self, _conn: &DbConn, _actor: User, _id: &str) -> Result { // TODO: check that _actor is actually one of the author? Ok(self) } @@ -780,7 +831,7 @@ impl AsObject for Post { type Error = Error; type Output = (); - fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result<()> { + fn activity(self, conn: &DbConn, actor: User, _id: &str) -> Result { let can_delete = self .get_authors(conn)? .into_iter() @@ -813,27 +864,54 @@ impl FromId for PostUpdate { Err(Error::NotFound) } - fn from_activity(conn: &DbConn, updated: LicensedArticle) -> Result { - Ok(PostUpdate { - ap_url: updated.object.object_props.id_string()?, - title: updated.object.object_props.name_string().ok(), - subtitle: updated.object.object_props.summary_string().ok(), - content: updated.object.object_props.content_string().ok(), - cover: updated - .object - .object_props - .icon_object::() - .ok() - .and_then(|img| Media::from_activity(conn, &img).ok().map(|m| m.id)), - source: updated - .object - .ap_object_props - .source_object::() - .ok() - .map(|x| x.content), - license: updated.custom_props.license_string().ok(), - tags: updated.object.object_props.tag, - }) + fn from_activity(conn: &DbConn, updated: Self::Object) -> Result { + let mut post_update = PostUpdate { + ap_url: updated + .ap_object_ref() + .id_unchecked() + .ok_or(Error::MissingApProperty)? + .to_string(), + title: updated + .ap_object_ref() + .name() + .and_then(|name| name.to_as_string()), + subtitle: updated + .ap_object_ref() + .summary() + .and_then(|summary| summary.to_as_string()), + content: updated + .ap_object_ref() + .content() + .and_then(|content| content.to_as_string()), + cover: None, + source: updated.source().and_then(|s| { + serde_json::to_value(s).ok().and_then(|obj| { + if !obj.is_object() { + return None; + } + obj.get("content") + .and_then(|content| content.as_str().map(|c| c.to_string())) + }) + }), + license: None, + tags: updated + .tag() + .and_then(|tags| serde_json::to_value(tags).ok()), + }; + post_update.cover = updated.ap_object_ref().icon().and_then(|img| { + img.iter() + .next() + .and_then(|img| { + img.clone() + .extend::() + .map(|img| img.and_then(|img| Media::from_activity(conn, &img).ok())) + .ok() + }) + .and_then(|m| m.map(|m| m.id)) + }); + post_update.license = updated.ext_one.license; + + Ok(post_update) } fn get_sender() -> &'static dyn Signer { @@ -893,8 +971,12 @@ impl AsObject for PostUpdate { serde_json::from_value::(tag.clone()) .map_err(Error::from) .and_then(|t| { - let tag_name = t.name_string()?; - if txt_hashtags.remove(&tag_name) { + let tag_name = t.name.as_ref().ok_or(Error::MissingApProperty)?; + let tag_name_str = tag_name + .as_xsd_string() + .or_else(|| tag_name.as_rdf_lang_string().map(|rls| &*rls.value)) + .ok_or(Error::MissingApProperty)?; + if txt_hashtags.remove(tag_name_str) { hashtags.push(t); } else { tags.push(t); @@ -1015,49 +1097,6 @@ mod tests { }); } - #[test] - fn licensed_article_serde() { - let mut article = Article::default(); - article.object_props.set_id_string("Yo".into()).unwrap(); - let mut license = Licensed::default(); - license.set_license_string("WTFPL".into()).unwrap(); - let full_article = LicensedArticle::new(article, license); - - let json = serde_json::to_value(full_article).unwrap(); - let article_from_json: LicensedArticle = serde_json::from_value(json).unwrap(); - assert_eq!( - "Yo", - &article_from_json.object.object_props.id_string().unwrap() - ); - assert_eq!( - "WTFPL", - &article_from_json.custom_props.license_string().unwrap() - ); - } - - #[test] - fn licensed_article_deserialization() { - let json = json!({ - "type": "Article", - "id": "https://plu.me/~/Blog/my-article", - "attributedTo": ["https://plu.me/@/Admin", "https://plu.me/~/Blog"], - "content": "Hello.", - "name": "My Article", - "summary": "Bye.", - "source": { - "content": "Hello.", - "mediaType": "text/markdown" - }, - "published": "2014-12-12T12:12:12Z", - "to": [plume_common::activity_pub::PUBLIC_VISIBILITY] - }); - let article: LicensedArticle = serde_json::from_value(json).unwrap(); - assert_eq!( - "https://plu.me/~/Blog/my-article", - &article.object.object_props.id_string().unwrap() - ); - } - #[test] fn to_activity() { let conn = db(); @@ -1074,10 +1113,10 @@ mod tests { "name": "Testing", "published": format_datetime(&post.creation_date), "source": { - "content": "", + "content": "Hello", "mediaType": "text/markdown" }, - "summary": "", + "summary": "Bye", "tag": [ { "href": "https://plu.me/@/user/", @@ -1116,10 +1155,10 @@ mod tests { "name": "Testing", "published": format_datetime(&post.creation_date), "source": { - "content": "", + "content": "Hello", "mediaType": "text/markdown" }, - "summary": "", + "summary": "Bye", "tag": [ { "href": "https://plu.me/@/user/", @@ -1161,10 +1200,10 @@ mod tests { "name": "Testing", "published": format_datetime(&post.creation_date), "source": { - "content": "", + "content": "Hello", "mediaType": "text/markdown" }, - "summary": "", + "summary": "Bye", "tag": [ { "href": "https://plu.me/@/user/", @@ -1200,10 +1239,36 @@ mod tests { if key == "id" { continue; } - assert_eq!(value, expected.get(key).unwrap()); + assert_json_eq!(value, expected.get(key).unwrap()); } Ok(()) }); } + + #[test] + fn build_delete() { + let conn = db(); + conn.test_transaction::<_, Error, _>(|| { + let (post, _mention, _posts, _users, _blogs) = prepare_activity(&conn); + let act = post.build_delete(&conn)?; + + let expected = json!({ + "actor": "https://plu.me/@/admin/", + "id": "https://plu.me/~/BlogName/testing#delete", + "object": { + "id": "https://plu.me/~/BlogName/testing", + "type": "Tombstone" + }, + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "type": "Delete" + }); + + assert_json_eq!(to_value(act)?, expected); + + Ok(()) + }); + } } diff --git a/plume-models/src/remote_fetch_actor.rs b/plume-models/src/remote_fetch_actor.rs index 097fb1da..e45a0e46 100644 --- a/plume-models/src/remote_fetch_actor.rs +++ b/plume-models/src/remote_fetch_actor.rs @@ -1,12 +1,16 @@ use crate::{ db_conn::{DbConn, DbPool}, follows, - posts::{LicensedArticle, Post}, + posts::Post, users::{User, UserEvent}, ACTOR_SYS, CONFIG, USER_CHAN, }; -use activitypub::activity::Create; -use plume_common::activity_pub::inbox::FromId; +use activitystreams::{ + activity::{ActorAndObjectRef, Create}, + base::AnyBase, + object::kind::ArticleType, +}; +use plume_common::activity_pub::{inbox::FromId, LicensedArticle}; use riker::actors::{Actor, ActorFactoryArgs, ActorRefFactory, Context, Sender, Subscribe, Tell}; use std::sync::Arc; use tracing::{error, info, warn}; @@ -68,13 +72,17 @@ fn fetch_and_cache_articles(user: &Arc, conn: &DbConn) { match create_acts { Ok(create_acts) => { for create_act in create_acts { - match create_act.create_props.object_object::() { - Ok(article) => { + match create_act.object_field_ref().as_single_base().map(|base| { + let any_base = AnyBase::from_base(base.clone()); // FIXME: Don't clone() + any_base.extend::() + }) { + Some(Ok(Some(article))) => { Post::from_activity(conn, article) .expect("Article from remote user couldn't be saved"); info!("Fetched article from remote user"); } - Err(e) => warn!("Error while fetching articles in background: {:?}", e), + Some(Err(e)) => warn!("Error while fetching articles in background: {:?}", e), + _ => warn!("Error while fetching articles in background"), } } } diff --git a/plume-models/src/reshares.rs b/plume-models/src/reshares.rs index 90ca0cc8..aec75796 100644 --- a/plume-models/src/reshares.rs +++ b/plume-models/src/reshares.rs @@ -2,13 +2,18 @@ use crate::{ db_conn::DbConn, instance::Instance, notifications::*, posts::Post, schema::reshares, timeline::*, users::User, Connection, Error, Result, CONFIG, }; -use activitypub::activity::{Announce, Undo}; +use activitystreams::{ + activity::{ActorAndObjectRef, Announce, Undo}, + base::AnyBase, + iri_string::types::IriString, + prelude::*, +}; use chrono::NaiveDateTime; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl}; use plume_common::activity_pub::{ inbox::{AsActor, AsObject, FromId}, sign::Signer, - Id, IntoId, PUBLIC_VISIBILITY, + PUBLIC_VISIBILITY, }; #[derive(Clone, Queryable, Identifiable)] @@ -61,16 +66,16 @@ impl Reshare { } pub fn to_activity(&self, conn: &Connection) -> Result { - let mut act = Announce::default(); - act.announce_props - .set_actor_link(User::get(conn, self.user_id)?.into_id())?; - act.announce_props - .set_object_link(Post::get(conn, self.post_id)?.into_id())?; - act.object_props.set_id_string(self.ap_url.clone())?; - act.object_props - .set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY.to_string())])?; - act.object_props - .set_cc_link_vec(vec![Id::new(self.get_user(conn)?.followers_endpoint)])?; + let mut act = Announce::new( + User::get(conn, self.user_id)?.ap_url.parse::()?, + Post::get(conn, self.post_id)?.ap_url.parse::()?, + ); + act.set_id(self.ap_url.parse::()?); + act.set_many_tos(vec![PUBLIC_VISIBILITY.parse::()?]); + act.set_many_ccs(vec![self + .get_user(conn)? + .followers_endpoint + .parse::()?]); Ok(act) } @@ -93,16 +98,16 @@ impl Reshare { } pub fn build_undo(&self, conn: &Connection) -> Result { - let mut act = Undo::default(); - act.undo_props - .set_actor_link(User::get(conn, self.user_id)?.into_id())?; - act.undo_props.set_object_object(self.to_activity(conn)?)?; - act.object_props - .set_id_string(format!("{}#delete", self.ap_url))?; - act.object_props - .set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY.to_string())])?; - act.object_props - .set_cc_link_vec(vec![Id::new(self.get_user(conn)?.followers_endpoint)])?; + let mut act = Undo::new( + User::get(conn, self.user_id)?.ap_url.parse::()?, + AnyBase::from_extended(self.to_activity(conn)?)?, + ); + act.set_id(format!("{}#delete", self.ap_url).parse::()?); + act.set_many_tos(vec![PUBLIC_VISIBILITY.parse::()?]); + act.set_many_ccs(vec![self + .get_user(conn)? + .followers_endpoint + .parse::()?]); Ok(act) } @@ -143,7 +148,10 @@ impl FromId for Reshare { NewReshare { post_id: Post::from_id( conn, - &act.announce_props.object_link::()?, + act.object_field_ref() + .as_single_id() + .ok_or(Error::MissingApProperty)? + .as_str(), None, CONFIG.proxy(), ) @@ -151,13 +159,19 @@ impl FromId for Reshare { .id, user_id: User::from_id( conn, - &act.announce_props.actor_link::()?, + act.actor_field_ref() + .as_single_id() + .ok_or(Error::MissingApProperty)? + .as_str(), None, CONFIG.proxy(), ) .map_err(|(_, e)| e)? .id, - ap_url: act.object_props.id_string()?, + ap_url: act + .id_unchecked() + .ok_or(Error::MissingApProperty)? + .to_string(), }, )?; res.notify(conn)?; diff --git a/plume-models/src/tags.rs b/plume-models/src/tags.rs index 0460efc2..681de5e1 100644 --- a/plume-models/src/tags.rs +++ b/plume-models/src/tags.rs @@ -1,6 +1,7 @@ use crate::{ap_url, instance::Instance, schema::tags, Connection, Error, Result}; +use activitystreams::iri_string::types::IriString; use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl}; -use plume_common::activity_pub::Hashtag; +use plume_common::activity_pub::{Hashtag, HashtagExt}; #[derive(Clone, Identifiable, Queryable)] pub struct Tag { @@ -25,13 +26,16 @@ impl Tag { list_by!(tags, for_post, post_id as i32); pub fn to_activity(&self) -> Result { - let mut ht = Hashtag::default(); - ht.set_href_string(ap_url(&format!( - "{}/tag/{}", - Instance::get_local()?.public_domain, - self.tag - )))?; - ht.set_name_string(self.tag.clone())?; + let mut ht = Hashtag::new(); + ht.set_href( + ap_url(&format!( + "{}/tag/{}", + Instance::get_local()?.public_domain, + self.tag + )) + .parse::()?, + ); + ht.set_name(self.tag.clone()); Ok(ht) } @@ -44,7 +48,7 @@ impl Tag { Tag::insert( conn, NewTag { - tag: tag.name_string()?, + tag: tag.name().ok_or(Error::MissingApProperty)?.as_str().into(), is_hashtag, post_id: post, }, @@ -52,13 +56,16 @@ impl Tag { } pub fn build_activity(tag: String) -> Result { - let mut ht = Hashtag::default(); - ht.set_href_string(ap_url(&format!( - "{}/tag/{}", - Instance::get_local()?.public_domain, - tag - )))?; - ht.set_name_string(tag)?; + let mut ht = Hashtag::new(); + ht.set_href( + ap_url(&format!( + "{}/tag/{}", + Instance::get_local()?.public_domain, + tag + )) + .parse::()?, + ); + ht.set_name(tag); Ok(ht) } @@ -78,6 +85,24 @@ mod tests { use assert_json_diff::assert_json_eq; use serde_json::to_value; + #[test] + fn from_activity() { + let conn = &db(); + conn.test_transaction::<_, Error, _>(|| { + let (posts, _users, _blogs) = fill_database(conn); + let post_id = posts[0].id; + let mut ht = Hashtag::new(); + ht.set_href(ap_url(&format!("https://plu.me/tag/a_tag")).parse::()?); + ht.set_name("a_tag".to_string()); + let tag = Tag::from_activity(conn, &ht, post_id, true)?; + + assert_eq!(&tag.tag, "a_tag"); + assert!(tag.is_hashtag); + + Ok(()) + }); + } + #[test] fn to_activity() { let conn = &db(); @@ -102,24 +127,6 @@ mod tests { }) } - #[test] - fn from_activity() { - let conn = &db(); - conn.test_transaction::<_, Error, _>(|| { - let (posts, _users, _blogs) = fill_database(conn); - let post_id = posts[0].id; - let mut ht = Hashtag::default(); - ht.set_href_string(ap_url(&format!("https://plu.me/tag/a_tag")))?; - ht.set_name_string("a_tag".into())?; - let tag = Tag::from_activity(conn, &ht, post_id, true)?; - - assert_eq!(&tag.tag, "a_tag"); - assert!(tag.is_hashtag); - - Ok(()) - }); - } - #[test] fn build_activity() { let conn = &db(); diff --git a/plume-models/src/users.rs b/plume-models/src/users.rs index 85aaf941..b33f9bc1 100644 --- a/plume-models/src/users.rs +++ b/plume-models/src/users.rs @@ -4,12 +4,15 @@ use crate::{ safe_string::SafeString, schema::users, timeline::Timeline, Connection, Error, Result, UserEvent::*, CONFIG, ITEMS_PER_PAGE, USER_CHAN, }; -use activitypub::{ +use activitystreams::{ activity::Delete, - actor::Person, + actor::{ApActor, AsApActor, Endpoints, Person}, + base::{AnyBase, Base}, collection::{OrderedCollection, OrderedCollectionPage}, - object::{Image, Tombstone}, - Activity, CustomObject, Endpoint, + iri_string::types::IriString, + markers::Activity, + object::{kind::ImageType, AsObject as _, Image, Tombstone}, + prelude::*, }; use chrono::{NaiveDateTime, Utc}; use diesel::{self, BelongingToDsl, ExpressionMethods, OptionalExtension, QueryDsl, RunQueryDsl}; @@ -25,7 +28,8 @@ use plume_common::{ inbox::{AsActor, AsObject, FromId}, request::get, sign::{gen_keypair, Error as SignError, Result as SignResult, Signer}, - ActivityStream, ApSignature, Id, IntoId, PublicKey, PUBLIC_VISIBILITY, + ActivityStream, ApSignature, CustomPerson, Id, IntoId, PublicKey, ToAsString, ToAsUri, + PUBLIC_VISIBILITY, }, utils, }; @@ -39,11 +43,8 @@ use std::{ hash::{Hash, Hasher}, sync::Arc, }; -use url::Url; use webfinger::*; -pub type CustomPerson = CustomObject; - pub enum Role { Admin = 0, Moderator = 1, @@ -247,8 +248,18 @@ impl User { let text = &res.text()?; // without this workaround, publicKey is not correctly deserialized let ap_sign = serde_json::from_str::(text)?; - let mut json = serde_json::from_str::(text)?; - json.custom_props = ap_sign; + let person = serde_json::from_str::(text)?; + let json = CustomPerson::new( + ApActor::new( + person + .clone() + .id_unchecked() + .ok_or(Error::MissingApProperty)? + .to_owned(), + person, + ), + ap_sign, + ); // FIXME: Don't clone() Ok(json) } @@ -260,35 +271,56 @@ impl User { User::fetch(&self.ap_url.clone()).and_then(|json| { let avatar = Media::save_remote( conn, - json.object - .object_props - .icon_image()? // FIXME: Fails when icon is not set - .object_props - .url_string()?, + json.ap_actor_ref() + .icon() + .ok_or(Error::MissingApProperty)? // FIXME: Fails when icon is not set + .iter() + .next() + .and_then(|i| { + i.clone() + .extend::() // FIXME: Don't clone() + .ok()? + .and_then(|url| Some(url.id_unchecked()?.to_string())) + }) + .ok_or(Error::MissingApProperty)?, self, ) .ok(); + let pub_key = &json.ext_one.public_key.public_key_pem; diesel::update(self) .set(( - users::username.eq(json.object.ap_actor_props.preferred_username_string()?), - users::display_name.eq(json.object.object_props.name_string()?), - users::outbox_url.eq(json.object.ap_actor_props.outbox_string()?), - users::inbox_url.eq(json.object.ap_actor_props.inbox_string()?), + users::username.eq(json + .ap_actor_ref() + .preferred_username() + .ok_or(Error::MissingApProperty)?), + users::display_name.eq(json + .ap_actor_ref() + .name() + .ok_or(Error::MissingApProperty)? + .to_as_string() + .ok_or(Error::MissingApProperty)?), + users::outbox_url.eq(json + .ap_actor_ref() + .outbox()? + .ok_or(Error::MissingApProperty)? + .as_str()), + users::inbox_url.eq(json.ap_actor_ref().inbox()?.as_str()), users::summary.eq(SafeString::new( &json - .object - .object_props - .summary_string() + .ap_actor_ref() + .summary() + .and_then(|summary| summary.to_as_string()) .unwrap_or_default(), )), - users::followers_endpoint.eq(json.object.ap_actor_props.followers_string()?), + users::followers_endpoint.eq(json + .ap_actor_ref() + .followers()? + .ok_or(Error::MissingApProperty)? + .as_str()), users::avatar_id.eq(avatar.map(|a| a.id)), users::last_fetched_date.eq(Utc::now().naive_utc()), - users::public_key.eq(json - .custom_props - .public_key_publickey()? - .public_key_pem_string()?), + users::public_key.eq(pub_key), )) .execute(conn) .map(|_| ()) @@ -432,17 +464,16 @@ impl User { Ok(ActivityStream::new(self.outbox_collection(conn)?)) } pub fn outbox_collection(&self, conn: &Connection) -> Result { - let mut coll = OrderedCollection::default(); + let mut coll = OrderedCollection::new(); let first = &format!("{}?page=1", &self.outbox_url); let last = &format!( "{}?page={}", &self.outbox_url, self.get_activities_count(conn) / i64::from(ITEMS_PER_PAGE) + 1 ); - coll.collection_props.set_first_link(Id::new(first))?; - coll.collection_props.set_last_link(Id::new(last))?; - coll.collection_props - .set_total_items_u64(self.get_activities_count(conn) as u64)?; + coll.set_first(first.parse::()?); + coll.set_last(last.parse::()?); + coll.set_total_items(self.get_activities_count(conn) as u64); Ok(coll) } pub fn outbox_page( @@ -461,27 +492,31 @@ impl User { ) -> Result { let acts = self.get_activities_page(conn, (min, max))?; let n_acts = self.get_activities_count(conn); - let mut coll = OrderedCollectionPage::default(); + let mut coll = OrderedCollectionPage::new(); if n_acts - i64::from(min) >= i64::from(ITEMS_PER_PAGE) { - coll.collection_page_props.set_next_link(Id::new(&format!( - "{}?page={}", - &self.outbox_url, - min / ITEMS_PER_PAGE + 2 - )))?; + coll.set_next( + format!("{}?page={}", &self.outbox_url, min / ITEMS_PER_PAGE + 2) + .parse::()?, + ); } if min > 0 { - coll.collection_page_props.set_prev_link(Id::new(&format!( - "{}?page={}", - &self.outbox_url, - min / ITEMS_PER_PAGE - )))?; + coll.set_prev( + format!("{}?page={}", &self.outbox_url, min / ITEMS_PER_PAGE) + .parse::()?, + ); } - coll.collection_props.items = serde_json::to_value(acts)?; - coll.collection_page_props - .set_part_of_link(Id::new(&self.outbox_url))?; + coll.set_many_items( + acts.iter() + .filter_map(|value| AnyBase::from_arbitrary_json(value).ok()), + ); + coll.set_part_of(self.outbox_url.parse::()?); Ok(coll) } - fn fetch_outbox_page(&self, url: &str) -> Result<(Vec, Option)> { + + pub fn fetch_outbox_page( + &self, + url: &str, + ) -> Result<(Vec, Option)> { let res = get(url, Self::get_sender(), CONFIG.proxy().cloned())?; let text = &res.text()?; let json: serde_json::Value = serde_json::from_str(text)?; @@ -495,7 +530,8 @@ impl User { let next = json.get("next").map(|x| x.as_str().unwrap().to_owned()); Ok((items, next)) } - pub fn fetch_outbox(&self) -> Result> { + + pub fn fetch_outbox(&self) -> Result> { let res = get( &self.outbox_url[..], Self::get_sender(), @@ -740,71 +776,58 @@ impl User { } pub fn to_activity(&self, conn: &Connection) -> Result { - let mut actor = Person::default(); - actor.object_props.set_id_string(self.ap_url.clone())?; - actor - .object_props - .set_name_string(self.display_name.clone())?; - actor - .object_props - .set_summary_string(self.summary_html.get().clone())?; - actor.object_props.set_url_string(self.ap_url.clone())?; - actor - .ap_actor_props - .set_inbox_string(self.inbox_url.clone())?; - actor - .ap_actor_props - .set_outbox_string(self.outbox_url.clone())?; - actor - .ap_actor_props - .set_preferred_username_string(self.username.clone())?; - actor - .ap_actor_props - .set_followers_string(self.followers_endpoint.clone())?; + let mut actor = ApActor::new(self.inbox_url.parse()?, Person::new()); + let ap_url = self.ap_url.parse::()?; + actor.set_id(ap_url.clone()); + actor.set_name(self.display_name.clone()); + actor.set_summary(self.summary_html.get().clone()); + actor.set_url(ap_url.clone()); + actor.set_inbox(self.inbox_url.parse()?); + actor.set_outbox(self.outbox_url.parse()?); + actor.set_preferred_username(self.username.clone()); + actor.set_followers(self.followers_endpoint.parse()?); if let Some(shared_inbox_url) = self.shared_inbox_url.clone() { - let mut endpoints = Endpoint::default(); - endpoints.set_shared_inbox_string(shared_inbox_url)?; - actor.ap_actor_props.set_endpoints_endpoint(endpoints)?; + let endpoints = Endpoints { + shared_inbox: Some(shared_inbox_url.parse::()?), + ..Endpoints::default() + }; + actor.set_endpoints(endpoints); } - let mut public_key = PublicKey::default(); - public_key.set_id_string(format!("{}#main-key", self.ap_url))?; - public_key.set_owner_string(self.ap_url.clone())?; - public_key.set_public_key_pem_string(self.public_key.clone())?; - let mut ap_signature = ApSignature::default(); - ap_signature.set_public_key_publickey(public_key)?; + let pub_key = PublicKey { + id: format!("{}#main-key", self.ap_url).parse()?, + owner: ap_url, + public_key_pem: self.public_key.clone(), + }; + let ap_signature = ApSignature { + public_key: pub_key, + }; if let Some(avatar_id) = self.avatar_id { - let mut avatar = Image::default(); - avatar - .object_props - .set_url_string(Media::get(conn, avatar_id)?.url()?)?; - actor.object_props.set_icon_object(avatar)?; + let mut avatar = Image::new(); + avatar.set_url(Media::get(conn, avatar_id)?.url()?.parse::()?); + actor.set_icon(avatar.into_any_base()?); } Ok(CustomPerson::new(actor, ap_signature)) } pub fn delete_activity(&self, conn: &Connection) -> Result { - let mut del = Delete::default(); + let mut tombstone = Tombstone::new(); + tombstone.set_id(self.ap_url.parse()?); - let mut tombstone = Tombstone::default(); - tombstone.object_props.set_id_string(self.ap_url.clone())?; - - del.delete_props - .set_actor_link(Id::new(self.ap_url.clone()))?; - del.delete_props.set_object_object(tombstone)?; - del.object_props - .set_id_string(format!("{}#delete", self.ap_url))?; - del.object_props - .set_to_link_vec(vec![Id::new(PUBLIC_VISIBILITY)])?; - del.object_props.set_cc_link_vec( + let mut del = Delete::new( + self.ap_url.parse::()?, + Base::retract(tombstone)?.into_generic()?, + ); + del.set_id(format!("{}#delete", self.ap_url).parse()?); + del.set_many_tos(vec![PUBLIC_VISIBILITY.parse::()?]); + del.set_many_ccs( self.get_followers(conn)? .into_iter() - .map(|f| Id::new(f.ap_url)) - .collect(), - )?; + .filter_map(|f| f.ap_url.parse::().ok()), + ); Ok(del) } @@ -930,9 +953,60 @@ impl FromId for User { } fn from_activity(conn: &DbConn, acct: CustomPerson) -> Result { - let url = Url::parse(&acct.object.object_props.id_string()?)?; - let inst = url.host_str().ok_or(Error::Url)?; - let instance = Instance::find_by_domain(conn, inst).or_else(|_| { + let actor = acct.ap_actor_ref(); + let username = actor + .preferred_username() + .ok_or(Error::MissingApProperty)? + .to_string(); + + if username.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) { + return Err(Error::InvalidValue); + } + + let summary = acct + .object_ref() + .summary() + .and_then(|prop| prop.to_as_string()) + .unwrap_or_default(); + let mut new_user = NewUser { + display_name: acct + .object_ref() + .name() + .and_then(|prop| prop.to_as_string()) + .unwrap_or_else(|| username.clone()), + username: username.clone(), + outbox_url: actor.outbox()?.ok_or(Error::MissingApProperty)?.to_string(), + inbox_url: actor.inbox()?.to_string(), + role: 2, + summary_html: SafeString::new(&summary), + summary, + public_key: acct.ext_one.public_key.public_key_pem.to_string(), + shared_inbox_url: actor + .endpoints()? + .and_then(|e| e.shared_inbox.map(|inbox| inbox.to_string())), + followers_endpoint: actor + .followers()? + .ok_or(Error::MissingApProperty)? + .to_string(), + ..NewUser::default() + }; + + let avatar_id = acct.object_ref().icon().and_then(|icon| icon.to_as_uri()); + + let (ap_url, inst) = { + let any_base = acct.into_any_base()?; + let id = any_base.id().ok_or(Error::MissingApProperty)?; + ( + id.to_string(), + id.authority_components() + .ok_or(Error::Url)? + .host() + .to_string(), + ) + }; + new_user.ap_url = ap_url; + + let instance = Instance::find_by_domain(conn, &inst).or_else(|_| { Instance::insert( conn, NewInstance { @@ -949,70 +1023,20 @@ impl FromId for User { }, ) })?; - - let username = acct.object.ap_actor_props.preferred_username_string()?; - - if username.contains(&['<', '>', '&', '@', '\'', '"', ' ', '\t'][..]) { - return Err(Error::InvalidValue); - } - - let fqn = if instance.local { - username.clone() + new_user.instance_id = instance.id; + new_user.fqn = if instance.local { + username } else { format!("{}@{}", username, instance.public_domain) }; - let user = User::insert( - conn, - NewUser { - display_name: acct - .object - .object_props - .name_string() - .unwrap_or_else(|_| username.clone()), - username, - outbox_url: acct.object.ap_actor_props.outbox_string()?, - inbox_url: acct.object.ap_actor_props.inbox_string()?, - role: 2, - summary: acct - .object - .object_props - .summary_string() - .unwrap_or_default(), - summary_html: SafeString::new( - &acct - .object - .object_props - .summary_string() - .unwrap_or_default(), - ), - email: None, - hashed_password: None, - instance_id: instance.id, - ap_url: acct.object.object_props.id_string()?, - public_key: acct - .custom_props - .public_key_publickey()? - .public_key_pem_string()?, - private_key: None, - shared_inbox_url: acct - .object - .ap_actor_props - .endpoints_endpoint() - .and_then(|e| e.shared_inbox_string()) - .ok(), - followers_endpoint: acct.object.ap_actor_props.followers_string()?, - fqn, - avatar_id: None, - }, - )?; + let user = User::insert(conn, new_user)?; + if let Some(avatar_id) = avatar_id { + let avatar = Media::save_remote(conn, avatar_id, &user); - if let Ok(icon) = acct.object.object_props.icon_image() { - if let Ok(url) = icon.object_props.url_string() { - let avatar = Media::save_remote(conn, url, &user); - - if let Ok(avatar) = avatar { - user.set_avatar(conn, avatar.id)?; + if let Ok(avatar) = avatar { + if let Err(e) = user.set_avatar(conn, avatar.id) { + tracing::error!("{:?}", e); } } } @@ -1435,10 +1459,8 @@ pub(crate) mod tests { "sharedInbox": "https://plu.me/inbox" }, "followers": "https://plu.me/@/admin/followers", - "following": null, "id": "https://plu.me/@/admin/", "inbox": "https://plu.me/@/admin/inbox", - "liked": null, "name": "The admin", "outbox": "https://plu.me/@/admin/outbox", "preferredUsername": "admin", @@ -1461,14 +1483,12 @@ pub(crate) mod tests { "sharedInbox": "https://plu.me/inbox" }, "followers": "https://plu.me/@/other/followers", - "following": null, "icon": { "url": "https://plu.me/static/media/example.png", "type": "Image", }, "id": "https://plu.me/@/other/", "inbox": "https://plu.me/@/other/inbox", - "liked": null, "name": "Another user", "outbox": "https://plu.me/@/other/outbox", "preferredUsername": "other", @@ -1524,7 +1544,6 @@ pub(crate) mod tests { let expected = json!({ "first": "https://plu.me/@/admin/outbox?page=1", - "items": null, "last": "https://plu.me/@/admin/outbox?page=5", "totalItems": 51, "type": "OrderedCollection", diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index c56b5a6e..7359779a 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -1,4 +1,4 @@ -use activitypub::collection::{OrderedCollection, OrderedCollectionPage}; +use activitystreams::collection::{OrderedCollection, OrderedCollectionPage}; use diesel::SaveChangesDsl; use rocket::{ http::ContentType, @@ -12,7 +12,7 @@ use validator::{Validate, ValidationError, ValidationErrors}; use crate::routes::{errors::ErrorPage, Page, RespondOrRedirect}; use crate::template_utils::{IntoContext, Ructe}; use crate::utils::requires_login; -use plume_common::activity_pub::{ActivityStream, ApRequest}; +use plume_common::activity_pub::{ActivityStream, ApRequest, CustomGroup}; use plume_common::utils; use plume_models::{ blog_authors::*, blogs::*, db_conn::DbConn, instance::Instance, medias::*, posts::Post, diff --git a/src/routes/comments.rs b/src/routes/comments.rs index 5585afff..5f60aae3 100644 --- a/src/routes/comments.rs +++ b/src/routes/comments.rs @@ -1,5 +1,5 @@ use crate::template_utils::Ructe; -use activitypub::object::Note; +use activitystreams::object::Note; use rocket::{ request::LenientForm, response::{Flash, Redirect}, diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 854a5621..09b097b8 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -15,7 +15,7 @@ use crate::routes::{ }; use crate::template_utils::{IntoContext, Ructe}; use crate::utils::requires_login; -use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest}; +use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest, LicensedArticle}; use plume_common::utils::md_to_html; use plume_models::{ blogs::*, diff --git a/src/routes/user.rs b/src/routes/user.rs index 576fc3c6..e1b1cfdd 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -1,4 +1,8 @@ -use activitypub::collection::{OrderedCollection, OrderedCollectionPage}; +use activitystreams::{ + collection::{OrderedCollection, OrderedCollectionPage}, + iri_string::types::IriString, + prelude::*, +}; use diesel::SaveChangesDsl; use rocket::{ http::{uri::Uri, ContentType, Cookies}, @@ -15,7 +19,7 @@ use crate::routes::{ }; use crate::template_utils::{IntoContext, Ructe}; use crate::utils::requires_login; -use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest, Id}; +use plume_common::activity_pub::{broadcast, ActivityStream, ApRequest, CustomPerson}; use plume_common::utils::md_to_html; use plume_models::{ blogs::Blog, @@ -561,17 +565,13 @@ pub fn ap_followers( .get_followers(&conn) .ok()? .into_iter() - .map(|f| Id::new(f.ap_url)) - .collect::>(); + .filter_map(|f| f.ap_url.parse::().ok()) + .collect::>(); - let mut coll = OrderedCollection::default(); - coll.object_props - .set_id_string(user.followers_endpoint) - .ok()?; - coll.collection_props - .set_total_items_u64(followers.len() as u64) - .ok()?; - coll.collection_props.set_items_link_vec(followers).ok()?; + let mut coll = OrderedCollection::new(); + coll.set_id(user.followers_endpoint.parse::().ok()?); + coll.set_total_items(followers.len() as u64); + coll.set_many_items(followers); Some(ActivityStream::new(coll)) }