From 88dc1c784794d4854fae5a5b707beb15942e250d Mon Sep 17 00:00:00 2001 From: Mu-Woong Lee Date: Tue, 14 Mar 2017 20:52:19 +0900 Subject: [PATCH] Implement SchemaLoader in the server Change-Id: I86e52eace38cdd0710e198712df1aa894bf68dd4 Signed-off-by: Mu-Woong Lee --- packaging/context-store.spec | 4 +- script/init.sql | 7 + src/server/SchemaLoader.cpp | 375 +++++++++++++++++++++++++++++++++++++++++++ src/server/SchemaLoader.h | 91 +++++++++++ 4 files changed, 476 insertions(+), 1 deletion(-) create mode 100644 script/init.sql create mode 100644 src/server/SchemaLoader.cpp create mode 100644 src/server/SchemaLoader.h diff --git a/packaging/context-store.spec b/packaging/context-store.spec index 573a430..5c4a488 100644 --- a/packaging/context-store.spec +++ b/packaging/context-store.spec @@ -61,7 +61,8 @@ make %{?_smp_mflags} %install %make_install - +mkdir -p %{buildroot}%{TZ_SYS_DATA}/%{name} +install -m 0644 script/init.sql %{buildroot}%{TZ_SYS_DATA}/%{name}/ %post /sbin/ldconfig @@ -89,6 +90,7 @@ popd %manifest packaging/%{name}.manifest %{_libdir}/lib%{name}-client-genuine.so* %{_libdir}/lib%{name}-server-genuine.so* +%{TZ_SYS_DATA}/%{name}/*.sql %files devel diff --git a/script/init.sql b/script/init.sql new file mode 100644 index 0000000..d0ec0c9 --- /dev/null +++ b/script/init.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS ContextStoreSchema ( + "uri" TEXT PRIMARY KEY, + "retention" INTEGER NOT NULL DEFAULT 0, + "limit" INTEGER NOT NULL DEFAULT 0, + "readPrivileges" TEXT NOT NULL, + "writePrivileges" TEXT NOT NULL +); diff --git a/src/server/SchemaLoader.cpp b/src/server/SchemaLoader.cpp new file mode 100644 index 0000000..6185762 --- /dev/null +++ b/src/server/SchemaLoader.cpp @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2017 Samsung Electronics 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. + */ + +#include +#include +#include +#include +#include +#include +#include "DatabaseManager.h" +#include "SchemaLoader.h" + +/* Schema XML example: + + + + + http://tizen.org/privilege/healthinfo + http://tizen.org/privilege/internal/service + + + + + + + */ + +#define URI_REGEX(CATEGORY) R"~(^http:\/\/[\w-]+(\.[\w-]+)*\/)~" CATEGORY R"~(\/[\w-]+(\.[\w-]+)*(\/[\w-]+(\.[\w-]+)*)*$)~" +#define COL_REGEX "^[A-Za-z]+\\w*$" + +#define CHK_NAME(NODE, NAME) (!xmlStrcmp(NODE->name, (const xmlChar*)(NAME))) + +using namespace ctx; + +static std::string __getXmlAttribute(xmlNode* node, const char* name) +{ + xmlChar* prop = xmlGetProp(node, reinterpret_cast(name)); + std::string out; + if (prop) { + out = reinterpret_cast(prop); + xmlFree(prop); + } + return out; +} + +static std::string __getXmlContent(xmlNode* node) +{ + xmlChar* content = xmlNodeGetContent(node); + _D("%s", content); + std::string out; + if (content) { + out = reinterpret_cast(content); + xmlFree(content); + } + return out; +} + +Schema::Schema() : + retention(0), + limit(0) +{ +} + +bool Schema::valid() +{ + if (retention > MAX_RETENTION || limit > MAX_LIMIT) + return false; + + if (retention == 0 && limit == 0) + return false; + + if (attributes.empty()) + return false; + + std::regex uriRegex(URI_REGEX("contextstore"), std::regex::optimize); + if (!std::regex_match(uri, uriRegex)) + return false; + + std::regex privilegeRegex(URI_REGEX("privilege"), std::regex::optimize); + for (auto& privil : readPrivileges) { + if (!std::regex_match(privil, privilegeRegex)) + return false; + } + + for (auto& privil : writePrivileges) { + if (!std::regex_match(privil, privilegeRegex)) + return false; + } + + std::regex columnNameRegex(COL_REGEX, std::regex::optimize); + for (auto& attr: attributes) { + if (!std::regex_match(attr.first, columnNameRegex)) + return false; + } + + return true; +} + +SchemaLoader::SchemaLoader() +{ +} + +SchemaLoader::~SchemaLoader() +{ +} + +bool SchemaLoader::load() +{ + bool result = true; + __getDatabase().beginTransaction(); + + if (!__createMetadataTables()) { + _E("Metadata table creation failed"); + result = false; + } + + try { + if (result && !__loadSchemaDir(__getSchemaDir())) { + _W("Schema loading failed"); + result = false; + } + } catch (std::regex_error& e) { + _E("%s", e.what()); + result = false; + } + + __getDatabase().endTransaction(); + return result; +} + +bool SchemaLoader::__createMetadataTables() +{ + std::string path = PathUtil::getSystemPath(TZ_SYS_DATA, BASE_PATH "/init.sql"); + IF_FAIL_RETURN(!path.empty(), false); + + std::ifstream inFile(path); + std::stringstream buffer; + buffer << inFile.rdbuf(); + + return __getDatabase().execute(buffer.str(), NULL); +} + +bool SchemaLoader::__loadSchemaDir(const std::string& dirPath) +{ + DIR* dir = NULL; + struct dirent entry; + struct dirent *result = NULL; + + dir = opendir(dirPath.c_str()); + IF_FAIL_RETURN_TAG(dir, false, _E, "Failed to open: %s", dirPath.c_str()); + + std::regex xmlFileRegex("^.*\\.(xml|XML)$", std::regex::optimize); + + while (true) { + if (readdir_r(dir, &entry, &result) != 0) + continue; + + if (result == NULL) + break; + + std::string filename = entry.d_name; + + if (!std::regex_match(filename, xmlFileRegex)) + continue; + + __parseSchemaFile(dirPath + "/" + filename); + } + + closedir(dir); + return true; +} + +void SchemaLoader::__parseSchemaFile(const std::string& filePath) +{ + _D("Parsing %s", filePath.c_str()); + + xmlDoc *doc = xmlReadFile(filePath.c_str(), NULL, 0); + IF_FAIL_VOID_TAG(doc, _W, "Could not parse %s", filePath.c_str()); + + xmlNode *node = xmlDocGetRootElement(doc); + + if (!node || !CHK_NAME(node, "ContextStore")) { + _W("Not a ContextStore schema XML"); + xmlFreeDoc(doc); + return; + } + + for (node = node->children; node != NULL; node = node->next) { + if (node->type != XML_ELEMENT_NODE) + continue; + + if (!CHK_NAME(node, "Schema")) { + _D("Skip '%s'", node->name); + continue; + } + + Schema schema; + if (__parseSchema(node, schema)) { + __insertMetadata(schema); + __createStoreTable(schema); + } + } + + xmlFreeDoc(doc); + return; +} + +bool SchemaLoader::__parseSchema(xmlNode* node, Schema& schema) +{ + std::string retention = __getXmlAttribute(node, "retention"); + std::string limit = __getXmlAttribute(node, "limit"); + schema.uri = __getXmlAttribute(node, "uri"); + + for (node = node->children; node != NULL; node = node->next) { + if (node->type != XML_ELEMENT_NODE) + continue; + + if (CHK_NAME(node, "Privilege")) + __parsePrivilege(node, schema); + else if (CHK_NAME(node, "Attribute")) + __parseAttribute(node, schema); + } + + if (!retention.empty()) + schema.retention = std::atoi(retention.c_str()); + + if (!limit.empty()) + schema.limit = std::atoi(limit.c_str()); + + return schema.valid(); +} + +void SchemaLoader::__parsePrivilege(xmlNode* node, Schema& schema) +{ + std::string access = __getXmlAttribute(node, "access"); + std::string privil = __getXmlContent(node); + + if (access == "Read") + schema.readPrivileges.push_back(privil); + else if (access == "Write") + schema.writePrivileges.push_back(privil); +} + +void SchemaLoader::__parseAttribute(xmlNode* node, Schema& schema) +{ + std::string name = __getXmlAttribute(node, "name"); + std::string type = __getXmlAttribute(node, "type"); + + IF_FAIL_VOID(!name.empty()); + + if (type == "Integer") + schema.attributes.push_back(std::make_pair(name, Schema::AttributeType::INTEGER)); + else if (type == "Real") + schema.attributes.push_back(std::make_pair(name, Schema::AttributeType::REAL)); + else if (type == "Text") + schema.attributes.push_back(std::make_pair(name, Schema::AttributeType::TEXT)); +} + +void SchemaLoader::__insertMetadata(Schema& schema) +{ + char* updateSql = NULL; + char* insertSql = NULL; + int length = 0; + + std::string readPrivil; + std::string writePrivil; + + for (auto& item : schema.readPrivileges) { + readPrivil.append(item); + readPrivil.append(PRIVILEGE_DELIM); + } + + for (auto& item : schema.writePrivileges) { + writePrivil.append(item); + writePrivil.append(PRIVILEGE_DELIM); + } + + length = asprintf(&updateSql, + "UPDATE ContextStoreSchema" + " SET uri='%s', retention=%u, \"limit\"=%u, readPrivileges='%s', writePrivileges='%s'" + " WHERE uri='%s'", + schema.uri.c_str(), schema.retention, schema.limit, readPrivil.c_str(), writePrivil.c_str(), + schema.uri.c_str()); + if (length <= 0) { + _E("Memory allocation failed"); + return; + } + + length = asprintf(&insertSql, + "INSERT OR IGNORE INTO ContextStoreSchema" + " (uri, retention, \"limit\", readPrivileges, writePrivileges)" + " VALUES ('%s', %u, %u, '%s', '%s')", + schema.uri.c_str(), schema.retention, schema.limit, readPrivil.c_str(), writePrivil.c_str()); + if (length <= 0) { + g_free(updateSql); + _E("Memory allocation failed"); + return; + } + + __getDatabase().execute(updateSql, NULL); + __getDatabase().execute(insertSql, NULL); + + g_free(updateSql); + g_free(insertSql); +} + +void SchemaLoader::__createStoreTable(Schema& schema) +{ + std::string attr = + COL_TIMESTAMP " DATETIME DEFAULT CURRENT_TIMESTAMP," + COL_OWNER_ID " TEXT NOT NULL DEFAULT ''"; + + for (auto& item : schema.attributes) { + attr = attr + "," + item.first; + switch (item.second) { + case Schema::AttributeType::INTEGER: + attr = attr + " INTEGER NOT NULL DEFAULT 0"; + break; + case Schema::AttributeType::REAL: + attr = attr + " REAL NOT NULL DEFAULT 0"; + break; + default: + attr = attr + " TEXT NOT NULL DEFAULT ''"; + break; + } + } + + std::string query = + "CREATE TABLE IF NOT EXISTS [" + schema.uri + "] (" + attr + ")"; + + __getDatabase().execute(query.c_str(), NULL); +} + + +SystemSchemaLoader::SystemSchemaLoader() +{ +} + +Database& SystemSchemaLoader::__getDatabase() +{ + return DatabaseManager::getSystem(); +} + +std::string SystemSchemaLoader::__getSchemaDir() +{ + return PathUtil::getSystemPath(TZ_SYS_DATA, BASE_PATH "/system"); +} + + +UserSchemaLoader::UserSchemaLoader() +{ +} + +Database& UserSchemaLoader::__getDatabase() +{ + return DatabaseManager::getUser(); +} + +std::string UserSchemaLoader::__getSchemaDir() +{ + return PathUtil::getSystemPath(TZ_SYS_DATA, BASE_PATH "/user"); +} diff --git a/src/server/SchemaLoader.h b/src/server/SchemaLoader.h new file mode 100644 index 0000000..4b08934 --- /dev/null +++ b/src/server/SchemaLoader.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 Samsung Electronics 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. + */ + +#ifndef __CONTEXT_STORE_SCHEMA_LOADER_H__ +#define __CONTEXT_STORE_SCHEMA_LOADER_H__ + +#include +#include +#include +#include +#include +#include + +namespace ctx { + + class Schema { + public: + enum class AttributeType { + UNDEFINED = 0, + INTEGER = 1, + REAL = 2, + TEXT = 3 + }; + + Schema(); + bool valid(); + + std::string uri; + unsigned int retention; + unsigned int limit; + std::vector readPrivileges; + std::vector writePrivileges; + std::vector> attributes; + }; + + class SchemaLoader { + public: + virtual ~SchemaLoader(); + + bool load(); + + private: + bool __createMetadataTables(); + bool __loadSchemaDir(const std::string& dirPath); + void __parseSchemaFile(const std::string& filePath); + bool __parseSchema(xmlNode* node, Schema& schema); + void __parsePrivilege(xmlNode* node, Schema& schema); + void __parseAttribute(xmlNode* node, Schema& schema); + void __insertMetadata(Schema& schema); + void __createStoreTable(Schema& schema); + + virtual std::string __getSchemaDir() = 0; + virtual Database& __getDatabase() = 0; + + protected: + SchemaLoader(); + }; + + class SystemSchemaLoader : public SchemaLoader { + public: + SystemSchemaLoader(); + + private: + std::string __getSchemaDir(); + Database& __getDatabase(); + }; + + class UserSchemaLoader : public SchemaLoader { + public: + UserSchemaLoader(); + + private: + std::string __getSchemaDir(); + Database& __getDatabase(); + }; +} + +#endif /* __CONTEXT_STORE_SCHEMA_LOADER_H__ */ -- 2.7.4