Implemented:
authorLomtev Dmytro <d.lomtev@samsung.com>
Thu, 18 May 2017 12:36:59 +0000 (15:36 +0300)
committerLomtev Dmytro <d.lomtev@samsung.com>
Thu, 18 May 2017 12:43:03 +0000 (15:43 +0300)
 - Server report logic;
 - Client report logic;
 - Control app report API.

19 files changed:
agent/scripts/.device-agent [deleted file]
agent/scripts/.gitignore
device_core/ctrl_app_lib/inc/nmlib.h
device_core/ctrl_app_lib/src/ctrl_app_support.cpp
device_core/iotivity_lib/inc/iotivity.h
device_core/iotivity_lib/inc/report_client.h [new file with mode: 0644]
device_core/iotivity_lib/src/iotivity.cpp
device_core/iotivity_lib/src/report_client.cpp [new file with mode: 0644]
device_core/secserver/CMakeLists.txt
device_core/secserver/macro.h [new file with mode: 0644]
device_core/secserver/report.cpp [new file with mode: 0644]
device_core/secserver/report.h [new file with mode: 0644]
device_core/secserver/report_resource.cpp [new file with mode: 0644]
device_core/secserver/report_resource.h [new file with mode: 0644]
device_core/secserver/secserver.cpp
device_core/secserver/selectquery.h [new file with mode: 0644]
device_core/secserver/sqlitedb.cpp [new file with mode: 0644]
device_core/secserver/sqlitedb.h [new file with mode: 0644]
device_core/utest/test_iot_dev_manager.cpp

diff --git a/agent/scripts/.device-agent b/agent/scripts/.device-agent
deleted file mode 100644 (file)
index 0cfbf08..0000000
+++ /dev/null
@@ -1 +0,0 @@
-2
index 9c5e689..5da2cd8 100644 (file)
@@ -1 +1 @@
-.device
\ No newline at end of file
+.device-agent
\ No newline at end of file
index 46cc378..e3e6e76 100644 (file)
@@ -228,18 +228,17 @@ NM_ErrorCode NM_unOwnDevice(NM_hDeviceList dev_list, const char* dev_id);
 
 /**
  * @brief NM_getDeviceReport get report for specified device
- * @param  dev_list [in] handle to device list containing this device
  * @param  dev_id   [in] device id
  * @param report    [out] pointer to buffer whith report data (as ASCII-Z)
  * @return error code
  */
-//NM_ErrorCode NM_getDeviceReport(NM_hDeviceList dev_list, const char* dev_id, char** report);
+NM_ErrorCode NM_getDeviceReport(NM_hContext ctx, const char* dev_id, char** report);
 
 /**
  * @brief NM_freeCharBuffer free memory
  * @param report    [out] pointer to buffer
  */
-//void NM_freeCharBuffer(char** buffer);
+void NM_freeCharBuffer(char* buffer);
 
 /**
  * @brief NM_getDeviceReport get policy for specified device
index b54d864..8084a3e 100644 (file)
@@ -352,3 +352,38 @@ void NM_unsubscribeNotifications(NM_hContext ctx)
         LOG_E(TAG, "Unsubscribe notifications error: %s", e.what());
     }
 }
+
+
+NM_ErrorCode NM_getDeviceReport(NM_hContext ctx, const char* dev_id, char** report)
+{
+    if (ctx == nullptr) return EC_NULL_POINTER;
+
+    try
+    {
+        *report = nullptr;
+        std::string result = ctx->instance->getDeviceReport(dev_id);
+        if (!result.empty())
+        {
+            *report = new char[result.length() + 1];
+            (*report)[result.length()] = '\0';
+            result.copy(*report, result.length());
+        }
+    }
+    catch(NMexception& e)
+    {
+        LOG_E(TAG, "Get device report error: %s, error code %d", e.what(), e.errorCode());
+        return static_cast<NM_ErrorCode>(e.errorCode());
+    }
+    catch (std::exception& e)
+    {
+        LOG_E(TAG, "Get device report error: %s", e.what());
+        return EC_INTERNAL_ERROR;
+    }
+
+    return EC_OK;
+}
+
+void NM_freeCharBuffer(char* buffer)
+{
+    if (buffer != nullptr) delete[] buffer;
+}
index 6d5d592..7a4619c 100644 (file)
@@ -104,7 +104,7 @@ public:
      * @brief getDeviceID returns device id (DUID) from iotivity persistant storage
      * @return string that indicates duid
      */
-    std::string getDeviceID();
+    const std::string& getDeviceID();
 
     /**
      * @brief subscribeNotifications subscribes to secure server notifications
@@ -131,6 +131,19 @@ public:
      */
     std::string getPersistentStoragePath();
 
+    /**
+     * @brief getDeviceReport return report for specified device
+     * @param uuid [in] device identifier
+     * @return report in JSON format or throw exception on error
+     */
+    std::string getDeviceReport(const std::string& uuid);
+
+    /**
+     * @brief postReport Post report to server. Used in IoTivity for test puposes
+     * @param report [in] report in JSON format
+     */
+    void postReport(const std::string& report);
+
 private:
     void guardUnauthorized();
 
diff --git a/device_core/iotivity_lib/inc/report_client.h b/device_core/iotivity_lib/inc/report_client.h
new file mode 100644 (file)
index 0000000..bd0a624
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef REPORT_CLIENT_H
+#define REPORT_CLIENT_H
+
+#include <string>
+#include <memory>
+#include <mutex>
+#include <condition_variable>
+#include <OCApi.h>
+#include <OCPlatform.h>
+
+namespace NetworkManager
+{
+
+class ReportClient
+{
+    std::shared_ptr<OC::OCResource> resource;
+    std::mutex report_mutex;
+    std::condition_variable signal;
+public:
+    ReportClient(const std::string& host);
+
+    operator bool() const;
+
+    std::string getReport(const OC::QueryParamsMap& params);
+
+    void postReport(const std::string& report);
+private:
+    void foundResourceCb(std::shared_ptr<OC::OCResource> found_resource);
+
+    void onReportObserveCb(const OC::HeaderOptions options, const OC::OCRepresentation& rep,
+                    const int& eCode, const int& sequenceNumber);
+};
+
+} // namespace NetworkManager
+
+
+#endif // REPORT_CLIENT_H
index 7eae00a..1904c3d 100644 (file)
@@ -7,7 +7,10 @@
 #include <functional>
 #include <thread>
 #include <chrono>
+#include <sstream>
+#include <iomanip>
 #include "nmlib.h"
+#include "report_client.h"
 
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wreorder"
@@ -92,6 +95,8 @@ struct Params
     PresenceHook presence_hook;
     std::mutex owned_mutex;
     std::shared_ptr<OCResource> notificationResource;
+    std::shared_ptr<ReportClient> reportClient;
+    std::string device_id;
     std::mutex notificationMtx;
 };
 
@@ -427,21 +432,41 @@ bool IoTivity::isSignedIn()
     return signedIn;
 }
 
-string IoTivity::getDeviceID()
+const string& IoTivity::getDeviceID()
 {
-    OCUUIdentity deviceId;
-    guardErrorCode(OC::OCPlatform::getDeviceId(&deviceId), "OCPlatform::getDeviceId()");
-
-    char s[128];
-    unsigned char *id = deviceId.id;
-
-    snprintf(s, sizeof(s), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
-             id[0],                    id[1],                  id[2],                  id[3],
-             id[4],                    id[5],                  id[6],                  id[7],
-             id[8],                    id[9],                  id[10],                 id[11],
-             id[12],                   id[13],                 id[14],                 id[15]);
+    if (params->device_id.empty())
+    {
+        OCUUIdentity deviceId;
+        if (OC_STACK_OK == OC::OCPlatform::getDeviceId(&deviceId))
+        {
+            unsigned char* id = deviceId.id;
+            ostringstream os;
+            os << hex << setfill('0');
+            os << setw(2) << (unsigned)id[0];
+            os << setw(2) << (unsigned)id[1];
+            os << setw(2) << (unsigned)id[2];
+            os << setw(2) << (unsigned)id[3] << '-';
+
+            os << setw(2) << (unsigned)id[4];
+            os << setw(2) << (unsigned)id[5] << '-';
+
+            os << setw(2) << (unsigned)id[6];
+            os << setw(2) << (unsigned)id[7] << '-';
+
+            os << setw(2) << (unsigned)id[8];
+            os << setw(2) << (unsigned)id[9] << '-';
+
+            os << setw(2) << (unsigned)id[10];
+            os << setw(2) << (unsigned)id[11];
+            os << setw(2) << (unsigned)id[12];
+            os << setw(2) << (unsigned)id[13];
+            os << setw(2) << (unsigned)id[14];
+            os << setw(2) << (unsigned)id[15];
+            params->device_id = os.str();
+        }
+    }
 
-    return string(s);
+    return params->device_id;
 
 }
 
@@ -525,6 +550,42 @@ void IoTivity::unsubscribeNotifications()
     }
 }
 
+std::string IoTivity::getDeviceReport(const std::string& uuid)
+{
+    if (!params->reportClient)
+    {
+        params->reportClient = make_shared<ReportClient>(oAuthCred.host);
+    }
+
+    if (!bool(*(params->reportClient)))
+    {
+        LOG_E(TAG, "Report resource not found");
+        params->reportClient.reset();
+        throw IoTInternalError("Report resource not found");
+    }
+
+    QueryParamsMap qp{{"did", uuid}};
+
+    return params->reportClient->getReport(qp);
+}
+
+void IoTivity::postReport(const std::string& report)
+{
+    if (!params->reportClient)
+    {
+        params->reportClient = make_shared<ReportClient>(oAuthCred.host);
+    }
+
+    if (!bool(*(params->reportClient)))
+    {
+        LOG_E(TAG, "Report resource not found");
+        params->reportClient.reset();
+        throw IoTInternalError("Report resource not found");
+    }
+
+    params->reportClient->postReport(report);
+}
+
 void IoTivity::setPersistentStoragePath(std::string newPath)
 {
     persistentStoragePath = newPath;
diff --git a/device_core/iotivity_lib/src/report_client.cpp b/device_core/iotivity_lib/src/report_client.cpp
new file mode 100644 (file)
index 0000000..36ac3ce
--- /dev/null
@@ -0,0 +1,150 @@
+
+#include "report_client.h"
+#include "logging.h"
+#include <functional>
+#include <chrono>
+#include "iotivity.h"
+
+using namespace OC;
+
+namespace PH = std::placeholders;
+
+#define TAG "NetworkManager"
+
+namespace NetworkManager
+{
+
+ReportClient::ReportClient(const std::string& host)
+{
+    std::string requestURI{OC_RSRVD_WELL_KNOWN_URI};
+    std::unique_lock<std::mutex> lock(report_mutex);
+    OCPlatform::findResource(host, requestURI, CT_DEFAULT, std::bind(&ReportClient::foundResourceCb, this, PH::_1));
+    signal.wait_for(lock, std::chrono::seconds(3));
+}
+
+ReportClient::operator bool() const
+{
+    return (bool)resource;
+}
+
+std::string ReportClient::getReport(const QueryParamsMap& query)
+{
+    std::string report;
+    std::unique_lock<std::mutex> lock(report_mutex);
+
+    if(resource)
+    {
+        resource->get(query, [this, &report](const HeaderOptions& hopts, const OCRepresentation& rep, const int eCode)
+            {
+                try
+                {
+                    if(eCode == OC_STACK_OK)
+                    {
+                        rep.getValue("report", report);
+                    }
+                    else
+                    {
+                        LOG_E(TAG, "Error (ReportClient::getReport): code=%d", eCode);
+                    }
+                }
+                catch(std::exception& e)
+                {
+                    LOG_E(TAG, "Exception (ReportClient::getReport): %s", e.what());
+                }
+
+                signal.notify_one();
+            });
+    }
+
+    signal.wait_for(lock, std::chrono::seconds(1));
+
+    return report;
+}
+
+void ReportClient::postReport(const std::string& report)
+{
+    if (!resource)
+    {
+        LOG_E(TAG, "Resource not found");
+        return;
+    }
+
+    OCRepresentation repr;
+    repr.setValue("report", report);
+
+    QueryParamsMap query{{"did", IoTivity::getInstance()->getDeviceID()}};
+
+    std::unique_lock<std::mutex> lock(report_mutex);
+
+    resource->post("core.security", DEFAULT_INTERFACE, repr, query,
+        [this] (const HeaderOptions & /*ho*/, const OCRepresentation &rep, const int eCode)
+        {
+            LOG_I(TAG, "post report return code=%d", eCode);
+            signal.notify_one();
+        }
+    );
+
+    signal.wait_for(lock, std::chrono::seconds(1));
+}
+
+void ReportClient::foundResourceCb(std::shared_ptr<OCResource> found_resource)
+{
+    try
+    {
+        std::unique_lock<std::mutex> lock(report_mutex);
+
+        if(!resource && found_resource)
+        {
+            for(auto &resource_type : found_resource->getResourceTypes())
+            {
+                if(resource_type == "core.security")
+                {
+                    if (found_resource->connectivityType() & OCConnectivityType::CT_ADAPTER_TCP)
+                    {
+                        resource = found_resource;
+                        LOG_I(TAG, "Address of selected report resource: %s", found_resource->host().c_str());
+                        signal.notify_one();
+                    }
+                }
+            }
+        }
+
+    }
+    catch(std::exception& e)
+    {
+        LOG_E(TAG, "Exception in foundResource: %s", e.what());
+    }
+}
+
+void ReportClient::onReportObserveCb(const HeaderOptions /*headerOptions*/, const OCRepresentation& rep,
+                const int& eCode, const int& sequenceNumber)
+{
+    try
+    {
+        if(eCode == OC_STACK_OK && sequenceNumber <= MAX_SEQUENCE_NUMBER)
+        {
+            if(sequenceNumber == OC_OBSERVE_REGISTER)
+            {
+                LOG_I(TAG, "Observe registration action is successful");
+            }
+        }
+        else
+        {
+            if(eCode == OC_STACK_OK)
+            {
+                LOG_D(TAG, "Observe registration failed");
+            }
+            else
+            {
+                LOG_E(TAG, "Observe response error: %d", eCode);
+            }
+        }
+    }
+    catch(std::exception& e)
+    {
+        LOG_E(TAG, "Observe exception: %s", e.what());
+    }
+
+}
+
+} // namespace NetworkManager
index 02f07a6..50ebc32 100644 (file)
@@ -4,6 +4,8 @@ project(${ProjectId})
 set(app ${ProjectId})
 file(GLOB SRCS *.cpp)
 
+pkg_check_modules(REQUIRED sqlite3 jsoncpp)
+
 include_directories(
        ../iotivity_lib/inc
        ../ctrl_app_lib/inc
@@ -12,6 +14,8 @@ include_directories(
 add_executable(${app} ${SRCS})
 target_link_libraries(${app}
        ${IOTIVITY_LIB_PROJECT_NAME}
+       sqlite3
+       jsoncpp
 )
 
 install(TARGETS ${app} DESTINATION ${TESTS_DIR})
diff --git a/device_core/secserver/macro.h b/device_core/secserver/macro.h
new file mode 100644 (file)
index 0000000..260acfe
--- /dev/null
@@ -0,0 +1,7 @@
+#ifndef MACRO_H
+#define MACRO_H
+
+#define STRINGIZE(x) STRINGIZE2(x)
+#define STRINGIZE2(x) #x
+
+#endif // MACRO_H
diff --git a/device_core/secserver/report.cpp b/device_core/secserver/report.cpp
new file mode 100644 (file)
index 0000000..72a1c96
--- /dev/null
@@ -0,0 +1,181 @@
+#include "sqlitedb.h"
+#include "report.h"
+#include "macro.h"
+#include <stdexcept>
+
+using namespace std;
+
+Report::Report()
+    : pk(-1)
+    , did("")
+    , name("")
+    , date("")
+    , data("")
+    , result(0)
+    , modified(false)
+{
+
+}
+
+Report::Report(const string& id, const string& name, const string& date, const string& report, int result)
+    : pk(-1)
+    , did(id)
+    , name(name)
+    , date(date)
+    , data(report)
+    , result(result)
+    , modified(false)
+{
+
+}
+
+void Report::registerModel()
+{
+    ostringstream os;
+    os << "CREATE TABLE IF NOT EXISTS " << Report::TABLE_NAME
+       << "(id INTEGER PRIMARY KEY AUTOINCREMENT,"
+       << "did CHAR(36) NOT NULL,"
+       << "name CHAR(32) NOT NULL,"
+       << "date CHAR(32) NOT NULL,"
+       << "data TEXT NOT NULL,"
+       << "result INTEGER);";
+    SQLiteDB::inst().exec(os.str(), &selecter, nullptr);
+}
+
+void Report::save()
+{
+    if (pk == -1)
+    {
+        ostringstream os;
+        os << "INSERT INTO " << TABLE_NAME << " (did,name,date,data,result) "
+           << "VALUES ('" << did << "', '" << name << "', '" << date << "', '"
+           << data << "', " << result << ");";
+        SQLiteDB::inst().exec(os.str(), &selecter, nullptr);
+        pk = SQLiteDB::inst().last_row_id();
+    }
+    else if (modified)
+    {
+        ostringstream os;
+        os << "UPDATE " << TABLE_NAME << " set did = '" << did
+           << "', name = '" << name
+           << "', date = '" << date
+           << "', data = '" << data
+           << "', result = " << result
+           << " WHERE id=" << pk << ";";
+        SQLiteDB::inst().exec(os.str(), &selecter, nullptr);
+    }
+    modified = false;
+}
+
+SelectQuery<Report> Report::get()
+{
+    SelectQuery<Report> q(&selecter);
+    return q;
+}
+
+void Report::setDid(const string& new_id)
+{
+    if (did != new_id)
+    {
+        did = new_id;
+        modified = true;
+    }
+}
+
+void Report::setName(const string& new_name)
+{
+    if (name != new_name)
+    {
+        name = new_name;
+        modified = true;
+    }
+}
+
+void Report::setDate(const string& new_date)
+{
+    if (date != new_date)
+    {
+        date = new_date;
+        modified = true;
+    }
+}
+
+void Report::setData(const string& new_report)
+{
+    if (data != new_report)
+    {
+        data = new_report;
+        modified = true;
+    }
+}
+
+void Report::setResult(int new_result)
+{
+    if (result != new_result)
+    {
+        result = new_result;
+        modified = true;
+    }
+}
+
+int Report::selecter(void* data, int argc, char **argv, char **col_name)
+{
+    if (data == nullptr) return 0;
+    vector<Report>* v = reinterpret_cast<vector<Report>*>(data);
+
+    if (argc != 6) throw logic_error("Wrong \"Report\" table format");
+
+    Report r;
+
+    for(int i = 0; i < argc; i++)
+    {
+
+        const char* arg = argv[i] == nullptr ? "" : argv[i];
+
+        if (r.pk_name == col_name[i])
+        {
+            r.pk = stol(arg);
+        }
+        else if (r.did_name == col_name[i])
+        {
+            r.did = arg;
+        }
+        else if (r.name_name == col_name[i])
+        {
+            r.name = arg;
+        }
+        else if (r.date_name == col_name[i])
+        {
+            r.date = arg;
+        }
+        else if (r.data_name == col_name[i])
+        {
+            r.data = arg;
+        }
+        else if (r.result_name == col_name[i])
+        {
+            r.result = stoi(arg);
+        }
+        else
+        {
+            throw logic_error(string{"Unknown column name: "} + col_name[i]);
+        }
+    }
+
+    v->push_back(r);
+
+    return 0;
+}
+
+const string Report::TABLE_NAME{STRINGIZE(Report) "s"};
+
+ostream& operator << (ostream& os, const Report& r)
+{
+    os << "[Report]" << endl
+       << "\tdid: " << r.did << endl
+       << "\tname: " << r.name << endl
+       << "\tdate: " << r.date << endl
+       << "\tdata: " << r.data << endl
+       << "\tresult: " << r.result;
+    return os;
+}
diff --git a/device_core/secserver/report.h b/device_core/secserver/report.h
new file mode 100644 (file)
index 0000000..cd216b4
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef REPORT_H
+#define REPORT_H
+
+#include "selectquery.h"
+
+class Report
+{
+public:
+    static const std::string TABLE_NAME;
+
+    Report();
+
+    Report(const std::string& id, const std::string& name, const std::string& date, const std::string& data, int result);
+
+    static void registerModel();
+
+    void save();
+
+    static SelectQuery<Report> get();
+
+    void setDid(const std::string& new_id);
+
+    void setName(const std::string& new_name);
+
+    void setDate(const std::string& new_date);
+
+    void setData(const std::string& new_data);
+
+    void setResult(int new_result);
+
+    const std::string& getDid() const
+    {
+        return did;
+    }
+
+    const std::string& getName() const
+    {
+        return name;
+    }
+
+    const std::string& getDate() const
+    {
+        return date;
+    }
+
+    const std::string& getData() const
+    {
+        return data;
+    }
+
+    int getResult() const
+    {
+        return result;
+    }
+
+    friend std::ostream& operator << (std::ostream&, const Report&);
+
+private:
+    static int selecter(void* data, int argc, char **argv, char **col_name);
+
+    long long pk;
+    std::string did;
+    std::string name;
+    std::string date;
+    std::string data;
+    int result;
+    bool modified;
+
+    const std::string pk_name{"id"};
+    const std::string did_name{"did"};
+    const std::string name_name{"name"};
+    const std::string date_name{"date"};
+    const std::string data_name{"data"};
+    const std::string result_name{"result"};
+};
+
+#endif // REPORT_H
diff --git a/device_core/secserver/report_resource.cpp b/device_core/secserver/report_resource.cpp
new file mode 100644 (file)
index 0000000..7d2d591
--- /dev/null
@@ -0,0 +1,280 @@
+/**
+ * @brief  Server report reasource
+ * @date   Created 17.05.2017
+ * @author Created 2017 in Samsung Ukraine R&D Center (SURC) under a contract
+ *         between LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine)
+ *         and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea).
+ *         Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved.
+ * @author Mail to: <A HREF="mailto:d.lomtev@samsung.com">Dmytro Lomtev, d.lomtev@samsung.com</A>
+ */
+
+#include <functional>
+//#include <mutex>
+#include <vector>
+#include <memory>
+#include "OCPlatform.h"
+#include "OCApi.h"
+#include "report_resource.h"
+#include "sqlitedb.h"
+#include "report.h"
+#include <stdexcept>
+#include <jsoncpp/json/reader.h>
+
+using namespace OC;
+using namespace std;
+namespace PH = std::placeholders;
+
+void sql_test()
+{
+    try
+    {
+        Report::registerModel();
+        Report r1{"9b5287b3-fe92-46fc-a924-488d2339c2ba", "sim", "17-05-2017 05:32:30", "Alert! Alles caput!", 3};
+        Report r2{"11223344-fe92-46fc-a924-488d2339c2ba", "alert", "17-05-2017 05:32:30", "Deus vult", 77};
+        Report r3{"00000000-fe92-46fc-a924-488d2339c2ba", "sim", "17-05-2017 05:32:30", "Spartaaaaa!", 0};
+        r1.save();
+        r2.save();
+        r3.save();
+
+        auto v = Report::get().filter("name", "sim").all();
+
+        cout << "SELECT 1 returned " << v.size() << " records" << endl;
+        int i = 1;
+
+        for (auto rec : v)
+        {
+            cout << "#" << i++ << endl << rec << endl;
+        }
+
+        v = Report::get().all();
+
+        cout << "SELECT 2 returned " << v.size() << " records" << endl;
+        i = 1;
+
+        for (auto rec : v)
+        {
+            cout << "#" << i++ << endl << rec << endl;
+        }
+
+        v = Report::get().filter("did", "00000000-fe92-46fc-a924-488d2339c2ba").filter("name", "sim").filter("result", 0).all();
+
+        cout << "SELECT 3 returned " << v.size() << " records" << endl;
+        i = 1;
+
+        for (auto rec : v)
+        {
+            cout << "#" << i++ << endl << rec << endl;
+        }
+    }
+    catch (exception& e)
+    {
+        cout << "SQLite test Exception: " << e.what() << endl;
+    }
+}
+
+
+ReportResource::ReportResource()
+    : m_reportUri("/a/report"), m_resourceHandle(nullptr)
+{
+    m_reportRep.setValue("report", "");
+    Report::registerModel();
+    Report r1{"9b5287b3-fe92-46fc-a924-488d2339c2ba", "sim", "17-05-2017 05:32:30", "Alert! Alles caput!", 3};
+    Report r2{"11223344-fe92-46fc-a924-488d2339c2ba", "alert", "17-05-2017 05:32:30", "Deus vult", 77};
+    Report r3{"9b5287b3-fe92-46fc-a924-488d2339c2ba", "sim", "17-05-2017 05:32:30", "Spartaaaaa!", 0};
+    r1.save();
+    r2.save();
+    r3.save();
+}
+
+void ReportResource::registerResource()
+{
+    string resourceTypeName = "core.security";
+
+    uint8_t resourceProperty = OC_DISCOVERABLE | OC_OBSERVABLE;
+
+    EntityHandler cb = std::bind(&ReportResource::entityHandler, this,PH::_1);
+
+    OCStackResult result = OCPlatform::registerResource(
+                                m_resourceHandle, m_reportUri, resourceTypeName,
+                                DEFAULT_INTERFACE, cb, resourceProperty);
+
+    if (OC_STACK_OK != result)
+    {
+        cout << "Security Resource creation was unsuccessful\n";
+    }
+    else
+    {
+        cout << "Report resource created\n";
+    }
+}
+
+OCResourceHandle ReportResource::getHandle()
+{
+    return m_resourceHandle;
+}
+
+Json::Value reportAsJson(const Report& r)
+{
+    Json::Value report;
+    report["did"] = r.getDid();
+    report["name"] = r.getName();
+    report["date"] = r.getDate();
+    report["data"] = r.getData();
+    report["result"] = r.getResult();
+    return report;
+}
+
+OCRepresentation ReportResource::get()
+{
+    cout << "IN > ReportResource::get()" <<endl;
+    auto records = Report::get().all();
+    Json::Value root;
+
+    for (auto r : records)
+    {
+        root.append(reportAsJson(r));
+    }
+
+    m_reportRep.setValue("report", root.toStyledString());
+    return m_reportRep;
+}
+
+OCRepresentation ReportResource::get(const QueryParamsMap& params)
+{
+    cout << "IN > ReportResource::get(const QueryParamsMap& params)" <<endl;
+    auto query = Report::get();
+
+    for (auto p: params)
+    {
+        cout << p.first << ": " << p.second <<endl;
+        query.filter(p.first, p.second);
+    }
+
+    auto records = query.all();
+
+    Json::Value root;
+
+    for (auto r : records)
+    {
+        root.append(reportAsJson(r));
+    }
+
+    if (records.empty())
+    {
+        m_reportRep.setValue("report", "");
+    }
+    else
+    {
+        m_reportRep.setValue("report", root.toStyledString());
+    }
+    return m_reportRep;
+}
+
+OCEntityHandlerResult ReportResource::entityHandler(std::shared_ptr<OCResourceRequest> request)
+{
+    OCEntityHandlerResult ehResult = OC_EH_ERROR;
+
+    if(request)
+    {
+        std::string requestType = request->getRequestType();
+        int requestFlag = request->getRequestHandlerFlag();
+
+        if(requestFlag & RequestHandlerFlag::RequestFlag)
+        {
+            auto pResponse = make_shared<OC::OCResourceResponse>();
+            pResponse->setRequestHandle(request->getRequestHandle());
+            pResponse->setResourceHandle(request->getResourceHandle());
+
+            if(requestType == "GET")
+            {
+                QueryParamsMap queries = request->getQueryParameters();
+                pResponse->setErrorCode(200);
+                pResponse->setResponseResult(OC_EH_OK);
+                pResponse->setResourceRepresentation(get(queries));
+                if(OC_STACK_OK == OCPlatform::sendResponse(pResponse))
+                {
+                    ehResult = OC_EH_OK;
+                }
+
+            }
+            else if(requestType == "PUT")
+            {
+                pResponse->setErrorCode(403);
+
+                if(OC_STACK_OK == OCPlatform::sendResponse(pResponse))
+                {
+                    ehResult = OC_EH_OK;
+                }
+            }
+            else if(requestType == "POST")
+            {
+                const auto& rep = request->getResourceRepresentation();
+                std::string new_report;
+
+                int rcode = 400;
+
+                if (rep.getValue("report", new_report))
+                {
+                    rcode = 200;
+                    Json::Value root;
+                    Json::Reader reader;
+
+                    std::cout << "[Report]" << std::endl << new_report << std::endl;
+
+                    if (!reader.parse(new_report, root))
+                    {
+                            std::cout << "[ERROR] Wrong report format" << std::endl;
+                            rcode = 400;
+                    }
+                    else
+                    {
+                        QueryParamsMap qm = request->getQueryParameters();
+                        std::cout << "Query parameters:" << std::endl;
+
+                        for (auto q : qm)
+                        {
+                            std::cout << q.first << ": " << q.second << std::endl;
+                        }
+
+                        std::string id = root["id"].asString();
+                        std::string date = root["date"].asString();
+                        std::string name = root["name"].asString();
+                        int result = root["id"].asInt();
+                        std::string data = root["data"].asString();
+
+                        try
+                        {
+                            Report{id,name,date,data,result}.save();
+                        }
+                        catch(std::exception& e)
+                        {
+                            std::cout <<"DB save error: " << e.what() << std::endl;
+                        }
+
+//                        pResponse->setResourceRepresentation(get());
+                        pResponse->setResponseResult(OC_EH_OK);
+                    }
+
+
+                }
+
+                pResponse->setErrorCode(rcode);
+
+
+                if(OC_STACK_OK == OCPlatform::sendResponse(pResponse))
+                {
+                    ehResult = OC_EH_OK;
+                }
+            }
+            else if(requestType == "DELETE")
+            {
+            }
+        }
+    }
+    else
+    {
+        std::cout << "Invalid request" << std::endl;
+    }
+
+    return ehResult;
+}
diff --git a/device_core/secserver/report_resource.h b/device_core/secserver/report_resource.h
new file mode 100644 (file)
index 0000000..db4a064
--- /dev/null
@@ -0,0 +1,62 @@
+/**
+ * @brief  Server report reasource
+ * @date   Created 17.05.2017
+ * @author Created 2017 in Samsung Ukraine R&D Center (SURC) under a contract
+ *         between LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine)
+ *         and "Samsung Electronics Co", Ltd (Seoul, Republic of Korea).
+ *         Copyright: (c) Samsung Electronics Co, Ltd 2017. All rights reserved.
+ * @author Mail to: <A HREF="mailto:d.lomtev@samsung.com">Dmytro Lomtev, d.lomtev@samsung.com</A>
+ */
+
+#include "OCPlatform.h"
+#include "OCApi.h"
+#include <string>
+
+
+using namespace OC;
+
+void sql_test();
+
+class ReportResource
+{
+private:
+    std::string m_reportUri;
+    OCResourceHandle m_resourceHandle;
+    OCRepresentation m_reportRep;
+
+public:
+    /**
+     * Constructor
+     */
+    ReportResource();
+
+    /**
+     * Register resource to make IOTivity clients to find it
+     */
+    void registerResource();
+    
+    /**
+     * Getter for resource handle
+     *
+     * @return OCResourceHandle object (used to publish resource on IoT cloud)
+     */
+    OCResourceHandle getHandle();
+    
+    /**
+     * Gets the updated representation.
+     *
+     * @return representation object
+     */
+    OCRepresentation get();
+
+    /**
+     * Gets the representation.
+     *
+     * @param params [in] Specific request parameters
+     * @return representation object
+     */
+    OCRepresentation get(const QueryParamsMap& params);
+    
+private:
+    OCEntityHandlerResult entityHandler(std::shared_ptr<OCResourceRequest> request);
+};
index d998987..52eda8d 100644 (file)
@@ -8,6 +8,7 @@
 #include <OCPlatform.h>
 #include "iotivity.h"
 #include "notification_resource.h"
+#include "report_resource.h"
 
 using namespace OC;
 using namespace NetworkManager;
@@ -99,8 +100,11 @@ static void mainLoop()
     bool isSecured = false;
     NotificationResource notifResource(isSecured);
     notifResource.registerResource();
+    ReportResource reportResouce;
+    reportResouce.registerResource();
 
     resourceHandles.push_back(notifResource.getHandle());
+    resourceHandles.push_back(reportResouce.getHandle());
 
     guardErrorCode(RDClient::Instance().publishResourceToRD(host, OCConnectivityType::CT_ADAPTER_TCP, resourceHandles,
                    &onPublish), "Notification resource publishResourceToRD()");
@@ -131,6 +135,9 @@ static void mainLoop()
 
 int main(int argc, char* argv[])
 {
+//    std::cout << "SQLite test start" << std::endl;
+//    sql_test();
+//    std::cout << "SQLite test finish" << std::endl;
     try
     {
         mainLoop();
diff --git a/device_core/secserver/selectquery.h b/device_core/secserver/selectquery.h
new file mode 100644 (file)
index 0000000..37bc77d
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef SELECTQUERY_H
+#define SELECTQUERY_H
+
+#include <type_traits>
+#include <string>
+#include <vector>
+#include <sstream>
+
+template<class T>
+class SelectQuery
+{
+public:
+    SelectQuery<T>(sqlite_exec_callback cb): callback(cb) {}
+
+    template<class R>
+    SelectQuery& filter(const std::string& name, const R& value)
+    {
+        filter(name, value, std::is_integral<R>());
+        return *this;
+    }
+
+    std::vector<T> all()
+    {
+        std::vector<T> v;
+        std::ostringstream os;
+        os << "SELECT * FROM " << T::TABLE_NAME;
+
+        if (!filters.empty())
+        {
+            auto it = filters.cbegin();
+            os << " WHERE " << *it;
+
+            while (++it != filters.cend())
+            {
+                os << " AND " << *it;
+            }
+        }
+
+        os << ';';
+
+        SQLiteDB::inst().exec(os.str(), callback, &v);
+        return v;
+    }
+
+private:
+    template<class R>
+    void filter(const std::string& name, const R& value, std::true_type)
+    {
+        filters.emplace_back(name + " = " + std::to_string(value));
+    }
+
+    template<class R>
+    void filter(const std::string& name, const R& value, std::false_type)
+    {
+        filters.emplace_back(name + " = '" + std::string{value} + "'");
+    }
+
+
+    sqlite_exec_callback callback;
+    std::vector<std::string> filters;
+};
+
+#endif // SELECTQUERY_H
diff --git a/device_core/secserver/sqlitedb.cpp b/device_core/secserver/sqlitedb.cpp
new file mode 100644 (file)
index 0000000..4f04825
--- /dev/null
@@ -0,0 +1,46 @@
+#include "sqlitedb.h"
+#include <sqlite3.h>
+#include <stdexcept>
+
+using namespace std;
+
+SQLiteDB::~SQLiteDB()
+{
+    sqlite3_close(db);
+}
+
+void SQLiteDB::exec(const string& SQL, sqlite_exec_callback cb, void* data)
+{
+    char* error_msg;
+    int ecode = sqlite3_exec(db, SQL.c_str(), cb, data, &error_msg);
+
+    if (ecode != SQLITE_OK)
+    {
+        string serr{error_msg};
+        sqlite3_free(error_msg);
+        throw runtime_error(serr.c_str());
+    }
+}
+
+SQLiteDB& SQLiteDB::inst()
+{
+    if (instance.db == nullptr)
+    {
+        int ecode = sqlite3_open(":memory:", &instance.db);
+        if (ecode != SQLITE_OK) throw runtime_error("Can not create memory data base");
+    }
+
+    return instance;
+}
+
+long long SQLiteDB::last_row_id()
+{
+    return sqlite3_last_insert_rowid(db);
+}
+
+SQLiteDB::SQLiteDB() : db(nullptr)
+{
+
+}
+
+SQLiteDB SQLiteDB::instance;
diff --git a/device_core/secserver/sqlitedb.h b/device_core/secserver/sqlitedb.h
new file mode 100644 (file)
index 0000000..6468c71
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef SQLITEDB_H
+#define SQLITEDB_H
+
+#include <string>
+
+typedef int (*sqlite_exec_callback)(void *user_data, int argc, char **argv, char **col_name);
+struct sqlite3;
+
+class SQLiteDB
+{
+public:
+    ~SQLiteDB();
+
+    void exec(const std::string& SQL, sqlite_exec_callback cb, void* data);
+
+    static SQLiteDB& inst();
+
+    long long last_row_id();
+
+private:
+    SQLiteDB();
+    sqlite3* db;
+    static SQLiteDB instance;
+};
+
+#endif // SQLITEDB_H
index 8e6f450..59b048c 100644 (file)
@@ -125,3 +125,27 @@ TEST_F(IoTDevManagerTest, presenceCallbackTest)
     std::this_thread::sleep_for(std::chrono::seconds(1));
     ASSERT_EQ(EC_OK, NM_freeDeviceList(&dev_list));
 }
+
+TEST_F(IoTDevManagerTest, reportTest)
+{
+    char* report = nullptr;
+    ASSERT_EQ(EC_OK, NM_getDeviceReport(ctx, "9b5287b3-fe92-46fc-a924-488d2339c2ba", &report));
+//    std::cout << report << std::endl;
+
+    const std::string must_be = "[\n   {\n      \"data\" : \"Alert! Alles caput!\",\n\
+      \"date\" : \"17-05-2017 05:32:30\",\n      \"did\" : \"9b5287b3-fe92-46fc-a924-488d2339c2ba\",\n\
+      \"name\" : \"sim\",\n      \"result\" : 3\n   },\n   {\n      \"data\" : \"Spartaaaaa!\",\n\
+      \"date\" : \"17-05-2017 05:32:30\",\n      \"did\" : \"9b5287b3-fe92-46fc-a924-488d2339c2ba\",\n\
+      \"name\" : \"sim\",\n      \"result\" : 0\n   }\n]\n";
+    ASSERT_EQ(must_be, report);
+    ASSERT_NO_THROW(NM_freeCharBuffer(report));
+}
+
+TEST_F(IoTDevManagerTest, reportTestWrongDid)
+{
+    char* report = nullptr;
+    ASSERT_EQ(EC_OK, NM_getDeviceReport(ctx, "wrong device id", &report));
+    ASSERT_EQ(nullptr, report);
+    ASSERT_NO_THROW(NM_freeCharBuffer(report));
+    ASSERT_NO_THROW(NM_freeCharBuffer(nullptr));
+}