Implement SchemaLoader in the server 05/118905/2
authorMu-Woong Lee <muwoong.lee@samsung.com>
Tue, 14 Mar 2017 11:52:19 +0000 (20:52 +0900)
committerMu-Woong Lee <muwoong.lee@samsung.com>
Tue, 14 Mar 2017 13:09:36 +0000 (06:09 -0700)
Change-Id: I86e52eace38cdd0710e198712df1aa894bf68dd4
Signed-off-by: Mu-Woong Lee <muwoong.lee@samsung.com>
packaging/context-store.spec
script/init.sql [new file with mode: 0644]
src/server/SchemaLoader.cpp [new file with mode: 0644]
src/server/SchemaLoader.h [new file with mode: 0644]

index 573a430..5c4a488 100644 (file)
@@ -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 (file)
index 0000000..d0ec0c9
--- /dev/null
@@ -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 (file)
index 0000000..6185762
--- /dev/null
@@ -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 <cstdio>
+#include <fstream>
+#include <sstream>
+#include <regex>
+#include <dirent.h>
+#include <PathUtil.h>
+#include "DatabaseManager.h"
+#include "SchemaLoader.h"
+
+/* Schema XML example:
+
+       <?xml version="1.0" encoding="UTF-8"?>
+       <ContextStore version="1">
+               <Schema uri="http://tizen.org/contextstore/name/subname" retention="24" limit="10">
+                       <Privilege access="Read">http://tizen.org/privilege/healthinfo</Privilege>
+                       <Privilege access="Write">http://tizen.org/privilege/internal/service</Privilege>
+                       <Attribute name="name1" type="Integer"/>
+                       <Attribute name="name2" type="Real"/>
+                       <Attribute name="name3" type="Text"/>
+               </Schema>
+       </ContextStore>
+
+ */
+
+#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<const xmlChar*>(name));
+       std::string out;
+       if (prop) {
+               out = reinterpret_cast<char*>(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<char*>(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 (file)
index 0000000..4b08934
--- /dev/null
@@ -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 <utility>
+#include <string>
+#include <vector>
+#include <libxml/tree.h>
+#include <Database.h>
+#include <ContextStoreTypesPrivate.h>
+
+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<std::string> readPrivileges;
+               std::vector<std::string> writePrivileges;
+               std::vector<std::pair<std::string, AttributeType>> 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__ */