From d544d28b6e3065a00c167d1b80518fa64407dbea Mon Sep 17 00:00:00 2001 From: timokoesters Date: Tue, 19 May 2020 18:31:34 +0200 Subject: [PATCH] feat: image thumbnails --- Cargo.lock | 294 ++++++++++++++++++++++++++++++------------ Cargo.toml | 9 +- README.md | 3 +- src/client_server.rs | 63 +++++---- src/database/media.rs | 103 ++++++++++++++- src/error.rs | 5 + 6 files changed, 365 insertions(+), 112 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1734df4..dbe8bc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" + [[package]] name = "arc-swap" version = "0.4.6" @@ -24,9 +30,9 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b" dependencies = [ - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", ] [[package]] @@ -96,6 +102,12 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5356f1d23ee24a1f785a56d1d1a5f0fd5b0f6a0c0fb2412ce11da71649ab78f6" +[[package]] +name = "bytemuck" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37fa13df2292ecb479ec23aa06f4507928bef07839be9ef15281411076629431" + [[package]] name = "byteorder" version = "1.3.4" @@ -110,9 +122,9 @@ checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" [[package]] name = "cc" -version = "1.0.52" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" +checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c" [[package]] name = "cfg-if" @@ -129,6 +141,12 @@ dependencies = [ "bitflags", ] +[[package]] +name = "color_quant" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dbbb57365263e881e805dc77d94697c9118fd94d8da011240555aa7b23445bd" + [[package]] name = "conduit" version = "0.1.0" @@ -136,6 +154,7 @@ dependencies = [ "base64 0.12.1", "directories", "http", + "image", "js_int", "log", "rand", @@ -224,6 +243,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "deflate" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e5d2a2273fed52a7f947ee55b092c4057025d7a3e04e5ecdbd25d6c3fb1bd7" +dependencies = [ + "adler32", + "byteorder", +] + [[package]] name = "devise" version = "0.3.0" @@ -239,7 +268,7 @@ version = "0.3.0" source = "git+https://github.com/SergioBenitez/Devise.git?rev=e58b3ac9a#e58b3ac9afc3b6ff10a8aaf02a3e768a8f530089" dependencies = [ "devise_core", - "quote 1.0.5", + "quote 1.0.6", ] [[package]] @@ -248,9 +277,9 @@ version = "0.3.0" source = "git+https://github.com/SergioBenitez/Devise.git?rev=e58b3ac9a#e58b3ac9afc3b6ff10a8aaf02a3e768a8f530089" dependencies = [ "bitflags", - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", ] [[package]] @@ -292,9 +321,9 @@ dependencies = [ [[package]] name = "fnv" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foreign-types" @@ -392,9 +421,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" dependencies = [ "proc-macro-hack", - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", ] [[package]] @@ -452,6 +481,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "gif" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471d90201b3b223f3451cd4ad53e34295f16a1df17b1edf3736d47761c3981af" +dependencies = [ + "color_quant", + "lzw", +] + [[package]] name = "h2" version = "0.2.5" @@ -482,9 +521,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +checksum = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71" dependencies = [ "libc", ] @@ -564,6 +603,22 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "image" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f4167a8f21fa2bb3f17a652a760acd7572645281c98e3b612a26242c96ee" +dependencies = [ + "bytemuck", + "byteorder", + "gif", + "jpeg-decoder", + "num-iter", + "num-rational", + "num-traits", + "png", +] + [[package]] name = "indexmap" version = "1.3.2" @@ -573,6 +628,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +dependencies = [ + "adler32", +] + [[package]] name = "iovec" version = "0.1.4" @@ -588,6 +652,15 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" +[[package]] +name = "jpeg-decoder" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b47b4c4e017b01abdc5bcc126d2d1002e5a75bbe3ce73f9f4f311a916363704" +dependencies = [ + "byteorder", +] + [[package]] name = "js-sys" version = "0.3.39" @@ -646,6 +719,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lzw" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" + [[package]] name = "matches" version = "0.1.8" @@ -760,6 +839,47 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.13.0" @@ -869,22 +989,22 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d480cb4e89522ccda96d0eed9af94180b7a5f93fb28f66e1fd7d68431663d1" +checksum = "edc93aeee735e60ecb40cf740eb319ff23eab1c5748abfdb5c180e4ce49f7791" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82996f11efccb19b685b14b5df818de31c1edcee3daa256ab5775dd98e72feb" +checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40" dependencies = [ - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", ] [[package]] @@ -906,10 +1026,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" [[package]] -name = "ppv-lite86" -version = "0.2.6" +name = "png" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" +checksum = "2c68a431ed29933a4eb5709aca9800989758c97759345860fa5db3cfced0b65d" +dependencies = [ + "bitflags", + "crc32fast", + "deflate", + "inflate", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" [[package]] name = "proc-macro-hack" @@ -934,9 +1066,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639" dependencies = [ "unicode-xid 0.2.0", ] @@ -952,11 +1084,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42934bc9c8ab0d3b273a16d8551c8f0fcff46be73276ca083ec2414c15c4ba5e" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" dependencies = [ - "proc-macro2 1.0.12", + "proc-macro2 1.0.13", ] [[package]] @@ -1108,7 +1240,7 @@ source = "git+https://github.com/SergioBenitez/Rocket.git?branch=async#78c8ac8cc dependencies = [ "devise", "indexmap", - "quote 1.0.5", + "quote 1.0.6", "rocket_http", "version_check 0.9.1", "yansi 0.5.0", @@ -1145,7 +1277,7 @@ dependencies = [ "percent-encoding 2.1.0", "ruma-api-macros", "ruma-identifiers", - "ruma-serde 0.2.0", + "ruma-serde 0.2.1", "serde", "serde_json", "strum", @@ -1157,16 +1289,15 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52b82b4567b9af9b40a86f7778821c016ea961f55e4fee255f8f24bb28ee7452" dependencies = [ - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", ] [[package]] name = "ruma-client-api" version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a57433fc6dded259aef2b12ceb91d78d3607b182278f648edd5c19c23d81cd" +source = "git+https://github.com/ruma/ruma-client-api.git#3a3ccabbf22c34da5c9de7cac54d9fbd3e571dcf" dependencies = [ "http", "js_int", @@ -1174,7 +1305,7 @@ dependencies = [ "ruma-common", "ruma-events", "ruma-identifiers", - "ruma-serde 0.1.3", + "ruma-serde 0.2.1", "serde", "serde_json", "strum", @@ -1182,39 +1313,40 @@ dependencies = [ [[package]] name = "ruma-common" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235f2fed35114ef3fbff1b7d5350f22ac712cffff55cc7b7732d39ae4adf6966" +checksum = "253416d67b4bde281f2781424232a58a946a4f1c451d5f857a8d0705d58eaf2a" dependencies = [ - "ruma-serde 0.1.3", + "matches", + "ruma-serde 0.2.1", "serde", "serde_json", ] [[package]] name = "ruma-events" -version = "0.21.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e952b80bddd83666a84db842d3e4d2ea43f8b4df8134864835c1807bd843fe21" +checksum = "0afc6d4da07d10213478d32dc42b6222df0a1ea319e9ced9f8a341617952d909" dependencies = [ "js_int", "ruma-common", "ruma-events-macros", "ruma-identifiers", - "ruma-serde 0.1.3", + "ruma-serde 0.2.1", "serde", "serde_json", ] [[package]] name = "ruma-events-macros" -version = "0.21.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60256afd4cb1bcdbcd9676e88cbbd09afc2541bd7b0858a559ce9ad7c6cfbb2a" +checksum = "fc706c4a53cc54c3a198cfbcd7dfff20448599d84f90e636d96034d0df5a9ac9" dependencies = [ - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", ] [[package]] @@ -1257,9 +1389,9 @@ dependencies = [ [[package]] name = "ruma-serde" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb00252245980b8f8d9833632e611aba8e1657c9fbefefe2b35c2817fc2b58a4" +checksum = "0dd3d04c6755bae23101dec7426d044b773ef517932f23d5a6254c4caa1cfce3" dependencies = [ "dtoa", "itoa", @@ -1387,9 +1519,9 @@ version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" dependencies = [ - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", ] [[package]] @@ -1481,9 +1613,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" dependencies = [ "heck", - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", ] [[package]] @@ -1499,12 +1631,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4696caa4048ac7ce2bcd2e484b3cef88c1004e41b8e945a277e2c25dc0b72060" +checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac" dependencies = [ - "proc-macro2 1.0.12", - "quote 1.0.5", + "proc-macro2 1.0.13", + "quote 1.0.6", "unicode-xid 0.2.0", ] @@ -1524,22 +1656,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467e5ff447618a916519a4e0d62772ab14f434897f3d63f05d8700ef1e9b22c1" +checksum = "5976891d6950b4f68477850b5b9e5aa64d955961466f9e174363f573e54e8ca7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63c1091225b9834089b429bc4a2e01223470e3183e891582909e9d1c4cb55d9" +checksum = "ab81dbd1cd69cd2ce22ecfbdd3bdb73334ba25350649408cc6c085f46d89573d" dependencies = [ - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", ] [[package]] @@ -1581,16 +1713,16 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" dependencies = [ - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", ] [[package]] name = "tokio-rustls" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "141afec0978abae6573065a48882c6bae44c5cc61db9b511ac4abf6a09bfd9cc" +checksum = "3068d891551949b37681724d6b73666787cc63fa8e255c812a41d2513aff9775" dependencies = [ "futures-core", "rustls", @@ -1760,9 +1892,9 @@ dependencies = [ "bumpalo", "lazy_static", "log", - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", "wasm-bindgen-shared", ] @@ -1784,7 +1916,7 @@ version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776" dependencies = [ - "quote 1.0.5", + "quote 1.0.6", "wasm-bindgen-macro-support", ] @@ -1794,9 +1926,9 @@ version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a" dependencies = [ - "proc-macro2 1.0.12", - "quote 1.0.5", - "syn 1.0.21", + "proc-macro2 1.0.13", + "quote 1.0.6", + "syn 1.0.22", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index e6ed01e..6edb8e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,10 @@ edition = "2018" [dependencies] rocket = { git = "https://github.com/SergioBenitez/Rocket.git", branch = "async", features = ["tls"] } http = "0.2.1" -ruma-client-api = "0.8.0" +ruma-client-api = { git = "https://github.com/ruma/ruma-client-api.git" } ruma-identifiers = { version = "0.16.1", features = ["rand"] } ruma-api = "0.16.1" -ruma-events = "0.21.1" +ruma-events = "0.21.2" ruma-signatures = { git = "https://github.com/ruma/ruma-signatures.git", rev = "1ca545cba8dfd43e0fc8e3c18e1311fb73390a97" } ruma-federation-api = { git = "https://github.com/ruma/ruma-federation-api.git", rev = "ccbf216f39bbbaa59131cc200eae5bd18aa1947c" } log = "0.4.8" @@ -31,5 +31,6 @@ rand = "0.7.3" rust-argon2 = "0.8.2" reqwest = "0.10.4" base64 = "0.12.1" -thiserror = "1.0.17" -ruma-common = "0.1.1" +thiserror = "1.0.18" +ruma-common = "0.1.2" +image = { version = "0.23.4", default-features = false, features = ["jpeg", "png", "gif"] } diff --git a/README.md b/README.md index 4c9baf5..5aabeeb 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,14 @@ A fast Matrix homeserver that's optimized for smaller, personal servers, instead - [x] Typing indications - [x] Invites, user search - [x] Password hashing +- [x] Riot E2EE +- [x] Media - [ ] Basic federation - [ ] State resolution - [ ] Permission system - [ ] Notifications (push rules) - [ ] Riot presence - [ ] Proper room creation -- [ ] Riot E2EE #### How can I contribute? diff --git a/src/client_server.rs b/src/client_server.rs index e5a6f9b..c21afc3 100644 --- a/src/client_server.rs +++ b/src/client_server.rs @@ -16,7 +16,7 @@ use ruma_client_api::{ directory::{self, get_public_rooms, get_public_rooms_filtered}, filter::{self, create_filter, get_filter}, keys::{claim_keys, get_keys, upload_keys}, - media::{create_content, get_content_thumbnail, get_content, get_media_config}, + media::{create_content, get_content, get_content_thumbnail, get_media_config}, membership::{ forget_room, get_member_events, invite_user, join_room_by_id, join_room_by_id_or_alias, leave_room, @@ -1307,9 +1307,7 @@ pub fn create_message_event_route( ) .expect("message events are always okay"); - MatrixResult(Ok(create_message_event::Response { - event_id: Some(event_id), - })) + MatrixResult(Ok(create_message_event::Response { event_id })) } #[put( @@ -1339,9 +1337,7 @@ pub fn create_state_event_for_key_route( ) .unwrap(); - MatrixResult(Ok(create_state_event_for_key::Response { - event_id: Some(event_id), - })) + MatrixResult(Ok(create_state_event_for_key::Response { event_id })) } #[put( @@ -1370,9 +1366,7 @@ pub fn create_state_event_for_empty_key_route( ) .unwrap(); - MatrixResult(Ok(create_state_event_for_empty_key::Response { - event_id: Some(event_id), - })) + MatrixResult(Ok(create_state_event_for_empty_key::Response { event_id })) } #[get("/_matrix/client/r0/rooms/<_room_id>/state", data = "")] @@ -1805,7 +1799,6 @@ pub fn send_event_to_device_route( #[get("/_matrix/media/r0/config")] pub fn get_media_config_route() -> MatrixResult { - warn!("TODO: get_media_config_route"); MatrixResult(Ok(get_media_config::Response { upload_size: (20_u32 * 1024 * 1024).into(), // 20 MB })) @@ -1816,24 +1809,38 @@ pub fn create_content_route( db: State<'_, Database>, body: Ruma, ) -> MatrixResult { - let mxc = format!("mxc://{}/{}", db.globals.server_name(), utils::random_string(MXC_LENGTH)); + let mxc = format!( + "mxc://{}/{}", + db.globals.server_name(), + utils::random_string(MXC_LENGTH) + ); db.media - .create(mxc.clone(), body.filename.as_ref(), &body.content_type, &body.file) + .create( + mxc.clone(), + body.filename.as_ref(), + &body.content_type, + &body.file, + ) .unwrap(); - MatrixResult(Ok(create_content::Response { - content_uri: mxc, - })) + MatrixResult(Ok(create_content::Response { content_uri: mxc })) } -#[get("/_matrix/media/r0/download/<_server_name>/<_media_id>", data = "")] +#[get( + "/_matrix/media/r0/download/<_server_name>/<_media_id>", + data = "" +)] pub fn get_content_route( db: State<'_, Database>, body: Ruma, _server_name: String, _media_id: String, ) -> MatrixResult { - if let Some((filename, content_type, file)) = db.media.get(format!("mxc://{}/{}", body.server_name, body.media_id)).unwrap() { + if let Some((filename, content_type, file)) = db + .media + .get(format!("mxc://{}/{}", body.server_name, body.media_id)) + .unwrap() + { MatrixResult(Ok(get_content::Response { file, content_type, @@ -1848,18 +1855,26 @@ pub fn get_content_route( } } -#[get("/_matrix/media/r0/thumbnail/<_server_name>/<_media_id>", data = "")] +#[get( + "/_matrix/media/r0/thumbnail/<_server_name>/<_media_id>", + data = "" +)] pub fn get_content_thumbnail_route( db: State<'_, Database>, body: Ruma, _server_name: String, _media_id: String, ) -> MatrixResult { - if let Some((_, content_type, file)) = db.media.get(format!("mxc://{}/{}", body.server_name, body.media_id)).unwrap() { - MatrixResult(Ok(get_content_thumbnail::Response { - file, - content_type, - })) + if let Some((_, content_type, file)) = db + .media + .get_thumbnail( + format!("mxc://{}/{}", body.server_name, body.media_id), + body.width.try_into().unwrap(), + body.height.try_into().unwrap(), + ) + .unwrap() + { + MatrixResult(Ok(get_content_thumbnail::Response { file, content_type })) } else { MatrixResult(Err(Error { kind: ErrorKind::NotFound, diff --git a/src/database/media.rs b/src/database/media.rs index 36d9410..96b95fe 100644 --- a/src/database/media.rs +++ b/src/database/media.rs @@ -1,7 +1,8 @@ use crate::{utils, Error, Result}; +use std::mem; pub struct Media { - pub(super) mediaid_file: sled::Tree, // MediaId = MXC + Filename + ContentType + pub(super) mediaid_file: sled::Tree, // MediaId = MXC + WidthHeight + Filename + ContentType } impl Media { @@ -15,6 +16,9 @@ impl Media { ) -> Result<()> { let mut key = mxc.as_bytes().to_vec(); key.push(0xff); + key.extend_from_slice(&0_u32.to_be_bytes()); // Width = 0 if it's not a thumbnail + key.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail + key.push(0xff); key.extend_from_slice(filename.map(|f| f.as_bytes()).unwrap_or_default()); key.push(0xff); key.extend_from_slice(content_type.as_bytes()); @@ -28,10 +32,19 @@ impl Media { pub fn get(&self, mxc: String) -> Result, String, Vec)>> { let mut prefix = mxc.as_bytes().to_vec(); prefix.push(0xff); + prefix.extend_from_slice(&0_u32.to_be_bytes()); // Width = 0 if it's not a thumbnail + prefix.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail + prefix.push(0xff); if let Some(r) = self.mediaid_file.scan_prefix(&prefix).next() { let (key, file) = r?; - let mut parts = key.split(|&b| b == 0xff).skip(1); + let mut parts = key.rsplit(|&b| b == 0xff); + + let content_type = utils::string_from_bytes( + parts + .next() + .ok_or(Error::BadDatabase("mediaid is invalid"))?, + )?; let filename_bytes = parts .next() @@ -42,13 +55,99 @@ impl Media { Some(utils::string_from_bytes(filename_bytes)?) }; + Ok(Some((filename, content_type, file.to_vec()))) + } else { + Ok(None) + } + } + + /// Downloads a file's thumbnail. + pub fn get_thumbnail( + &self, + mxc: String, + width: u32, + height: u32, + ) -> Result, String, Vec)>> { + let mut main_prefix = mxc.as_bytes().to_vec(); + main_prefix.push(0xff); + + let mut thumbnail_prefix = main_prefix.clone(); + thumbnail_prefix.extend_from_slice(&width.to_be_bytes()); + thumbnail_prefix.extend_from_slice(&height.to_be_bytes()); + thumbnail_prefix.push(0xff); + + let mut original_prefix = main_prefix; + original_prefix.extend_from_slice(&0_u32.to_be_bytes()); // Width = 0 if it's not a thumbnail + original_prefix.extend_from_slice(&0_u32.to_be_bytes()); // Height = 0 if it's not a thumbnail + original_prefix.push(0xff); + + if let Some(r) = self.mediaid_file.scan_prefix(&thumbnail_prefix).next() { + // Using saved thumbnail + let (key, file) = r?; + let mut parts = key.rsplit(|&b| b == 0xff); + let content_type = utils::string_from_bytes( parts .next() .ok_or(Error::BadDatabase("mediaid is invalid"))?, )?; + let filename_bytes = parts + .next() + .ok_or(Error::BadDatabase("mediaid is invalid"))?; + let filename = if filename_bytes.is_empty() { + None + } else { + Some(utils::string_from_bytes(filename_bytes)?) + }; + Ok(Some((filename, content_type, file.to_vec()))) + } else if let Some(r) = self.mediaid_file.scan_prefix(&original_prefix).next() { + // Generate a thumbnail + let (key, file) = r?; + let mut parts = key.rsplit(|&b| b == 0xff); + + let content_type = utils::string_from_bytes( + parts + .next() + .ok_or(Error::BadDatabase("mediaid is invalid"))?, + )?; + + let filename_bytes = parts + .next() + .ok_or(Error::BadDatabase("mediaid is invalid"))?; + let filename = if filename_bytes.is_empty() { + None + } else { + Some(utils::string_from_bytes(filename_bytes)?) + }; + + if let Ok(image) = image::load_from_memory(&file) { + let thumbnail = image.thumbnail(width, height); + let mut thumbnail_bytes = Vec::new(); + thumbnail.write_to(&mut thumbnail_bytes, image::ImageOutputFormat::Jpeg(75))?; + + // Save thumbnail in database so we don't have to generate it again next time + let mut thumbnail_key = key.to_vec(); + let width_index = thumbnail_key + .iter() + .position(|&b| b == 0xff) + .ok_or(Error::BadDatabase("mediaid is invalid"))? + + 1; + let mut widthheight = width.to_be_bytes().to_vec(); + widthheight.extend_from_slice(&height.to_be_bytes()); + + thumbnail_key.splice( + width_index..width_index + 2 * mem::size_of::(), + widthheight, + ); + + self.mediaid_file.insert(thumbnail_key, &*thumbnail_bytes)?; + + Ok(Some((filename, content_type, thumbnail_bytes))) + } else { + Ok(None) + } } else { Ok(None) } diff --git a/src/error.rs b/src/error.rs index 71fd918..a91d409 100644 --- a/src/error.rs +++ b/src/error.rs @@ -29,6 +29,11 @@ pub enum Error { #[from] source: ruma_events::InvalidEvent, }, + #[error("could not generate image")] + ImageError { + #[from] + source: image::error::ImageError, + }, #[error("bad request")] BadRequest(&'static str), #[error("problem in that database")]