From 9a7be5880fdaa5244cbb0d00facd77ebd642c116 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=BB=BA?= <10267290+xujian026@user.noreply.gitee.com> Date: Thu, 26 Dec 2024 20:21:24 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0libsecret=E7=9A=84?= =?UTF-8?q?=E9=B8=BF=E8=92=99=E5=8C=96=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 徐建 --- .../libsecret/0001-secret-password.patch | 3121 +++++++++++++++++ thirdparty/libsecret/HPKBUILD | 103 + thirdparty/libsecret/arm64-v8a-cross-file.txt | 26 + .../libsecret/armeabi-v7a-cross-file.txt | 26 + 4 files changed, 3276 insertions(+) create mode 100644 thirdparty/libsecret/0001-secret-password.patch create mode 100644 thirdparty/libsecret/HPKBUILD create mode 100644 thirdparty/libsecret/arm64-v8a-cross-file.txt create mode 100644 thirdparty/libsecret/armeabi-v7a-cross-file.txt diff --git a/thirdparty/libsecret/0001-secret-password.patch b/thirdparty/libsecret/0001-secret-password.patch new file mode 100644 index 00000000..77a0790c --- /dev/null +++ b/thirdparty/libsecret/0001-secret-password.patch @@ -0,0 +1,3121 @@ +From 4ce6a4cb426c72cb8ca63c8cd118b44d2c7947c8 Mon Sep 17 00:00:00 2001 +From: xujian +Date: Sun, 8 Dec 2024 09:59:33 +0800 +Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9secret-password=E7=B3=BB?= + =?UTF-8?q?=E5=88=97=E6=8E=A5=E5=8F=A3?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + egg/egg-secure-memory.h | 8 + + libsecret/meson.build | 30 +- + libsecret/ohos-huks-crypto.cpp | 218 ++++ + libsecret/ohos-huks-crypto.h | 60 ++ + libsecret/ohos-secret-store.cpp | 243 +++++ + libsecret/ohos-secret-store.h | 100 ++ + libsecret/secret-password.cpp | 1688 +++++++++++++++++++++++++++++++ + libsecret/test-ohos-password.c | 596 +++++++++++ + meson.build | 2 +- + meson_options.txt | 1 + + tool/secret-tool.c | 13 + + 11 files changed, 2956 insertions(+), 3 deletions(-) + create mode 100644 libsecret/ohos-huks-crypto.cpp + create mode 100644 libsecret/ohos-huks-crypto.h + create mode 100644 libsecret/ohos-secret-store.cpp + create mode 100644 libsecret/ohos-secret-store.h + create mode 100644 libsecret/secret-password.cpp + create mode 100644 libsecret/test-ohos-password.c + +diff --git a/egg/egg-secure-memory.h b/egg/egg-secure-memory.h +index 660bc6f..7c320ef 100644 +--- a/egg/egg-secure-memory.h ++++ b/egg/egg-secure-memory.h +@@ -106,10 +106,18 @@ char* egg_secure_strdup_full (const char *tag, const char *str, int options); + + char* egg_secure_strndup_full (const char *tag, const char *str, size_t length, int options); + ++#ifdef __cplusplus ++extern "C" { ++#endif ++ + void egg_secure_strclear (char *str); + + void egg_secure_strfree (char *str); + ++#ifdef __cplusplus ++} ++#endif ++ + typedef struct { + const char *tag; + size_t request_length; +diff --git a/libsecret/meson.build b/libsecret/meson.build +index 85ad68f..e8c8cc2 100644 +--- a/libsecret/meson.build ++++ b/libsecret/meson.build +@@ -7,7 +7,6 @@ libsecret_sources = [ + 'secret-collection.c', + 'secret-item.c', + 'secret-methods.c', +- 'secret-password.c', + 'secret-prompt.c', + 'secret-retrievable.c', + 'secret-schema.c', +@@ -25,6 +24,8 @@ libsecret_headers = [ + 'secret-backend.h', + 'secret-collection.h', + 'secret-item.h', ++ 'ohos-huks-crypto.h', ++ 'ohos-secret-store.h', + 'secret-password.h', + 'secret-paths.h', + 'secret-prompt.h', +@@ -36,6 +37,18 @@ libsecret_headers = [ + 'secret-value.h', + ] + ++if get_option('is_ohos') ++ libsecret_sources += [ ++ 'ohos-huks-crypto.cpp', ++ 'ohos-secret-store.cpp', ++ 'secret-password.cpp', ++ ] ++else ++ libsecret_sources += [ ++ 'secret-password.c', ++ ] ++endif ++ + if with_crypto + libsecret_sources += [ + 'secret-file-backend.c', +@@ -115,7 +128,11 @@ if get_option('introspection') + 'secret-item.c', + 'secret-item.h', + 'secret-methods.c', +- 'secret-password.c', ++ 'ohos-secret-store.cpp', ++ 'ohos-secret-store.h', ++ 'ohos-huks-crypto.cpp', ++ 'ohos-huks-crypto.h', ++ 'secret-password.cpp', + 'secret-password.h', + 'secret-paths.c', + 'secret-paths.h', +@@ -230,6 +247,15 @@ test_names = [ + 'test-item', + 'test-collection', + ] ++ohos_test_names = [ ++ 'test-attributes', ++ 'test-value', ++ 'test-ohos-password', ++] ++ ++if get_option('is_ohos') ++ test_names = ohos_test_names ++endif + + if with_crypto + test_names += [ +diff --git a/libsecret/ohos-huks-crypto.cpp b/libsecret/ohos-huks-crypto.cpp +new file mode 100644 +index 0000000..658437a +--- /dev/null ++++ b/libsecret/ohos-huks-crypto.cpp +@@ -0,0 +1,218 @@ ++// ++// Created on 2024/11/25. ++// ++// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, ++// please include "napi/native_api.h". ++ ++#include "ohos-huks-crypto.h" ++#include ++#include ++#include ++ ++#ifdef __aarch64__ ++ ++#include ++#else ++ ++#include ++#include ++#include ++#endif ++ ++static const uint32_t IV_SIZE = 16; ++static const std::string alias = "chrome_huks_os_crypt_password_v1"; ++// alias whitelist ++static const std::unordered_set WHITE_LIST = { ++ "chrome_huks_os_crypt_password_v1"}; ++OhosHuksCrypto& OhosHuksCrypto::GetInstance() { ++ static OhosHuksCrypto instance; ++ return instance; ++} ++ ++bool OhosHuksCrypto::IsStringInWhitelist(const std::string& str) { ++ return WHITE_LIST.find(str) != WHITE_LIST.end(); ++} ++ ++OH_Huks_Result OhosHuksCrypto::InitParamSet(struct OH_Huks_ParamSet** paramSet, ++ const struct OH_Huks_Param* params, ++ uint32_t paramCount) { ++ OH_Huks_Result ret = OH_Huks_InitParamSet(paramSet); ++ if (ret.errorCode != OH_HUKS_SUCCESS) { ++ return ret; ++ } ++ ++ ret = OH_Huks_AddParams(*paramSet, params, paramCount); ++ if (ret.errorCode != OH_HUKS_SUCCESS) { ++ OH_Huks_FreeParamSet(paramSet); ++ return ret; ++ } ++ ++ ret = OH_Huks_BuildParamSet(paramSet); ++ if (ret.errorCode != OH_HUKS_SUCCESS) { ++ OH_Huks_FreeParamSet(paramSet); ++ return ret; ++ } ++ ++ return ret; ++} ++ ++struct OH_Huks_Param g_genEncDecParams[] = { ++ {.tag = OH_HUKS_TAG_ALGORITHM, .uint32Param = OH_HUKS_ALG_AES}, ++ {.tag = OH_HUKS_TAG_PURPOSE, ++ .uint32Param = OH_HUKS_KEY_PURPOSE_ENCRYPT | OH_HUKS_KEY_PURPOSE_DECRYPT}, ++ {.tag = OH_HUKS_TAG_KEY_SIZE, .uint32Param = OH_HUKS_AES_KEY_SIZE_256}, ++ {.tag = OH_HUKS_TAG_PADDING, .uint32Param = OH_HUKS_PADDING_PKCS7}, ++ {.tag = OH_HUKS_TAG_BLOCK_MODE, .uint32Param = OH_HUKS_MODE_CBC}}; ++ ++bool OhosHuksCrypto::EncryptKey(const std::string& alias, ++ const std::string& plaintext, ++ std::string* ciphertext) { ++ return Crypt(true, alias, plaintext, ciphertext); ++} ++ ++bool OhosHuksCrypto::DecryptKey(const std::string& alias, ++ const std::string& ciphertext, ++ std::string* plaintext) { ++ return Crypt(false, alias, ciphertext, plaintext); ++} ++ ++bool OhosHuksCrypto::Crypt(bool isEncrypt, ++ const std::string& alias, ++ const std::string& input, ++ std::string* output) { ++ if (output == nullptr || !IsStringInWhitelist(alias)) { ++ return false; ++ } ++ struct OH_Huks_Blob inData; ++ std::string validData; ++ uint8_t IV[IV_SIZE] = {0}; ++ std::size_t cipherIndex = 0; ++ if (isEncrypt) { ++ cipherIndex = IV_SIZE; ++ RandBytes(IV, IV_SIZE); ++ inData = {(unsigned int)input.length(), (uint8_t*)input.c_str()}; ++ } else { ++ if (input.length() < IV_SIZE) { ++ *output = std::string(); ++ return false; ++ } ++ memcpy((uint8_t*)IV, input.substr(0, IV_SIZE).c_str(), IV_SIZE); ++ validData = input.substr(IV_SIZE); ++ inData = {(unsigned int)validData.length(), (uint8_t*)validData.c_str()}; ++ } ++ ++ unsigned int purpose = ++ isEncrypt ? OH_HUKS_KEY_PURPOSE_ENCRYPT : OH_HUKS_KEY_PURPOSE_DECRYPT; ++ struct OH_Huks_Param g_cryptParams[] = { ++ {.tag = OH_HUKS_TAG_ALGORITHM, .uint32Param = OH_HUKS_ALG_AES}, ++ {.tag = OH_HUKS_TAG_PURPOSE, .uint32Param = purpose}, ++ {.tag = OH_HUKS_TAG_KEY_SIZE, .uint32Param = OH_HUKS_AES_KEY_SIZE_256}, ++ {.tag = OH_HUKS_TAG_PADDING, .uint32Param = OH_HUKS_PADDING_PKCS7}, ++ {.tag = OH_HUKS_TAG_BLOCK_MODE, .uint32Param = OH_HUKS_MODE_CBC}, ++ {.tag = OH_HUKS_TAG_IV, .blob = {.size = IV_SIZE, .data = (uint8_t*)IV}}}; ++ ++ struct OH_Huks_Blob keyAlias = {static_cast(alias.length()), (uint8_t*)alias.c_str()}; ++ struct OH_Huks_ParamSet* genParamSet = nullptr; ++ struct OH_Huks_ParamSet* cryptParamSet = nullptr; ++ uint32_t outputSize = input.length() + IV_SIZE + IV_SIZE; ++ output->clear(); ++ output->resize(outputSize, 0); ++ if (isEncrypt) { ++ memcpy((void*)(output->data()), (uint8_t*)IV, IV_SIZE); ++ } ++ struct OH_Huks_Blob outData = {outputSize, (uint8_t*)output->data() + cipherIndex}; ++ OH_Huks_Result ohResult = ++ InitParamSet(&genParamSet, g_genEncDecParams, ++ sizeof(g_genEncDecParams) / sizeof(OH_Huks_Param)); ++ if (ohResult.errorCode != OH_HUKS_SUCCESS) { ++ OH_Huks_FreeParamSet(&genParamSet); ++ *output = std::string(); ++ return false; ++ } ++ ++ ohResult = OH_Huks_IsKeyItemExist(&keyAlias, genParamSet); ++ if (ohResult.errorCode != OH_HUKS_SUCCESS) { ++ if (!isEncrypt) { ++ OH_Huks_FreeParamSet(&genParamSet); ++ *output = std::string(); ++ return false; ++ } ++ ohResult = OH_Huks_GenerateKeyItem(&keyAlias, genParamSet, nullptr); ++ if (ohResult.errorCode != OH_HUKS_SUCCESS) { ++ OH_Huks_FreeParamSet(&genParamSet); ++ *output = std::string(); ++ return false; ++ } ++ } ++ ++ ohResult = InitParamSet(&cryptParamSet, g_cryptParams, ++ sizeof(g_cryptParams) / sizeof(OH_Huks_Param)); ++ if (ohResult.errorCode != OH_HUKS_SUCCESS) { ++ OH_Huks_FreeParamSet(&genParamSet); ++ OH_Huks_FreeParamSet(&cryptParamSet); ++ *output = std::string(); ++ return false; ++ } ++ ++ uint8_t handleC[sizeof(uint64_t)] = {0}; ++ struct OH_Huks_Blob handleCrypt = {sizeof(uint64_t), handleC}; ++ ohResult = ++ OH_Huks_InitSession(&keyAlias, cryptParamSet, &handleCrypt, nullptr); ++ if (ohResult.errorCode != OH_HUKS_SUCCESS) { ++ OH_Huks_FreeParamSet(&genParamSet); ++ OH_Huks_FreeParamSet(&cryptParamSet); ++ *output = std::string(); ++ return false; ++ } ++ ohResult = ++ OH_Huks_FinishSession(&handleCrypt, cryptParamSet, &inData, &outData); ++ if (ohResult.errorCode != OH_HUKS_SUCCESS) { ++ OH_Huks_FreeParamSet(&genParamSet); ++ OH_Huks_FreeParamSet(&cryptParamSet); ++ *output = std::string(); ++ return false; ++ } ++ ++ OH_Huks_FreeParamSet(&genParamSet); ++ OH_Huks_FreeParamSet(&cryptParamSet); ++ if (outData.data == nullptr) { ++ *output = std::string(); ++ return false; ++ } ++ ++ output->resize(cipherIndex + outData.size); ++ return true; ++} ++ ++void OhosHuksCrypto::RandBytes(uint8_t *bytes, size_t length) ++{ ++ if (bytes == nullptr || length == 0) { ++ return; // 确保 bytes 非空且长度有效 ++ } ++#ifdef __aarch64__ ++ // 真实随机数生成器 ++ std::random_device rd; ++ // 随机数引擎 ++ std::mt19937 generator(rd()); ++ // 输出范围为 0 到 255 ++ std::uniform_int_distribution distribution(0, 255); ++ ++ for (size_t i = 0; i < length; ++i) { ++ bytes[i] = distribution(generator); // 生成随机字节并存入 buffer 中 ++ } ++#else ++ int device = open("/dev/random", O_RDONLY); ++ read(device, bytes, length); ++ close(device); ++#endif ++} ++ ++void* OhosHuksCrypto::memcpy(void* destination, const void* source, size_t count) { ++ auto* dst = reinterpret_cast(destination); ++ auto* src = reinterpret_cast(source); ++ while (count--) ++ *dst++ = *src++; ++ return destination; ++} ++ ++ +diff --git a/libsecret/ohos-huks-crypto.h b/libsecret/ohos-huks-crypto.h +new file mode 100644 +index 0000000..0f4745e +--- /dev/null ++++ b/libsecret/ohos-huks-crypto.h +@@ -0,0 +1,60 @@ ++// ++// Created on 2024/11/25. ++// ++// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, ++// please include "napi/native_api.h". ++ ++#if !defined (__OHOS__) ++#error "Only Use By OHOS Platform!!!" ++#endif ++ ++#ifndef MYAPPLICATION_ENCODE_H ++#define MYAPPLICATION_ENCODE_H ++#include ++#include ++#include ++#include ++#include ++#include ++ ++class OhosHuksCrypto { ++public: ++ static OhosHuksCrypto& GetInstance(); ++ ++ ~OhosHuksCrypto() = default; ++ ++ bool EncryptKey(const std::string& alias, ++ const std::string& plaintext, ++ std::string* ciphertext); ++ ++ bool DecryptKey(const std::string& alias, ++ const std::string& ciphertext, ++ std::string* plaintext); ++ ++private: ++ OhosHuksCrypto() = default; ++ ++ OhosHuksCrypto(const OhosHuksCrypto& other) = delete; ++ ++ OhosHuksCrypto& operator=(const OhosHuksCrypto&) = delete; ++ ++ OH_Huks_Result InitParamSet(struct OH_Huks_ParamSet** paramSet, ++ const struct OH_Huks_Param* params, ++ uint32_t paramCount); ++ ++ bool Crypt(bool isEncrypt, ++ const std::string& alias, ++ const std::string& input, ++ std::string* output); ++ ++ bool IsStringInWhitelist(const std::string& str); ++ ++ void RandBytes(uint8_t *bytes, size_t length); ++ ++ void* memcpy(void* destination, const void* source, size_t count); ++}; ++ ++ ++ ++ ++#endif //MYAPPLICATION_ENCODE_H +diff --git a/libsecret/ohos-secret-store.cpp b/libsecret/ohos-secret-store.cpp +new file mode 100644 +index 0000000..15ba03c +--- /dev/null ++++ b/libsecret/ohos-secret-store.cpp +@@ -0,0 +1,243 @@ ++#include "ohos-secret-store.h" ++#include "ohos-huks-crypto.h" ++ ++#include ++ ++ ++static const int is_debug = true; ++static const std::string COLUMN_VALUE = "_VALUE"; ++static const int COLUMN_VALUE_INDEX = 0; ++ ++const std::string HUKS_CRYPTO_ALIAS = "chrome_huks_os_crypt_password_v1"; ++ ++ ++static std::string replace_all(std::string &str, std::string oldStr, std::string newStr){ ++ std::string::size_type pos = str.find(oldStr); ++ while(pos != std::string::npos){ ++ str.replace(pos, oldStr.size(), newStr); ++ pos = str.find(oldStr); ++ } ++ return str; ++} ++ ++static std::string make_table_name(const SecretSchema *schema) { ++ std::string table_name(schema->name); ++ table_name = replace_all(table_name, ".", "_"); ++ ++ return "t_" + table_name; ++} ++ ++static std::string make_field_name(const std::string &field) { ++ std::string new_field = field; ++ new_field = replace_all(new_field, ":", "_"); ++ return "_" + new_field; ++} ++ ++static std::string make_create_table_sql(const SecretSchema *schema) { ++ // 建表初始化 ++ std::vector columns; ++ for (int i = 0; i < G_N_ELEMENTS (schema->attributes); i++) { ++ if (!schema->attributes[i].name) { ++ break; ++ } ++ if (!g_strcmp0("NULL", schema->attributes[i].name)) { ++ continue; ++ } ++ columns.push_back(schema->attributes[i].name); ++ } ++ ++ std::string create_table_sql = "CREATE TABLE IF NOT EXISTS " + make_table_name(schema) + ++ " ( " + COLUMN_VALUE + " TEXT NOT NULL, _collection TEXT"; ++ for (auto it = columns.begin();it != columns.end(); it++) { ++ std::string one_column = ", " + make_field_name(*it) + " TEXT"; ++ create_table_sql += one_column; ++ } ++ if (is_debug) { ++ OH_LOG_INFO(LOG_APP, "secret-store create_table debug_msg: %{public}s", create_table_sql.c_str()); ++ } ++ return create_table_sql + ")"; ++} ++ ++// 封装function进c风格调用 ++class FunctionUnion{ ++public: ++ FunctionUnion(std::function f, GError **e):func(f), error(e) {} ++ ++ static void call_functional(gpointer node_key, gpointer node_value, gpointer user_data) { ++ FunctionUnion *obj = (FunctionUnion *)user_data; ++ obj->func(node_key, node_value, obj->error); ++ } ++ ++private: ++ std::function func; ++ GError **error; ++}; ++ ++static std::shared_ptr new_filter(const std::string &table_name, GHashTable *attributes, GError **error) { ++ OH_Predicates *predicates_obj = OH_Rdb_CreatePredicates(table_name.c_str()); ++ g_set_error_return_val_if_fail(predicates_obj, nullptr, "ohos_rdb:OH_Rdb_CreatePredicates call error"); ++ auto predicates = std::shared_ptr(predicates_obj, [](OH_Predicates *obj) { obj->destroy(obj); }); ++ ++ std::string debug_msg; ++ // 过滤条件拼接 ++ std::function for_each_func = [&predicates, &debug_msg](gpointer node_key, gpointer node_value, gpointer user_data) { ++ if (!g_strcmp0("NULL", (const char*)node_key)) { ++ return ; ++ } ++ GError **error = (GError **)user_data; ++ OH_VObject *value_obj = OH_Rdb_CreateValueObject(); ++ g_set_error_return_if_fail(value_obj, "ohos_rdb:OH_Rdb_CreateValueObject call error"); ++ auto vobject = std::shared_ptr(value_obj, [](OH_VObject *obj) { obj->destroy(obj); }); ++ ++ debug_msg += "|" + std::string((const char*)node_key) + " " + std::string((const char*)node_value); ++ int errcode = vobject->putText(vobject.get(), (const char*)node_value); ++ g_set_error_return_if_fail(errcode == RDB_OK_CODE, "ohos_rdb:OH_Rdb_CreateValueObject call erro"); ++ predicates->equalTo(predicates.get(), make_field_name((const char*)node_key).c_str(), vobject.get()); ++ }; ++ if (is_debug) { ++ OH_LOG_INFO(LOG_APP, "secret-store filter debug_msg: %{public}s", debug_msg.c_str()); ++ } ++ ++ FunctionUnion function_union(for_each_func, error); ++ g_hash_table_foreach(attributes, FunctionUnion::call_functional, &function_union); ++ g_return_val_if_fail(*error == NULL, nullptr); ++ ++ return predicates; ++} ++ ++static std::shared_ptr new_insert_bucket(GHashTable *attributes, GError **error) { ++ OH_VBucket *bucket_obj = OH_Rdb_CreateValuesBucket(); ++ g_set_error_return_val_if_fail(bucket_obj, nullptr, "ohos_rdb:OH_Rdb_CreateValuesBucket call error"); ++ auto bucket = std::shared_ptr(bucket_obj, [](OH_VBucket *obj) { obj->destroy(obj); }); ++ ++ std::function for_each_func = [&bucket](gpointer node_key, gpointer node_value, gpointer user_data) { ++ if (!g_strcmp0("NULL", (const char*)node_key)) { ++ return ; ++ } ++ GError **error = (GError **)user_data; ++ int errcode = bucket->putText(bucket.get(), make_field_name((const char*)node_key).c_str(), (const char*)node_value); ++ g_set_error_return_if_fail(errcode == RDB_OK_CODE, "ohos_rdb:bucket:putText call error"); ++ }; ++ FunctionUnion function_union(for_each_func, error); ++ g_hash_table_foreach(attributes, FunctionUnion::call_functional, &function_union); ++ g_return_val_if_fail(*error == NULL, nullptr); ++ ++ return bucket; ++} ++ ++static void rdb_row_get_text(OH_Cursor *cursor, int index, std::function callback, GError **error) { ++ int err_code = 0; ++ size_t valuelen = 0; ++ err_code = cursor->getSize(cursor, index, &valuelen); ++ g_set_error_return_if_fail(err_code == RDB_OK_CODE, "ohos_rdb:cursor->getSize call error"); ++ ++ // 数值长度为0的情况回调 ++ if (valuelen == 0) { ++ callback("", 0); ++ return ; ++ } ++ ++ uint8_t *value = (uint8_t *)new char[valuelen + 1]; ++ err_code = cursor->getBlob(cursor, index, value, valuelen + 1); ++ g_set_error_return_if_fail(err_code == RDB_OK_CODE, "ohos_rdb:cursor->getText call error"); ++ ++ // 正常数据回调 ++ callback((const char*)value, (size_t)valuelen); ++ delete[] value; ++} ++ ++std::unique_ptr RdbKvStore::default_config(const SecretSchema *schema) { ++ std::unique_ptr config = std::make_unique(); ++ // 该路径为应用沙箱路径 ++ config->dataBaseDir = "/data/storage/el2/base/files/libsecret-v1/"; ++ config->storeName = schema->name; ++ config->bundleName = nullptr; ++ config->moduleName = nullptr; ++ // 数据库是否加密 ++ config->isEncrypt = false; ++ config->securityLevel = OH_Rdb_SecurityLevel::S1; ++ config->selfSize = sizeof(OH_Rdb_Config); ++ ++ return config; ++} ++ ++void RdbKvStore::put_record(const SecretSchema *schema, GHashTable *attributes, const char *password, GError **error) { ++ ensure_rdb_exist(schema, error); ++ g_return_if_fail(*error == NULL); ++ ++ auto bucket = new_insert_bucket(attributes, error); ++ g_return_if_fail(*error == NULL && bucket != nullptr); ++ ++ OhosHuksCrypto &crypto = OhosHuksCrypto::GetInstance(); ++ std::string encrypassword; ++ bool crypto_success = crypto.EncryptKey(HUKS_CRYPTO_ALIAS, password, &encrypassword); ++ g_set_error_return_if_fail(crypto_success == true, "ohos_crypto:EncryptKey call error"); ++ ++ int errcode = bucket->putBlob(bucket.get(), COLUMN_VALUE.c_str(), (const uint8_t *)encrypassword.c_str(), ++ (uint32_t)encrypassword.length()); ++ g_set_error_return_if_fail(errcode == RDB_OK_CODE, "ohos_rdb:bucket:putText call error"); ++ ++ int rowId = OH_Rdb_Insert(this->store_.get(), make_table_name(schema).c_str(), bucket.get()); ++ g_set_error_return_if_fail(rowId != RDB_ERROR_CODE, "ohos_rdb:OH_Rdb_Insert call error"); ++} ++ ++std::string RdbKvStore::get_record(const SecretSchema *schema, GHashTable *attributes, GError **error) { ++ ensure_rdb_exist(schema, error); ++ g_return_val_if_fail(*error == NULL, ""); ++ ++ auto predicates = new_filter(make_table_name(schema), attributes, error); ++ g_return_val_if_fail(*error == NULL && predicates != nullptr, ""); ++ ++ const char *columnNames[] = {COLUMN_VALUE.c_str()}; ++ int len = sizeof(columnNames) / sizeof(columnNames[0]); ++ OH_Cursor *cursor_obj = OH_Rdb_Query(this->store_.get(), predicates.get(), columnNames, len); ++ g_set_error_return_val_if_fail(cursor_obj, "", "ohos_rdb:OH_Rdb_Query call error"); ++ auto cursor = std::shared_ptr(cursor_obj, [](OH_Cursor *obj) { obj->destroy(obj); }); ++ ++ // 结果为空,返回,否则取第一个 ++ int err_code = cursor->goToNextRow(cursor.get()); ++ g_return_val_if_fail(err_code == OH_Rdb_ErrCode::RDB_OK, ""); ++ ++ std::string value; ++ rdb_row_get_text(cursor.get(), COLUMN_VALUE_INDEX, [&value](const char *data, size_t len) { value = std::string(data, len); }, error); ++ g_set_error_return_val_if_fail(*error == NULL, "", "ohos_rdb:rdb_row_get_text call error"); ++ ++ std::string ans; ++ OhosHuksCrypto &crypto = OhosHuksCrypto::GetInstance(); ++ bool crypto_success = crypto.DecryptKey(HUKS_CRYPTO_ALIAS, value, &ans); ++ g_set_error_return_val_if_fail(crypto_success == true, "", "ohos_crypto:DecryptKey call error"); ++ ++ return ans; ++} ++ ++bool RdbKvStore::remove(const SecretSchema *schema, GHashTable *attributes, GError **error) { ++ ensure_rdb_exist(schema, error); ++ g_return_val_if_fail(*error == NULL, false); ++ ++ auto predicates = new_filter(make_table_name(schema), attributes, error); ++ g_return_val_if_fail(*error == NULL && predicates != nullptr, false); ++ ++ int deleteRows = OH_Rdb_Delete(this->store_.get(), predicates.get()); ++ g_set_error_return_val_if_fail(deleteRows >= 0, false, "ohos_rdb:OH_Rdb_Delete call error"); ++ ++ return deleteRows > 0; ++} ++ ++std::shared_ptr RdbKvStore::ensure_rdb_exist(const SecretSchema *schema, GError **error) { ++ if (this->store_ != nullptr) { ++ return this->store_; ++ } ++ ++ int err_code = 0; ++ OH_Rdb_Store *store_obj = OH_Rdb_GetOrOpen(config_.get(), &err_code); ++ g_set_error_return_val_if_fail (err_code == RDB_OK_CODE && store_obj, NULL, "ohos_rdb:OH_Rdb_GetOrOpen call error"); ++ ++ // store_生命周期随类释放 ++ this->store_ = std::shared_ptr(store_obj, [](OH_Rdb_Store *del_store){ ++ OH_Rdb_CloseStore(del_store); ++ }); ++ err_code = OH_Rdb_Execute(this->store_.get(), make_create_table_sql(schema).c_str()); ++ g_set_error_return_val_if_fail (err_code == RDB_OK_CODE, NULL, "ohos_rdb:OH_Rdb_Execute call create table error"); ++ ++ return this->store_; ++} +diff --git a/libsecret/ohos-secret-store.h b/libsecret/ohos-secret-store.h +new file mode 100644 +index 0000000..06e3d93 +--- /dev/null ++++ b/libsecret/ohos-secret-store.h +@@ -0,0 +1,100 @@ ++#if !defined (__OHOS__) ++#error "Only Use By OHOS Platform!!!" ++#endif ++ ++#ifndef SECRET_STORE_H ++#define SECRET_STORE_H ++ ++#define __SECRET_INSIDE_HEADER__ ++ ++#include "secret-schema.h" ++#include "secret-value.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "glib.h" ++ ++#include ++#include ++#include ++#include ++ ++ ++#define g_set_error_return_val_if_fail(expr, val, log_msg) \ ++ G_STMT_START { \ ++ if (G_LIKELY(expr)) { \ ++ } else { \ ++ g_set_error(error, SECRET_ERROR, RDB_ERROR, log_msg); \ ++ g_return_if_fail_warning(G_LOG_DOMAIN, G_STRFUNC, #expr); \ ++ return (val); \ ++ } \ ++ } \ ++ G_STMT_END ++ ++#define g_set_error_return_if_fail(expr, log_msg) \ ++ G_STMT_START { \ ++ if (G_LIKELY(expr)) { \ ++ } else { \ ++ g_set_error(error, SECRET_ERROR, RDB_ERROR, log_msg); \ ++ g_return_if_fail_warning(G_LOG_DOMAIN, G_STRFUNC, #expr); \ ++ return ; \ ++ } \ ++ } \ ++ G_STMT_END ++ ++#define g_return_val_if_fail(expr, val) \ ++ G_STMT_START { \ ++ if (G_LIKELY(expr)) { \ ++ } else { \ ++ return (val); \ ++ } \ ++ } \ ++ G_STMT_END ++ ++#define g_return_if_fail(expr) \ ++ G_STMT_START { \ ++ if (G_LIKELY(expr)) { \ ++ } else { \ ++ return ; \ ++ } \ ++ } \ ++ G_STMT_END ++ ++// todo: 临时 ++const int RDB_ERROR = 1; ++ ++static const int RDB_OK_CODE = 0; ++static const int RDB_ERROR_CODE = -1; ++ ++class RdbKvStore{ ++ ++public: ++ RdbKvStore(std::unique_ptr config):config_(std::move(config)) { } ++ ++ ~RdbKvStore() { ++ store_ = nullptr; ++ } ++ ++ static std::unique_ptr default_config(const SecretSchema *schema); ++ ++ void put_record(const SecretSchema *schema, GHashTable *attributes, const char *password, GError **error); ++ ++ std::string get_record(const SecretSchema *schema, GHashTable *attributes, GError **error); ++ ++ bool remove(const SecretSchema *schema, GHashTable *attributes, GError **error); ++ ++private: ++ ++ std::shared_ptr ensure_rdb_exist(const SecretSchema *schema, GError **error); ++ ++ std::shared_ptr store_ = nullptr; ++ std::unique_ptr config_ = nullptr; ++}; ++ ++ ++#endif +diff --git a/libsecret/secret-password.cpp b/libsecret/secret-password.cpp +new file mode 100644 +index 0000000..d7366d2 +--- /dev/null ++++ b/libsecret/secret-password.cpp +@@ -0,0 +1,1688 @@ ++/* libsecret - GLib wrapper for Secret Service ++ * ++ * Copyright 2011 Collabora Ltd. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License as published ++ * by the Free Software Foundation; either version 2.1 of the licence or (at ++ * your option) any later version. ++ * ++ * See the included COPYING file for more information. ++ * ++ * Author: Stef Walter ++ */ ++ ++#define __SECRET_INSIDE_HEADER__ ++ ++#include "config.h" ++ ++#include "secret-attributes.h" ++#include "secret-password.h" ++#include "secret-private.h" ++#include "secret-retrievable.h" ++#include "secret-backend.h" ++#include "secret-value.h" ++ ++#include "ohos-secret-store.h" ++ ++#include ++ ++/** ++ * secret_password_store: (skip) ++ * @schema: the schema for attributes ++ * @collection: (nullable): a collection alias, or D-Bus object path of the ++ * collection where to store the secret ++ * @label: label for the secret ++ * @password: the null-terminated password to store ++ * @cancellable: (nullable): optional cancellation object ++ * @callback: called when the operation completes ++ * @user_data: data to be passed to the callback ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Store a password in the secret service. ++ * ++ * The variable argument list should contain pairs of a) The attribute name as ++ * a null-terminated string, followed by b) attribute value, either a character ++ * string, an int number, or a gboolean value, as defined in the @schema. ++ * The list of attributes should be terminated with a %NULL. ++ * ++ * If the attributes match a secret item already stored in the collection, then ++ * the item will be updated with these new values. ++ * ++ * If @collection is %NULL, then the default collection will be ++ * used. Use [const@COLLECTION_SESSION] to store the password in the session ++ * collection, which doesn't get stored across login sessions. ++ * ++ * This method will return immediately and complete asynchronously. ++ */ ++void ++secret_password_store (const SecretSchema *schema, ++ const gchar *collection, ++ const gchar *label, ++ const gchar *password, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data, ++ ...) ++{ ++ GHashTable *attributes; ++ va_list va; ++ ++ g_return_if_fail (schema != NULL); ++ g_return_if_fail (label != NULL); ++ g_return_if_fail (password != NULL); ++ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); ++ ++ va_start (va, user_data); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return; ++ ++ secret_password_storev (schema, attributes, collection, label, password, ++ cancellable, callback, user_data); ++ ++ g_hash_table_unref (attributes); ++} ++ ++typedef struct { ++ const SecretSchema *schema; ++ GHashTable *attributes; ++ gchar *collection; ++ gchar *label; ++ SecretValue *value; ++} StoreClosure; ++ ++static void ++store_closure_free (gpointer data) ++{ ++ StoreClosure *store = (StoreClosure *)data; ++ _secret_schema_unref_if_nonstatic (store->schema); ++ g_hash_table_unref (store->attributes); ++ g_free (store->collection); ++ g_free (store->label); ++ secret_value_unref (store->value); ++ g_free (store); ++} ++ ++static void ++on_store (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GTask *task = G_TASK (user_data); ++ SecretBackend *backend = SECRET_BACKEND (source); ++ SecretBackendInterface *iface; ++ GError *error = NULL; ++ ++ iface = SECRET_BACKEND_GET_IFACE (backend); ++ g_return_if_fail (iface->store_finish != NULL); ++ ++ if (!iface->store_finish (backend, result, &error)) { ++ g_task_return_error (task, error); ++ g_object_unref (task); ++ return; ++ } ++ ++ g_task_return_boolean (task, TRUE); ++ g_object_unref (task); ++} ++ ++static void ++on_store_backend (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GTask *task = G_TASK (user_data); ++ StoreClosure *store = (StoreClosure *)g_task_get_task_data (task); ++ SecretBackend *backend; ++ SecretBackendInterface *iface; ++ GError *error = NULL; ++ ++ backend = secret_backend_get_finish (result, &error); ++ if (backend == NULL) { ++ g_task_return_error (task, error); ++ g_object_unref (task); ++ return; ++ } ++ ++ iface = SECRET_BACKEND_GET_IFACE (backend); ++ g_return_if_fail (iface->store != NULL); ++ ++ iface->store (backend, store->schema, store->attributes, ++ store->collection, store->label, store->value, ++ g_task_get_cancellable (task), ++ on_store, ++ task); ++} ++ ++/** ++ * secret_password_storev: (rename-to secret_password_store) ++ * @schema: (nullable): the schema for attributes ++ * @attributes: (element-type utf8 utf8) (transfer full): the attribute keys and values ++ * @collection: (nullable): a collection alias, or D-Bus object path of the ++ * collection where to store the secret ++ * @label: label for the secret ++ * @password: the null-terminated password to store ++ * @cancellable: (nullable): optional cancellation object ++ * @callback: (scope async): called when the operation completes ++ * @user_data: data to be passed to the callback ++ * ++ * Store a password in the secret service. ++ * ++ * The @attributes should be a set of key and value string pairs. ++ * ++ * If the attributes match a secret item already stored in the collection, then ++ * the item will be updated with these new values. ++ * ++ * If @collection is %NULL, then the default collection will be ++ * used. Use [const@COLLECTION_SESSION] to store the password in the session ++ * collection, which doesn't get stored across login sessions. ++ * ++ * This method will return immediately and complete asynchronously. ++ */ ++void ++secret_password_storev (const SecretSchema *schema, ++ GHashTable *attributes, ++ const gchar *collection, ++ const gchar *label, ++ const gchar *password, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ StoreClosure *store; ++ GTask *task; ++ ++ g_return_if_fail (label != NULL); ++ g_return_if_fail (password != NULL); ++ g_return_if_fail (attributes != NULL); ++ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); ++ ++ /* Warnings raised already */ ++ if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE)) ++ return; ++ ++ task = g_task_new (NULL, cancellable, callback, user_data); ++ store = g_new0 (StoreClosure, 1); ++ store->schema = _secret_schema_ref_if_nonstatic (schema); ++ store->attributes = g_hash_table_ref (attributes); ++ store->collection = g_strdup (collection); ++ store->label = g_strdup (label); ++ store->value = secret_value_new (password, -1, "text/plain"); ++ g_task_set_task_data (task, store, store_closure_free); ++ ++ secret_backend_get (SECRET_BACKEND_OPEN_SESSION, ++ cancellable, ++ on_store_backend, task); ++} ++ ++/** ++ * secret_password_store_binary: (skip) ++ * @schema: the schema for attributes ++ * @collection: (nullable): a collection alias, or D-Bus object path of the ++ * collection where to store the secret ++ * @label: label for the secret ++ * @value: a [struct@Value] ++ * @cancellable: (nullable): optional cancellation object ++ * @callback: called when the operation completes ++ * @user_data: data to be passed to the callback ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Store a password in the secret service. ++ * ++ * This is similar to [func@password_store], but takes a ++ * [struct@Value] as the argument instead of a null-terminated password. ++ * ++ * This method will return immediately and complete asynchronously. ++ * ++ * Since: 0.19.0 ++ */ ++void ++secret_password_store_binary (const SecretSchema *schema, ++ const gchar *collection, ++ const gchar *label, ++ SecretValue *value, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data, ++ ...) ++{ ++ GHashTable *attributes; ++ va_list va; ++ ++ g_return_if_fail (schema != NULL); ++ g_return_if_fail (label != NULL); ++ g_return_if_fail (value != NULL); ++ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); ++ ++ va_start (va, user_data); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return; ++ ++ secret_password_storev_binary (schema, attributes, collection, label, value, ++ cancellable, callback, user_data); ++ ++ g_hash_table_unref (attributes); ++} ++ ++/** ++ * secret_password_storev_binary: (rename-to secret_password_store_binary) ++ * @schema: (nullable): the schema for attributes ++ * @attributes: (element-type utf8 utf8) (transfer full): the attribute keys and values ++ * @collection: (nullable): a collection alias, or D-Bus object path of the ++ * collection where to store the secret ++ * @label: label for the secret ++ * @value: a [struct@Value] ++ * @cancellable: (nullable): optional cancellation object ++ * @callback: (scope async): called when the operation completes ++ * @user_data: data to be passed to the callback ++ * ++ * Store a password in the secret service. ++ * ++ * This is similar to [func@password_storev], but takes a ++ * [struct@Value] as the argument instead of a null-terminated password. ++ * ++ * This method will return immediately and complete asynchronously. ++ * ++ * Since: 0.19.0 ++ */ ++void ++secret_password_storev_binary (const SecretSchema *schema, ++ GHashTable *attributes, ++ const gchar *collection, ++ const gchar *label, ++ SecretValue *value, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ StoreClosure *store; ++ GTask *task; ++ ++ g_return_if_fail (label != NULL); ++ g_return_if_fail (value != NULL); ++ g_return_if_fail (attributes != NULL); ++ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); ++ ++ /* Warnings raised already */ ++ if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE)) ++ return; ++ ++ task = g_task_new (NULL, cancellable, callback, user_data); ++ store = g_new0 (StoreClosure, 1); ++ store->schema = _secret_schema_ref_if_nonstatic (schema); ++ store->attributes = g_hash_table_ref (attributes); ++ store->collection = g_strdup (collection); ++ store->label = g_strdup (label); ++ store->value = secret_value_ref (value); ++ g_task_set_task_data (task, store, store_closure_free); ++ ++ secret_backend_get (SECRET_BACKEND_OPEN_SESSION, ++ cancellable, ++ on_store_backend, task); ++} ++ ++/** ++ * secret_password_store_finish: ++ * @result: the asynchronous result passed to the callback ++ * @error: location to place an error on failure ++ * ++ * Finish asynchronous operation to store a password in the secret service. ++ * ++ * Returns: whether the storage was successful or not ++ */ ++gboolean ++secret_password_store_finish (GAsyncResult *result, ++ GError **error) ++{ ++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ++ g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE); ++ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++/** ++ * secret_password_store_sync: ++ * @schema: the schema for attributes ++ * @collection: (nullable): a collection alias, or D-Bus object path of the ++ * collection where to store the secret ++ * @label: label for the secret ++ * @password: the null-terminated password to store ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Store a password in the secret service. ++ * ++ * The variable argument list should contain pairs of a) The attribute name as ++ * a null-terminated string, followed by b) attribute value, either a character ++ * string, an int number, or a gboolean value, as defined in the @schema. ++ * The list of attributes should be terminated with a %NULL. ++ * ++ * If the attributes match a secret item already stored in the collection, then ++ * the item will be updated with these new values. ++ * ++ * If @collection is %NULL, then the default collection will be ++ * used. Use [const@COLLECTION_SESSION] to store the password in the session ++ * collection, which doesn't get stored across login sessions. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: whether the storage was successful or not ++ */ ++gboolean ++secret_password_store_sync (const SecretSchema *schema, ++ const gchar *collection, ++ const gchar *label, ++ const gchar *password, ++ GCancellable *cancellable, ++ GError **error, ++ ...) ++{ ++ GHashTable *attributes; ++ va_list va; ++ ++ g_return_val_if_fail (schema != NULL, FALSE); ++ g_return_val_if_fail (label != NULL, FALSE); ++ g_return_val_if_fail (password != NULL, FALSE); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); ++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ++ ++ va_start (va, error); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return FALSE; ++ ++ gboolean ret = secret_password_storev_sync (schema, attributes, collection, ++ label, password, cancellable, error); ++ ++ g_hash_table_unref (attributes); ++ return ret; ++} ++ ++/** ++ * secret_password_storev_sync: (rename-to secret_password_store_sync) ++ * @schema: (nullable): the schema for attributes ++ * @attributes: (element-type utf8 utf8): the attribute keys and values ++ * @collection: (nullable): a collection alias, or D-Bus object path of the ++ * collection where to store the secret ++ * @label: label for the secret ++ * @password: the null-terminated password to store ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * ++ * Store a password in the secret service. ++ * ++ * The @attributes should be a set of key and value string pairs. ++ * ++ * If the attributes match a secret item already stored in the collection, then ++ * the item will be updated with these new values. ++ * ++ * If @collection is %NULL, then the default collection will be ++ * used. Use [const@COLLECTION_SESSION] to store the password in the session ++ * collection, which doesn't get stored across login sessions. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: whether the storage was successful or not ++ */ ++gboolean ++secret_password_storev_sync (const SecretSchema *schema, ++ GHashTable *attributes, ++ const gchar *collection, ++ const gchar *label, ++ const gchar *password, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_return_val_if_fail (label != NULL, FALSE); ++ g_return_val_if_fail (password != NULL, FALSE); ++ g_return_val_if_fail (attributes != NULL, FALSE); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); ++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ++ ++ /* Warnings raised already */ ++ if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE)) ++ return FALSE; ++ ++ if (collection) { ++- g_hash_table_insert (attributes, g_strdup ("collection"), g_strdup (collection)); ++ } ++ ++ RdbKvStore store(RdbKvStore::default_config(schema)); ++ store.put_record(schema, attributes, password, error); ++ ++ return *error == NULL; ++} ++ ++/** ++ * secret_password_store_binary_sync: ++ * @schema: the schema for attributes ++ * @collection: (nullable): a collection alias, or D-Bus object path of the ++ * collection where to store the secret ++ * @label: label for the secret ++ * @value: a [struct@Value] ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Store a password in the secret service. ++ * ++ * This is similar to [func@password_store_sync], but takes a ++ * [struct@Value] as the argument instead of a null terminated password. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: whether the storage was successful or not ++ * ++ * Since: 0.19.0 ++ */ ++gboolean ++secret_password_store_binary_sync (const SecretSchema *schema, ++ const gchar *collection, ++ const gchar *label, ++ SecretValue *value, ++ GCancellable *cancellable, ++ GError **error, ++ ...) ++{ ++ GHashTable *attributes; ++ va_list va; ++ gboolean ret; ++ ++ g_return_val_if_fail (schema != NULL, FALSE); ++ g_return_val_if_fail (label != NULL, FALSE); ++ g_return_val_if_fail (value != NULL, FALSE); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); ++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ++ ++ va_start (va, error); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return FALSE; ++ ++ ret = secret_password_storev_binary_sync (schema, attributes, collection, ++ label, value, cancellable, error); ++ ++ g_hash_table_unref (attributes); ++ return ret; ++} ++ ++/** ++ * secret_password_storev_binary_sync: (rename-to secret_password_store_binary_sync) ++ * @schema: (nullable): the schema for attributes ++ * @attributes: (element-type utf8 utf8): the attribute keys and values ++ * @collection: (nullable): a collection alias, or D-Bus object path of the ++ * collection where to store the secret ++ * @label: label for the secret ++ * @value: a [struct@Value] ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * ++ * Store a password in the secret service. ++ * ++ * This is similar to [func@password_storev_sync], but takes a [struct@Value] as ++ * the argument instead of a null-terminated passwords. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: whether the storage was successful or not ++ * ++ * Since: 0.19.0 ++ */ ++gboolean ++secret_password_storev_binary_sync (const SecretSchema *schema, ++ GHashTable *attributes, ++ const gchar *collection, ++ const gchar *label, ++ SecretValue *value, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_return_val_if_fail (label != NULL, FALSE); ++ g_return_val_if_fail (value != NULL, FALSE); ++ g_return_val_if_fail (attributes != NULL, FALSE); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); ++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ++ ++ /* Warnings raised already */ ++ if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, FALSE)) ++ return FALSE; ++ ++ if (collection) { ++- g_hash_table_insert (attributes, g_strdup ("collection"), g_strdup (collection)); ++ } ++ ++ const gchar *password = secret_value_get(value, NULL); ++ RdbKvStore store(RdbKvStore::default_config(schema)); ++ store.put_record(schema, attributes, password, error); ++ ++ return *error == NULL; ++} ++ ++/** ++ * secret_password_lookup: (skip) ++ * @schema: the schema for the attributes ++ * @cancellable: (nullable): optional cancellation object ++ * @callback: called when the operation completes ++ * @user_data: data to be passed to the callback ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Lookup a password in the secret service. ++ * ++ * The variable argument list should contain pairs of a) The attribute name as ++ * a null-terminated string, followed by b) attribute value, either a character ++ * string, an int number, or a gboolean value, as defined in the password ++ * @schema. The list of attributes should be terminated with a %NULL. ++ * ++ * If no secret is found then %NULL is returned. ++ * ++ * This method will return immediately and complete asynchronously. ++ */ ++void ++secret_password_lookup (const SecretSchema *schema, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data, ++ ...) ++{ ++ GHashTable *attributes; ++ va_list va; ++ ++ g_return_if_fail (schema != NULL); ++ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); ++ ++ va_start (va, user_data); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return; ++ ++ secret_password_lookupv (schema, attributes, cancellable, ++ callback, user_data); ++ ++ g_hash_table_unref (attributes); ++} ++ ++typedef struct { ++ const SecretSchema *schema; ++ GHashTable *attributes; ++} LookupClosure; ++ ++static void ++lookup_closure_free (gpointer data) ++{ ++ LookupClosure *closure = (LookupClosure *)data; ++ _secret_schema_unref_if_nonstatic (closure->schema); ++ g_hash_table_unref (closure->attributes); ++ g_free (closure); ++} ++ ++static void ++on_lookup (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GTask *task = G_TASK (user_data); ++ SecretBackend *backend = SECRET_BACKEND (source); ++ SecretBackendInterface *iface; ++ SecretValue *value; ++ GError *error = NULL; ++ ++ iface = SECRET_BACKEND_GET_IFACE (backend); ++ g_return_if_fail (iface->store_finish != NULL); ++ ++ value = iface->lookup_finish (backend, result, &error); ++ if (error) { ++ g_task_return_error (task, error); ++ g_object_unref (task); ++ return; ++ } ++ ++ if (value) ++ g_task_return_pointer (task, value, secret_value_unref); ++ else ++ g_task_return_pointer (task, NULL, NULL); ++ g_object_unref (task); ++} ++ ++static void ++on_lookup_backend (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GTask *task = G_TASK (user_data); ++ LookupClosure *lookup = (LookupClosure *)g_task_get_task_data (task); ++ SecretBackend *backend; ++ SecretBackendInterface *iface; ++ GError *error = NULL; ++ ++ backend = secret_backend_get_finish (result, &error); ++ if (backend == NULL) { ++ g_task_return_error (task, error); ++ g_object_unref (task); ++ return; ++ } ++ ++ iface = SECRET_BACKEND_GET_IFACE (backend); ++ g_return_if_fail (iface->store != NULL); ++ ++ iface->lookup (backend, lookup->schema, lookup->attributes, ++ g_task_get_cancellable (task), ++ on_lookup, ++ task); ++} ++ ++/** ++ * secret_password_lookupv: (rename-to secret_password_lookup) ++ * @schema: (nullable): the schema for attributes ++ * @attributes: (element-type utf8 utf8) (transfer full): the attribute keys and values ++ * @cancellable: (nullable): optional cancellation object ++ * @callback: (scope async): called when the operation completes ++ * @user_data: data to be passed to the callback ++ * ++ * Lookup a password in the secret service. ++ * ++ * The @attributes should be a set of key and value string pairs. ++ * ++ * If no secret is found then %NULL is returned. ++ * ++ * This method will return immediately and complete asynchronously. ++ */ ++void ++secret_password_lookupv (const SecretSchema *schema, ++ GHashTable *attributes, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ LookupClosure *lookup; ++ GTask *task; ++ ++ g_return_if_fail (attributes != NULL); ++ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); ++ ++ /* Warnings raised already */ ++ if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE)) ++ return; ++ ++ task = g_task_new (NULL, cancellable, callback, user_data); ++ lookup = g_new0 (LookupClosure, 1); ++ lookup->schema = _secret_schema_ref_if_nonstatic (schema); ++ lookup->attributes = g_hash_table_ref (attributes); ++ g_task_set_task_data (task, lookup, lookup_closure_free); ++ ++ secret_backend_get (SECRET_BACKEND_OPEN_SESSION, ++ cancellable, ++ on_lookup_backend, task); ++} ++ ++/** ++ * secret_password_lookup_nonpageable_finish: (skip) ++ * @result: the asynchronous result passed to the callback ++ * @error: location to place an error on failure ++ * ++ * Finish an asynchronous operation to lookup a password in the secret service. ++ * ++ * Returns: (transfer full): a new password string stored in nonpageable memory ++ * which must be freed with [func@password_free] when done ++ */ ++gchar * ++secret_password_lookup_nonpageable_finish (GAsyncResult *result, ++ GError **error) ++{ ++ SecretValue *value; ++ ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ g_return_val_if_fail (g_task_is_valid (result, NULL), NULL); ++ ++ value = (SecretValue *)g_task_propagate_pointer (G_TASK (result), error); ++ if (value == NULL) ++ return NULL; ++ ++ return _secret_value_unref_to_password (value); ++} ++ ++/** ++ * secret_password_lookup_binary_finish: (skip) ++ * @result: the asynchronous result passed to the callback ++ * @error: location to place an error on failure ++ * ++ * Finish an asynchronous operation to lookup a password in the secret service. ++ * ++ * Returns: (transfer full): a newly allocated [struct@Value], which should be ++ * released with [method@Value.unref], or %NULL if no secret found ++ * ++ * Since: 0.19.0 ++ */ ++SecretValue * ++secret_password_lookup_binary_finish (GAsyncResult *result, ++ GError **error) ++{ ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ g_return_val_if_fail (g_task_is_valid (result, NULL), NULL); ++ ++ return (SecretValue *)g_task_propagate_pointer (G_TASK (result), error); ++} ++ ++/** ++ * secret_password_lookup_finish: ++ * @result: the asynchronous result passed to the callback ++ * @error: location to place an error on failure ++ * ++ * Finish an asynchronous operation to lookup a password in the secret service. ++ * ++ * Returns: (transfer full): a new password string which should be freed with ++ * [func@password_free] or may be freed with [func@GLib.free] when done ++ */ ++gchar * ++secret_password_lookup_finish (GAsyncResult *result, ++ GError **error) ++{ ++ SecretValue *value; ++ ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ g_return_val_if_fail (g_task_is_valid (result, NULL), NULL); ++ ++ value = (SecretValue *)g_task_propagate_pointer (G_TASK (result), error); ++ if (value == NULL) ++ return NULL; ++ ++ return _secret_value_unref_to_string (value); ++} ++ ++/** ++ * secret_password_lookup_sync: (skip) ++ * @schema: the schema for the attributes ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Lookup a password in the secret service. ++ * ++ * The variable argument list should contain pairs of a) The attribute name as ++ * a null-terminated string, followed by b) attribute value, either a character ++ * string, an int number, or a gboolean value, as defined in the password ++ * @schema. The list of attributes should be terminated with a %NULL. ++ * ++ * If no secret is found then %NULL is returned. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: (transfer full): a new password string which should be freed with ++ * [func@password_free] or may be freed with [func@GLib.free] when done ++ */ ++gchar * ++secret_password_lookup_sync (const SecretSchema *schema, ++ GCancellable *cancellable, ++ GError **error, ++ ...) ++{ ++ GHashTable *attributes; ++ gchar *password; ++ va_list va; ++ ++ g_return_val_if_fail (schema != NULL, NULL); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ ++ va_start (va, error); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return NULL; ++ ++ password = secret_password_lookupv_nonpageable_sync(schema, attributes, cancellable, error); ++ ++ g_hash_table_unref (attributes); ++ return password; ++} ++ ++/** ++ * secret_password_lookup_nonpageable_sync: (skip) ++ * @schema: the schema for the attributes ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Lookup a password in the secret service. ++ * ++ * The variable argument list should contain pairs of a) The attribute name as ++ * a null-terminated string, followed by b) attribute value, either a character ++ * string, an int number, or a gboolean value, as defined in the password ++ * @schema. The list of attributes should be terminated with a %NULL. ++ * ++ * If no secret is found then %NULL is returned. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: (transfer full): a new password string stored in nonpageable memory ++ * which must be freed with [func@password_free] when done ++ */ ++gchar * ++secret_password_lookup_nonpageable_sync (const SecretSchema *schema, ++ GCancellable *cancellable, ++ GError **error, ++ ...) ++{ ++ GHashTable *attributes; ++ gchar *password; ++ va_list va; ++ ++ g_return_val_if_fail (schema != NULL, NULL); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ ++ va_start (va, error); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return NULL; ++ ++ password = secret_password_lookupv_nonpageable_sync(schema, attributes, cancellable, error); ++ ++ g_hash_table_unref (attributes); ++ ++ return password; ++} ++ ++/** ++ * secret_password_lookupv_nonpageable_sync: (skip) ++ * @schema: (nullable): the schema for attributes ++ * @attributes: (element-type utf8 utf8): the attribute keys and values ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * ++ * Lookup a password in the secret service. ++ * ++ * The @attributes should be a set of key and value string pairs. ++ * ++ * If no secret is found then %NULL is returned. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: (transfer full): a new password string stored in non pageable memory ++ * which should be freed with [func@password_free] when done ++ */ ++gchar * ++secret_password_lookupv_nonpageable_sync (const SecretSchema *schema, ++ GHashTable *attributes, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ gchar *password; ++ ++ g_return_val_if_fail (attributes != NULL, NULL); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ ++ RdbKvStore store(RdbKvStore::default_config(schema)); ++ std::string pwd = store.get_record(schema, attributes, error); ++ g_return_val_if_fail(*error == NULL && pwd.length() > 0, NULL); ++ ++ password = g_strdup(pwd.c_str()); ++ std::fill(pwd.begin(), pwd.end(), '\0'); ++ pwd.clear(); ++ ++ return password; ++} ++ ++/** ++ * secret_password_lookup_binary_sync: (skip) ++ * @schema: the schema for the attributes ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Lookup a password in the secret service. ++ * ++ * This is similar to [func@password_lookup_sync], but returns a ++ * [struct@Value] instead of a null-terminated password. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: (transfer full): a newly allocated [struct@Value], which should be ++ * released with [method@Value.unref], or %NULL if no secret found ++ * ++ * Since: 0.19.0 ++ */ ++SecretValue * ++secret_password_lookup_binary_sync (const SecretSchema *schema, ++ GCancellable *cancellable, ++ GError **error, ++ ...) ++{ ++ GHashTable *attributes; ++ SecretValue *value; ++ va_list va; ++ ++ g_return_val_if_fail (schema != NULL, NULL); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ ++ va_start (va, error); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return NULL; ++ ++ value = secret_password_lookupv_binary_sync(schema, attributes, cancellable, error); ++ ++ g_hash_table_unref (attributes); ++ ++ return value; ++} ++ ++/** ++ * secret_password_lookupv_binary_sync: (skip) ++ * @schema: (nullable): the schema for attributes ++ * @attributes: (element-type utf8 utf8): the attribute keys and values ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * ++ * Lookup a password in the secret service. ++ * ++ * This is similar to [func@password_lookupv_sync], but returns a ++ * [struct@Value] instead of a null-terminated password. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: (transfer full): a newly allocated [struct@Value], which should be ++ * released with [method@Value.unref], or %NULL if no secret found ++ * ++ * Since: 0.19.0 ++ */ ++SecretValue * ++secret_password_lookupv_binary_sync (const SecretSchema *schema, ++ GHashTable *attributes, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ SecretValue *value; ++ ++ g_return_val_if_fail (attributes != NULL, NULL); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ ++ /* Warnings raised already */ ++ if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE)) ++ return FALSE; ++ ++ gchar *password = secret_password_lookupv_nonpageable_sync(schema, attributes, cancellable, error); ++ if (password == NULL) { ++ return NULL; ++ } ++ ++ value = secret_value_new (password, -1, "text/plain"); ++ ++ return value; ++} ++ ++/** ++ * secret_password_lookupv_sync: (rename-to secret_password_lookup_sync) ++ * @schema: (nullable): the schema for attributes ++ * @attributes: (element-type utf8 utf8): the attribute keys and values ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * ++ * Lookup a password in the secret service. ++ * ++ * The @attributes should be a set of key and value string pairs. ++ * ++ * If no secret is found then %NULL is returned. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: (transfer full): a new password string which should be freed with ++ * [func@password_free] or may be freed with [func@GLib.free] when done ++ */ ++gchar * ++secret_password_lookupv_sync (const SecretSchema *schema, ++ GHashTable *attributes, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_return_val_if_fail (attributes != NULL, NULL); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ ++ /* Warnings raised already */ ++ if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE)) ++ return FALSE; ++ ++ return secret_password_lookupv_nonpageable_sync(schema, attributes, cancellable, error); ++} ++ ++/** ++ * secret_password_clear: ++ * @schema: the schema for the attributes ++ * @cancellable: (nullable): optional cancellation object ++ * @callback: called when the operation completes ++ * @user_data: data to be passed to the callback ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Clear unlocked matching passwords from the secret service. ++ * ++ * The variable argument list should contain pairs of a) The attribute name as ++ * a null-terminated string, followed by b) attribute value, either a character ++ * string, an int number, or a gboolean value, as defined in the password ++ * @schema. The list of attributes should be terminated with a %NULL. ++ * ++ * All unlocked items that match the attributes will be deleted. ++ * ++ * This method will return immediately and complete asynchronously. ++ */ ++void ++secret_password_clear (const SecretSchema *schema, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data, ++ ...) ++{ ++ GHashTable *attributes; ++ va_list va; ++ ++ g_return_if_fail (schema != NULL); ++ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); ++ ++ va_start (va, user_data); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return; ++ ++ secret_password_clearv (schema, attributes, cancellable, ++ callback, user_data); ++ ++ g_hash_table_unref (attributes); ++} ++ ++typedef struct { ++ const SecretSchema *schema; ++ GHashTable *attributes; ++} ClearClosure; ++ ++static void ++clear_closure_free (gpointer data) ++{ ++ ClearClosure *closure = (ClearClosure *)data; ++ _secret_schema_unref_if_nonstatic (closure->schema); ++ g_hash_table_unref (closure->attributes); ++ g_free (closure); ++} ++ ++static void ++on_clear (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GTask *task = G_TASK (user_data); ++ SecretBackend *backend = SECRET_BACKEND (source); ++ SecretBackendInterface *iface; ++ GError *error = NULL; ++ ++ iface = SECRET_BACKEND_GET_IFACE (backend); ++ g_return_if_fail (iface->clear_finish != NULL); ++ ++ if (!iface->clear_finish (backend, result, &error)) { ++ if (error) ++ g_task_return_error (task, error); ++ else ++ g_task_return_boolean (task, FALSE); ++ g_object_unref (task); ++ return; ++ } ++ ++ g_task_return_boolean (task, TRUE); ++ g_object_unref (task); ++} ++ ++static void ++on_clear_backend (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GTask *task = G_TASK (user_data); ++ ClearClosure *clear = (ClearClosure *)g_task_get_task_data (task); ++ SecretBackend *backend; ++ SecretBackendInterface *iface; ++ GError *error = NULL; ++ ++ backend = secret_backend_get_finish (result, &error); ++ if (backend == NULL) { ++ g_task_return_error (task, error); ++ g_object_unref (task); ++ return; ++ } ++ ++ iface = SECRET_BACKEND_GET_IFACE (backend); ++ g_return_if_fail (iface->clear != NULL); ++ ++ iface->clear (backend, clear->schema, clear->attributes, ++ g_task_get_cancellable (task), ++ on_clear, ++ task); ++} ++ ++/** ++ * secret_password_clearv: (rename-to secret_password_clear) ++ * @schema: (nullable): the schema for the attributes ++ * @attributes: (element-type utf8 utf8) (transfer full): the attribute keys and values ++ * @cancellable: (nullable): optional cancellation object ++ * @callback: (scope async): called when the operation completes ++ * @user_data: data to be passed to the callback ++ * ++ * Remove unlocked matching passwords from the secret service. ++ * ++ * The @attributes should be a set of key and value string pairs. ++ * ++ * All unlocked items that match the attributes will be deleted. ++ * ++ * This method will return immediately and complete asynchronously. ++ */ ++void ++secret_password_clearv (const SecretSchema *schema, ++ GHashTable *attributes, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ ClearClosure *clear; ++ GTask *task; ++ ++ g_return_if_fail (attributes != NULL); ++ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); ++ ++ /* Warnings raised already */ ++ if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE)) ++ return; ++ ++ task = g_task_new (NULL, cancellable, callback, user_data); ++ clear = g_new0 (ClearClosure, 1); ++ clear->schema = _secret_schema_ref_if_nonstatic (schema); ++ clear->attributes = g_hash_table_ref (attributes); ++ g_task_set_task_data (task, clear, clear_closure_free); ++ ++ secret_backend_get ((SecretBackendFlags)SECRET_SERVICE_NONE, ++ cancellable, ++ on_clear_backend, task); ++} ++ ++/** ++ * secret_password_clear_finish: ++ * @result: the asynchronous result passed to the callback ++ * @error: location to place an error on failure ++ * ++ * Finish an asynchronous operation to remove passwords from the secret ++ * service. ++ * ++ * Returns: whether any passwords were removed ++ */ ++gboolean ++secret_password_clear_finish (GAsyncResult *result, ++ GError **error) ++{ ++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ++ g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE); ++ ++ return g_task_propagate_boolean (G_TASK (result), error); ++} ++ ++/** ++ * secret_password_clear_sync: ++ * @schema: the schema for the attributes ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Remove unlocked matching passwords from the secret service. ++ * ++ * The variable argument list should contain pairs of a) The attribute name as ++ * a null-terminated string, followed by b) attribute value, either a character ++ * string, an int number, or a gboolean value, as defined in the password ++ * @schema. The list of attributes should be terminated with a %NULL. ++ * ++ * All unlocked items that match the attributes will be deleted. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: whether the any passwords were removed ++ */ ++gboolean ++secret_password_clear_sync (const SecretSchema* schema, ++ GCancellable *cancellable, ++ GError **error, ++ ...) ++{ ++ GHashTable *attributes; ++ va_list va; ++ ++ g_return_val_if_fail (schema != NULL, FALSE); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); ++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ++ ++ va_start (va, error); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ RdbKvStore store(RdbKvStore::default_config(schema)); ++ bool success = store.remove(schema, attributes, error); ++ if (*error != NULL) { ++ g_hash_table_unref (attributes); ++ return FALSE; ++ } ++ ++ g_hash_table_unref (attributes); ++ ++ return success; ++} ++ ++/** ++ * secret_password_clearv_sync: (rename-to secret_password_clear_sync) ++ * @schema: (nullable): the schema for the attributes ++ * @attributes: (element-type utf8 utf8): the attribute keys and values ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * ++ * Remove unlocked matching passwords from the secret service. ++ * ++ * The @attributes should be a set of key and value string pairs. ++ * ++ * All unlocked items that match the attributes will be deleted. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: whether any passwords were removed ++ */ ++gboolean ++secret_password_clearv_sync (const SecretSchema *schema, ++ GHashTable *attributes, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ g_return_val_if_fail (attributes != NULL, FALSE); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); ++ g_return_val_if_fail (error == NULL || *error == NULL, FALSE); ++ ++ RdbKvStore store(RdbKvStore::default_config(schema)); ++ store.remove(schema, attributes, error); ++ if (*error != NULL) { ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++/** ++ * secret_password_search: (skip) ++ * @schema: the schema for the attributes ++ * @flags: search option flags ++ * @cancellable: (nullable): optional cancellation object ++ * @callback: called when the operation completes ++ * @user_data: data to be passed to the callback ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Search for items in the secret service. ++ * ++ * The variable argument list should contain pairs of a) The attribute name as ++ * a null-terminated string, followed by b) attribute value, either a character ++ * string, an int number, or a gboolean value, as defined in the password ++ * @schema. The list of attributes should be terminated with a %NULL. ++ * ++ * This method will return immediately and complete asynchronously. ++ * ++ * Since: 0.19.0 ++ */ ++void ++secret_password_search (const SecretSchema *schema, ++ SecretSearchFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data, ++ ...) ++{ ++ GHashTable *attributes; ++ va_list va; ++ ++ g_return_if_fail (schema != NULL); ++ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); ++ ++ va_start (va, user_data); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return; ++ ++ secret_password_searchv (schema, attributes, flags, cancellable, ++ callback, user_data); ++ ++ g_hash_table_unref (attributes); ++} ++ ++typedef struct { ++ const SecretSchema *schema; ++ GHashTable *attributes; ++ SecretSearchFlags flags; ++} SearchClosure; ++ ++static void ++search_closure_free (gpointer data) ++{ ++ SearchClosure *closure = (SearchClosure *)data; ++ _secret_schema_unref_if_nonstatic (closure->schema); ++ g_hash_table_unref (closure->attributes); ++ g_free (closure); ++} ++ ++static void ++object_list_free (gpointer data) ++{ ++ GList *list = (GList *)data; ++ g_list_free_full (list, g_object_unref); ++} ++ ++static void ++on_search (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GTask *task = G_TASK (user_data); ++ SecretBackend *backend = SECRET_BACKEND (source); ++ SecretBackendInterface *iface; ++ GError *error = NULL; ++ GList *items; ++ ++ iface = SECRET_BACKEND_GET_IFACE (backend); ++ g_return_if_fail (iface->search_finish != NULL); ++ ++ items = iface->search_finish (backend, result, &error); ++ if (error) { ++ g_task_return_error (task, error); ++ g_object_unref (task); ++ return; ++ } ++ ++ g_task_return_pointer (task, items, object_list_free); ++ g_object_unref (task); ++} ++ ++static void ++on_search_backend (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GTask *task = G_TASK (user_data); ++ SearchClosure *search = (SearchClosure *)g_task_get_task_data (task); ++ SecretBackend *backend; ++ SecretBackendInterface *iface; ++ GError *error = NULL; ++ ++ backend = secret_backend_get_finish (result, &error); ++ if (backend == NULL) { ++ g_task_return_error (task, error); ++ g_object_unref (task); ++ return; ++ } ++ ++ iface = SECRET_BACKEND_GET_IFACE (backend); ++ g_return_if_fail (iface->search != NULL); ++ ++ iface->search (backend, ++ search->schema, search->attributes, search->flags, ++ g_task_get_cancellable (task), ++ on_search, ++ task); ++} ++ ++/** ++ * secret_password_searchv: (rename-to secret_password_search) ++ * @schema: (nullable): the schema for attributes ++ * @attributes: (element-type utf8 utf8) (transfer full): the attribute keys and values ++ * @flags: search option flags ++ * @cancellable: (nullable): optional cancellation object ++ * @callback: (scope async): called when the operation completes ++ * @user_data: data to be passed to the callback ++ * ++ * Search for items in the secret service. ++ * ++ * The @attributes should be a set of key and value string pairs. ++ * ++ * This method will return immediately and complete asynchronously. ++ * ++ * Since: 0.19.0 ++ */ ++void ++secret_password_searchv (const SecretSchema *schema, ++ GHashTable *attributes, ++ SecretSearchFlags flags, ++ GCancellable *cancellable, ++ GAsyncReadyCallback callback, ++ gpointer user_data) ++{ ++ SearchClosure *search; ++ GTask *task; ++ ++ g_return_if_fail (attributes != NULL); ++ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); ++ ++ /* Warnings raised already */ ++ if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE)) ++ return; ++ ++ task = g_task_new (NULL, cancellable, callback, user_data); ++ search = g_new0 (SearchClosure, 1); ++ search->schema = _secret_schema_ref_if_nonstatic (schema); ++ search->attributes = g_hash_table_ref (attributes); ++ search->flags = flags; ++ g_task_set_task_data (task, search, search_closure_free); ++ ++ secret_backend_get ((SecretBackendFlags)SECRET_SERVICE_NONE, ++ cancellable, ++ on_search_backend, task); ++} ++ ++/** ++ * secret_password_search_finish: ++ * @result: the asynchronous result passed to the callback ++ * @error: location to place an error on failure ++ * ++ * Finish an asynchronous operation to search for items in the secret service. ++ * ++ * Returns: (transfer full) (element-type Secret.Retrievable): a list of ++ * [iface@Retrievable] containing attributes of the matched items ++ * ++ * Since: 0.19.0 ++ */ ++GList * ++secret_password_search_finish (GAsyncResult *result, ++ GError **error) ++{ ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ g_return_val_if_fail (g_task_is_valid (result, NULL), NULL); ++ ++ return (GList *)g_task_propagate_pointer (G_TASK (result), error); ++} ++ ++/** ++ * secret_password_search_sync: (skip) ++ * @schema: the schema for the attributes ++ * @flags: search option flags ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * @...: the attribute keys and values, terminated with %NULL ++ * ++ * Search for items in the secret service. ++ * ++ * The variable argument list should contain pairs of a) The attribute name as ++ * a null-terminated string, followed by b) attribute value, either a character ++ * string, an int number, or a gboolean value, as defined in the password ++ * @schema. The list of attributes should be terminated with a %NULL. ++ * ++ * If no secret is found then %NULL is returned. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: (transfer full) (element-type Secret.Retrievable): a list of ++ * [iface@Retrievable] containing attributes of the matched items ++ * ++ * Since: 0.19.0 ++ */ ++GList * ++secret_password_search_sync (const SecretSchema *schema, ++ SecretSearchFlags flags, ++ GCancellable *cancellable, ++ GError **error, ++ ...) ++{ ++ GHashTable *attributes; ++ GList *items; ++ va_list va; ++ ++ g_return_val_if_fail (schema != NULL, NULL); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ ++ va_start (va, error); ++ attributes = secret_attributes_buildv (schema, va); ++ va_end (va); ++ ++ /* Precondition failed, already warned */ ++ if (!attributes) ++ return NULL; ++ ++ items = secret_password_searchv_sync (schema, attributes, flags, ++ cancellable, error); ++ ++ g_hash_table_unref (attributes); ++ ++ return items; ++} ++ ++/** ++ * secret_password_searchv_sync: (rename-to secret_password_search_sync) ++ * @schema: (nullable): the schema for attributes ++ * @attributes: (element-type utf8 utf8): the attribute keys and values ++ * @flags: search option flags ++ * @cancellable: (nullable): optional cancellation object ++ * @error: location to place an error on failure ++ * ++ * Search for items in the secret service. ++ * ++ * The @attributes should be a set of key and value string pairs. ++ * ++ * If no secret is found then %NULL is returned. ++ * ++ * This method may block indefinitely and should not be used in user interface ++ * threads. ++ * ++ * Returns: (transfer full) (element-type Secret.Retrievable): a list of ++ * [iface@Retrievable] containing attributes of the matched items ++ * ++ * Since: 0.19.0 ++ */ ++GList * ++secret_password_searchv_sync (const SecretSchema *schema, ++ GHashTable *attributes, ++ SecretSearchFlags flags, ++ GCancellable *cancellable, ++ GError **error) ++{ ++ SecretSync *sync; ++ GList *items; ++ ++ g_return_val_if_fail (attributes != NULL, NULL); ++ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); ++ g_return_val_if_fail (error == NULL || *error == NULL, NULL); ++ ++ /* Warnings raised already */ ++ if (schema != NULL && !_secret_attributes_validate (schema, attributes, G_STRFUNC, TRUE)) ++ return NULL; ++ ++ sync = _secret_sync_new (); ++ g_main_context_push_thread_default (sync->context); ++ ++ secret_password_searchv (schema, attributes, flags, cancellable, ++ _secret_sync_on_result, sync); ++ ++ g_main_loop_run (sync->loop); ++ ++ items = secret_password_search_finish (sync->result, error); ++ ++ g_main_context_pop_thread_default (sync->context); ++ _secret_sync_free (sync); ++ ++ return items; ++} ++ ++/** ++ * secret_password_free: (skip) ++ * @password: (nullable): password to free ++ * ++ * Clear the memory used by a password, and then free it. ++ * ++ * This function must be used to free nonpageable memory returned by ++ * [func@password_lookup_nonpageable_finish], ++ * [func@password_lookup_nonpageable_sync] or ++ * [func@password_lookupv_nonpageable_sync]. ++ */ ++void ++secret_password_free (gchar *password) ++{ ++ if (password == NULL) ++ return; ++ ++ egg_secure_strfree (password); ++} ++ ++/** ++ * secret_password_wipe: ++ * @password: (nullable): password to clear ++ * ++ * Clear the memory used by a password. ++ */ ++void ++secret_password_wipe (gchar *password) ++{ ++ if (password == NULL) ++ return; ++ ++ egg_secure_strclear (password); ++} +diff --git a/libsecret/test-ohos-password.c b/libsecret/test-ohos-password.c +new file mode 100644 +index 0000000..3fc0358 +--- /dev/null ++++ b/libsecret/test-ohos-password.c +@@ -0,0 +1,596 @@ ++/* libsecret - GLib wrapper for Secret Service ++ * ++ * Copyright 2012 Red Hat Inc. ++ * ++ * This program is free software: you can redistribute it and/or modify ++ * it under the terms of the GNU Lesser General Public License as published ++ * by the Free Software Foundation; either version 2 of the licence or (at ++ * your option) any later version. ++ * ++ * See the included COPYING file for more information. ++ * ++ * Author: Stef Walter ++ */ ++ ++#include "config.h" ++ ++#undef G_DISABLE_ASSERT ++ ++#include "secret-password.h" ++#include "secret-paths.h" ++#include "secret-private.h" ++ ++#include "mock-service.h" ++ ++#include "egg/egg-testing.h" ++ ++#include ++ ++#include ++#include ++ ++static const SecretSchema MOCK_SCHEMA = { ++ "org.mock.Schema", ++ SECRET_SCHEMA_NONE, ++ { ++ { "number", SECRET_SCHEMA_ATTRIBUTE_INTEGER }, ++ { "string", SECRET_SCHEMA_ATTRIBUTE_STRING }, ++ { "even", SECRET_SCHEMA_ATTRIBUTE_BOOLEAN }, ++ } ++}; ++ ++static const SecretSchema NO_NAME_SCHEMA = { ++ "unused.Schema.Name", ++ SECRET_SCHEMA_DONT_MATCH_NAME, ++ { ++ { "number", SECRET_SCHEMA_ATTRIBUTE_INTEGER }, ++ { "string", SECRET_SCHEMA_ATTRIBUTE_STRING }, ++ } ++}; ++ ++typedef struct { ++ GPid pid; ++} Test; ++ ++static void ++setup (Test *test, ++ gconstpointer data) ++{ ++ gboolean ret; ++ char *collection_path = NULL; ++ GError *error = NULL; ++ ++ collection_path = "/org/freedesktop/secrets/english"; ++ ret = secret_password_store_sync (&MOCK_SCHEMA, collection_path, ++ "Label here", "111", NULL, &error, ++ "number", 1 , ++ "string", "one", ++ "even", FALSE, ++ NULL); ++ g_assert_no_error (error); ++ ++ collection_path = "/org/freedesktop/secrets/english"; ++ ret = secret_password_store_sync (&MOCK_SCHEMA, collection_path, ++ "Label here", "222", NULL, &error, ++ "number", 2, ++ "string", "two", ++ "even", TRUE, ++ "xdg:schema", "org.mock.Schema", ++ NULL); ++ g_assert_no_error (error); ++ ++ collection_path = "/org/freedesktop/secrets/english"; ++ ret = secret_password_store_sync (&MOCK_SCHEMA, collection_path, ++ "Label here", "333", NULL, &error, ++ "number", 3, ++ "string", "three", ++ "even", FALSE, ++ "xdg:schema", "org.mock.Schema", ++ NULL); ++ g_assert_no_error (error); ++ ++ collection_path = "/org/freedesktop/secrets/spanish"; ++ ret = secret_password_store_sync (&MOCK_SCHEMA, collection_path, ++ "Label here", "111", NULL, &error, ++ "number", 1, ++ "string", "uno", ++ "even", FALSE, ++ "xdg:schema", "org.mock.Schema", ++ NULL); ++ g_assert_no_error (error); ++ ++ collection_path = "/org/freedesktop/secrets/spanish"; ++ ret = secret_password_store_sync (&MOCK_SCHEMA, collection_path, ++ "Label here", "222", NULL, &error, ++ "number", 2, ++ "string", "dos", ++ "even", TRUE, ++ "xdg:schema", "org.mock.Schema", ++ NULL); ++ g_assert_no_error (error); ++ ++ collection_path = "/org/freedesktop/secrets/spanish"; ++ ret = secret_password_store_sync (&MOCK_SCHEMA, collection_path, ++ "Label here", "333", NULL, &error, ++ "number", 3, ++ "string", "three", ++ "even", FALSE, ++ "xdg:schema", "org.mock.Schema", ++ NULL); ++ g_assert_no_error (error); ++} ++ ++static void ++teardown (Test *test, ++ gconstpointer unused) ++{ ++ ++} ++ ++static void ++on_complete_get_result (GObject *source, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ GAsyncResult **ret = user_data; ++ g_assert_nonnull (ret); ++ g_assert_null (*ret); ++ *ret = g_object_ref (result); ++ egg_test_wait_stop (); ++} ++ ++static void ++test_lookup_sync (Test *test, ++ gconstpointer used) ++{ ++ gchar *password; ++ GError *error = NULL; ++ ++ password = secret_password_lookup_nonpageable_sync (&MOCK_SCHEMA, NULL, &error, ++ "even", FALSE, ++ "string", "one", ++ "number", 1, ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_cmpstr (password, ==, "111"); ++ ++ secret_password_free (password); ++} ++ ++static void ++test_lookup_async (Test *test, ++ gconstpointer used) ++{ ++ GAsyncResult *result = NULL; ++ GError *error = NULL; ++ gchar *password; ++ ++ secret_password_lookup (&MOCK_SCHEMA, NULL, on_complete_get_result, &result, ++ "even", FALSE, ++ "string", "one", ++ "number", 1, ++ NULL); ++ g_assert_null (result); ++ ++ egg_test_wait (); ++ ++ password = secret_password_lookup_nonpageable_finish (result, &error); ++ g_assert_no_error (error); ++ g_object_unref (result); ++ ++ g_assert_cmpstr (password, ==, "111"); ++ secret_password_free (password); ++} ++ ++static void ++test_lookup_no_name (Test *test, ++ gconstpointer used) ++{ ++ GError *error = NULL; ++ gchar *password; ++ ++ /* should return null, because nothing with mock schema and 3 */ ++ password = secret_password_lookup_sync (&NO_NAME_SCHEMA, NULL, &error, ++ "number", 3, ++ NULL); ++ g_assert_no_error (error); ++ g_assert_null (password); ++ ++ /* should return an item, because we have a prime schema with 3, and flags not to match name */ ++ password = secret_password_lookup_sync (&MOCK_SCHEMA, NULL, &error, ++ "number", 3, ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_cmpstr (password, ==, "333"); ++ ++ secret_password_free (password); ++} ++ ++static void ++test_store_sync (Test *test, ++ gconstpointer used) ++{ ++ const gchar *collection_path = "/org/freedesktop/secrets/collection/english"; ++ GError *error = NULL; ++ gchar *password; ++ gboolean ret; ++ ++ ret = secret_password_store_sync (&MOCK_SCHEMA, collection_path, ++ "Label here", "the password", NULL, &error, ++ "even", TRUE, ++ "string", "twelve", ++ "number", 12, ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_true (ret); ++ ++ password = secret_password_lookup_nonpageable_sync (&MOCK_SCHEMA, NULL, &error, ++ "string", "twelve", ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_cmpstr (password, ==, "the password"); ++ ++ secret_password_free (password); ++} ++ ++static void ++test_store_async (Test *test, ++ gconstpointer used) ++{ ++ const gchar *collection_path = "/org/freedesktop/secrets/collection/english"; ++ GAsyncResult *result = NULL; ++ GError *error = NULL; ++ gchar *password; ++ gboolean ret; ++ ++ secret_password_store (&MOCK_SCHEMA, collection_path, "Label here", ++ "the password", NULL, on_complete_get_result, &result, ++ "even", TRUE, ++ "string", "twelve", ++ "number", 12, ++ NULL); ++ g_assert_null (result); ++ ++ egg_test_wait (); ++ ++ ret = secret_password_store_finish (result, &error); ++ g_assert_no_error (error); ++ g_assert_true (ret); ++ g_object_unref (result); ++ ++ password = secret_password_lookup_nonpageable_sync (&MOCK_SCHEMA, NULL, &error, ++ "string", "twelve", ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_cmpstr (password, ==, "the password"); ++ ++ secret_password_free (password); ++} ++ ++static void ++test_store_unlock (Test *test, ++ gconstpointer unused) ++{ ++ const gchar *collection_path = "/org/freedesktop/secrets/collection/english"; ++ GAsyncResult *result = NULL; ++ SecretCollection *collection; ++ SecretService *service; ++ GError *error = NULL; ++ gchar *password; ++ gboolean ret; ++ GList *objects; ++ gint count; ++ ++ service = secret_service_get_sync (SECRET_SERVICE_NONE, NULL, &error); ++ g_assert_no_error (error); ++ ++ /* Check collection state */ ++ collection = secret_collection_new_for_dbus_path_sync (service, collection_path, ++ SECRET_COLLECTION_NONE, NULL, &error); ++ g_assert_no_error (error); ++ g_assert_false (secret_collection_get_locked (collection)); ++ ++ /* Lock it, use async, so collection properties update */ ++ objects = g_list_append (NULL, collection); ++ secret_service_lock (service, objects, NULL, on_complete_get_result, &result); ++ egg_test_wait (); ++ count = secret_service_lock_finish (service, result, NULL, &error); ++ g_assert_cmpint (count, ==, 1); ++ g_clear_object (&result); ++ g_list_free (objects); ++ ++ /* Check collection state */ ++ g_assert_true (secret_collection_get_locked (collection)); ++ ++ /* Store the password, use async so collection properties update */ ++ secret_password_store (&MOCK_SCHEMA, collection_path, "Label here", ++ "the password", NULL, on_complete_get_result, &result, ++ "even", TRUE, ++ "string", "twelve", ++ "number", 12, ++ NULL); ++ g_assert_null (result); ++ egg_test_wait (); ++ ret = secret_password_store_finish (result, &error); ++ g_assert_no_error (error); ++ g_assert_true (ret); ++ g_clear_object (&result); ++ ++ /* Check collection state */ ++ g_assert_false (secret_collection_get_locked (collection)); ++ ++ ++ password = secret_password_lookup_nonpageable_sync (&MOCK_SCHEMA, NULL, &error, ++ "string", "twelve", ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_cmpstr (password, ==, "the password"); ++ ++ secret_password_free (password); ++ g_object_unref (collection); ++ g_object_unref (service); ++} ++ ++static void ++test_delete_sync (Test *test, ++ gconstpointer used) ++{ ++ GError *error = NULL; ++ gboolean ret; ++ ++ ret = secret_password_clear_sync (&MOCK_SCHEMA, NULL, &error, ++ "even", FALSE, ++ "string", "one", ++ "number", 1, ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_true (ret); ++} ++ ++static void ++test_delete_async (Test *test, ++ gconstpointer used) ++{ ++ GError *error = NULL; ++ GAsyncResult *result = NULL; ++ gboolean ret; ++ ++ secret_password_clear (&MOCK_SCHEMA, NULL, ++ on_complete_get_result, &result, ++ "even", FALSE, ++ "string", "one", ++ "number", 1, ++ NULL); ++ ++ g_assert_null (result); ++ ++ egg_test_wait (); ++ ++ ret = secret_password_clear_finish (result, &error); ++ g_assert_no_error (error); ++ g_assert_true (ret); ++ ++ g_object_unref (result); ++} ++ ++static void ++test_clear_no_name (Test *test, ++ gconstpointer used) ++{ ++ const gchar *paths[] = { "/org/freedesktop/secrets/collection/german", NULL }; ++ SecretService *service; ++ GError *error = NULL; ++ gboolean ret; ++ ++ /* Shouldn't match anything, because no item with 5 in mock schema */ ++ ret = secret_password_clear_sync (&MOCK_SCHEMA, NULL, &error, ++ "number", 5, ++ NULL); ++ g_assert_no_error (error); ++ g_assert_false (ret); ++ ++ ++ ret = secret_password_clear_sync (&MOCK_SCHEMA, NULL, &error, ++ "number", 3, ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_true (ret); ++} ++ ++static void ++free_attributes (gpointer data, ++ gpointer user_data) ++{ ++ g_object_unref ((GObject *)data); ++} ++ ++static void ++test_search_sync (Test *test, ++ gconstpointer used) ++{ ++ GList *items; ++ GError *error = NULL; ++ ++ items = secret_password_search_sync (&MOCK_SCHEMA, SECRET_SEARCH_ALL, ++ NULL, &error, ++ "even", FALSE, ++ "string", "one", ++ "number", 1, ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_cmpint (g_list_length (items), ==, 1); ++ ++ g_list_foreach (items, free_attributes, NULL); ++ g_list_free (items); ++} ++ ++static void ++test_search_async (Test *test, ++ gconstpointer used) ++{ ++ GAsyncResult *result = NULL; ++ GError *error = NULL; ++ GList *items; ++ ++ secret_password_search (&MOCK_SCHEMA, SECRET_SEARCH_ALL, ++ NULL, on_complete_get_result, &result, ++ "even", FALSE, ++ "string", "one", ++ "number", 1, ++ NULL); ++ g_assert (result == NULL); ++ ++ egg_test_wait (); ++ ++ items = secret_password_search_finish (result, &error); ++ g_assert_no_error (error); ++ g_object_unref (result); ++ ++ g_assert_cmpint (g_list_length (items), ==, 1); ++ ++ g_list_foreach (items, free_attributes, NULL); ++ g_list_free (items); ++} ++ ++static void ++test_search_no_name (Test *test, ++ gconstpointer used) ++{ ++ GError *error = NULL; ++ GList *items; ++ ++ /* should return null, because nothing with mock schema and 5 */ ++ items = secret_password_search_sync (&MOCK_SCHEMA, SECRET_SEARCH_ALL, ++ NULL, &error, ++ "number", 5, ++ NULL); ++ g_assert_no_error (error); ++ g_assert (items == NULL); ++ ++ /* should return an item, because we have a prime schema with 5, and flags not to match name */ ++ items = secret_password_search_sync (&NO_NAME_SCHEMA, SECRET_SEARCH_ALL, ++ NULL, &error, ++ "number", 5, ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_cmpint (g_list_length (items), ==, 1); ++ ++ g_list_foreach (items, free_attributes, NULL); ++ g_list_free (items); ++} ++ ++static void ++test_binary_sync (Test *test, ++ gconstpointer used) ++{ ++ const gchar *collection_path = "/org/freedesktop/secrets/collection/english"; ++ GError *error = NULL; ++ SecretValue *value; ++ gboolean ret; ++ ++ value = secret_value_new ("the password", -1, "text/plain"); ++ ret = secret_password_store_binary_sync (&MOCK_SCHEMA, collection_path, ++ "Label here", value, NULL, &error, ++ "even", TRUE, ++ "string", "twelve", ++ "number", 12, ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_true (ret); ++ secret_value_unref (value); ++ ++ value = secret_password_lookup_binary_sync (&MOCK_SCHEMA, NULL, &error, ++ "string", "twelve", ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_cmpstr (secret_value_get_text (value), ==, "the password"); ++ ++ secret_value_unref (value); ++} ++ ++static void ++test_binary_async (Test *test, ++ gconstpointer used) ++{ ++ const gchar *collection_path = "/org/freedesktop/secrets/collection/english"; ++ GAsyncResult *result = NULL; ++ GError *error = NULL; ++ SecretValue *value; ++ gboolean ret; ++ ++ value = secret_value_new ("the password", -1, "text/plain"); ++ secret_password_store_binary (&MOCK_SCHEMA, collection_path, "Label here", ++ value, NULL, on_complete_get_result, &result, ++ "even", TRUE, ++ "string", "twelve", ++ "number", 12, ++ NULL); ++ g_assert_null (result); ++ secret_value_unref (value); ++ ++ egg_test_wait (); ++ ++ ret = secret_password_store_finish (result, &error); ++ g_assert_no_error (error); ++ g_assert_true (ret); ++ g_object_unref (result); ++ ++ value = secret_password_lookup_binary_sync (&MOCK_SCHEMA, NULL, &error, ++ "string", "twelve", ++ NULL); ++ ++ g_assert_no_error (error); ++ g_assert_nonnull (value); ++ ++ g_assert_cmpstr (secret_value_get_text (value), ==, "the password"); ++ ++ secret_value_unref (value); ++} ++ ++static void ++test_password_free_null (void) ++{ ++ secret_password_free (NULL); ++} ++ ++int ++main (int argc, char **argv) ++{ ++ g_test_init (&argc, &argv, NULL); ++ g_set_prgname ("test-password"); ++ ++ g_test_add ("/password/lookup-sync", Test, "mock-service-normal.py", setup, test_lookup_sync, teardown); ++ // g_test_add ("/password/lookup-async", Test, "mock-service-normal.py", setup, test_lookup_async, teardown); ++ g_test_add ("/password/lookup-no-name", Test, "mock-service-normal.py", setup, test_lookup_no_name, teardown); ++ ++ g_test_add ("/password/store-sync", Test, "mock-service-normal.py", setup, test_store_sync, teardown); ++ // g_test_add ("/password/store-async", Test, "mock-service-normal.py", setup, test_store_async, teardown); ++ // g_test_add ("/password/store-unlock", Test, "mock-service-normal.py", setup, test_store_unlock, teardown); ++ ++ g_test_add ("/password/delete-sync", Test, "mock-service-delete.py", setup, test_delete_sync, teardown); ++ // g_test_add ("/password/delete-async", Test, "mock-service-delete.py", setup, test_delete_async, teardown); ++ g_test_add ("/password/clear-no-name", Test, "mock-service-delete.py", setup, test_clear_no_name, teardown); ++ ++ // g_test_add ("/password/search-sync", Test, "mock-service-normal.py", setup, test_search_sync, teardown); ++ // g_test_add ("/password/search-async", Test, "mock-service-normal.py", setup, test_search_async, teardown); ++ // g_test_add ("/password/search-no-name", Test, "mock-service-normal.py", setup, test_search_no_name, teardown); ++ ++ g_test_add ("/password/binary-sync", Test, "mock-service-normal.py", setup, test_binary_sync, teardown); ++ // g_test_add ("/password/binary-async", Test, "mock-service-normal.py", setup, test_binary_async, teardown); ++ ++ g_test_add_func ("/password/free-null", test_password_free_null); ++ ++ return egg_tests_run_with_loop (); ++} +diff --git a/meson.build b/meson.build +index d3ca2ce..c665559 100644 +--- a/meson.build ++++ b/meson.build +@@ -1,4 +1,4 @@ +-project('libsecret', 'c', ++project('libsecret', ['c', 'cpp'], + version: '0.21.3', + license: 'GPL2+', + meson_version: '>= 0.50', +diff --git a/meson_options.txt b/meson_options.txt +index 936eff8..93459c3 100644 +--- a/meson_options.txt ++++ b/meson_options.txt +@@ -8,3 +8,4 @@ option('bashcompdir', type: 'string', value: '', description: 'Override default + option('bash_completion', type: 'feature', value: 'auto', description: 'Install bash completion files') + option('tpm2', type: 'boolean', value: false, description: 'With TPM2 Software Stack') + option('pam', type: 'boolean', value: false, description: 'Build PAM module') ++option('is_ohos', type: 'boolean', value: false, description: 'using at ohos platform') +diff --git a/tool/secret-tool.c b/tool/secret-tool.c +index c72e403..8ff6104 100644 +--- a/tool/secret-tool.c ++++ b/tool/secret-tool.c +@@ -23,6 +23,7 @@ + + #include + ++#include + #include + #include + #include +@@ -281,12 +282,24 @@ read_password_stdin (void) + } + } + ++#define MAX_PASSWORD_LENGTH 1024 ++ + static SecretValue * + read_password_tty (void) + { + gchar *password; ++#ifdef __OHOS__ ++ static char s_password[MAX_PASSWORD_LENGTH]; ++ printf("Password: "); ++ ++ fgets(s_password, MAX_PASSWORD_LENGTH, stdin); ++ s_password[strcspn(s_password, "\n")] = '\0'; + ++ password = g_strdup(s_password); ++ memset(s_password, 0, sizeof s_password); ++#else + password = getpass ("Password: "); ++#endif + return secret_value_new_full (password, -1, "text/plain", + (GDestroyNotify)secret_password_wipe); + } +-- +2.36.1.windows.1 + diff --git a/thirdparty/libsecret/HPKBUILD b/thirdparty/libsecret/HPKBUILD new file mode 100644 index 00000000..46fb7e27 --- /dev/null +++ b/thirdparty/libsecret/HPKBUILD @@ -0,0 +1,103 @@ +# Copyright (c) 2024 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Contributor: xujian +# Maintainer: xujian + +pkgname=libsecret +pkgver=0.21.3 +pkgrel=0 +pkgdesc="GLib is a general-purpose, portable utility library, which provides many useful data types, macros, type conversions, string utilities, file utilities, a mainloop abstraction, and so on." +url="https://docs.gtk.org/glib/" +archs=("arm64-v8a") # armeabi-v7a 需要开启 -mfloat-abi=softfp 编译OK +license=("Apache 2.0" "CC0-1.0" "GPL-2.0" "LGPL-2.1" "LLVM" "old-glib" "MIT") +depends=("glib" "pcre2" "libffi") +makedepends=("meson") + +source="https://gitlab.gnome.org/GNOME/$pkgname.git" + +downloadpackage=false +autounpack=false +buildtools="meson" + +builddir=$pkgname +packagename= + +clonesrcflag=true + +prepare() { + # 源码下载 + if $clonesrcflag + then + git clone -b $pkgver $source > $publicbuildlog 2>&1 + if [ $? != 0 ] + then + return -1 + fi + # 打patch + pushd libsecret + git apply ../0001-secret-password.patch + popd + + cd $builddir + git submodule update --init --recursive >> $publicbuildlog 2>&1 + if [ $? != 0 ] + then + return -2 + fi + cd $OLDPWD + clonesrcflag=false + fi + # 依赖库加入 pkg_config_path 路径 + for depend in ${depends[@]} + do + dependpath=$LYCIUM_ROOT/usr/$depend/$ARCH/lib/pkgconfig + if [ ! -d ${dependpath} ] + then + continue + fi + pkgconfigpath=$pkgconfigpath"${dependpath}:" + done + pkgconfigpath=${pkgconfigpath%:*} + + cp $ARCH-cross-file.txt $builddir + mkdir -p $builddir/$ARCH-build +} + +build() { + cd $builddir + ohos_sdk_path=${OHOS_SDK//\//\\\/} + sed -i 's/ohos_sdk/'"$ohos_sdk_path"'/g' $ARCH-cross-file.txt + + # meson 过程会下载代码, 可能时间较久, 请保持网络通常 + PKG_CONFIG_PATH=$pkgconfigpath meson $ARCH-build --cross-file $ARCH-cross-file.txt --prefix=$LYCIUM_ROOT/usr/$pkgname/$ARCH -D is_ohos=true -D gtk_doc=false -D introspection=false > $buildlog 2>&1 + $Ninja -v -C $ARCH-build >> $buildlog 2>&1 + ret=$? + cd $OLDPWD + return $ret +} + +package() { + cd $builddir + $Ninja -v -C $ARCH-build install >> $buildlog 2>&1 + cd $OLDPWD +} + +check() { + echo "The test must be on an OpenHarmony device!" +} + +# 清理环境 +cleanbuild() { + rm -rf ${PWD}/$builddir +} diff --git a/thirdparty/libsecret/arm64-v8a-cross-file.txt b/thirdparty/libsecret/arm64-v8a-cross-file.txt new file mode 100644 index 00000000..f5842856 --- /dev/null +++ b/thirdparty/libsecret/arm64-v8a-cross-file.txt @@ -0,0 +1,26 @@ +[binaries] +c = 'ohos_sdk/native/llvm/bin/aarch64-linux-ohos-clang' +cpp = 'ohos_sdk/native/llvm/bin/aarch64-linux-ohos-clang++' +ar = 'ohos_sdk/native/llvm/bin/llvm-ar' +strip = 'ohos_sdk/native/llvm/bin/llvm-strip' +ld = 'ohos_sdk/native/llvm/bin/ld.lld' +pkgconfig = '/usr/bin/pkg-config' + +[host_machine] +system = 'linux' +cpu_family = 'aarch64' +cpu = 'arm64-v8a' +endian = 'little' + +[properties] +needs_exe_wrapper = true +skip_sanity_check = true +sys_root = '' +platform = 'generic' +pkg_config_libdir = '' + +[built-in options] +c_args = ['-fPIC', '-D__OHOS__', '-D__MUSL__=1', '-mfpu=neon'] +cpp_args = ['-fPIC', '-D__OHOS__', '-D__MUSL__=1', '-mfpu=neon'] +c_link_args = [] +cpp_link_args = ['-lnative_rdb_ndk.z', '-lhuks_ndk.z', '-lhilog_ndk.z'] diff --git a/thirdparty/libsecret/armeabi-v7a-cross-file.txt b/thirdparty/libsecret/armeabi-v7a-cross-file.txt new file mode 100644 index 00000000..567d645b --- /dev/null +++ b/thirdparty/libsecret/armeabi-v7a-cross-file.txt @@ -0,0 +1,26 @@ +[binaries] +c = 'ohos_sdk/native/llvm/bin/arm-linux-ohos-clang' +cpp = 'ohos_sdk/native/llvm/bin/arm-linux-ohos-clang++' +ar = 'ohos_sdk/native/llvm/bin/llvm-ar' +strip = 'ohos_sdk/native/llvm/bin/llvm-strip' +ld = 'ohos_sdk/native/llvm/bin/ld.lld' +pkgconfig = '/usr/bin/pkg-config' + +[host_machine] +system = 'linux' +cpu_family = 'arm' +cpu = 'armeabi-v7a' +endian = 'little' + +[properties] +needs_exe_wrapper = true +skip_sanity_check = true +sys_root = '' +platform = 'generic' +pkg_config_libdir = '' + +[built-in options] +c_args = ['-fPIC', '-D__OHOS__', '-march=armv7a', '-mfloat-abi=softfp', '-mfpu=neon', '-D__MUSL__=1'] +cpp_args = ['-fPIC', '-D__OHOS__', '-march=armv7a', '-mfloat-abi=softfp', '-mfpu=neon', '-D__MUSL__=1'] +c_link_args = [] +cpp_link_args = ['-lnative_rdb_ndk.z', '-lhuks_ndk.z', '-lhilog_ndk.z'] -- Gitee From 6e38e732775d2d9c254ed25b09b1685a54d36e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=BB=BA?= <10267290+xujian026@user.noreply.gitee.com> Date: Mon, 13 Jan 2025 17:23:11 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E7=A7=BB=E5=8A=A8libsecret=E5=88=B0communi?= =?UTF-8?q?ty=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 徐建 --- {thirdparty => community}/libsecret/0001-secret-password.patch | 0 {thirdparty => community}/libsecret/HPKBUILD | 0 {thirdparty => community}/libsecret/arm64-v8a-cross-file.txt | 0 {thirdparty => community}/libsecret/armeabi-v7a-cross-file.txt | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {thirdparty => community}/libsecret/0001-secret-password.patch (100%) rename {thirdparty => community}/libsecret/HPKBUILD (100%) rename {thirdparty => community}/libsecret/arm64-v8a-cross-file.txt (100%) rename {thirdparty => community}/libsecret/armeabi-v7a-cross-file.txt (100%) diff --git a/thirdparty/libsecret/0001-secret-password.patch b/community/libsecret/0001-secret-password.patch similarity index 100% rename from thirdparty/libsecret/0001-secret-password.patch rename to community/libsecret/0001-secret-password.patch diff --git a/thirdparty/libsecret/HPKBUILD b/community/libsecret/HPKBUILD similarity index 100% rename from thirdparty/libsecret/HPKBUILD rename to community/libsecret/HPKBUILD diff --git a/thirdparty/libsecret/arm64-v8a-cross-file.txt b/community/libsecret/arm64-v8a-cross-file.txt similarity index 100% rename from thirdparty/libsecret/arm64-v8a-cross-file.txt rename to community/libsecret/arm64-v8a-cross-file.txt diff --git a/thirdparty/libsecret/armeabi-v7a-cross-file.txt b/community/libsecret/armeabi-v7a-cross-file.txt similarity index 100% rename from thirdparty/libsecret/armeabi-v7a-cross-file.txt rename to community/libsecret/armeabi-v7a-cross-file.txt -- Gitee From 30c17188b415ec0ddc8f0061d12e52bba60b2f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=BB=BA?= Date: Fri, 17 Jan 2025 14:27:42 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 徐建 --- community/libsecret/0001-secret-password.patch | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/community/libsecret/0001-secret-password.patch b/community/libsecret/0001-secret-password.patch index 77a0790c..b3072ca7 100644 --- a/community/libsecret/0001-secret-password.patch +++ b/community/libsecret/0001-secret-password.patch @@ -2463,7 +2463,7 @@ new file mode 100644 index 0000000..3fc0358 --- /dev/null +++ b/libsecret/test-ohos-password.c -@@ -0,0 +1,596 @@ +@@ -0,0 +1,591 @@ +/* libsecret - GLib wrapper for Secret Service + * + * Copyright 2012 Red Hat Inc. @@ -2541,7 +2541,6 @@ index 0000000..3fc0358 + "number", 2, + "string", "two", + "even", TRUE, -+ "xdg:schema", "org.mock.Schema", + NULL); + g_assert_no_error (error); + @@ -2551,7 +2550,6 @@ index 0000000..3fc0358 + "number", 3, + "string", "three", + "even", FALSE, -+ "xdg:schema", "org.mock.Schema", + NULL); + g_assert_no_error (error); + @@ -2561,7 +2559,6 @@ index 0000000..3fc0358 + "number", 1, + "string", "uno", + "even", FALSE, -+ "xdg:schema", "org.mock.Schema", + NULL); + g_assert_no_error (error); + @@ -2571,7 +2568,6 @@ index 0000000..3fc0358 + "number", 2, + "string", "dos", + "even", TRUE, -+ "xdg:schema", "org.mock.Schema", + NULL); + g_assert_no_error (error); + @@ -2581,7 +2577,6 @@ index 0000000..3fc0358 + "number", 3, + "string", "three", + "even", FALSE, -+ "xdg:schema", "org.mock.Schema", + NULL); + g_assert_no_error (error); +} -- Gitee