diff --git a/build-edition.sh b/build-edition.sh index d2c0750..5d65fc3 100755 --- a/build-edition.sh +++ b/build-edition.sh @@ -10,6 +10,4 @@ echo $EDITION_FEATURES echo ENV SETTINGS: env -#OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 RUSTFLAGS="-C relocation-model=static -C linker=ld.lld" cargo build --release --features "$EDITION_FEATURES" --target x86_64-unknown-linux-musl -OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 cargo sqlx prepare --workspace -- --features "$EDITION_FEATURES" -OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 cargo build --release --features "$EDITION_FEATURES" +OPENSSL_LIB_DIR=/usr/lib OPENSSL_INCLUDE_DIR=/usr/include/openssl OPENSSL_STATIC=1 RUSTFLAGS="-C relocation-model=static -C linker=ld.lld" cargo build --release --features "$EDITION_FEATURES" --target x86_64-unknown-linux-musl diff --git a/rnex-core/.sqlx/query-039d134f0d1b681b55b1394a710b44a630eb066846c777c462b8c9341e26b696.json b/rnex-core/.sqlx/query-039d134f0d1b681b55b1394a710b44a630eb066846c777c462b8c9341e26b696.json deleted file mode 100644 index f91446a..0000000 --- a/rnex-core/.sqlx/query-039d134f0d1b681b55b1394a710b44a630eb066846c777c462b8c9341e26b696.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO datastore.objects (\n owner, size, name, data_type, meta_binary,\n permission, permission_recipients,\n delete_permission, delete_permission_recipients,\n flag, period, refer_data_id, tags,\n persistence_slot_id, extra_data, creation_date, update_date\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17\n ) RETURNING data_id\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "data_id", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Int4", - "Int4", - "Text", - "Int4", - "Bytea", - "Int4", - "Int4Array", - "Int4", - "Int4Array", - "Int4", - "Int4", - "Int8", - "TextArray", - "Int4", - "TextArray", - "Timestamp", - "Timestamp" - ] - }, - "nullable": [ - false - ] - }, - "hash": "039d134f0d1b681b55b1394a710b44a630eb066846c777c462b8c9341e26b696" -} diff --git a/rnex-core/.sqlx/query-a5e86d7e66d0e16e5f35df883e11d9f4ef96adbdab7ca7d008acf596af91fc2b.json b/rnex-core/.sqlx/query-1c2be699b4bfc7e5e6d3a74d7badf67d1812b99e1ec952a044fc03e1a5c63703.json similarity index 85% rename from rnex-core/.sqlx/query-a5e86d7e66d0e16e5f35df883e11d9f4ef96adbdab7ca7d008acf596af91fc2b.json rename to rnex-core/.sqlx/query-1c2be699b4bfc7e5e6d3a74d7badf67d1812b99e1ec952a044fc03e1a5c63703.json index 539fdeb..befa7e1 100644 --- a/rnex-core/.sqlx/query-a5e86d7e66d0e16e5f35df883e11d9f4ef96adbdab7ca7d008acf596af91fc2b.json +++ b/rnex-core/.sqlx/query-1c2be699b4bfc7e5e6d3a74d7badf67d1812b99e1ec952a044fc03e1a5c63703.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT data_id, owner, size, name, data_type, meta_binary,\n permission, permission_recipients, delete_permission, delete_permission_recipients,\n period, refer_data_id, flag, tags, creation_date, update_date\n FROM datastore.objects WHERE data_id = $1", + "query": "SELECT data_id, owner, size, name, data_type, meta_binary,\n permission, permission_recipients, delete_permission, delete_permission_recipients,\n period, refer_data_id, flag, tags, creation_date, update_date\n FROM datastore.objects WHERE data_id = $1", "describe": { "columns": [ { @@ -108,5 +108,5 @@ true ] }, - "hash": "a5e86d7e66d0e16e5f35df883e11d9f4ef96adbdab7ca7d008acf596af91fc2b" + "hash": "1c2be699b4bfc7e5e6d3a74d7badf67d1812b99e1ec952a044fc03e1a5c63703" } diff --git a/rnex-core/.sqlx/query-219fec3fc852f36de99e5f00ca7a1675439bb44c91158f8b8a696e326c45447c.json b/rnex-core/.sqlx/query-219fec3fc852f36de99e5f00ca7a1675439bb44c91158f8b8a696e326c45447c.json new file mode 100644 index 0000000..cf80412 --- /dev/null +++ b/rnex-core/.sqlx/query-219fec3fc852f36de99e5f00ca7a1675439bb44c91158f8b8a696e326c45447c.json @@ -0,0 +1,38 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO datastore.objects (\n owner, size, name, data_type, meta_binary,\n permission, permission_recipients,\n delete_permission, delete_permission_recipients,\n flag, period, refer_data_id, tags,\n persistence_slot_id, extra_data, creation_date, update_date\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17\n ) RETURNING data_id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "data_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int4", + "Int4", + "Text", + "Int4", + "Bytea", + "Int4", + "Int4Array", + "Int4", + "Int4Array", + "Int4", + "Int4", + "Int8", + "TextArray", + "Int4", + "TextArray", + "Timestamp", + "Timestamp" + ] + }, + "nullable": [ + false + ] + }, + "hash": "219fec3fc852f36de99e5f00ca7a1675439bb44c91158f8b8a696e326c45447c" +} diff --git a/rnex-core/.sqlx/query-29d4f5c07b36c3d3b6b54a86a1757f27247530878b7f82feeb65802d995a38c4.json b/rnex-core/.sqlx/query-29d4f5c07b36c3d3b6b54a86a1757f27247530878b7f82feeb65802d995a38c4.json new file mode 100644 index 0000000..0c3393d --- /dev/null +++ b/rnex-core/.sqlx/query-29d4f5c07b36c3d3b6b54a86a1757f27247530878b7f82feeb65802d995a38c4.json @@ -0,0 +1,16 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO datastore.object_custom_rankings (data_id, application_id, value)\n VALUES ($1, $2, $3)\n ON CONFLICT (data_id, application_id)\n DO UPDATE SET value = datastore.object_custom_rankings.value + EXCLUDED.value\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Int8", + "Int8" + ] + }, + "nullable": [] + }, + "hash": "29d4f5c07b36c3d3b6b54a86a1757f27247530878b7f82feeb65802d995a38c4" +} diff --git a/rnex-core/.sqlx/query-29e18f241dc860dca12c03cabb788d2d26f7644e85bfd65423e6e32c469e3695.json b/rnex-core/.sqlx/query-29e18f241dc860dca12c03cabb788d2d26f7644e85bfd65423e6e32c469e3695.json deleted file mode 100644 index 5411a38..0000000 --- a/rnex-core/.sqlx/query-29e18f241dc860dca12c03cabb788d2d26f7644e85bfd65423e6e32c469e3695.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n INSERT INTO datastore.object_custom_rankings (data_id, application_id, value)\n VALUES ($1, $2, $3)\n ON CONFLICT (data_id, application_id)\n DO UPDATE SET value = datastore.object_custom_rankings.value + EXCLUDED.value\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Int8", - "Int8", - "Int8" - ] - }, - "nullable": [] - }, - "hash": "29e18f241dc860dca12c03cabb788d2d26f7644e85bfd65423e6e32c469e3695" -} diff --git a/rnex-core/.sqlx/query-74408b6923370db5de1589b62360f608f018cb20125bf0ed30fdfb4319a14cf0.json b/rnex-core/.sqlx/query-37d449b81e2aa3abdbdaf38587ae1a6a6c5c38acb06d91c5b0924c3f0a5d2e92.json similarity index 51% rename from rnex-core/.sqlx/query-74408b6923370db5de1589b62360f608f018cb20125bf0ed30fdfb4319a14cf0.json rename to rnex-core/.sqlx/query-37d449b81e2aa3abdbdaf38587ae1a6a6c5c38acb06d91c5b0924c3f0a5d2e92.json index 775d98d..35c78ee 100644 --- a/rnex-core/.sqlx/query-74408b6923370db5de1589b62360f608f018cb20125bf0ed30fdfb4319a14cf0.json +++ b/rnex-core/.sqlx/query-37d449b81e2aa3abdbdaf38587ae1a6a6c5c38acb06d91c5b0924c3f0a5d2e92.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT data_id\n FROM datastore.objects\n WHERE owner = $1 AND data_type > 2 AND data_type < 50\n ", + "query": "\n SELECT data_id\n FROM datastore.objects\n WHERE owner = $1 AND data_type > 2 AND data_type < 50\n ", "describe": { "columns": [ { @@ -18,5 +18,5 @@ false ] }, - "hash": "74408b6923370db5de1589b62360f608f018cb20125bf0ed30fdfb4319a14cf0" + "hash": "37d449b81e2aa3abdbdaf38587ae1a6a6c5c38acb06d91c5b0924c3f0a5d2e92" } diff --git a/rnex-core/.sqlx/query-48d94383f1a261f19060f5c75d9f7803ee88233c53efaa2df9966a6c72c8c763.json b/rnex-core/.sqlx/query-3d06238fddc72d1ba452602e1a8002e9186ce1dfc6c68b52d9d2a8a38f5c3a1f.json similarity index 50% rename from rnex-core/.sqlx/query-48d94383f1a261f19060f5c75d9f7803ee88233c53efaa2df9966a6c72c8c763.json rename to rnex-core/.sqlx/query-3d06238fddc72d1ba452602e1a8002e9186ce1dfc6c68b52d9d2a8a38f5c3a1f.json index 7a1cc6f..166c25d 100644 --- a/rnex-core/.sqlx/query-48d94383f1a261f19060f5c75d9f7803ee88233c53efaa2df9966a6c72c8c763.json +++ b/rnex-core/.sqlx/query-3d06238fddc72d1ba452602e1a8002e9186ce1dfc6c68b52d9d2a8a38f5c3a1f.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT buffer\n FROM datastore.buffer_queues\n WHERE data_id = $1 AND slot = $2\n ORDER BY creation_date ASC\n ", + "query": "\n SELECT buffer\n FROM datastore.buffer_queues\n WHERE data_id = $1 AND slot = $2\n ORDER BY creation_date ASC\n ", "describe": { "columns": [ { @@ -19,5 +19,5 @@ false ] }, - "hash": "48d94383f1a261f19060f5c75d9f7803ee88233c53efaa2df9966a6c72c8c763" + "hash": "3d06238fddc72d1ba452602e1a8002e9186ce1dfc6c68b52d9d2a8a38f5c3a1f" } diff --git a/rnex-core/.sqlx/query-8605011b998a4608c739bf5ab388a7a9bf551126712c1d1089a4263453090e79.json b/rnex-core/.sqlx/query-8605011b998a4608c739bf5ab388a7a9bf551126712c1d1089a4263453090e79.json new file mode 100644 index 0000000..dd1c783 --- /dev/null +++ b/rnex-core/.sqlx/query-8605011b998a4608c739bf5ab388a7a9bf551126712c1d1089a4263453090e79.json @@ -0,0 +1,29 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT\n rankings.data_id,\n rankings.value\n FROM datastore.object_custom_rankings rankings\n JOIN UNNEST($1::bigint[]) WITH ORDINALITY AS rows(data_id, ord)\n ON rankings.data_id = rows.data_id\n AND rankings.application_id = $2\n ORDER BY rows.ord\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "data_id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "value", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8Array", + "Int8" + ] + }, + "nullable": [ + false, + true + ] + }, + "hash": "8605011b998a4608c739bf5ab388a7a9bf551126712c1d1089a4263453090e79" +} diff --git a/rnex-core/.sqlx/query-c2a0ca72fdcb060a7fb1c670b25e1efdbf3cef31e4b6cea4adbd51ee0218f5aa.json b/rnex-core/.sqlx/query-8706ac06d78ffaa2a45418be7ae71340561031d8e5c91f46c041f83e54c31a7d.json similarity index 69% rename from rnex-core/.sqlx/query-c2a0ca72fdcb060a7fb1c670b25e1efdbf3cef31e4b6cea4adbd51ee0218f5aa.json rename to rnex-core/.sqlx/query-8706ac06d78ffaa2a45418be7ae71340561031d8e5c91f46c041f83e54c31a7d.json index 095ca6f..dbab55e 100644 --- a/rnex-core/.sqlx/query-c2a0ca72fdcb060a7fb1c670b25e1efdbf3cef31e4b6cea4adbd51ee0218f5aa.json +++ b/rnex-core/.sqlx/query-8706ac06d78ffaa2a45418be7ae71340561031d8e5c91f46c041f83e54c31a7d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT slot, total_value, count, initial_value\n FROM datastore.object_ratings\n WHERE data_id = $1\n ", + "query": "\n SELECT slot, total_value, count, initial_value\n FROM datastore.object_ratings\n WHERE data_id = $1\n ", "describe": { "columns": [ { @@ -36,5 +36,5 @@ true ] }, - "hash": "c2a0ca72fdcb060a7fb1c670b25e1efdbf3cef31e4b6cea4adbd51ee0218f5aa" + "hash": "8706ac06d78ffaa2a45418be7ae71340561031d8e5c91f46c041f83e54c31a7d" } diff --git a/rnex-core/.sqlx/query-8909f81fbf26a9b559d26e138568be8946430d93821dbccb6dc9324ef0544c17.json b/rnex-core/.sqlx/query-8909f81fbf26a9b559d26e138568be8946430d93821dbccb6dc9324ef0544c17.json deleted file mode 100644 index 867701f..0000000 --- a/rnex-core/.sqlx/query-8909f81fbf26a9b559d26e138568be8946430d93821dbccb6dc9324ef0544c17.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT\n rankings.data_id,\n rankings.value\n FROM datastore.object_custom_rankings rankings\n JOIN UNNEST($1::bigint[]) WITH ORDINALITY AS rows(data_id, ord)\n ON rankings.data_id = rows.data_id\n AND rankings.application_id = $2\n ORDER BY rows.ord\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "data_id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "value", - "type_info": "Int8" - } - ], - "parameters": { - "Left": [ - "Int8Array", - "Int8" - ] - }, - "nullable": [ - false, - true - ] - }, - "hash": "8909f81fbf26a9b559d26e138568be8946430d93821dbccb6dc9324ef0544c17" -} diff --git a/rnex-core/.sqlx/query-60d8ebcf914d887439096df400fa4beaf37e879c28c81c22abeddb3d030535df.json b/rnex-core/.sqlx/query-93be6b6b0ac5d85881e6e223a7d48f5eb4a3761dd71129ba6939cdd0d62569fb.json similarity index 57% rename from rnex-core/.sqlx/query-60d8ebcf914d887439096df400fa4beaf37e879c28c81c22abeddb3d030535df.json rename to rnex-core/.sqlx/query-93be6b6b0ac5d85881e6e223a7d48f5eb4a3761dd71129ba6939cdd0d62569fb.json index 4f07b49..0a8127b 100644 --- a/rnex-core/.sqlx/query-60d8ebcf914d887439096df400fa4beaf37e879c28c81c22abeddb3d030535df.json +++ b/rnex-core/.sqlx/query-93be6b6b0ac5d85881e6e223a7d48f5eb4a3761dd71129ba6939cdd0d62569fb.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "\n SELECT under_review, access_password\n FROM datastore.objects\n WHERE data_id = $1 AND upload_completed = TRUE AND deleted = FALSE\n ", + "query": "\n SELECT under_review, access_password\n FROM datastore.objects\n WHERE data_id = $1 AND upload_completed = TRUE AND deleted = FALSE\n ", "describe": { "columns": [ { @@ -24,5 +24,5 @@ false ] }, - "hash": "60d8ebcf914d887439096df400fa4beaf37e879c28c81c22abeddb3d030535df" + "hash": "93be6b6b0ac5d85881e6e223a7d48f5eb4a3761dd71129ba6939cdd0d62569fb" } diff --git a/rnex-core/.sqlx/query-18fb671d9d97353420446413b74077f9b06ffe11cab2693e4e365c265f8e2418.json b/rnex-core/.sqlx/query-efe4bf3602782a0d521274956e0fcecccf8f0f8dd20d890a76acf85265b2192c.json similarity index 81% rename from rnex-core/.sqlx/query-18fb671d9d97353420446413b74077f9b06ffe11cab2693e4e365c265f8e2418.json rename to rnex-core/.sqlx/query-efe4bf3602782a0d521274956e0fcecccf8f0f8dd20d890a76acf85265b2192c.json index 7471873..60714c0 100644 --- a/rnex-core/.sqlx/query-18fb671d9d97353420446413b74077f9b06ffe11cab2693e4e365c265f8e2418.json +++ b/rnex-core/.sqlx/query-efe4bf3602782a0d521274956e0fcecccf8f0f8dd20d890a76acf85265b2192c.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT data_id, owner, size, name, data_type, meta_binary,\n permission, permission_recipients, delete_permission, delete_permission_recipients,\n period, refer_data_id, flag, tags, creation_date, update_date,\n access_password, under_review\n FROM datastore.objects\n WHERE owner = $1 AND persistence_slot_id = $2\n AND upload_completed = TRUE AND deleted = FALSE", + "query": "SELECT data_id, owner, size, name, data_type, meta_binary,\n permission, permission_recipients, delete_permission, delete_permission_recipients,\n period, refer_data_id, flag, tags, creation_date, update_date,\n access_password, under_review\n FROM datastore.objects\n WHERE owner = $1 AND persistence_slot_id = $2\n AND upload_completed = TRUE AND deleted = FALSE", "describe": { "columns": [ { @@ -121,5 +121,5 @@ false ] }, - "hash": "18fb671d9d97353420446413b74077f9b06ffe11cab2693e4e365c265f8e2418" + "hash": "efe4bf3602782a0d521274956e0fcecccf8f0f8dd20d890a76acf85265b2192c" } diff --git a/rnex-core/src/nex/datastore.rs b/rnex-core/src/nex/datastore.rs index 498a75d..763bc3e 100644 --- a/rnex-core/src/nex/datastore.rs +++ b/rnex-core/src/nex/datastore.rs @@ -1,780 +1,785 @@ -use crate::define_rmc_proto; -use macros::rmc_struct; -use rnex_core::prudp::socket_addr::PRUDPSockAddr; -use std::sync::{Weak}; -use chrono::Utc; -use sqlx::types::time; -use sqlx::types::time::PrimitiveDateTime; -use rnex_core::PID; -use rnex_core::nex::remote_console::RemoteConsole; -use rnex_core::nex::s3presigner::S3Presigner; -use rnex_core::rmc::response::ErrorCode; -use rnex_core::rmc::protocols::secure::{Secure, RawSecure, RawSecureInfo, RemoteSecure}; -use rnex_core::rmc::protocols::datastore::{BufferQueueParam, CompletePostParam, DataStoreCustomRankingResult, DataStoreGetCustomRankingByDataIDParam, DataStorePrepareGetParam, DataStoreReqGetInfo, DataStoreSearchParam, GetMetaInfo, GetMetaParam, KeyValue, Permission, PersistenceTarget, RateCustomRankingParam, RatingInfo, RatingInfoWithSlot}; -use rnex_core::rmc::protocols::datastore::{DataStore, RawDataStore, RawDataStoreInfo, RemoteDataStore, PreparePostParam, ReqPostInfo}; -use crate::nex::user::User; -use rnex_core::executables::common::{RNEX_DATASTORE_S3_BUCKET, RNEX_DATASTORE_S3_ENDPOINT, get_db}; -use rnex_core::rmc::structures::qbuffer::QBuffer; -use sqlx::types::chrono::DateTime; -use rnex_core::kerberos::KerberosDateTime; -use rnex_core::rmc::structures::qresult::QResult; +use cfg_if::cfg_if; +cfg_if! { + if #[cfg(feature = "datastore")] { + use crate::define_rmc_proto; + use macros::rmc_struct; + use rnex_core::prudp::socket_addr::PRUDPSockAddr; + use std::sync::{Weak}; + use chrono::Utc; + use sqlx::types::time; + use sqlx::types::time::PrimitiveDateTime; + use rnex_core::PID; + use rnex_core::nex::remote_console::RemoteConsole; + use rnex_core::nex::s3presigner::S3Presigner; + use rnex_core::rmc::response::ErrorCode; + use rnex_core::rmc::protocols::secure::{Secure, RawSecure, RawSecureInfo, RemoteSecure}; + use rnex_core::rmc::protocols::datastore::{BufferQueueParam, CompletePostParam, DataStoreCustomRankingResult, DataStoreGetCustomRankingByDataIDParam, DataStorePrepareGetParam, DataStoreReqGetInfo, DataStoreSearchParam, GetMetaInfo, GetMetaParam, KeyValue, Permission, PersistenceTarget, RateCustomRankingParam, RatingInfo, RatingInfoWithSlot}; + use rnex_core::rmc::protocols::datastore::{DataStore, RawDataStore, RawDataStoreInfo, RemoteDataStore, PreparePostParam, ReqPostInfo}; + use crate::nex::user::User; + use rnex_core::executables::common::{RNEX_DATASTORE_S3_BUCKET, RNEX_DATASTORE_S3_ENDPOINT, get_db}; + use rnex_core::rmc::structures::qbuffer::QBuffer; + use sqlx::types::chrono::DateTime; + use rnex_core::kerberos::KerberosDateTime; + use rnex_core::rmc::structures::qresult::QResult; -fn map_row_to_meta_info( - row_data_id: i64, - row_owner: i32, - row_size: i32, - row_name: String, - row_data_type: i16, - row_meta_binary: Vec, - row_permission: i16, - row_permission_recipients: Vec, - row_delete_permission: i16, - row_delete_permission_recipients: Vec, - row_period: i16, - row_refer_data_id: i64, - row_flag: i32, - row_tags: Vec, - row_creation_date: chrono::NaiveDateTime, - row_update_date: chrono::NaiveDateTime, - ratings: Vec -) -> GetMetaInfo { - GetMetaInfo { - dataid: row_data_id as u64, - owner: row_owner as u32, - size: row_size as u32, - name: row_name, - data_type: row_data_type as u16, - meta_binary: QBuffer(row_meta_binary), - permission: Permission { - permission: row_permission as u8, - recipient_ids: row_permission_recipients.into_iter().map(|id| id as u32).collect(), - }, - del_permission: Permission { - permission: row_delete_permission as u8, - recipient_ids: row_delete_permission_recipients.into_iter().map(|id| id as u32).collect(), - }, - period: row_period as u16, - status: 0, - referred_count: 0, - refer_dat_id: row_refer_data_id as u32, - flag: row_flag as u32, - tags: row_tags, - expire_time: KerberosDateTime::from_u64(0x9C3F3E0000), - created_time: KerberosDateTime::from_naive(row_creation_date), - updated_time: KerberosDateTime::from_naive(row_update_date), - referred_time: KerberosDateTime::from_naive(row_creation_date), - ratings, - } -} - -async fn check_object_availability(data_id: u64, password: u64) -> Result<(), ErrorCode> { - let row = sqlx::query!( - r#" - SELECT under_review, access_password - FROM datastore.objects - WHERE data_id = $1 AND upload_completed = TRUE AND deleted = FALSE - "#, - data_id as i64 - ) - .fetch_optional(get_db()) - .await - .map_err(|e| { - eprintln!("Availability check DB error: {:?}", e); - ErrorCode::DataStore_SystemFileError - })? - .ok_or(ErrorCode::DataStore_NotFound)?; - - let access_password = row.access_password as u64; - if access_password != 0 && access_password != password { - return Err(ErrorCode::DataStore_InvalidPassword); - } - - if row.under_review { - return Err(ErrorCode::DataStore_UnderReviewing); - } - - Ok(()) -} - -async fn get_object_ratings(data_id: u64, password: u64) -> Result, ErrorCode> { - check_object_availability(data_id, password).await?; - - let rows = sqlx::query!( - r#" - SELECT slot, total_value, count, initial_value - FROM datastore.object_ratings - WHERE data_id = $1 - "#, - data_id as i64 - ) - .fetch_all(get_db()) - .await - .map_err(|e| { - eprintln!("Ratings fetch error: {:?}", e); - ErrorCode::DataStore_SystemFileError - })?; - - let ratings = rows.into_iter().map(|row| { - RatingInfoWithSlot { - slot: row.slot as i8, - rating: RatingInfo { - total_value: row.total_value.unwrap_or(0), - count: row.count as u32, - initial_value: row.initial_value.unwrap_or(0), - }, - } - }).collect(); - - Ok(ratings) -} - -async fn get_object_info_by_data_id(data_id: u64, password: u64) -> Result { - check_object_availability(data_id, password).await?; - - let row = sqlx::query!( - r#"SELECT data_id, owner, size, name, data_type, meta_binary, - permission, permission_recipients, delete_permission, delete_permission_recipients, - period, refer_data_id, flag, tags, creation_date, update_date - FROM datastore.objects WHERE data_id = $1"#, - data_id as i64 - ) - .fetch_optional(get_db()) - .await - .map_err(|_| ErrorCode::DataStore_SystemFileError)? - .ok_or(ErrorCode::DataStore_NotFound)?; - - let ratings = get_object_ratings(data_id, password).await?; - - Ok(map_row_to_meta_info( - row.data_id, - row.owner.unwrap_or(0), - row.size.unwrap_or(0), - row.name.unwrap_or_default(), - row.data_type.unwrap_or(0) as i16, - row.meta_binary.unwrap_or_default(), - row.permission.unwrap_or(0) as i16, - row.permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(), - row.delete_permission.unwrap_or(0) as i16, - row.delete_permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(), - row.period.unwrap_or(0) as i16, - row.refer_data_id.unwrap_or(0), - row.flag.unwrap_or(0), - row.tags.unwrap_or_default(), - row.creation_date.map(|dt| chrono::NaiveDateTime::new( - chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(), - chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap() - )).unwrap_or_default(), - row.update_date.map(|dt| chrono::NaiveDateTime::new( - chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(), - chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap() - )).unwrap_or_default(), - ratings - )) -} - -async fn get_object_info_by_persistence_target(target: PersistenceTarget, password: u64) -> Result { - let row = sqlx::query!( - r#"SELECT data_id, owner, size, name, data_type, meta_binary, - permission, permission_recipients, delete_permission, delete_permission_recipients, - period, refer_data_id, flag, tags, creation_date, update_date, - access_password, under_review - FROM datastore.objects - WHERE owner = $1 AND persistence_slot_id = $2 - AND upload_completed = TRUE AND deleted = FALSE"#, - target.owner as i32, - target.persistence_slot_id as i16 - ) - .fetch_optional(get_db()) - .await - .map_err(|_| ErrorCode::DataStore_SystemFileError)? - .ok_or(ErrorCode::DataStore_NotFound)?; - - let db_password = row.access_password as u64; - if db_password != 0 && db_password != password { - return Err(ErrorCode::DataStore_InvalidPassword); - } - - if row.under_review { - return Err(ErrorCode::DataStore_UnderReviewing); - } - - let ratings = get_object_ratings(row.data_id as u64, password).await?; - - Ok(map_row_to_meta_info( - row.data_id, - row.owner.unwrap_or(0), - row.size.unwrap_or(0), - row.name.unwrap_or_default(), - row.data_type.unwrap_or(0) as i16, - row.meta_binary.unwrap_or_default(), - row.permission.unwrap_or(0) as i16, - row.permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(), - row.delete_permission.unwrap_or(0) as i16, - row.delete_permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(), - row.period.unwrap_or(0) as i16, - row.refer_data_id.unwrap_or(0), - row.flag.unwrap_or(0), - row.tags.unwrap_or_default(), - row.creation_date.map(|dt| chrono::NaiveDateTime::new( - chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(), - chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap() - )).unwrap_or_default(), - row.update_date.map(|dt| chrono::NaiveDateTime::new( - chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(), - chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap() - )).unwrap_or_default(), - ratings - )) -} - -async fn get_buffer_queues_by_data_id_and_slot( - data_id: u64, - slot: u32 -) -> Result, ErrorCode> { - check_object_availability(data_id, 0).await?; - - let rows = sqlx::query!( - r#" - SELECT buffer - FROM datastore.buffer_queues - WHERE data_id = $1 AND slot = $2 - ORDER BY creation_date ASC - "#, - data_id as i64, - slot as i32 - ) - .fetch_all(get_db()) - .await - .map_err(|e| { - log::error!("Buffer queue fetch error: {:?}", e); - ErrorCode::DataStore_SystemFileError - })?; - - let buffer_queues = rows - .into_iter() - .map(|row| QBuffer(row.buffer)) - .collect(); - - Ok(buffer_queues) -} - -fn verify_object_permission( - owner_id: u32, - viewer_id: u32, - permission: &Permission, -) -> Result<(), ErrorCode> { - if owner_id == viewer_id { - return Ok(()); - } - - match permission.permission { - 0 => Ok(()), // All can read - 1 => Err(ErrorCode::DataStore_PermissionDenied), // Friends only, unimplemented - 2 => { - // Recipient IDs can read - if permission.recipient_ids.contains(&viewer_id) { - Ok(()) - } else { - Err(ErrorCode::DataStore_PermissionDenied) + fn map_row_to_meta_info( + row_data_id: i64, + row_owner: i32, + row_size: i32, + row_name: String, + row_data_type: i16, + row_meta_binary: Vec, + row_permission: i16, + row_permission_recipients: Vec, + row_delete_permission: i16, + row_delete_permission_recipients: Vec, + row_period: i16, + row_refer_data_id: i64, + row_flag: i32, + row_tags: Vec, + row_creation_date: chrono::NaiveDateTime, + row_update_date: chrono::NaiveDateTime, + ratings: Vec + ) -> GetMetaInfo { + GetMetaInfo { + dataid: row_data_id as u64, + owner: row_owner as u32, + size: row_size as u32, + name: row_name, + data_type: row_data_type as u16, + meta_binary: QBuffer(row_meta_binary), + permission: Permission { + permission: row_permission as u8, + recipient_ids: row_permission_recipients.into_iter().map(|id| id as u32).collect(), + }, + del_permission: Permission { + permission: row_delete_permission as u8, + recipient_ids: row_delete_permission_recipients.into_iter().map(|id| id as u32).collect(), + }, + period: row_period as u16, + status: 0, + referred_count: 0, + refer_dat_id: row_refer_data_id as u32, + flag: row_flag as u32, + tags: row_tags, + expire_time: KerberosDateTime::from_u64(0x9C3F3E0000), + created_time: KerberosDateTime::from_naive(row_creation_date), + updated_time: KerberosDateTime::from_naive(row_update_date), + referred_time: KerberosDateTime::from_naive(row_creation_date), + ratings, } } - 3 => Err(ErrorCode::DataStore_PermissionDenied), // Owner only, redundant - _ => Err(ErrorCode::DataStore_InvalidArgument), // ??? haxx0r - } -} -fn filter_properties_by_result_option( - meta_info: &mut GetMetaInfo, - result_option: u8, -) { - if (result_option & 0x01) == 0 { - meta_info.meta_binary = QBuffer(Vec::new()); - } - - if (result_option & 0x04) == 0 { - meta_info.ratings = Vec::new(); - } - - // No idea what the other things do. :shrug: -} - -// Dawg... -async fn get_custom_rankings_by_data_ids( - application_id: u32, - data_ids: Vec -) -> Vec { - let mut results = Vec::with_capacity(data_ids.len()); - - let rows = sqlx::query!( - r#" - SELECT - rankings.data_id, - rankings.value - FROM datastore.object_custom_rankings rankings - JOIN UNNEST($1::bigint[]) WITH ORDINALITY AS rows(data_id, ord) - ON rankings.data_id = rows.data_id - AND rankings.application_id = $2 - ORDER BY rows.ord - "#, - &data_ids.iter().map(|&id| id as i64).collect::>(), - application_id as i32 - ) - .fetch_all(get_db()) - .await; - - let rows = match rows { - Ok(r) => r, - Err(e) => { - log::error!("Custom ranking query error: {:?}", e); - return results; - } - }; - - for row in rows { - let data_id = row.data_id as u64; - let score = row.value.unwrap_or(0) as u32; - - if let Ok(meta) = get_object_info_by_data_id(data_id, 0).await { - results.push(DataStoreCustomRankingResult { - order: 0, - score, - meta_info: meta, - }); - } else { - log::warn!("Could not find metadata for ranked object {}", data_id); - } - } - - results -} - -async fn get_user_course_object_ids(owner_pid: u32) -> Result, ErrorCode> { - let rows = sqlx::query!( - r#" - SELECT data_id - FROM datastore.objects - WHERE owner = $1 AND data_type > 2 AND data_type < 50 - "#, - owner_pid as i64 - ) - .fetch_all(get_db()) - .await - .map_err(|e| { - log::error!("error fetching course IDs for PID {}: {:?}", owner_pid, e); - ErrorCode::DataStore_SystemFileError - })?; - - let mut valid_ids = Vec::new(); - for row in rows { - let data_id = row.data_id as u64; - // always check avail - if check_object_availability(data_id, 0).await.is_ok() { - valid_ids.push(data_id); - } - } - - Ok(valid_ids) -} - -fn get_blacklist_1() -> Vec { - vec![ - "けされ", "消され", "削除され", "リセットされ", "BANされ", "BANされ", - "キミのコース", "君のコース", "きみのコース", "い い ね", "遊びます", "地震", - "震災", "被災", "津波", "バンされ", "い~ね", "震度", "じしん", "banされ", - "くわしくは", "詳しくは", "ちんちん", "ち0こ", "bicth", "い.い.ね", - "ナイ~ス", "い&い", "い-いね", "いぃね", "nigger", "ngger", "star if u", - "Star if u", "Star if you", "star if you", "PENlS", "マンコ", "butthole", - "LILI", "vagina", "vagyna", "うんち", "うんこ", "ウンコ", "Iine", - "EENE", "まんこ", "ウンチ", "niglet", "nigglet", "please like", "きんたま", - "Butthole", "llね", "iいね", "give a star", "ちんぽ", "亀頭", "penis", - "ウンコ", "plz more stars", "star plz", "い()ね", "PLEASE star", "Bitte Sterne", - ].into_iter().map(String::from).collect() -} - -fn get_blacklist_2() -> Vec { - vec![ - "ゼロから", "0から", "0から", "い  い  ね", "いい", "東日本", "大震", - ].into_iter().map(String::from).collect() -} - -fn get_blacklist_3() -> Vec { - vec![ - "いいね", "下さい", "ください", "押して", "おして", "返す", "かえす", - "星", "してくれ", "するよ", "☆くれたら", "☆あげます", "★くれたら", - "★あげます", "しね", "ころす", "ころされた", "アナル", "ファック", - "キンタマ", "○ね", "キチガイ", "うんこ", "KITIGAI", "金玉", "おっぱい", - "☆おす", "☆押す", "★おす", "★押す", "いいする", "いいよ", "イイネ", - "ケツ", "うんち", "かくせいざい", "覚せい剤", "シャブ", "きんたま", - "ちんちん", "おしっこ", "ちんぽこ", "ころして", "グッド", "グット", - "レ●プ", "バーカ", "きちがい", "ちんげ", "マンコ", "まんこ", "チンポ", - "クズ", "ウンコ", "ナイスおねがいします", "penis", "イイね", "☆よろ", - "ナイス!して", "ま/んこ", "まん/こ", - ].into_iter().map(String::from).collect() -} - -impl DataStore for User { - async fn get_meta(&self, mut metaparam: GetMetaParam) -> Result { - let mut meta_info = if metaparam.dataid != 0 { - get_object_info_by_data_id(metaparam.dataid, metaparam.access_password).await? - } else { - get_object_info_by_persistence_target(metaparam.persistence_target, metaparam.access_password).await? - }; - - let current_pid = self.pid; - verify_object_permission(meta_info.owner, current_pid, &meta_info.permission)?; - - filter_properties_by_result_option(&mut meta_info, metaparam.result_option); - - Ok(meta_info) - } - - async fn prepare_post_object(&self, postparam: PreparePostParam) -> Result { - let recipient_ids: Vec = postparam.permission.recipient_ids.iter().map(|&id| id as i32).collect(); - let del_recipient_ids: Vec = postparam.del_permission.recipient_ids.iter().map(|&id| id as i32).collect(); - let now = time::OffsetDateTime::now_utc(); - - let row = sqlx::query!( + async fn check_object_availability(data_id: u64, password: u64) -> Result<(), ErrorCode> { + let row = sqlx::query!( r#" - INSERT INTO datastore.objects ( - owner, size, name, data_type, meta_binary, - permission, permission_recipients, - delete_permission, delete_permission_recipients, - flag, period, refer_data_id, tags, - persistence_slot_id, extra_data, creation_date, update_date - ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17 - ) RETURNING data_id + SELECT under_review, access_password + FROM datastore.objects + WHERE data_id = $1 AND upload_completed = TRUE AND deleted = FALSE "#, - self.pid as i32, - postparam.size as i32, - postparam.name, - postparam.data_type as i32, - &postparam.meta_binary.0, - postparam.permission.permission as i32, - &recipient_ids, - postparam.del_permission.permission as i32, - &del_recipient_ids, - postparam.flag as i32, - postparam.period as i32, - postparam.refer_data_id as i64, - &postparam.tags, - postparam.persistence_init_param.persistence_slot_id as i32, - &postparam.extra_data, - time::PrimitiveDateTime::new(now.date(), now.time()), - time::PrimitiveDateTime::new(now.date(), now.time()) + data_id as i64 ) - .fetch_one(get_db()) - .await - .map_err(|e| { - log::error!("DB Error: {:?}", e); - ErrorCode::DataStore_SystemFileError - })?; - - let data_id = row.data_id as u64; - let presigner = S3Presigner::new( - &format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT), - format!("{}", *RNEX_DATASTORE_S3_BUCKET) - ).await; - - let key = format!("data/{}.bin", data_id); - - let (upload_url, fields) = presigner.generate_presigned_post(&key).await; - - let form_fields = fields.into_iter().map(|(k, v)| { - KeyValue { key: k, value: v } - }).collect(); - - Ok(ReqPostInfo { - dataid: data_id, - url: upload_url, - request_headers: vec![], - form_fields, - root_ca_cert: vec![], - }) - } - - async fn complete_post_object(&self, completeparam: CompletePostParam) -> Result<(), ErrorCode> { - log::info!("Data ID: {:?}", completeparam.dataid); - log::info!("Success: {:?}", completeparam.success); - - let record = sqlx::query!( - r#"SELECT owner, under_review FROM datastore.objects WHERE data_id = $1"#, - completeparam.dataid as i64 - ) - .fetch_optional(get_db()) - .await - .map_err(|e| { - eprintln!("select error: {:?}", e); - ErrorCode::DataStore_SystemFileError - })?; - - let record = record.ok_or(ErrorCode::DataStore_NotFound)?; - - if record.under_review { - return Err(ErrorCode::DataStore_UnderReviewing); - } - - if record.owner.unwrap_or(0) as u32 != self.pid { - return Err(ErrorCode::DataStore_PermissionDenied); - } - - if completeparam.success { - sqlx::query!( - r#"UPDATE datastore.objects SET upload_completed = true WHERE data_id = $1"#, - completeparam.dataid as i64 - ) - .execute(get_db()) + .fetch_optional(get_db()) .await .map_err(|e| { - eprintln!("update error: {:?}", e); + eprintln!("Availability check DB error: {:?}", e); ErrorCode::DataStore_SystemFileError - })?; - } else { - return Err(ErrorCode::Transport_TemporaryServerError); - } + })? + .ok_or(ErrorCode::DataStore_NotFound)?; - Ok(()) - } - - async fn rate_custom_ranking(&self, rankingparam: Vec) -> Result<(), ErrorCode> { - for abcparam in rankingparam { - let exists = sqlx::query_scalar!( - r#"SELECT EXISTS(SELECT 1 FROM datastore.objects WHERE data_id = $1)"#, - abcparam.dataid as i64 - ) - .fetch_one(get_db()) - .await - .map_err(|_| ErrorCode::DataStore_SystemFileError)?; - - if !exists.unwrap_or(false) { - return Err(ErrorCode::DataStore_NotFound); + let access_password = row.access_password as u64; + if access_password != 0 && access_password != password { + return Err(ErrorCode::DataStore_InvalidPassword); } - sqlx::query!( - r#" - INSERT INTO datastore.object_custom_rankings (data_id, application_id, value) - VALUES ($1, $2, $3) - ON CONFLICT (data_id, application_id) - DO UPDATE SET value = datastore.object_custom_rankings.value + EXCLUDED.value - "#, - abcparam.dataid as i64, - abcparam.appid as i32, - abcparam.score as i32 - ) - .execute(get_db()) + if row.under_review { + return Err(ErrorCode::DataStore_UnderReviewing); + } + + Ok(()) + } + + async fn get_object_ratings(data_id: u64, password: u64) -> Result, ErrorCode> { + check_object_availability(data_id, password).await?; + + let rows = sqlx::query!( + r#" + SELECT slot, total_value, count, initial_value + FROM datastore.object_ratings + WHERE data_id = $1 + "#, + data_id as i64 + ) + .fetch_all(get_db()) .await .map_err(|e| { - log::error!("update/insert error: {:?}", e); + eprintln!("Ratings fetch error: {:?}", e); ErrorCode::DataStore_SystemFileError })?; + + let ratings = rows.into_iter().map(|row| { + RatingInfoWithSlot { + slot: row.slot as i8, + rating: RatingInfo { + total_value: row.total_value.unwrap_or(0), + count: row.count as u32, + initial_value: row.initial_value.unwrap_or(0), + }, + } + }).collect(); + + Ok(ratings) } - Ok(()) - } + async fn get_object_info_by_data_id(data_id: u64, password: u64) -> Result { + check_object_availability(data_id, password).await?; - async fn get_application_config(&self, appid: u32) -> Result, ErrorCode> { - const MAX_COURSE_UPLOADS: i32 = 100; + let row = sqlx::query!( + r#"SELECT data_id, owner, size, name, data_type, meta_binary, + permission, permission_recipients, delete_permission, delete_permission_recipients, + period, refer_data_id, flag, tags, creation_date, update_date + FROM datastore.objects WHERE data_id = $1"#, + data_id as i64 + ) + .fetch_optional(get_db()) + .await + .map_err(|_| ErrorCode::DataStore_SystemFileError)? + .ok_or(ErrorCode::DataStore_NotFound)?; - let config = match appid { - 0 => vec![ - 0x00000001, 0x00000032, 0x00000096, 0x0000012c, 0x000001f4, - 0x00000320, 0x00000514, 0x000007d0, 0x00000bb8, 0x00001388, - MAX_COURSE_UPLOADS, 0x00000014, 0x0000001e, 0x00000028, 0x00000032, - 0x0000003c, 0x00000046, 0x00000050, 0x0000005a, 0x00000064, - 0x00000023, 0x0000004b, 0x00000023, 0x0000004b, 0x00000032, - 0x00000000, 0x00000003, 0x00000003, 0x00000064, 0x00000006, - 0x00000001, 0x00000060, 0x00000005, 0x00000060, 0x00000000, - 0x000007e4, 0x00000001, 0x00000001, 0x0000000c, 0x00000000, - ], - 1 => vec![ - 2, - 1770179696, - 1770179664, - 1770179640, - 1770180827, - 1770180777, - 1770180745, - 1770177625, - 1770177590, - ], - 2 => vec![0x000007df, 0x0000000c, 0x00000016, 0x00000005, 0x00000000], - 10 => vec![35, 75, 96, 40, 5, 6], - _ => { - log::error!("unknown SMM app id: {}", appid); - return Err(ErrorCode::DataStore_Unknown); - } - }; + let ratings = get_object_ratings(data_id, password).await?; - Ok(config) - } - - async fn get_custom_ranking_by_data_id( - &self, - custom_ranking_param: DataStoreGetCustomRankingByDataIDParam - ) -> Result<(Vec, Vec), ErrorCode> { - println!("appid: {:?}", custom_ranking_param.application_id); - println!("dataid list: {:?}", custom_ranking_param.data_id_list); - println!("result option: {:?}", custom_ranking_param.result_option); - - let mut ranking_results = get_custom_rankings_by_data_ids(custom_ranking_param.application_id, custom_ranking_param.data_id_list).await; - - let mut q_results = Vec::with_capacity(ranking_results.len()); - - for result in &mut ranking_results { - if (custom_ranking_param.result_option & 0x01) == 0 { - result.meta_info.tags = Vec::new(); - } - - if (custom_ranking_param.result_option & 0x02) == 0 { - result.meta_info.ratings = Vec::new(); - } - - if (custom_ranking_param.result_option & 0x04) == 0 { - result.meta_info.meta_binary = QBuffer(Vec::new()); - } - - if (custom_ranking_param.result_option & 0x20) == 0 { - result.score = 0; - } - - q_results.push(QResult::success(ErrorCode::Core_Unknown)); + Ok(map_row_to_meta_info( + row.data_id, + row.owner.unwrap_or(0), + row.size.unwrap_or(0), + row.name.unwrap_or_default(), + row.data_type.unwrap_or(0) as i16, + row.meta_binary.unwrap_or_default(), + row.permission.unwrap_or(0) as i16, + row.permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(), + row.delete_permission.unwrap_or(0) as i16, + row.delete_permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(), + row.period.unwrap_or(0) as i16, + row.refer_data_id.unwrap_or(0), + row.flag.unwrap_or(0), + row.tags.unwrap_or_default(), + row.creation_date.map(|dt| chrono::NaiveDateTime::new( + chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(), + chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap() + )).unwrap_or_default(), + row.update_date.map(|dt| chrono::NaiveDateTime::new( + chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(), + chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap() + )).unwrap_or_default(), + ratings + )) } - Ok((ranking_results, q_results)) - } + async fn get_object_info_by_persistence_target(target: PersistenceTarget, password: u64) -> Result { + let row = sqlx::query!( + r#"SELECT data_id, owner, size, name, data_type, meta_binary, + permission, permission_recipients, delete_permission, delete_permission_recipients, + period, refer_data_id, flag, tags, creation_date, update_date, + access_password, under_review + FROM datastore.objects + WHERE owner = $1 AND persistence_slot_id = $2 + AND upload_completed = TRUE AND deleted = FALSE"#, + target.owner as i32, + target.persistence_slot_id as i16 + ) + .fetch_optional(get_db()) + .await + .map_err(|_| ErrorCode::DataStore_SystemFileError)? + .ok_or(ErrorCode::DataStore_NotFound)?; - async fn get_buffer_queue(&self, bufferparam: BufferQueueParam) -> Result, ErrorCode> { - // log::info!("GetBufferQueue: dataid={}, slot={}", param.dataid, param.slot); - - let buffers = get_buffer_queues_by_data_id_and_slot(bufferparam.dataid, bufferparam.slot).await?; - - Ok(buffers) - } - - async fn prepare_get_object(&self, prepare_get_param: DataStorePrepareGetParam) -> Result { - let meta_info = if prepare_get_param.dataid != 0 { - get_object_info_by_data_id(prepare_get_param.dataid, prepare_get_param.access_password).await? - } else { - get_object_info_by_persistence_target(prepare_get_param.persistence_target, prepare_get_param.access_password).await? - }; - - verify_object_permission(meta_info.owner, self.pid, &meta_info.permission)?; - - let presigner = S3Presigner::new( - &format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT), - format!("{}", *RNEX_DATASTORE_S3_BUCKET) - ).await; - - let key = format!("data/{}.bin", meta_info.dataid); - let download_url = presigner.generate_presigned_get(&key); - - Ok(DataStoreReqGetInfo { - url: download_url, - request_headers: vec![], - size: meta_info.size, - root_ca_cert: vec![], - dataid: meta_info.dataid, - }) - } - - async fn followings_latest_course_search_object( - &self, - course_search_param: DataStoreSearchParam, - _extra_data: Vec - ) -> Result, ErrorCode> { - let mut all_results = Vec::new(); - - for &owner_pid in &course_search_param.owner_ids { - let course_ids = get_user_course_object_ids(owner_pid).await?; - - if course_ids.is_empty() { - continue; + let db_password = row.access_password as u64; + if db_password != 0 && db_password != password { + return Err(ErrorCode::DataStore_InvalidPassword); } - let mut results = get_custom_rankings_by_data_ids(0, course_ids).await; - - // Flag 0x1: Return Tags - // Flag 0x2: Return Ratings - // Flag 0x4: Return MetaBinary - // Flag 0x20: Return Score - for res in &mut results { - if course_search_param.result_option & 0x1 == 0 { - res.meta_info.tags = Vec::new(); - } - if course_search_param.result_option & 0x2 == 0 { - res.meta_info.ratings = Vec::new(); - } - if course_search_param.result_option & 0x4 == 0 { - res.meta_info.meta_binary = rnex_core::rmc::structures::qbuffer::QBuffer(Vec::new()); - } - if course_search_param.result_option & 0x20 == 0 { - res.score = 0; - } + if row.under_review { + return Err(ErrorCode::DataStore_UnderReviewing); } - all_results.extend(results); + let ratings = get_object_ratings(row.data_id as u64, password).await?; + + Ok(map_row_to_meta_info( + row.data_id, + row.owner.unwrap_or(0), + row.size.unwrap_or(0), + row.name.unwrap_or_default(), + row.data_type.unwrap_or(0) as i16, + row.meta_binary.unwrap_or_default(), + row.permission.unwrap_or(0) as i16, + row.permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(), + row.delete_permission.unwrap_or(0) as i16, + row.delete_permission_recipients.unwrap_or_default().into_iter().map(|id| id as i64).collect(), + row.period.unwrap_or(0) as i16, + row.refer_data_id.unwrap_or(0), + row.flag.unwrap_or(0), + row.tags.unwrap_or_default(), + row.creation_date.map(|dt| chrono::NaiveDateTime::new( + chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(), + chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap() + )).unwrap_or_default(), + row.update_date.map(|dt| chrono::NaiveDateTime::new( + chrono::NaiveDate::from_ymd_opt(dt.year(), dt.month() as u32, dt.day() as u32).unwrap(), + chrono::NaiveTime::from_hms_opt(dt.hour() as u32, dt.minute() as u32, dt.second() as u32).unwrap() + )).unwrap_or_default(), + ratings + )) } - // note: we assume the client sorts the data lol + async fn get_buffer_queues_by_data_id_and_slot( + data_id: u64, + slot: u32 + ) -> Result, ErrorCode> { + check_object_availability(data_id, 0).await?; - Ok(all_results) - } + let rows = sqlx::query!( + r#" + SELECT buffer + FROM datastore.buffer_queues + WHERE data_id = $1 AND slot = $2 + ORDER BY creation_date ASC + "#, + data_id as i64, + slot as i32 + ) + .fetch_all(get_db()) + .await + .map_err(|e| { + log::error!("Buffer queue fetch error: {:?}", e); + ErrorCode::DataStore_SystemFileError + })?; - async fn get_application_config_string(&self, application_id: u32) -> Result, ErrorCode> { - let config = match application_id { - 128 => get_blacklist_1(), - 129 => get_blacklist_2(), - 130 => get_blacklist_3(), - _ => { - log::warn!("unsupported application_id in GetApplicationConfigString: {}", application_id); - Vec::new() + let buffer_queues = rows + .into_iter() + .map(|row| QBuffer(row.buffer)) + .collect(); + + Ok(buffer_queues) + } + + fn verify_object_permission( + owner_id: u32, + viewer_id: u32, + permission: &Permission, + ) -> Result<(), ErrorCode> { + if owner_id == viewer_id { + return Ok(()); } - }; - Ok(config) - } - - async fn get_metas_multiple_param( - &self, - params: Vec - ) -> Result<(Vec, Vec), ErrorCode> { - let mut metas = Vec::with_capacity(params.len()); - let mut results = Vec::with_capacity(params.len()); - - for param in params { - let info_result = if param.dataid != 0 { - get_object_info_by_data_id(param.dataid, param.access_password).await - } else { - get_object_info_by_persistence_target(param.persistence_target, param.access_password).await - }; - - match info_result { - Ok(mut meta) => { - if let Err(e) = verify_object_permission(meta.owner, self.pid, &meta.permission) { - metas.push(GetMetaInfo::default()); - results.push(QResult::error(e)); + match permission.permission { + 0 => Ok(()), // All can read + 1 => Err(ErrorCode::DataStore_PermissionDenied), // Friends only, unimplemented + 2 => { + // Recipient IDs can read + if permission.recipient_ids.contains(&viewer_id) { + Ok(()) } else { - if param.result_option & 0x1 == 0 { - meta.tags = Vec::new(); - } - if param.result_option & 0x2 == 0 { - meta.ratings = Vec::new(); - } - if param.result_option & 0x4 == 0 { - meta.meta_binary = rnex_core::rmc::structures::qbuffer::QBuffer(Vec::new()); - } - - metas.push(meta); - results.push(QResult::success(ErrorCode::Core_Unknown)); + Err(ErrorCode::DataStore_PermissionDenied) } } - Err(e) => { - metas.push(GetMetaInfo::default()); - results.push(QResult::error(e)); - } + 3 => Err(ErrorCode::DataStore_PermissionDenied), // Owner only, redundant + _ => Err(ErrorCode::DataStore_InvalidArgument), // ??? haxx0r } } - Ok((metas, results)) + fn filter_properties_by_result_option( + meta_info: &mut GetMetaInfo, + result_option: u8, + ) { + if (result_option & 0x01) == 0 { + meta_info.meta_binary = QBuffer(Vec::new()); + } + + if (result_option & 0x04) == 0 { + meta_info.ratings = Vec::new(); + } + + // No idea what the other things do. :shrug: + } + + // Dawg... + async fn get_custom_rankings_by_data_ids( + application_id: u32, + data_ids: Vec + ) -> Vec { + let mut results = Vec::with_capacity(data_ids.len()); + + let rows = sqlx::query!( + r#" + SELECT + rankings.data_id, + rankings.value + FROM datastore.object_custom_rankings rankings + JOIN UNNEST($1::bigint[]) WITH ORDINALITY AS rows(data_id, ord) + ON rankings.data_id = rows.data_id + AND rankings.application_id = $2 + ORDER BY rows.ord + "#, + &data_ids.iter().map(|&id| id as i64).collect::>(), + application_id as i32 + ) + .fetch_all(get_db()) + .await; + + let rows = match rows { + Ok(r) => r, + Err(e) => { + log::error!("Custom ranking query error: {:?}", e); + return results; + } + }; + + for row in rows { + let data_id = row.data_id as u64; + let score = row.value.unwrap_or(0) as u32; + + if let Ok(meta) = get_object_info_by_data_id(data_id, 0).await { + results.push(DataStoreCustomRankingResult { + order: 0, + score, + meta_info: meta, + }); + } else { + log::warn!("Could not find metadata for ranked object {}", data_id); + } + } + + results + } + + async fn get_user_course_object_ids(owner_pid: u32) -> Result, ErrorCode> { + let rows = sqlx::query!( + r#" + SELECT data_id + FROM datastore.objects + WHERE owner = $1 AND data_type > 2 AND data_type < 50 + "#, + owner_pid as i64 + ) + .fetch_all(get_db()) + .await + .map_err(|e| { + log::error!("error fetching course IDs for PID {}: {:?}", owner_pid, e); + ErrorCode::DataStore_SystemFileError + })?; + + let mut valid_ids = Vec::new(); + for row in rows { + let data_id = row.data_id as u64; + // always check avail + if check_object_availability(data_id, 0).await.is_ok() { + valid_ids.push(data_id); + } + } + + Ok(valid_ids) + } + + fn get_blacklist_1() -> Vec { + vec![ + "けされ", "消され", "削除され", "リセットされ", "BANされ", "BANされ", + "キミのコース", "君のコース", "きみのコース", "い い ね", "遊びます", "地震", + "震災", "被災", "津波", "バンされ", "い~ね", "震度", "じしん", "banされ", + "くわしくは", "詳しくは", "ちんちん", "ち0こ", "bicth", "い.い.ね", + "ナイ~ス", "い&い", "い-いね", "いぃね", "nigger", "ngger", "star if u", + "Star if u", "Star if you", "star if you", "PENlS", "マンコ", "butthole", + "LILI", "vagina", "vagyna", "うんち", "うんこ", "ウンコ", "Iine", + "EENE", "まんこ", "ウンチ", "niglet", "nigglet", "please like", "きんたま", + "Butthole", "llね", "iいね", "give a star", "ちんぽ", "亀頭", "penis", + "ウンコ", "plz more stars", "star plz", "い()ね", "PLEASE star", "Bitte Sterne", + ].into_iter().map(String::from).collect() + } + + fn get_blacklist_2() -> Vec { + vec![ + "ゼロから", "0から", "0から", "い  い  ね", "いい", "東日本", "大震", + ].into_iter().map(String::from).collect() + } + + fn get_blacklist_3() -> Vec { + vec![ + "いいね", "下さい", "ください", "押して", "おして", "返す", "かえす", + "星", "してくれ", "するよ", "☆くれたら", "☆あげます", "★くれたら", + "★あげます", "しね", "ころす", "ころされた", "アナル", "ファック", + "キンタマ", "○ね", "キチガイ", "うんこ", "KITIGAI", "金玉", "おっぱい", + "☆おす", "☆押す", "★おす", "★押す", "いいする", "いいよ", "イイネ", + "ケツ", "うんち", "かくせいざい", "覚せい剤", "シャブ", "きんたま", + "ちんちん", "おしっこ", "ちんぽこ", "ころして", "グッド", "グット", + "レ●プ", "バーカ", "きちがい", "ちんげ", "マンコ", "まんこ", "チンポ", + "クズ", "ウンコ", "ナイスおねがいします", "penis", "イイね", "☆よろ", + "ナイス!して", "ま/んこ", "まん/こ", + ].into_iter().map(String::from).collect() + } + + impl DataStore for User { + async fn get_meta(&self, mut metaparam: GetMetaParam) -> Result { + let mut meta_info = if metaparam.dataid != 0 { + get_object_info_by_data_id(metaparam.dataid, metaparam.access_password).await? + } else { + get_object_info_by_persistence_target(metaparam.persistence_target, metaparam.access_password).await? + }; + + let current_pid = self.pid; + verify_object_permission(meta_info.owner, current_pid, &meta_info.permission)?; + + filter_properties_by_result_option(&mut meta_info, metaparam.result_option); + + Ok(meta_info) + } + + async fn prepare_post_object(&self, postparam: PreparePostParam) -> Result { + let recipient_ids: Vec = postparam.permission.recipient_ids.iter().map(|&id| id as i32).collect(); + let del_recipient_ids: Vec = postparam.del_permission.recipient_ids.iter().map(|&id| id as i32).collect(); + let now = time::OffsetDateTime::now_utc(); + + let row = sqlx::query!( + r#" + INSERT INTO datastore.objects ( + owner, size, name, data_type, meta_binary, + permission, permission_recipients, + delete_permission, delete_permission_recipients, + flag, period, refer_data_id, tags, + persistence_slot_id, extra_data, creation_date, update_date + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17 + ) RETURNING data_id + "#, + self.pid as i32, + postparam.size as i32, + postparam.name, + postparam.data_type as i32, + &postparam.meta_binary.0, + postparam.permission.permission as i32, + &recipient_ids, + postparam.del_permission.permission as i32, + &del_recipient_ids, + postparam.flag as i32, + postparam.period as i32, + postparam.refer_data_id as i64, + &postparam.tags, + postparam.persistence_init_param.persistence_slot_id as i32, + &postparam.extra_data, + time::PrimitiveDateTime::new(now.date(), now.time()), + time::PrimitiveDateTime::new(now.date(), now.time()) + ) + .fetch_one(get_db()) + .await + .map_err(|e| { + log::error!("DB Error: {:?}", e); + ErrorCode::DataStore_SystemFileError + })?; + + let data_id = row.data_id as u64; + let presigner = S3Presigner::new( + &format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT), + format!("{}", *RNEX_DATASTORE_S3_BUCKET) + ).await; + + let key = format!("data/{}.bin", data_id); + + let (upload_url, fields) = presigner.generate_presigned_post(&key).await; + + let form_fields = fields.into_iter().map(|(k, v)| { + KeyValue { key: k, value: v } + }).collect(); + + Ok(ReqPostInfo { + dataid: data_id, + url: upload_url, + request_headers: vec![], + form_fields, + root_ca_cert: vec![], + }) + } + + async fn complete_post_object(&self, completeparam: CompletePostParam) -> Result<(), ErrorCode> { + log::info!("Data ID: {:?}", completeparam.dataid); + log::info!("Success: {:?}", completeparam.success); + + let record = sqlx::query!( + r#"SELECT owner, under_review FROM datastore.objects WHERE data_id = $1"#, + completeparam.dataid as i64 + ) + .fetch_optional(get_db()) + .await + .map_err(|e| { + eprintln!("select error: {:?}", e); + ErrorCode::DataStore_SystemFileError + })?; + + let record = record.ok_or(ErrorCode::DataStore_NotFound)?; + + if record.under_review { + return Err(ErrorCode::DataStore_UnderReviewing); + } + + if record.owner.unwrap_or(0) as u32 != self.pid { + return Err(ErrorCode::DataStore_PermissionDenied); + } + + if completeparam.success { + sqlx::query!( + r#"UPDATE datastore.objects SET upload_completed = true WHERE data_id = $1"#, + completeparam.dataid as i64 + ) + .execute(get_db()) + .await + .map_err(|e| { + eprintln!("update error: {:?}", e); + ErrorCode::DataStore_SystemFileError + })?; + } else { + return Err(ErrorCode::Transport_TemporaryServerError); + } + + Ok(()) + } + + async fn rate_custom_ranking(&self, rankingparam: Vec) -> Result<(), ErrorCode> { + for abcparam in rankingparam { + let exists = sqlx::query_scalar!( + r#"SELECT EXISTS(SELECT 1 FROM datastore.objects WHERE data_id = $1)"#, + abcparam.dataid as i64 + ) + .fetch_one(get_db()) + .await + .map_err(|_| ErrorCode::DataStore_SystemFileError)?; + + if !exists.unwrap_or(false) { + return Err(ErrorCode::DataStore_NotFound); + } + + sqlx::query!( + r#" + INSERT INTO datastore.object_custom_rankings (data_id, application_id, value) + VALUES ($1, $2, $3) + ON CONFLICT (data_id, application_id) + DO UPDATE SET value = datastore.object_custom_rankings.value + EXCLUDED.value + "#, + abcparam.dataid as i64, + abcparam.appid as i32, + abcparam.score as i32 + ) + .execute(get_db()) + .await + .map_err(|e| { + log::error!("update/insert error: {:?}", e); + ErrorCode::DataStore_SystemFileError + })?; + } + + Ok(()) + } + + async fn get_application_config(&self, appid: u32) -> Result, ErrorCode> { + const MAX_COURSE_UPLOADS: i32 = 100; + + let config = match appid { + 0 => vec![ + 0x00000001, 0x00000032, 0x00000096, 0x0000012c, 0x000001f4, + 0x00000320, 0x00000514, 0x000007d0, 0x00000bb8, 0x00001388, + MAX_COURSE_UPLOADS, 0x00000014, 0x0000001e, 0x00000028, 0x00000032, + 0x0000003c, 0x00000046, 0x00000050, 0x0000005a, 0x00000064, + 0x00000023, 0x0000004b, 0x00000023, 0x0000004b, 0x00000032, + 0x00000000, 0x00000003, 0x00000003, 0x00000064, 0x00000006, + 0x00000001, 0x00000060, 0x00000005, 0x00000060, 0x00000000, + 0x000007e4, 0x00000001, 0x00000001, 0x0000000c, 0x00000000, + ], + 1 => vec![ + 2, + 1770179696, + 1770179664, + 1770179640, + 1770180827, + 1770180777, + 1770180745, + 1770177625, + 1770177590, + ], + 2 => vec![0x000007df, 0x0000000c, 0x00000016, 0x00000005, 0x00000000], + 10 => vec![35, 75, 96, 40, 5, 6], + _ => { + log::error!("unknown SMM app id: {}", appid); + return Err(ErrorCode::DataStore_Unknown); + } + }; + + Ok(config) + } + + async fn get_custom_ranking_by_data_id( + &self, + custom_ranking_param: DataStoreGetCustomRankingByDataIDParam + ) -> Result<(Vec, Vec), ErrorCode> { + println!("appid: {:?}", custom_ranking_param.application_id); + println!("dataid list: {:?}", custom_ranking_param.data_id_list); + println!("result option: {:?}", custom_ranking_param.result_option); + + let mut ranking_results = get_custom_rankings_by_data_ids(custom_ranking_param.application_id, custom_ranking_param.data_id_list).await; + + let mut q_results = Vec::with_capacity(ranking_results.len()); + + for result in &mut ranking_results { + if (custom_ranking_param.result_option & 0x01) == 0 { + result.meta_info.tags = Vec::new(); + } + + if (custom_ranking_param.result_option & 0x02) == 0 { + result.meta_info.ratings = Vec::new(); + } + + if (custom_ranking_param.result_option & 0x04) == 0 { + result.meta_info.meta_binary = QBuffer(Vec::new()); + } + + if (custom_ranking_param.result_option & 0x20) == 0 { + result.score = 0; + } + + q_results.push(QResult::success(ErrorCode::Core_Unknown)); + } + + Ok((ranking_results, q_results)) + } + + async fn get_buffer_queue(&self, bufferparam: BufferQueueParam) -> Result, ErrorCode> { + // log::info!("GetBufferQueue: dataid={}, slot={}", param.dataid, param.slot); + + let buffers = get_buffer_queues_by_data_id_and_slot(bufferparam.dataid, bufferparam.slot).await?; + + Ok(buffers) + } + + async fn prepare_get_object(&self, prepare_get_param: DataStorePrepareGetParam) -> Result { + let meta_info = if prepare_get_param.dataid != 0 { + get_object_info_by_data_id(prepare_get_param.dataid, prepare_get_param.access_password).await? + } else { + get_object_info_by_persistence_target(prepare_get_param.persistence_target, prepare_get_param.access_password).await? + }; + + verify_object_permission(meta_info.owner, self.pid, &meta_info.permission)?; + + let presigner = S3Presigner::new( + &format!("https://{}", *RNEX_DATASTORE_S3_ENDPOINT), + format!("{}", *RNEX_DATASTORE_S3_BUCKET) + ).await; + + let key = format!("data/{}.bin", meta_info.dataid); + let download_url = presigner.generate_presigned_get(&key); + + Ok(DataStoreReqGetInfo { + url: download_url, + request_headers: vec![], + size: meta_info.size, + root_ca_cert: vec![], + dataid: meta_info.dataid, + }) + } + + async fn followings_latest_course_search_object( + &self, + course_search_param: DataStoreSearchParam, + _extra_data: Vec + ) -> Result, ErrorCode> { + let mut all_results = Vec::new(); + + for &owner_pid in &course_search_param.owner_ids { + let course_ids = get_user_course_object_ids(owner_pid).await?; + + if course_ids.is_empty() { + continue; + } + + let mut results = get_custom_rankings_by_data_ids(0, course_ids).await; + + // Flag 0x1: Return Tags + // Flag 0x2: Return Ratings + // Flag 0x4: Return MetaBinary + // Flag 0x20: Return Score + for res in &mut results { + if course_search_param.result_option & 0x1 == 0 { + res.meta_info.tags = Vec::new(); + } + if course_search_param.result_option & 0x2 == 0 { + res.meta_info.ratings = Vec::new(); + } + if course_search_param.result_option & 0x4 == 0 { + res.meta_info.meta_binary = rnex_core::rmc::structures::qbuffer::QBuffer(Vec::new()); + } + if course_search_param.result_option & 0x20 == 0 { + res.score = 0; + } + } + + all_results.extend(results); + } + + // note: we assume the client sorts the data lol + + Ok(all_results) + } + + async fn get_application_config_string(&self, application_id: u32) -> Result, ErrorCode> { + let config = match application_id { + 128 => get_blacklist_1(), + 129 => get_blacklist_2(), + 130 => get_blacklist_3(), + _ => { + log::warn!("unsupported application_id in GetApplicationConfigString: {}", application_id); + Vec::new() + } + }; + + Ok(config) + } + + async fn get_metas_multiple_param( + &self, + params: Vec + ) -> Result<(Vec, Vec), ErrorCode> { + let mut metas = Vec::with_capacity(params.len()); + let mut results = Vec::with_capacity(params.len()); + + for param in params { + let info_result = if param.dataid != 0 { + get_object_info_by_data_id(param.dataid, param.access_password).await + } else { + get_object_info_by_persistence_target(param.persistence_target, param.access_password).await + }; + + match info_result { + Ok(mut meta) => { + if let Err(e) = verify_object_permission(meta.owner, self.pid, &meta.permission) { + metas.push(GetMetaInfo::default()); + results.push(QResult::error(e)); + } else { + if param.result_option & 0x1 == 0 { + meta.tags = Vec::new(); + } + if param.result_option & 0x2 == 0 { + meta.ratings = Vec::new(); + } + if param.result_option & 0x4 == 0 { + meta.meta_binary = rnex_core::rmc::structures::qbuffer::QBuffer(Vec::new()); + } + + metas.push(meta); + results.push(QResult::success(ErrorCode::Core_Unknown)); + } + } + Err(e) => { + metas.push(GetMetaInfo::default()); + results.push(QResult::error(e)); + } + } + } + + Ok((metas, results)) + } + } } } \ No newline at end of file