Initial commit of the Bridging Project's Mini Plugin Manager + 1 plugin. 07/16707/41
authorGaganpreet Kaur <gaganpreetx.kaur@intel.com>
Thu, 23 Feb 2017 04:19:48 +0000 (09:49 +0530)
committerTodd Malsbary <todd.malsbary@intel.com>
Fri, 24 Feb 2017 00:08:16 +0000 (00:08 +0000)
This commit is comprised of the Mini Plugin Manager framework along with
1 plugin and 1 stubbed plugin. This commit allows a user to test the full
end-to-end story of the MPM.

Change-Id: I3914963442cb175b438c3e3d8ff2d244f8eb339b
Signed-off-by: Joseph Morrow <joseph.l.morrow@intel.com>
Signed-off-by: vijendrx <vijendrax.kumar@intel.com>
Signed-off-by: Mandeep Shetty <mandeep.shetty@intel.com>
Signed-off-by: skambalx <srikarx.kambaluru@intel.com>
Signed-off-by: Gaganpreet Kaur <gaganpreetx.kaur@intel.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/16707
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
Reviewed-by: George Nash <george.nash@intel.com>
Reviewed-by: Ossama Othman <ossama.othman@intel.com>
Reviewed-by: Todd Malsbary <todd.malsbary@intel.com>
36 files changed:
.gitignore
Readme.scons.txt
SConstruct
bridging/SConscript [new file with mode: 0644]
bridging/common/ConcurrentIotivityUtils.cpp [new file with mode: 0644]
bridging/common/SConscript [new file with mode: 0644]
bridging/common/curlClient.cpp [new file with mode: 0644]
bridging/common/messageHandler.cpp [new file with mode: 0644]
bridging/common/pipeHandler.cpp [new file with mode: 0644]
bridging/common/pluginIf.cpp [new file with mode: 0644]
bridging/common/pluginProcess.cpp [new file with mode: 0644]
bridging/common/pluginServer.cpp [new file with mode: 0644]
bridging/include/ConcurrentIotivityUtils.h [new file with mode: 0644]
bridging/include/IotivityWorkItem.h [new file with mode: 0644]
bridging/include/JsonHelper.h [new file with mode: 0644]
bridging/include/WorkQueue.h [new file with mode: 0644]
bridging/include/curlClient.h [new file with mode: 0644]
bridging/include/messageHandler.h [new file with mode: 0644]
bridging/include/mpmErrorCode.h [new file with mode: 0644]
bridging/include/pluginIf.h [new file with mode: 0644]
bridging/include/pluginServer.h [new file with mode: 0644]
bridging/mini_plugin_manager/SConscript [new file with mode: 0644]
bridging/mini_plugin_manager/miniPluginManager.cpp [new file with mode: 0644]
bridging/mini_plugin_manager/miniPluginManager.h [new file with mode: 0644]
bridging/mpm_client/MPMSampleClient.cpp [new file with mode: 0644]
bridging/mpm_client/README [new file with mode: 0644]
bridging/mpm_client/SConscript [new file with mode: 0644]
bridging/plugins/lifx_plugin/README [new file with mode: 0644]
bridging/plugins/lifx_plugin/SConscript [new file with mode: 0644]
bridging/plugins/lifx_plugin/lifx.cnf.sample [new file with mode: 0644]
bridging/plugins/lifx_plugin/lifxResource.cpp [new file with mode: 0644]
bridging/plugins/lifx_plugin/lifx_objects/lifx.cpp [new file with mode: 0644]
bridging/plugins/lifx_plugin/lifx_objects/lifx.h [new file with mode: 0644]
bridging/plugins/stub_plugin/stub_plugin.cpp [new file with mode: 0644]
extlibs/rapidjson/SConscript [new file with mode: 0644]
resource/c_common/platform_features.h

index c94f3b6..ccdaa84 100644 (file)
@@ -124,6 +124,7 @@ extlibs/bluez/bluez
 extlibs/mbedtls/mbedtls
 extlibs/raxmpp/raxmpp
 extlibs/yaml/yaml
+extlibs/rapidjson/rapidjson
 
 # Ignore editor (e.g. Emacs) backup and autosave files
 *~
index c1a94a4..7deaf4a 100644 (file)
@@ -8,7 +8,7 @@
       $ sudo apt-get install git-core scons ssh build-essential g++ doxygen valgrind
 
     Install external libraries:
-      $ sudo apt-get install libboost-dev libboost-program-options-dev libboost-thread-dev uuid-dev libssl-dev libtool libglib2.0-dev
+      $ sudo apt-get install libboost-dev libboost-program-options-dev libboost-thread-dev uuid-dev libssl-dev libtool libglib2.0-dev libcap-dev libcurl4-openssl-dev autotools-dev autoconf
 
     Build release binaries:
       $ scons
index 74b4589..38bf323 100644 (file)
@@ -72,6 +72,9 @@ SConscript(build_dir + 'cloud/SConscript')
 # Build "plugin interface" sub-project
 SConscript(build_dir + 'plugins/SConscript')
 
+# Build "bridging" sub-project
+SConscript(build_dir + 'bridging/SConscript')
+
 # Append targets information to the help information, to see help info, execute command line:
 #     $ scon [options] -h
 env.PrintTargets()
diff --git a/bridging/SConscript b/bridging/SConscript
new file mode 100644 (file)
index 0000000..eac2c98
--- /dev/null
@@ -0,0 +1,49 @@
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# 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.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+##
+# Bridging build script
+##
+
+import os.path
+
+Import('env')
+
+target_os = env.get('TARGET_OS')
+build_sample = 'ON'
+src_dir = env.get('SRC_DIR')
+
+# rapidjson fetch
+SConscript(os.path.join(env.get('SRC_DIR'), 'extlibs', 'rapidjson', 'SConscript'))
+
+if target_os not in ['android', 'arduino', 'darwin', 'ios', 'tizen', 'msys_nt', 'windows']:
+
+    SConscript(os.path.join('common', 'SConscript'))
+
+    SConscript(os.path.join('mini_plugin_manager', 'SConscript'))
+
+    SConscript(os.path.join('mpm_client', 'SConscript'))
+
+    SConscript(os.path.join('plugins', 'lifx_plugin', 'SConscript'))
+
+#    SConscript(os.path.join('plugins', 'hue_plugin', 'SConscript'))
+
+#    SConscript(os.path.join('plugins', 'nest_plugin', 'SConscript'))
+
+#    SConscript(os.path.join('plugins', 'lyric_plugin', 'SConscript'))
diff --git a/bridging/common/ConcurrentIotivityUtils.cpp b/bridging/common/ConcurrentIotivityUtils.cpp
new file mode 100644 (file)
index 0000000..0c77e4b
--- /dev/null
@@ -0,0 +1,193 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 <sstream>
+#include "octypes.h"
+#include "ConcurrentIotivityUtils.h"
+#include "ocpayload.h"
+#include "logger.h"
+
+#define TAG "CONCURRENT_IOTIVITY_UTILS"
+
+using namespace OC::Bridging;
+
+// Static member initializations.
+std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>> ConcurrentIotivityUtils::m_queue;
+
+void ConcurrentIotivityUtils::startWorkerThreads()
+{
+    if (m_threadStarted)
+    {
+        throw "Work Queue Processor already started";
+    }
+    m_processWorkQueueThread = std::thread(&ConcurrentIotivityUtils::processWorkQueue, this);
+    m_ocProcessThread = std::thread(&ConcurrentIotivityUtils::callOCProcess, this);
+    m_threadStarted = true;
+}
+
+void ConcurrentIotivityUtils::stopWorkerThreads()
+{
+    m_shutDownOCProcessThread = true;
+    m_queue->shutdown();
+    m_processWorkQueueThread.join();
+    m_ocProcessThread.join();
+    m_threadStarted = false;
+}
+
+OCStackResult ConcurrentIotivityUtils::queueCreateResource(const std::string &uri,
+        const std::string &resourceType,
+        const std::string &interface, OCEntityHandler entityHandler,
+        void *callbackParam, uint8_t resourceProperties)
+{
+    std::unique_ptr<IotivityWorkItem> item = make_unique<CreateResourceItem>(
+                uri, resourceType, interface, entityHandler, callbackParam, resourceProperties
+            );
+
+    m_queue->put(std::move(item));
+
+    return OC_STACK_OK;
+}
+
+OCStackResult ConcurrentIotivityUtils::respondToRequest(OCEntityHandlerRequest *request,
+        OCRepPayload *payload, OCEntityHandlerResult responseCode)
+{
+    std::unique_ptr<OCEntityHandlerResponse> response = make_unique<OCEntityHandlerResponse>();
+
+    response->requestHandle = request->requestHandle;
+    response->resourceHandle = request->resource;
+    response->ehResult = responseCode;
+
+    // Clone a copy since this allocation is going across thread boundaries.
+    response->payload = (OCPayload *) OCRepPayloadClone(payload);
+
+    if (payload != NULL && response->payload == NULL)
+    {
+        return OC_STACK_NO_MEMORY;
+    }
+
+    std::unique_ptr<IotivityWorkItem> item = make_unique<SendResponseItem>(std::move(response));
+    m_queue->put(std::move(item));
+
+    return OC_STACK_OK;
+}
+
+OCStackResult ConcurrentIotivityUtils::respondToRequestWithError(OCEntityHandlerRequest *request,
+        const std::string &errorMessage,
+        OCEntityHandlerResult errorCode)
+{
+    OCRepPayload *errorPayload = NULL;
+
+    if (!errorMessage.empty())
+    {
+        errorPayload = OCRepPayloadCreate();
+
+        if (!errorPayload)
+        {
+            return OC_STACK_NO_MEMORY;
+        }
+
+        OCRepPayloadSetPropString(errorPayload, "x.org.iotivity.error", errorMessage.c_str());
+    }
+
+    OCStackResult res = respondToRequest(request, errorPayload, errorCode);
+
+    if (errorPayload)
+    {
+        OCRepPayloadDestroy(errorPayload);
+    }
+
+    return res;
+}
+
+OCStackResult ConcurrentIotivityUtils::queueNotifyObservers(const std::string &resourceUri)
+{
+    std::unique_ptr<IotivityWorkItem> item = make_unique<NotifyObserversItem>(resourceUri);
+    m_queue->put(std::move(item));
+    return OC_STACK_OK;
+}
+
+OCStackResult ConcurrentIotivityUtils::queueDeleteResource(const std::string &uri)
+{
+    std::unique_ptr<IotivityWorkItem> item = make_unique<DeleteResourceItem>(uri);
+    m_queue->put(std::move(item));
+    return OC_STACK_OK;
+}
+
+bool ConcurrentIotivityUtils::getUriFromHandle(OCResourceHandle handle, std::string &uri)
+{
+    const char *uri_c = OCGetResourceUri(handle);
+
+    if (uri_c == NULL)
+    {
+        return false;
+    }
+
+    uri = uri_c;
+    return true;
+}
+
+void ConcurrentIotivityUtils::getKeyValueParams(const std::string &query,
+        std::map<std::string, std::string> &keyValueMap)
+{
+    if (query.empty())
+    {
+        return;
+    }
+
+    std::stringstream ss(query);
+
+    std::string keyValuePair;
+
+    while (std::getline(ss, keyValuePair, '&'))
+    {
+        auto keyValueSeparator = keyValuePair.find('=');
+
+        if (keyValueSeparator == std::string::npos)
+        {
+            continue;
+        }
+
+        std::string key = keyValuePair.substr(0, keyValueSeparator);
+        std::string value = keyValuePair.substr(keyValueSeparator + 1);
+
+        keyValueMap[key] = value;
+    }
+}
+
+bool ConcurrentIotivityUtils::isRequestForDefaultInterface(const std::string &query)
+{
+    if (query.empty())
+    {
+        return false;
+    }
+    std::map<std::string, std::string> keyValueParams;
+
+    getKeyValueParams(query, keyValueParams);
+
+    auto it = keyValueParams.find(OC_RSRVD_INTERFACE);
+
+    if (it == keyValueParams.end())
+    {
+        return false;
+    }
+
+    return it->second == OC_RSRVD_INTERFACE_DEFAULT;
+}
diff --git a/bridging/common/SConscript b/bridging/common/SConscript
new file mode 100644 (file)
index 0000000..061b721
--- /dev/null
@@ -0,0 +1,87 @@
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# 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.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+##
+# MPM Common Plugin build script
+##
+
+import os
+import os.path
+
+Import('env')
+
+target_os = env.get('TARGET_OS')
+src_dir = env.get('SRC_DIR')
+bridging_path = os.path.join(src_dir, 'bridging')
+
+mpmcommon_env = env.Clone()
+
+print "Reading MPM Common Plugin script"
+
+def maskFlags(flags):
+    flags = [flags.replace('-Wl,--no-undefined', '' ) for flags in flags]
+    return flags
+
+######################################################################
+# Build flags
+######################################################################
+mpmcommon_env.PrependUnique(CPPPATH = [
+                              os.path.join(src_dir, 'resource', 'include'),
+                              ])
+mpmcommon_env.AppendUnique(CPPPATH = [ os.path.join(bridging_path, 'include'),
+                             ])
+
+if target_os not in ['arduino', 'windows']:
+    mpmcommon_env.AppendUnique(CPPDEFINES = ['WITH_POSIX'])
+    mpmcommon_env.AppendUnique(CXXFLAGS = ['-std=c++0x', '-Wall', '-Wextra', '-Werror', '-fpic'])
+
+if target_os in ['darwin','ios']:
+    mpmcommon_env.AppendUnique(CPPDEFINES = ['_DARWIN_C_SOURCE'])
+
+mpmcommon_env.AppendUnique(RPATH = [mpmcommon_env.get('BUILD_DIR')])
+mpmcommon_env.AppendUnique(LIBPATH = [mpmcommon_env.get('BUILD_DIR')])
+mpmcommon_env['LINKFLAGS'] = maskFlags(env['LINKFLAGS'])
+
+if mpmcommon_env.get('LOGGING'):
+    mpmcommon_env.AppendUnique(CPPDEFINES = ['TB_LOG'])
+
+mpmcommon_env.PrependUnique(LIBS = ['m',
+                               'octbstack',
+                               'ocsrm',
+                               'connectivity_abstraction',
+                               'coap',
+                               'curl' ])
+
+#####################################################################
+# Source files and Target(s)
+######################################################################
+mpmcommon_src = [
+         os.path.join(bridging_path, 'common', 'pluginIf.cpp'),
+         os.path.join(bridging_path, 'common', 'pluginServer.cpp'),
+         os.path.join(bridging_path, 'common', 'pipeHandler.cpp'),
+         os.path.join(bridging_path, 'common', 'messageHandler.cpp'),
+         os.path.join(bridging_path, 'common', 'curlClient.cpp'),
+         os.path.join(bridging_path, 'common', 'pluginProcess.cpp'),
+         os.path.join(bridging_path, 'common', 'ConcurrentIotivityUtils.cpp')
+         ]
+
+mpmcommon_env.AppendUnique(MPMCOMMON_SRC = mpmcommon_src)
+mpmcommonlib = mpmcommon_env.StaticLibrary('mpmcommon', mpmcommon_env.get('MPMCOMMON_SRC'))
+mpmcommon_env.InstallTarget(mpmcommonlib, 'mpmcommon')
+mpmcommon_env.UserInstallTargetLib(mpmcommonlib, 'mpmcommon')
diff --git a/bridging/common/curlClient.cpp b/bridging/common/curlClient.cpp
new file mode 100644 (file)
index 0000000..658619b
--- /dev/null
@@ -0,0 +1,184 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 "curlClient.h"
+#include <iostream>
+#include "logger.h"
+
+using namespace std;
+using namespace OC::Bridging;
+
+#define TAG "CURL_CLIENT"
+
+#define DEFAULT_CURL_TIMEOUT_SECONDS     60L
+
+
+size_t CurlClient::WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+    size_t realsize = size * nmemb;
+    MemoryChunk *mem = static_cast<MemoryChunk *>(userp);
+
+    mem->memory = static_cast<char *>(realloc(mem->memory, mem->size + realsize + 1));
+    if (mem->memory == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "not enough memory!");
+        return 0;
+    }
+
+    memcpy(&(mem->memory[mem->size]), contents, realsize);
+    mem->size += realsize;
+    mem->memory[mem->size] = 0;
+
+    return realsize;
+}
+
+int CurlClient::decomposeHeader(const char *header, std::vector<std::string> &headers)
+{
+    size_t npos = 0;
+    if (NULL == header)
+    {
+        return MPM_RESULT_INVALID_PARAMETER;
+    }
+
+    std::string header_s = header;
+
+    npos = header_s.find("\r\n");
+    while (npos != std::string::npos)
+    {
+        std::string s = header_s.substr(0, npos);
+        headers.push_back(s);
+        header_s = header_s.substr(npos + 2);
+        npos = header_s.find("\r\n");
+    }
+
+    return MPM_RESULT_OK;
+}
+
+int CurlClient::doInternalRequest(const std::string &url,
+                                  const std::string &method,
+                                  const std::vector<std::string> &inHeaders,
+                                  const std::string &request,
+                                  const std::string &username,
+                                  std::vector<std::string> &outHeaders,
+                                  std::string &response)
+{
+    int result = MPM_RESULT_OK;
+    CURL *curl = NULL;
+    CURLcode res = CURLE_OK;
+    struct curl_slist *headers = NULL;
+    MemoryChunk rsp_body;
+    MemoryChunk rsp_header;
+    m_lastResponseCode = INVALID_RESPONSE_CODE; //initialize recorded code value in case of
+    //early return
+
+    curl = curl_easy_init();
+    if (curl != NULL)
+    {
+        curl_easy_reset(curl);
+
+        for (unsigned int i = 0; i < inHeaders.size(); i++)
+        {
+            headers = curl_slist_append(headers, inHeaders[i].c_str());
+            if (NULL == headers)
+            {
+                OIC_LOG(ERROR, TAG, "curl_slist_append failed");
+                result = MPM_RESULT_OUT_OF_MEMORY;
+                goto CLEANUP;
+            }
+        }
+
+        // Expect the transfer to complete within DEFAULT_CURL_TIMEOUT seconds
+        curl_easy_setopt(curl, CURLOPT_TIMEOUT, DEFAULT_CURL_TIMEOUT_SECONDS);
+
+        // Set CURLOPT_VERBOSE to 1L below to see detailed debugging
+        // information on curl operations.
+        curl_easy_setopt(curl, CURLOPT_VERBOSE, 0);
+        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
+        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
+        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.c_str());
+        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
+        curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, WriteCallback);
+        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &rsp_body);
+        curl_easy_setopt(curl, CURLOPT_HEADERDATA, &rsp_header);
+        if (CURLUSESSL_NONE != m_useSsl)
+        {
+            curl_easy_setopt(curl, CURLOPT_USE_SSL, m_useSsl);
+        }
+
+        if (!username.empty())
+        {
+            curl_easy_setopt(curl, CURLOPT_USERNAME, username.c_str());
+        }
+
+        if (!method.empty())
+        {
+            // NOTE: The documentation for CURLOPT_CUSTOMREQUEST only lists HTTP, FTP, IMAP, POP3, and SMTP
+            //       as valid options, although it says all this option does is change the string used in
+            //       the request. (Basically, don't know whether this option has any effect as currently
+            //       used?
+
+            /// only required for GET, PUT, DELETE
+            curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str());
+        }
+
+        res = curl_easy_perform(curl);
+        if (res != CURLE_OK)
+        {
+            OIC_LOG_V(ERROR, TAG, "curl_easy_perform failed with %lu", (unsigned long) res);
+            result = MPM_RESULT_NETWORK_ERROR;
+            goto CLEANUP;
+        }
+
+        if (CURLE_OK != curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &m_lastResponseCode))
+        {
+            OIC_LOG(WARNING, TAG, "curl_easy_getinfo(CURLINFO_RESPONSE_CODE) failed.");
+            m_lastResponseCode = INVALID_RESPONSE_CODE;
+        }
+
+        response = rsp_body.memory;
+
+        decomposeHeader(rsp_header.memory, outHeaders);
+    }
+    else
+    {
+        OIC_LOG(ERROR, TAG, "curl_easy_init failed");
+        result = MPM_RESULT_INTERNAL_ERROR;
+    }
+
+CLEANUP:
+    if (NULL != headers)
+    {
+        curl_slist_free_all(headers);
+    }
+
+    free(rsp_body.memory);
+
+    free(rsp_header.memory);
+
+    if (NULL != curl)
+    {
+        curl_easy_cleanup(curl);
+    }
+
+    return result;
+}
diff --git a/bridging/common/messageHandler.cpp b/bridging/common/messageHandler.cpp
new file mode 100644 (file)
index 0000000..acf0811
--- /dev/null
@@ -0,0 +1,358 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the plugin server implementation.  Most of this
+ * implementation is reused for other plugins.  There is NO customization
+ * required of functions in this file to accommodate plugin specific code.
+ */
+
+
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "oic_malloc.h"
+#include "pluginIf.h"
+#include "pluginServer.h"
+#include "cbor.h"
+#include "logger.h"
+#include "StringConstants.h"
+
+#define TAG "MESSAGE_HANDLER"
+
+#define NAME                    "NAME"
+#define MANUFACTURER            "MF"
+#define DEVICETYPE              "DEVICE_TYPE"
+#define PLUGINSPECIFICDETAILS   "PluginSpecificDetails"
+#define RESOURCES               "RESOURCES"
+
+#define VERIFY_CBOR_SUCCESS(log_tag, err, log_message) \
+    if ((CborNoError != (err)) && (CborErrorOutOfMemory != (err))) \
+    { \
+        if ((log_tag) && (log_message)) \
+        { \
+            OIC_LOG_V(ERROR, TAG, "cbor error - %s \n", log_message); \
+        } \
+    } \
+
+MPMCommonPluginCtx *g_com_ctx;
+
+void MPMRequestHandler(MPMPipeMessage *pipe_message, MPMPluginCtx *ctx)
+{
+    OIC_LOG(DEBUG, TAG, "Inside request_handler");
+
+    switch (pipe_message->msgType)
+    {
+        case MPM_SCAN:
+            OIC_LOG(DEBUG, TAG, "plugin_scan called");
+            pluginScan(ctx, pipe_message);
+            break;
+        case MPM_ADD:
+            OIC_LOG(DEBUG, TAG, "plugin_add called");
+            pluginAdd(ctx, pipe_message);
+            break;
+        case MPM_REMOVE:
+            OIC_LOG(DEBUG, TAG, "plugin_remove called");
+            pluginRemove(ctx, pipe_message);
+            break;
+        case MPM_RECONNECT:
+            OIC_LOG(DEBUG, TAG, "Plugin reconnect called");
+            pluginReconnect(ctx, pipe_message);
+            break;
+        default:OIC_LOG(DEBUG, TAG, "Currently not supported");
+            break;
+    }
+}
+
+MPMResult MPMSendResponse(const void *response, size_t size, MPMMessageType type)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+
+    OIC_LOG_V(DEBUG, TAG, "Inside response_handler and size of payload = %d", (int)size);
+
+    MPMPipeMessage pipe_message;
+
+    pipe_message.payloadSize = size;
+    pipe_message.msgType = type;
+    pipe_message.payload = (uint8_t *)response;
+
+    result = MPMWritePipeMessage(g_com_ctx->parent_reads_fds.write_fd, &pipe_message);
+
+    return result;
+}
+
+static int64_t AddTextStringToMap(CborEncoder *map, const char *key, size_t keylen,
+                                  const char *value)
+{
+    int64_t err = cbor_encode_text_string(map, key, keylen);
+    if (CborNoError != err)
+    {
+        return err;
+    }
+    return cbor_encode_text_string(map, value, strlen(value));
+}
+
+static int64_t AddstructureToMap(CborEncoder *map, const char *key, size_t keylen,
+                                 const char *value, size_t valueSize)
+{
+    int64_t err = cbor_encode_text_string(map, key, keylen);
+    if (CborNoError != err)
+    {
+        return err;
+    }
+    return cbor_encode_text_string(map, value, valueSize);
+}
+
+int64_t MPMFormMetaData(MPMResourceList *list, MPMDeviceSpecificData *deviceDetails,
+                        uint8_t *buff, size_t size, void *details, size_t payloadSize)
+{
+
+    CborEncoder encoder;
+    int64_t err = CborNoError;
+    CborEncoder rootArray, rootMap, linkMap;
+    CborEncoder linkArray;
+    MPMResourceList *temp = NULL;
+
+    cbor_encoder_init(&encoder, buff, size, 0);
+
+    err = cbor_encoder_create_array(&encoder, &rootArray, 1);
+    VERIFY_CBOR_SUCCESS(TAG, err, " Creating Root Array");
+
+    err = cbor_encoder_create_map(&rootArray, &rootMap, CborIndefiniteLength);
+    VERIFY_CBOR_SUCCESS(TAG, err, "Creating Root MAP");
+
+    if (deviceDetails)
+    {
+        err = AddTextStringToMap(&rootMap, NAME, sizeof(NAME) - 1,
+                                 deviceDetails->devName);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Adding device name");
+
+        err = AddTextStringToMap(&rootMap, MANUFACTURER, sizeof(MANUFACTURER) - 1,
+                                 deviceDetails->manufacturerName);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Adding Manufacture name");
+
+        err = AddTextStringToMap(&rootMap, DEVICETYPE, sizeof(DEVICETYPE) - 1,
+                                 deviceDetails->devType);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Adding Device Type");
+    }
+
+    if (details)
+    {
+        err = AddstructureToMap(&rootMap, PLUGINSPECIFICDETAILS, sizeof(PLUGINSPECIFICDETAILS) - 1,
+                                (const char *)details, payloadSize);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Adding Plugin specific Details");
+    }
+
+    err = cbor_encode_text_string(&rootMap, RESOURCES, sizeof(RESOURCES) - 1);
+    VERIFY_CBOR_SUCCESS(TAG, err, "Encoding Resources string");
+
+    err = cbor_encoder_create_array(&rootMap, &linkArray, CborIndefiniteLength);
+    VERIFY_CBOR_SUCCESS(TAG, err, "Creating Link Array");
+
+    for ( ; list ; )
+    {
+        temp = list;
+        OIC_LOG_V(DEBUG, TAG, " href - %s\n rt -  %s\n if - %s\n bm - %d\n", list->href, list->rt,
+                  list->interfaces, list->bitmap);
+        // resource map inside the links array.
+        err = cbor_encoder_create_map(&linkArray, &linkMap, 4);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Creating Link Map");
+
+        err = AddTextStringToMap(&linkMap, OC::Key::RESOURCETYPESKEY.c_str(),
+                                 OC::Key::RESOURCETYPESKEY.size(),
+                                 list->rt);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Adding Resource type");
+
+        err = AddTextStringToMap(&linkMap, OC::Key::URIKEY.c_str(), OC::Key::URIKEY.size(),
+                                 list->href);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Adding OC::Key::URIKEY");
+
+        err = AddTextStringToMap(&linkMap, OC::Key::INTERFACESKEY.c_str(),
+                                 OC::Key::INTERFACESKEY.size(), list->interfaces);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Adding Resource Interface");
+
+        err = cbor_encode_text_string(&linkMap, OC::Key::BMKEY.c_str(), OC::Key::BMKEY.size());
+        VERIFY_CBOR_SUCCESS(TAG, err, "Encoding Bitmap string");
+
+        err = cbor_encode_int(&linkMap, list->bitmap);
+        VERIFY_CBOR_SUCCESS(TAG, err, "encoding bit map");
+
+        // close link map inside link array
+        err = cbor_encoder_close_container(&linkArray, &linkMap);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Closing link map");
+
+        list = list -> next;
+        OICFree(temp);
+    }
+
+    // Close links array inside the root map.
+    err = cbor_encoder_close_container(&rootMap, &linkArray);
+    VERIFY_CBOR_SUCCESS(TAG, err, "Closing link array");
+
+    // close root map inside the root array.
+    err = cbor_encoder_close_container(&rootArray, &rootMap);
+    VERIFY_CBOR_SUCCESS(TAG, err, "Closing Root Map");
+
+    // Close the final root array.
+    err = cbor_encoder_close_container(&encoder, &rootArray);
+    VERIFY_CBOR_SUCCESS(TAG, err, "Closing root Array");
+
+    return err;
+}
+
+void MPMParseMetaData(const uint8_t *buff, size_t size, MPMResourceList **list, void **details)
+{
+    int64_t err = CborNoError;
+    CborValue rootMapValue, linkMapValue;
+    CborValue resourceMapValue;
+    CborValue curVal;
+    CborValue rootValue;
+    CborParser parser;
+    int bitmap;
+
+    err = cbor_parser_init(buff, size, 0, &parser, &rootValue);
+    VERIFY_CBOR_SUCCESS(TAG, err, "Parser cbor init");
+
+    if (cbor_value_is_array(&rootValue))
+    {
+        OIC_LOG_V(DEBUG, TAG, "ENCODED DATA - %s ", (char *)buff);
+        err = cbor_value_enter_container(&rootValue, &rootMapValue);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Entering root array");
+        if (!cbor_value_is_map(&rootMapValue))
+        {
+            OIC_LOG(ERROR, TAG, "ERROR, Malformed packet");
+            return ;
+        }
+
+        if (cbor_value_is_map(&rootMapValue))
+        {
+            // Parsing device details
+            err = cbor_value_map_find_value(&rootMapValue, NAME, &curVal);
+            VERIFY_CBOR_SUCCESS(TAG, err, "finding Name in map");
+            if (cbor_value_is_valid(&curVal))
+            {
+                if (cbor_value_is_text_string(&curVal))
+                {
+                    size_t len = 0;
+                    char *input = NULL;
+                    err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+                    VERIFY_CBOR_SUCCESS(TAG, err, "Duplicating name string");
+                    OIC_LOG_V(DEBUG, TAG, "\"NAME\":%s\n", input);
+                    free(input);
+                }
+            }
+        }
+
+        err = cbor_value_map_find_value(&rootMapValue, MANUFACTURER, &curVal);
+        VERIFY_CBOR_SUCCESS(TAG, err, "Finding Manufacturer details in map");
+        if (cbor_value_is_valid(&curVal))
+        {
+            if (cbor_value_is_text_string(&curVal))
+            {
+                size_t len = 0;
+                char *input = NULL;
+                err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+                VERIFY_CBOR_SUCCESS(TAG, err, " Copying Text string");
+                OIC_LOG_V(DEBUG, TAG, "\"MF\":%s\n", input);
+                free(input);
+            }
+        }
+
+        err = cbor_value_map_find_value(&rootMapValue, PLUGINSPECIFICDETAILS, &curVal);
+        VERIFY_CBOR_SUCCESS(TAG, err, " Finding PLUGINSPECIFICDETAILS in map ");
+        if (cbor_value_is_valid(&curVal))
+        {
+            if (cbor_value_is_text_string(&curVal))
+            {
+                size_t len = 0;
+                char *input = NULL;
+                err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+                VERIFY_CBOR_SUCCESS(TAG, err, " Copying Text string");
+                *details = (void *)input;
+            }
+        }
+
+        err = cbor_value_map_find_value(&rootMapValue, RESOURCES, &linkMapValue);
+        VERIFY_CBOR_SUCCESS(TAG, err, " Finding RESOURCES in map ");
+        // Enter the links array and start iterating through the array processing
+        // each resource which shows up as a map.
+        if (cbor_value_is_valid(&linkMapValue))
+        {
+
+            err = cbor_value_enter_container(&linkMapValue, &resourceMapValue);
+            VERIFY_CBOR_SUCCESS(TAG, err, " Entering Link map ");
+            while (cbor_value_is_map(&resourceMapValue))
+            {
+                MPMResourceList *tempPtr;
+                tempPtr = (MPMResourceList *) OICCalloc(1, sizeof(MPMResourceList));
+                if (tempPtr == NULL)
+                {
+                    OIC_LOG(ERROR, TAG, "calloc failed");
+                    return;
+                }
+                size_t len = 0;
+                char *input = NULL;
+                err = cbor_value_map_find_value(&resourceMapValue, OC::Key::URIKEY.c_str(), &curVal);
+                VERIFY_CBOR_SUCCESS(TAG, err, " Finding Uri in map ");
+
+                err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+                VERIFY_CBOR_SUCCESS(TAG, err, " Copying Text string");
+                strncpy(tempPtr->href, input, MPM_MAX_LENGTH_64);
+                OIC_LOG_V(DEBUG, TAG, "\"ref\":%s\n", input);
+                free(input);
+                input = NULL;
+
+                err = cbor_value_map_find_value(&resourceMapValue, OC::Key::RESOURCETYPESKEY.c_str(), &curVal);
+                VERIFY_CBOR_SUCCESS(TAG, err, " Finding Rt in link map ");
+                err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+                VERIFY_CBOR_SUCCESS(TAG, err, " Copying Text string");
+                strncpy(tempPtr->rt, input, MPM_MAX_LENGTH_64);
+                OIC_LOG_V(DEBUG, TAG, "\"rt\":%s\n", input);
+                free(input);
+                input = NULL;
+
+                err = cbor_value_map_find_value(&resourceMapValue, OC::Key::INTERFACESKEY.c_str(), &curVal);
+                VERIFY_CBOR_SUCCESS(TAG, err, " Finding If's in link map ");
+                err = cbor_value_dup_text_string(&curVal, &input, &len, NULL);
+                VERIFY_CBOR_SUCCESS(TAG, err, " Copying Text string");
+                strncpy(tempPtr->interfaces, input, MPM_MAX_LENGTH_64);
+                OIC_LOG_V(DEBUG, TAG, "\"if\":%s\n", input);
+                free(input);
+                input = NULL;
+
+                err = cbor_value_map_find_value(&resourceMapValue, OC::Key::BMKEY.c_str(), &curVal);
+                VERIFY_CBOR_SUCCESS(TAG, err, " Finding Bms in link map ");
+                if (cbor_value_is_integer(&curVal))
+                {
+                    err = cbor_value_get_int(&curVal, &bitmap);
+                    VERIFY_CBOR_SUCCESS(TAG, err, " Getting bit map value fromx link map ");
+                    tempPtr->bitmap = bitmap;
+                    OIC_LOG_V(DEBUG, TAG, "\"bm\":%d\n", bitmap);
+                }
+
+                tempPtr->next = *list;
+                *list  = tempPtr;
+                err = cbor_value_advance(&resourceMapValue);
+                VERIFY_CBOR_SUCCESS(TAG, err, "in resource map value advance");
+            }
+        }
+    }
+}
diff --git a/bridging/common/pipeHandler.cpp b/bridging/common/pipeHandler.cpp
new file mode 100644 (file)
index 0000000..eb97286
--- /dev/null
@@ -0,0 +1,132 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/**
+ * This file is mainly intended for writing and reading the messages over the pipe,
+ * This is the common code functionality where either client or MPM can call these
+ * functions to write to the pipe and read from the pipe.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include "messageHandler.h"
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "platform_features.h"
+#include "oic_malloc.h"
+#include "logger.h"
+#include <cinttypes>
+
+#define TAG "PIPE_HANDLER"
+
+MPMResult MPMWritePipeMessage(int fd, const MPMPipeMessage *pipe_message)
+{
+    ssize_t ret = 0;
+    OIC_LOG(DEBUG, TAG, "writing message over pipe");
+
+    OIC_LOG_V(DEBUG, TAG, "Message type = %d, payload size = %" PRIuPTR, pipe_message->msgType,
+              pipe_message->payloadSize);
+
+    ret = write(fd, &pipe_message->payloadSize, sizeof(size_t));
+    if (ret < 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Error writing message over the pipe - [%s]", strerror(errno));
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    ret = write(fd, &pipe_message->msgType, sizeof(MPMMessageType));
+    if (ret < 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Error writing message over the pipe - [%s]", strerror(errno));
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    if (pipe_message->payloadSize > 0)
+    {
+        ret = write(fd, pipe_message->payload, pipe_message->payloadSize);
+
+        if (ret < 0)
+        {
+            OIC_LOG_V(ERROR, TAG, "Error writing message over the pipe - [%s]", strerror(errno));
+            return MPM_RESULT_INTERNAL_ERROR;
+        }
+    }
+
+    return MPM_RESULT_OK;
+}
+
+
+ssize_t MPMReadPipeMessage(int fd, MPMPipeMessage *pipe_message)
+{
+    ssize_t ret = 0, bytesRead =0;
+    OIC_LOG(DEBUG, TAG, "reading message from pipe");
+    OIC_LOG_V(DEBUG, TAG, "Message type = %d, payload size = %" PRIuPTR , pipe_message->msgType,
+                  pipe_message->payloadSize);
+
+    ret = read(fd, &pipe_message->payloadSize, sizeof(size_t));
+    if (ret < 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Error Reading message from the pipe - [%s]", strerror(errno));
+        return ret;
+    }
+    bytesRead = ret;
+
+    ret = read(fd, &pipe_message->msgType, sizeof(MPMMessageType));
+    if (ret < 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Error Reading message from the pipe - [%s]", strerror(errno));
+        return ret;
+    }
+    bytesRead += ret;
+
+
+    if (pipe_message->msgType == MPM_NOMSG)
+    {
+        bytesRead = 0;
+    }
+    else if (pipe_message->payloadSize > 0 && pipe_message->payloadSize <= SIZE_MAX)
+    {
+        pipe_message->payload = (uint8_t *) OICCalloc(1, pipe_message->payloadSize);
+        if (!pipe_message->payload)
+        {
+            OIC_LOG(ERROR, TAG, "failed to allocate memory");
+            bytesRead = 0;
+        }
+        else
+        {
+            ret = read(fd, (void*)pipe_message->payload, pipe_message->payloadSize);
+            if (ret < 0)
+            {
+                OIC_LOG_V(ERROR, TAG, "Error Reading message from the pipe - [%s]", strerror(errno));
+                return ret;
+            }
+            bytesRead += ret;
+        }
+    }
+    else
+    {
+        pipe_message->payload = NULL;
+        OIC_LOG(DEBUG, TAG, "no payload received");
+    }
+    return bytesRead;
+}
diff --git a/bridging/common/pluginIf.cpp b/bridging/common/pluginIf.cpp
new file mode 100644 (file)
index 0000000..e4b5353
--- /dev/null
@@ -0,0 +1,378 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the "C" plugin interface implementation */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <spawn.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "messageHandler.h"
+#include "logger.h"
+#include "oic_malloc.h"
+#include "mpmErrorCode.h"
+#include "pluginIf.h"
+#include "pluginServer.h"
+
+#define TAG "PLUGIN_IF"
+
+/* this function waits for a child process to signal that it is complete
+ * @param[in] child_pid child process id
+ * @param[in] timeout time to wait for child to complete
+ */
+static void waitForChildProcessToComplete(pid_t child_pid, int32_t timeout);
+
+
+/* This is a timed wait for pipe write; the written value is returned in the passed
+ * in message buffer.
+ * @param[in]  fd_to_parent       File descriptor
+ * @param[out] message            Message from the child
+ * @param[in]  timeout            Time to wait for pipe write in seconds
+ */
+static void timedWaitForPipeWrite(int fd_to_parent, MPMPipeMessage *message,
+                                  int32_t timeout);
+
+
+/**
+ * The purpose of this function is to create a context or instance of the
+ * plugin.  There is nothing stopping one from creating more than one
+ * instance of the plugin.  Currently it is expected that only one instance
+ * is created.
+ *
+ * @return  A pointer to the context data of this plugin that is managed
+ *          by this plugin.
+ */
+static MPMCommonPluginCtx *create()
+{
+    /* always try to create a new instance */
+    MPMCommonPluginCtx *ctx = (MPMCommonPluginCtx *) OICCalloc(1, sizeof(MPMCommonPluginCtx));
+
+    if (ctx == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Unable to allocate context.");
+    }
+    return ctx;
+}
+
+/**
+ * This function forks a new process and starts an OCF server in that
+ * process.  The plan is to only allow one fork per instance of
+ * the plugin.  There is a check for this, the start will fail if you
+ * call start twice on the same instance.
+ *
+ * @param[in] ctx        the plugin context created with the "create" function
+ *
+ * @return 0 on success, 1 on error
+ */
+static int start(MPMCommonPluginCtx *ctx)
+{
+    int result = 1;
+
+    if (ctx && !ctx->started)
+    {
+        pid_t pid;
+        MPMPipeMessage pipe_message;
+
+        pipe_message.payloadSize = 0;
+        pipe_message.msgType = MPM_NOMSG;
+        pipe_message.payload = NULL;
+
+        /* Create unnamed pipes for IPC prior to the fork so that both
+         * parent and child have the same information on the unnamed
+         * pipes.
+         */
+        int parent_result = pipe(&(ctx->parent_reads_fds.read_fd));
+        if (parent_result == -1)
+        {
+            OIC_LOG(ERROR, TAG, "Failed to create IPC unnamed pipe for parent.");
+            return result;
+        }
+        int child_result = pipe(&(ctx->child_reads_fds.read_fd));
+
+        if (child_result == -1)
+        {
+            OIC_LOG(ERROR, TAG, "Failed to create IPC unnamed pipe for child.");
+            close(ctx->parent_reads_fds.read_fd);
+            close(ctx->parent_reads_fds.write_fd);
+            return result;
+        }
+
+        switch (pid = fork())
+        {
+            case 0:
+                /* Child(plugin) process.
+                 * Close unused edges of unnamed pipes from the
+                 * child's perspective. Child is not going
+                 * write to the child's read pipe nor is the child
+                 * going to read from the parent's read pipe
+                 */
+                close(ctx->child_reads_fds.write_fd);
+                close(ctx->parent_reads_fds.read_fd);
+
+                /* Start the OCF server. This is a blocking call and will
+                   return only when the plugin stops*/
+                MPMPluginService(ctx);
+
+                OIC_LOG(INFO, TAG, "Child process complete.");
+
+                /* Close the other sides of the pipes from
+                 * the child's perspective
+                 */
+                close(ctx->child_reads_fds.read_fd);
+                close(ctx->parent_reads_fds.write_fd);
+
+                exit(0);
+                break;
+
+            default:
+
+                /* Parent process */
+                ctx->child_pid = pid;
+
+                /* The parent is not going to read
+                 * from the child's read pipe and the parent is not
+                 * going to write the parents read pipe.
+                 */
+                close(ctx->child_reads_fds.read_fd);
+                close(ctx->parent_reads_fds.write_fd);
+
+                /* The plugin may fail to create or start.
+                 * The parent must wait here for some time to
+                 * learn what happened.
+                 */
+                timedWaitForPipeWrite(ctx->parent_reads_fds.read_fd,
+                                      &pipe_message,
+                                      MPM_TIMEOUT_VAL_IN_SEC);
+                if (pipe_message.msgType == MPM_DONE)
+                {
+                    /* plugin was successful with its create and start */
+                    OIC_LOG_V(INFO, TAG, "Child: %d started successfully", ctx->child_pid);
+                    result = 0;
+                    ctx->started = true;
+                }
+                else
+                {
+                    /* We have a problem, and the plugin is NOT going to load.
+                     */
+                    OIC_LOG_V(ERROR, TAG, "Child: %d failed, either plugin create or start.",
+                              ctx->child_pid);
+
+                    OIC_LOG(ERROR, TAG, "Forced completion of the child");
+
+                    /* The parent must wait here until the child process is dead.
+                     * but this time we are not going to wait, we are only going
+                     * to force completion
+                     */
+                    waitForChildProcessToComplete(ctx->child_pid, 0);
+
+                    /* Let's close the rest of the pipe interfaces. Sides of the pipes
+                     * from the parent's perspective
+                     */
+                    close(ctx->child_reads_fds.write_fd);
+                    close(ctx->parent_reads_fds.read_fd);
+                }
+
+                OICFree((void*)pipe_message.payload);
+
+                break;
+
+            case -1:
+                perror("fork");
+                OIC_LOG(ERROR, TAG, "Fork returned error.");
+                break;
+        }
+    }
+    else
+    {
+        OIC_LOG(ERROR, TAG, "Plugin instance already forked or has not been created.");
+    }
+    return (result);
+}
+
+/**
+ * This function writes a stop message over the pipe to indicate the child
+ * to stop
+ *
+ * @param[in] ctx        the plugin context created with the "create" function
+ */
+static void stop(MPMCommonPluginCtx *ctx)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+
+    if (ctx && ctx->started)
+    {
+        /* IPC message variable */
+        MPMPipeMessage pipe_message;
+
+        pipe_message.payloadSize = 0;
+        pipe_message.msgType = MPM_STOP;
+        pipe_message.payload = NULL;
+
+        result = MPMWritePipeMessage(ctx->child_reads_fds.write_fd, &pipe_message);
+        if (result != MPM_RESULT_OK)
+        {
+            OIC_LOG(ERROR, TAG, "Failed to write to pipe for stop");
+            return;
+        }
+        OIC_LOG_V(INFO, TAG, "Parent telling the child process pid: %d to stop.", ctx->child_pid);
+
+        /* the parent must wait here until the child process is dead */
+        waitForChildProcessToComplete(ctx->child_pid, MPM_TIMEOUT_VAL_IN_SEC);
+
+        ctx->started = false;
+    }
+    else
+    {
+        OIC_LOG(INFO, TAG, "Stop has no effect; Plugin has not been started");
+    }
+    return;
+}
+
+/**
+ * This function calls the "stop" function follwed by destruction of the
+ * context created with the "create" function
+ *
+ * @param[in] ctx        the plugin context created with the "create" function
+ */
+static void destroy(MPMCommonPluginCtx *ctx)
+{
+    if (ctx && ctx->started)
+    {
+        stop(ctx);
+    }
+    OICFree(ctx);
+}
+
+static void waitForChildProcessToComplete(pid_t child_pid, int32_t timeout)
+{
+    int32_t waittime = 0;
+    int status = 0;
+    pid_t wpid = 0;
+
+    do
+    {
+        wpid = waitpid(child_pid, &status, WNOHANG);
+        if (wpid == 0)
+        {
+            if (waittime < timeout)
+            {
+                OIC_LOG_V(INFO, TAG, "Parent waiting on child: %d second(s): %d",
+                          child_pid, waittime);
+                sleep(1);
+                waittime++;
+            }
+            else
+            {
+                OIC_LOG_V(INFO, TAG, "Parent forced stopping of child: %d", child_pid);
+                kill(child_pid, SIGKILL);
+                break;
+            }
+        }
+    }
+    while ((wpid == 0) && (waittime <= timeout));
+
+    if (WIFEXITED(status))
+    {
+        OIC_LOG_V(INFO, TAG, "Child: %d completed \"exited\" with status: %d",
+                  child_pid, WEXITSTATUS(status));
+    }
+    else if (WIFSIGNALED(status))
+    {
+        OIC_LOG_V(INFO, TAG, "Child: %d completed \"signaled\" with status: %d",
+                  child_pid, WTERMSIG(status));
+    }
+}
+
+static void timedWaitForPipeWrite(int fd, MPMPipeMessage *msg, int32_t timeout)
+{
+    if (NULL != msg)
+    {
+        struct timeval tv;
+        fd_set fdset;
+        int nfd = -1;
+        int32_t waittime = 0;
+        ssize_t nbytes = 0;
+
+        /* wait for 1 second on each time through the loop for up to timeout */
+        tv.tv_sec = 0;
+        tv.tv_usec = 0;
+
+        do
+        {
+            FD_ZERO(&(fdset));
+            FD_SET(fd, &(fdset));
+            sleep(1);  /* tried to set the timeout on select and it was not reliable */
+            nfd = select(fd + 1, &(fdset), NULL, NULL, &tv);
+            if (nfd == -1)
+            {
+                OIC_LOG_V(ERROR, TAG, "select error :[%s]", strerror(errno));
+                break;
+            }
+            else if (nfd)
+            {
+                if (FD_ISSET(fd, &(fdset)))
+                {
+                    nbytes = MPMReadPipeMessage(fd, msg);
+                }
+            }
+            else
+            {
+                /* got nothing case */
+                OIC_LOG_V(INFO, TAG, "Parent waiting on child seconds(s): %d", waittime);
+            }
+
+            waittime++;
+
+        }
+        while ((nbytes == 0) && (waittime <= timeout));
+    }
+}
+
+typedef MPMCommonPluginCtx *(*create_t)();
+typedef int (*start_t)(MPMCommonPluginCtx *ctx);
+typedef void (*stop_t)(MPMCommonPluginCtx *ctx);
+typedef void (*destroy_t)(MPMCommonPluginCtx *ctx);
+
+typedef struct plugin_runtime_tag
+{
+    create_t create;
+    start_t start;
+    stop_t stop;
+    destroy_t destroy;
+} MPMPluginRuntime;
+
+// This is the symbol table that will be loaded by the mini plugin manager.
+// This is the "interface" between the mini plugin manager and the plugin.
+MPMPluginRuntime plugin_funcs =
+{
+    create,
+    start,
+    stop,
+    destroy
+};
+
diff --git a/bridging/common/pluginProcess.cpp b/bridging/common/pluginProcess.cpp
new file mode 100644 (file)
index 0000000..46044b2
--- /dev/null
@@ -0,0 +1,26 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 "pluginServer.h"
+
+void MPMPluginSpecificProcess(void)
+{
+}
diff --git a/bridging/common/pluginServer.cpp b/bridging/common/pluginServer.cpp
new file mode 100644 (file)
index 0000000..eb251dc
--- /dev/null
@@ -0,0 +1,491 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the plugin server implementation.  Most of this
+ * implementation is reused for other plugins.  There is NO customization
+ * required of functions in this file to accommodate plugin specific code.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "messageHandler.h"
+#include "pluginServer.h"
+#include "oic_malloc.h"
+#include "pluginIf.h"
+#include "ocpayload.h"
+#include "logger.h"
+#include "WorkQueue.h"
+#include "ConcurrentIotivityUtils.h"
+#include <iostream>
+#include <octypes.h>
+
+#define TAG "PLUGIN_SERVER"
+#define OC_KEY_VALUE_DELIMITER             "="
+
+using namespace OC::Bridging;
+
+//function prototypes
+
+/**
+ * This function initializes all the boilerplate items for the OCF server.
+ * it relies strictly on the "C" layer of the OCF stack.  This is common
+ * code for all plugins.
+ *
+ * @param[in] ctx            The context structure used by this implementation
+ * @param[in] deviceName     Name of the device as defined by the plugin
+ * @param[in] resourceType   Resource type as defined by the plugin
+ *
+ * @returns MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if initialization failure
+ */
+static MPMResult initInfrastructure(MPMCommonPluginCtx *ctx, const char *deviceName,
+                                    const char *resourceType);
+
+/**
+ * this function is a blocking function that holds the iotivity processing
+ * loop. this OCF server relies strictly on the "C" layer of the OCF stack.
+ * This is common code for all plugins.
+ *
+ * @param[in,out] result     Child process status
+ * @param[in]     ctx        The context structure used by this implementation
+ *
+ * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if maintenance failure
+ */
+static MPMResult maintainInfrastructure(MPMResult result, MPMCommonPluginCtx *ctx);
+
+const char *g_date_of_manufacture = NULL;
+const char *g_firmware_version = NULL;
+const char *g_manufacturer_name = "Intel";
+const char *g_operating_system_version = NULL;
+const char *g_hardware_version = NULL;
+const char *g_platform_id = "ce530bf4-40ab-11e6-9cca-ff46602aca08";
+const char *g_manufacturer_url = NULL;
+const char *g_model_number = NULL;
+const char *g_platform_version = NULL;
+const char *g_support_url = NULL;
+const char *g_version = NULL;
+const char *g_system_time = NULL;
+
+static pthread_t processMessageFromPipeThread;
+
+/* plugin specific context storage point.  the plugin owns the allocation
+ * and freeing of its own context
+ */
+MPMPluginCtx *g_plugin_context = NULL;
+
+
+/* Secure Virtual Resource database for Iotivity Server
+ * It contains Server's Identity and the PSK credentials
+ * of other devices which the server trusts
+ */
+static char CRED_FILE[] = "./oic_svr_db_server.json";
+extern MPMCommonPluginCtx *g_com_ctx;
+
+FILE *serverFOpen(const char *path, const char *mode)
+{
+    if (0 == strcmp(path, OC_SECURITY_DB_DAT_FILE_NAME))
+    {
+        return fopen(CRED_FILE, mode);
+    }
+    else
+    {
+        return fopen(path, mode);
+    }
+}
+
+std::unique_ptr<ConcurrentIotivityUtils> iotivityUtils = NULL;
+
+/**
+ * This is a non blocking pipe read function
+ *
+ * @param[in] fd            file descriptor from where messages are to be read
+ * @param[in] com_ctx       common context
+ * @param[in] ctx           plugin specific context
+ *
+ * @return false if STOP request has come from the MPM, true if STOP request
+ *         has not come from the MPM
+ */
+bool processMessagesFromMPM(int fd, MPMCommonPluginCtx *com_ctx, MPMPluginCtx *ctx)
+{
+    struct timeval tv;
+    fd_set fdset;
+    int nfd = -1;
+    ssize_t nbytes = 0;
+    bool shutdown = false;
+    MPMPipeMessage pipe_message;
+    g_com_ctx = com_ctx;
+
+    tv.tv_sec = 15;
+    tv.tv_usec = 0;
+
+    pipe_message.payloadSize = 0;
+    pipe_message.msgType = MPM_NOMSG;
+    pipe_message.payload = NULL;
+
+    FD_ZERO(&(fdset));
+    FD_SET(fd, &(fdset));
+    nfd = select(fd + 1, &(fdset), NULL, NULL, &tv);
+    if (nfd == -1)
+    {
+        OIC_LOG_V(ERROR, TAG, "select error: %s", strerror(errno));
+    }
+    else
+    {
+        if (FD_ISSET(fd, &(fdset)))
+        {
+            nbytes = MPMReadPipeMessage(fd, &pipe_message);
+            if (nbytes == 0)
+            {
+                OIC_LOG(DEBUG, TAG, "EOF was read and file descriptor was found to be closed");
+                shutdown = true;
+            }
+            if (nbytes > 0)
+            {
+                if (pipe_message.msgType == MPM_STOP)
+                {
+                    shutdown =  true;
+                }
+                else
+                {
+                    MPMRequestHandler(&pipe_message, ctx);
+                }
+            }
+
+            OICFree((void*)pipe_message.payload);
+
+        }
+    }
+    return (shutdown);
+}
+
+MPMResult MPMPluginService(MPMCommonPluginCtx *ctx)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    if (ctx == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Plugin context is NULL");
+        goto HandleError;
+    }
+
+    // plugin create is in the individual plugin.
+    result = pluginCreate(&g_plugin_context);
+
+    if (result != MPM_RESULT_OK || g_plugin_context == NULL)
+    {
+        OIC_LOG_V(ERROR, TAG, "Creation failed result: %d", result);
+        goto HandleError;
+    }
+
+    // initialize the OCF infrastructure to be setup for a server
+    result = initInfrastructure(ctx, g_plugin_context->device_name, g_plugin_context->resource_type);
+
+    if (result != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Error (%d) initializing OCF infrastructure", result);
+        goto HandleError;
+    }
+    if (ctx->reconnect_file_name != NULL)
+    {
+        strncpy(g_plugin_context->reconnect_file_name, ctx->reconnect_file_name,
+                strlen(ctx->reconnect_file_name));
+    }
+    else
+    {
+        memset(g_plugin_context->reconnect_file_name, 0, MPM_MAX_FILE_NAME_LENGTH);
+    }
+    // plugin start is in the individual plugin.
+    result = pluginStart(g_plugin_context);
+
+    if (result != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to start %s plugin result: %d", g_plugin_context->device_name,
+                  result);
+    }
+
+HandleError :
+    /* Sends the status of the child to Parent and
+     * in the case of success it act as blocking call.
+    */
+    result = maintainInfrastructure(result, ctx);
+
+    return (result);
+}
+
+static MPMResult setPlatformInfoParams(OCPlatformInfo &platform_info)
+{
+    if ((strlen(g_manufacturer_name) > MAX_MANUFACTURER_NAME_LENGTH))
+    {
+        OIC_LOG(ERROR, TAG, "Manufacture name string length exceeded max length");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    if (g_manufacturer_url != NULL && (strlen(g_manufacturer_url) > MAX_MANUFACTURER_URL_LENGTH))
+    {
+        OIC_LOG(ERROR, TAG, "Url string length exceeded max length");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    platform_info.platformID = const_cast<char *> (g_platform_id);
+    platform_info.manufacturerName = const_cast<char *> (g_manufacturer_name);
+    platform_info.manufacturerUrl = const_cast<char *> (g_manufacturer_url);
+    platform_info.modelNumber = const_cast<char *> (g_model_number);
+    platform_info.dateOfManufacture = const_cast<char *> (g_date_of_manufacture);
+    platform_info.platformVersion = const_cast<char *> (g_platform_version);
+    platform_info.operatingSystemVersion = const_cast<char *> (g_operating_system_version);
+    platform_info.hardwareVersion = const_cast<char *> (g_hardware_version);
+    platform_info.firmwareVersion = const_cast<char *> (g_firmware_version);
+    platform_info.supportUrl = const_cast<char *> (g_support_url);
+    platform_info.systemTime = const_cast<char *> (g_system_time);
+
+    return MPM_RESULT_OK;
+}
+
+static MPMResult setDeviceInfoParams(const char *deviceName, const char *resourceType,
+                                     OCDeviceInfo &device_info)
+{
+    if (!deviceName || deviceName[0] == '\0')
+    {
+        OIC_LOG(ERROR, TAG, "Device name is NULL");
+        return MPM_RESULT_INVALID_PARAMETER;
+    }
+    device_info.deviceName = const_cast<char *> (deviceName);
+
+    OCStringLL *stringll = NULL;
+
+    OCResourcePayloadAddStringLL(&stringll, OC_RSRVD_RESOURCE_TYPE_DEVICE);
+    OCResourcePayloadAddStringLL(&stringll, resourceType);
+
+    device_info.types = stringll;
+
+    device_info.dataModelVersions = NULL;
+    device_info.specVersion = NULL;
+
+    return MPM_RESULT_OK;
+}
+
+static MPMResult initInfrastructure(MPMCommonPluginCtx *ctx, const char *deviceName,
+                                    const char *resourceType)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+
+    uint16_t port = 0;
+
+    if (ctx != NULL)
+    {
+        /* Create the unnamed pipe listener thread that will clear the
+         * child's stay in the loop variable when the parent wants to
+         * shut down the child process.
+         */
+        // Initialize Persistent Storage for SVR database
+        static OCPersistentStorage ps = {g_plugin_context->open, fread, fwrite, fclose, unlink};
+        OCRegisterPersistentStorageHandler(&ps);
+
+        OCStackResult ocResult = OCInit(NULL, port, OC_SERVER);
+        if (ocResult != OC_STACK_OK)
+        {
+            OIC_LOG(ERROR, TAG, "OCStack init error");
+            return result;
+        }
+
+        std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>> q =
+            std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>>
+            (new WorkQueue<std::unique_ptr<IotivityWorkItem>>());
+
+        iotivityUtils =  make_unique<ConcurrentIotivityUtils>(std::move(q));
+        iotivityUtils->startWorkerThreads();
+
+        OCPlatformInfo platform_info;
+        if (setPlatformInfoParams(platform_info) != MPM_RESULT_OK)
+        {
+            OIC_LOG(ERROR, TAG, "Platform info setting failed locally!");
+            return result;
+        }
+
+        if (OCSetPlatformInfo(platform_info) != OC_STACK_OK)
+        {
+            OIC_LOG(ERROR, TAG, "Platform Registration failed!");
+            return result;
+        }
+
+        OCDeviceInfo device_info = {NULL, NULL, NULL, NULL};
+        if (setDeviceInfoParams(deviceName, resourceType, device_info) != MPM_RESULT_OK)
+        {
+            OIC_LOG(ERROR, TAG, "Device info setting failed locally!");
+            return result;
+        }
+
+        if (OCSetDeviceInfo(device_info) != OC_STACK_OK)
+        {
+            OIC_LOG(ERROR, TAG, "Device Registration failed!");
+            return result;
+        }
+        // Iotivity does not take ownership of 'types' and clones itself a copy.
+        OCFreeOCStringLL(device_info.types);
+    }
+    return MPM_RESULT_OK;
+}
+
+void *processMessageFromPipeThreadProc(void *arg)
+{
+    MPMCommonPluginCtx *ctx = (MPMCommonPluginCtx *)arg;
+    while (true)
+    {
+        bool result = processMessagesFromMPM(ctx->child_reads_fds.read_fd, ctx, g_plugin_context);
+        if (result != MPM_RESULT_OK)
+        {
+            OIC_LOG(INFO, TAG, "Leaving processMessageFromPipeThreadProc ");
+            break;
+        }
+    }
+    pthread_exit(NULL);
+    return NULL;
+}
+
+MPMResult maintainInfrastructure(MPMResult result, MPMCommonPluginCtx *ctx)
+{
+    MPMPipeMessage pipe_message;
+    void *res;
+
+    if (ctx != NULL)
+    {
+        /* child tells status to parent prior to getting in the
+         * processing loop
+         */
+        if (result == MPM_RESULT_OK)
+        {
+            pipe_message.msgType = MPM_DONE;
+            pipe_message.payloadSize = 0;
+            pipe_message.payload = NULL;
+            result = MPMWritePipeMessage(ctx->parent_reads_fds.write_fd, &pipe_message);
+        }
+        else
+        {
+            pipe_message.msgType = MPM_ERROR;
+            pipe_message.payloadSize = 0;
+            pipe_message.payload = NULL;
+            result = MPMWritePipeMessage(ctx->parent_reads_fds.write_fd, &pipe_message);
+        }
+
+        if (result == MPM_RESULT_OK)
+        {
+            pthread_create(&processMessageFromPipeThread, NULL, processMessageFromPipeThreadProc, ctx);
+            pthread_join(processMessageFromPipeThread, &res);
+        }
+        else
+        {
+            return result;
+        }
+
+        OIC_LOG_V(INFO, TAG, "Stopping %s plugin", g_plugin_context->device_name);
+
+        result = pluginStop(g_plugin_context);
+
+        if (result != MPM_RESULT_OK)
+        {
+            OIC_LOG_V(ERROR, TAG, "Error(%d) stopping plugin %s", result, g_plugin_context->device_name);
+        }
+
+        result = pluginDestroy(g_plugin_context);
+        if (result != MPM_RESULT_OK)
+        {
+            OIC_LOG_V(ERROR, TAG, "Error(%d) stopping plugin %s", result, g_plugin_context->device_name);
+        }
+
+        iotivityUtils->stopWorkerThreads();
+
+        OCStackResult ocResult = OCStop();
+
+        if (ocResult != OC_STACK_OK)
+        {
+            OIC_LOG_V(ERROR, TAG, "Error(%d) stopping Iotivity", ocResult);
+            result = MPM_RESULT_INTERNAL_ERROR;
+        }
+    }
+    else
+    {
+        OIC_LOG(ERROR, TAG, "Bad context handed to maintain ocf infrastructure");
+    }
+    return (result);
+}
+
+MPMResult MPMExtractFiltersFromQuery(char *query, char **filterOne, char **filterTwo)
+{
+
+    char *key = NULL;
+    char *value = NULL;
+    char *restOfQuery = NULL;
+    int numKeyValuePairsParsed = 0;
+
+    *filterOne = NULL;
+    *filterTwo = NULL;
+
+    if (!query)
+    {
+        // This is fine. This call is stemming from a non-entity handler request.
+        return MPM_RESULT_OK;
+    }
+
+    char *keyValuePair = strtok_r(query, OC_QUERY_SEPARATOR, &restOfQuery);
+
+    while (keyValuePair)
+    {
+        if (numKeyValuePairsParsed >= 2)
+        {
+            OIC_LOG(ERROR, TAG, "More than 2 queries params in URI.");
+            return MPM_RESULT_INVALID_PARAMETER;
+        }
+
+        key = strtok_r(keyValuePair, OC_KEY_VALUE_DELIMITER, &value);
+
+        if (!key || !value)
+        {
+            return MPM_RESULT_INVALID_PARAMETER;
+        }
+        else if (strncasecmp(key, OC_RSRVD_INTERFACE, sizeof(OC_RSRVD_INTERFACE) - 1) == 0)
+        {
+            *filterOne = value;     // if
+        }
+        else if (strncasecmp(key, OC_RSRVD_RESOURCE_TYPE, sizeof(OC_RSRVD_INTERFACE) - 1) == 0)
+        {
+            *filterTwo = value;     // rt
+        }
+        else
+        {
+            OIC_LOG_V(ERROR, TAG, "Unsupported query key: %s", key);
+            return MPM_RESULT_INVALID_PARAMETER;
+        }
+        ++numKeyValuePairsParsed;
+
+        keyValuePair = strtok_r(NULL, OC_QUERY_SEPARATOR, &restOfQuery);
+    }
+    if (*filterOne != NULL || *filterTwo != NULL)
+    {
+        OIC_LOG_V(ERROR, TAG, "Extracted params if: %s and rt: %s.", *filterOne, *filterTwo);
+    }
+
+    return MPM_RESULT_OK;
+}
diff --git a/bridging/include/ConcurrentIotivityUtils.h b/bridging/include/ConcurrentIotivityUtils.h
new file mode 100644 (file)
index 0000000..c55927f
--- /dev/null
@@ -0,0 +1,229 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 _CONCURRENTIOTIVITYUTILS_H_
+#define _CONCURRENTIOTIVITYUTILS_H_
+
+#include <queue>
+#include <mutex>
+#include <memory>
+#include <condition_variable>
+#include <thread>
+#include <unistd.h>
+#include <iostream>
+#include <string>
+#include <memory>
+#include <map>
+#include "IotivityWorkItem.h"
+#include "WorkQueue.h"
+#include "ocstack.h"
+#include "octypes.h"
+
+namespace OC
+{
+    namespace Bridging
+    {
+        template<typename T, typename... Args>
+        std::unique_ptr<T> make_unique(Args &&... args)
+        {
+            return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+        }
+
+        /**
+         * Provides a synchronized C++ wrapper over the Iotivity CSDK.
+         * Accepts workItems from the plugins for common operations.
+         * A consumer thread processes these worker items and makes calls into Iotivity.
+         * Another thread calls OCProcess() to check for network requests.
+         */
+        class ConcurrentIotivityUtils
+        {
+            private:
+
+                static std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>> m_queue;
+                std::mutex m_iotivityApiCallMutex;
+
+                std::thread m_processWorkQueueThread, m_ocProcessThread;
+                bool m_threadStarted;
+                bool m_shutDownOCProcessThread;
+                static const int OCPROCESS_SLEEP_MICROSECONDS = 200000;
+
+                // Fetches work item from queue and processes it.
+                void processWorkQueue()
+                {
+                    while (true)
+                    {
+                        std::unique_ptr<IotivityWorkItem> workItem;
+                        bool fetchedWorkItem = m_queue->get(&workItem);
+
+                        if (fetchedWorkItem)
+                        {
+                            std::lock_guard<std::mutex> lock(m_iotivityApiCallMutex);
+                            workItem->process();
+                        }
+                        else
+                        {
+                            break;
+                        }
+                    }
+                }
+
+                void callOCProcess()
+                {
+                    while (!m_shutDownOCProcessThread)
+                    {
+                        {
+                            std::lock_guard<std::mutex> lock(m_iotivityApiCallMutex);
+                            OCProcess();
+                        }
+                        // Hopefully it's enough for other threads to be scheduled
+                        // instead of the spin here. OCProcess is very lightweight though.
+                        usleep(OCPROCESS_SLEEP_MICROSECONDS);
+                    }
+                }
+
+            public:
+
+
+                ConcurrentIotivityUtils(std::unique_ptr<WorkQueue<std::unique_ptr<IotivityWorkItem>>>
+                                        queueToMonitor)
+                {
+                    m_queue = std::move(queueToMonitor);
+                    m_threadStarted = false;
+                    m_shutDownOCProcessThread = false;
+                }
+
+                /**
+                 * Starts 2 worker threads. One to service the concurrent work queue to call
+                 * into Iotivity. One to process network requests by calling OCProcess()
+                 */
+                void startWorkerThreads();
+
+                /**
+                 * Stops the 2 worker threads started by startWorkerThreads. @see startWorkerThreads
+                 */
+                void stopWorkerThreads();
+
+                /**
+                 * Gets the string uri associated with an Iotivity handle.
+                 * @warning This function is not thread safe and should only be called from entityHandler
+                 *          specified when creating the resource. The entityhandler is called with the
+                 *          Iotivity access mutex locked and this function does not modify anything
+                 *          in the stack.
+                 *
+                 * @param[in]  handle    handle for the resource
+                 * @param[out] uri       uri associated with the handle
+                 *
+                 * @return true if the resource is found and uri will be populated, else false.
+                 */
+                bool static getUriFromHandle(OCResourceHandle handle, std::string &uri);
+
+                /**
+                 * Sends out OBSERVE notifications for the resource with the given uri.
+                 * Notifications are sent out using OC_NA_QOS.
+                 *
+                 * @param[in] resourceUri      resource uri for fetching handle and notifying
+                 *
+                 * @return OCStackResult OC_STACK_OK on success, some other value upon failure.
+                 */
+                OCStackResult static queueNotifyObservers(const std::string &resourceUri);
+
+                /**
+                 * Create an Iotivity resource with the given properties.
+                 *
+                 * @param[in] uri
+                 * @param[in] resourceType
+                 * @param[in] interface
+                 * @param[in] entityHandler  Callback function that will be called on requests to this resource.
+                 * @param[in] callbackParam
+                 * @param[in] resourceProperties
+                 *
+                 * @return OCStackResult OC_STACK_OK on success, some other value upon failure.
+                 */
+                OCStackResult static
+                queueCreateResource(const std::string &uri, const std::string &resourceType,
+                               const std::string &interface,
+                               OCEntityHandler entityHandler, void *callbackParam,
+                               uint8_t resourceProperties);
+
+                /**
+                 * Delete the Iotivity resource given in the uri.
+                 *
+                 * @param[in] uri
+                 *
+                 * @return OCStackResult OC_STACK_OK on success, some other value upon failure.
+                 */
+                OCStackResult static queueDeleteResource(const std::string &uri);
+
+                /**
+                 * Send a response to a request.
+                 *
+                 * @param[in] request OCEntityHandleRequest type that was handed in the entityhandler.
+                 * @param[in] payload The response payload. This is cloned and callee still has ownership
+                 *                and the onus to free this.
+                 * @param[in] responseCode The response code of type OCEntityHandlerResult in ocstack.h
+                 *
+                 * @return OCStackResult OC_STACK_OK on success, some other value upon failure.
+                 */
+                OCStackResult static
+                respondToRequest(OCEntityHandlerRequest *request, OCRepPayload *payload,
+                                 OCEntityHandlerResult responseCode);
+
+                /**
+                 * Respond with an error message. Internally calls
+                 * ConcurrentIotivityUtils::respondToRequest() after creating
+                 * a payload containing the error message. The error message
+                 * key will be x.org.iotivity.error and the value will be
+                 * errorMessage.
+                 *
+                 * @param[in] request EntityHandler request
+                 * @param[in] errorMessage May be NULL.
+                 * @param[in] errorCode entity handler result
+                 *
+                 * @return OCStackResult OC_STACK_OK on success, some other value upon failure.
+                 */
+                OCStackResult static respondToRequestWithError(OCEntityHandlerRequest *request,
+                        const std::string &errorMessage,
+                        OCEntityHandlerResult errorCode);
+
+                /**
+                 * Parse the query parameter as a keyValueMap
+                 *
+                 * @param[in] query query to be parsed
+                 * @param[in,out] keyValueMap key value map of the query
+                 */
+                void static getKeyValueParams(const std::string &query,
+                                              std::map<std::string, std::string> &keyValueMap);
+
+                /**
+                 * Can be called from the entity handler to handle requests for default interface
+                 *
+                 * @param[in] query
+                 *
+                 * @return true if request for default interface (oic.if.baseline) else false.
+                 */
+                bool static isRequestForDefaultInterface(const std::string &query);
+
+        };
+    }
+}
+
+#endif //_CONCURRENTIOTIVITYUTILS_H_
diff --git a/bridging/include/IotivityWorkItem.h b/bridging/include/IotivityWorkItem.h
new file mode 100644 (file)
index 0000000..b6cbcaf
--- /dev/null
@@ -0,0 +1,182 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 _IOTIVITYWORKITEM_H_
+#define _IOTIVITYWORKITEM_H_
+
+#include "ocstack.h"
+#include "octypes.h"
+#include "ocpayload.h"
+#include "logger.h"
+#include <string>
+
+#define LOG "IOTIVITY_WORK_ITEM"
+
+namespace OC
+{
+    namespace Bridging
+    {
+        /**
+         * Basic workitem to act as a base class so more specific
+         * child classes can populate fields and
+         */
+        class IotivityWorkItem
+        {
+            public:
+                virtual void process() = 0;
+                virtual ~IotivityWorkItem() {};
+
+            protected:
+                std::string m_uri;
+        };
+
+        /**
+         * Creates an object used to create Iotivity resources.
+         */
+        class CreateResourceItem : public IotivityWorkItem
+        {
+            public:
+                CreateResourceItem(
+                    std::string uri,
+                    std::string resourceType,
+                    std::string interface,
+                    OCEntityHandler entityHandler,
+                    void *callbackParam,
+                    uint8_t resourceProperties
+                )
+                : m_resourceType(resourceType)
+                , m_interface(interface)
+                , m_entityHandler(entityHandler)
+                , m_callbackParam(callbackParam)
+                , m_resourceProperties(resourceProperties)
+                {
+                    m_uri = uri;
+                }
+
+                virtual void process()
+                {
+                    OCResourceHandle handle;
+
+                    OCStackResult res = OCCreateResource(&handle, m_resourceType.c_str(),
+                                                         m_interface.c_str(),
+                                                         m_uri.c_str(),
+                                                         m_entityHandler,
+                                                         m_callbackParam,
+                                                         m_resourceProperties);
+
+                    if (res == OC_STACK_OK)
+                    {
+                        OIC_LOG_V(INFO, LOG, "Created and saved %s", m_uri.c_str());
+                    }
+                    else
+                    {
+                        OIC_LOG_V(ERROR, LOG, "Failed to create %s", m_uri.c_str());
+                    }
+                }
+            private:
+                std::string m_resourceType;
+                std::string m_interface;
+                OCEntityHandler m_entityHandler;
+                void *m_callbackParam;
+                uint8_t m_resourceProperties;
+        };
+
+        /**
+         * Creates an object used to create send Iotivity responses.
+         */
+        class SendResponseItem : public IotivityWorkItem
+        {
+            public:
+                SendResponseItem(std::unique_ptr<OCEntityHandlerResponse> response)
+                : m_response(std::move(response))
+                {}
+
+                virtual void process()
+                {
+                    OCDoResponse((m_response).get());
+                    OCPayloadDestroy(m_response->payload);
+                }
+
+            private:
+                std::unique_ptr<OCEntityHandlerResponse> m_response;
+        };
+
+        /**
+         * Creates an object used to notify observers of Iotivity resources.
+         */
+        class NotifyObserversItem : public IotivityWorkItem
+        {
+            public:
+                NotifyObserversItem(const std::string &uri)
+                {
+                    m_uri = uri;
+                }
+
+                virtual void process()
+                {
+                    OCResourceHandle handle = OCGetResourceHandleAtUri(m_uri.c_str());
+
+                    if (!handle)
+                    {
+                        OIC_LOG_V(ERROR, LOG, "No handle for %s. Not notifying observers.", m_uri.c_str());
+                        return;
+                    }
+                    OCNotifyAllObservers(handle, OC_NA_QOS);
+                }
+
+        };
+
+        /**
+         * Creates an object used to delete Iotivity resources.
+         */
+        class DeleteResourceItem : public IotivityWorkItem
+        {
+            public:
+                DeleteResourceItem(const std::string &uri)
+                {
+                    m_uri = uri;
+                }
+
+                virtual void process()
+                {
+                    OCResourceHandle handle = OCGetResourceHandleAtUri(m_uri.c_str());
+
+                     if (!handle)
+                     {
+                         OIC_LOG_V(ERROR, LOG, "No handle for %s. Nothing to delete", m_uri.c_str());
+                         return;
+                     }
+
+                     OCStackResult res = OCDeleteResource(handle);
+                     if (res == OC_STACK_OK)
+                     {
+                         OIC_LOG_V(INFO, LOG, "Deleted %s", m_uri.c_str());
+                     }
+                     else
+                     {
+                         OIC_LOG_V(ERROR, LOG, "Failed to delete %s", m_uri.c_str());
+                     }
+                }
+
+        };
+    }
+}
+#endif // _IOTIVITYWORKITEM_H_
diff --git a/bridging/include/JsonHelper.h b/bridging/include/JsonHelper.h
new file mode 100644 (file)
index 0000000..b531083
--- /dev/null
@@ -0,0 +1,110 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 __JSONHELPER_H__
+#define __JSONHELPER_H__
+
+#include "rapidjson.h"
+#include "document.h"
+#include "stringbuffer.h"
+#include "writer.h"
+#include <string>
+
+#include <typeinfo>
+
+class JsonHelper
+{
+    public:
+
+        static std::string toString(rapidjson::Value &doc)
+        {
+            rapidjson::StringBuffer sb;
+            std::string jsonRep;
+
+            rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
+            doc.Accept(writer);
+            return sb.GetString();
+        }
+
+        static std::string toString(rapidjson::Value::ConstMemberIterator &it)
+        {
+            rapidjson::StringBuffer sb;
+            std::string jsonRep;
+
+            rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
+            it->value.Accept(writer);
+            return sb.GetString();
+        }
+
+        template<typename T>
+        static void setMember(rapidjson::Document &doc, std::string obj,
+                              std::string name, T &value)
+        {
+            if (doc.HasMember(obj.c_str()))
+            {
+                rapidjson::Document::AllocatorType &allocator = doc.GetAllocator();
+                doc[obj.c_str()].RemoveMember(name.c_str());
+                rapidjson::Value nameValue(name.c_str(), allocator);
+                doc[obj.c_str()].AddMember(nameValue, value, allocator);
+            }
+        }
+
+        template<typename T>
+        static void setMember(rapidjson::Document &doc, const std::string &name, T &value)
+        {
+            if (doc.HasMember(name.c_str()))
+            {
+                doc[name.c_str()] = value;
+            }
+            else
+            {
+                rapidjson::Document::AllocatorType &allocator = doc.GetAllocator();
+                rapidjson::Value nameValue(name.c_str(), allocator);
+                doc.AddMember(nameValue, value, allocator);
+            }
+
+        }
+
+        template<typename T>
+        static bool getMember(rapidjson::Document &doc, std::string name, T &value)
+        {
+            if (doc.HasMember(name.c_str()))
+            {
+                if (typeid(T) == typeid(std::string))
+                {
+                    value = doc[name.c_str()].GetString();
+                }
+                return true;
+            }
+            return false;
+        }
+
+        static void tryRemoveMember(rapidjson::Document &doc, std::string name)
+        {
+            if (doc.HasMember(name.c_str()))
+            {
+                doc.RemoveMember(name.c_str());
+            }
+        }
+};
+
+#endif /* __JSON_HELPER_H__ */
diff --git a/bridging/include/WorkQueue.h b/bridging/include/WorkQueue.h
new file mode 100644 (file)
index 0000000..115697d
--- /dev/null
@@ -0,0 +1,112 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 _WORKQUEUE_H_
+#define _WORKQUEUE_H_
+
+#include <queue>
+#include <mutex>
+#include <memory>
+#include <condition_variable>
+#include <thread>
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+namespace OC
+{
+    namespace Bridging
+    {
+        /**
+         * Provides a generic, minimal thread safe queue.
+         */
+        template<class T>
+        class WorkQueue
+        {
+            private:
+
+                std::queue<T> m_workQueue;
+                std::mutex m_workQueueMutex;
+                std::condition_variable m_cv;
+                bool m_signalToShutDown;
+
+            public:
+
+                inline WorkQueue()
+                {
+                    m_signalToShutDown = false;
+                }
+
+                /**
+                 * Puts the arg in the queue and notifies all threads waiting
+                 * to fetch things from the queue.
+                 *
+                 * @para[in] m item The item to insert into the queue.
+                 */
+                void put(T item)
+                {
+                    std::unique_lock<std::mutex> lock(m_workQueueMutex);
+
+                    m_workQueue.push(std::move(item));
+                    m_cv.notify_all();
+                }
+
+                /**
+                 * Blocking function to fetch an item from the queue.
+                 *
+                 * @param[in] item The item to insert into the queue.
+                 * @return true if an item is fetched from the queue.
+                           false if the queue is shutdown.
+                 */
+                bool get(T *item)
+                {
+                    std::unique_lock<std::mutex> lock(m_workQueueMutex);
+
+                    m_cv.wait(lock, [this]()
+                    {
+                        return m_workQueue.size() > 0 || m_signalToShutDown;
+                    });
+
+                    if (m_signalToShutDown)
+                    {
+                        return false;
+                    }
+
+                    *item = std::move(m_workQueue.front());
+                    m_workQueue.pop();
+                    return true;
+                }
+
+                /**
+                 * Notifies all waiting threads that the queue is being shut down.
+                 */
+                void shutdown()
+                {
+                    std::unique_lock<std::mutex> lock(m_workQueueMutex);
+                    m_signalToShutDown = true;
+                    m_cv.notify_all();
+                }
+        };
+    }
+}
+
+#endif // _WORKQUEUE_H_
diff --git a/bridging/include/curlClient.h b/bridging/include/curlClient.h
new file mode 100644 (file)
index 0000000..c8217f3
--- /dev/null
@@ -0,0 +1,177 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 _CURLCLIENT_H_
+#define _CURLCLIENT_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <memory.h>
+#include <string>
+#include <vector>
+#include <map>
+#include <curl/curl.h>
+#include <stdexcept>
+#include "mpmErrorCode.h"
+#include "StringConstants.h"
+
+namespace OC
+{
+    namespace Bridging
+    {
+        const char CURL_CONTENT_TYPE_JSON[] = "content-type: application/json";
+        const char CURL_CONTENT_TYPE_URL_ENCODED[] = "content-type: application/x-www-form-urlencoded";
+        const char CURL_HEADER_ACCEPT_JSON[] = "accept: application/json";
+
+        const long INVALID_RESPONSE_CODE = 0;
+
+        class CurlClient
+        {
+
+            public:
+                enum class CurlMethod
+                {
+                    GET, PUT, POST, DELETE, HEAD
+                };
+
+                virtual ~CurlClient() { }
+
+                long getLastResponseCode()
+                {
+                    return m_lastResponseCode;
+                }
+
+                CurlClient(CurlMethod method, const std::string &url)
+                {
+                    if (url.empty())
+                    {
+                        throw "Curl method or url is empty";
+                    }
+
+                    m_method = getCurlMethodString(method);
+                    m_url = url;
+                    m_useSsl = CURLUSESSL_TRY;
+                }
+
+                CurlClient &setRequestHeaders(std::vector<std::string> &requestHeaders)
+                {
+                    m_requestHeaders = requestHeaders;
+                    return *this;
+                }
+
+                CurlClient &addRequestHeader(const std::string &header)
+                {
+                    m_requestHeaders.push_back(header);
+                    return *this;
+                }
+
+                CurlClient &setUserName(const std::string &userName)
+                {
+                    m_username = userName;
+                    return *this;
+                }
+
+                CurlClient &setRequestBody(std::string &requestBody)
+                {
+                    m_requestBody = requestBody;
+                    return *this;
+                }
+
+                CurlClient &setUseSSLOption(curl_usessl sslOption)
+                {
+                    m_useSsl = sslOption;
+                    return *this;
+                }
+
+                int send()
+                {
+                    return doInternalRequest(m_url, m_method, m_requestHeaders, m_requestBody, m_username, m_outHeaders,
+                                             m_response);
+                }
+
+                std::string getResponseBody()
+                {
+                    return m_response;
+                }
+
+                std::vector<std::string> getResponseHeaders()
+                {
+                    return m_outHeaders;
+                }
+
+
+            private:
+
+                std::string getCurlMethodString(CurlMethod method)
+                {
+                    if (method == CurlMethod::GET)          return OC::PlatformCommands::GET;
+                    else if (method == CurlMethod::PUT)     return OC::PlatformCommands::PUT;
+                    else if (method == CurlMethod::POST)    return OC::PlatformCommands::POST;
+                    else if (method == CurlMethod::DELETE)  return OC::PlatformCommands::DELETE;
+                    else if (method == CurlMethod::HEAD)    return "HEAD";
+
+                    else throw std::runtime_error("Invalid CurlMethod");
+                }
+
+                std::string m_url;
+                std::string m_method;
+                std::vector<std::string> m_requestHeaders;
+                std::string m_requestBody;
+                std::string m_username;
+                std::string m_response;
+                std::vector<std::string> m_outHeaders;
+
+                /// Indicates whether to use CURLOPT_USE_SSL option in doInternalRequest.
+                /// Curl default is no SSL (CURLUSESSL_NONE). Specify one of the other CURL SSL options
+                /// (for example, CURLUSESSL_TRY) if you need to perform SSL transactions.
+                curl_usessl m_useSsl;
+
+                static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp);
+
+                // Represents contiguous memory to hold a HTTP response.
+                typedef struct _MemoryChunk
+                {
+                    _MemoryChunk() : size(0)
+                    {
+                        memory = static_cast<char *>(malloc(1));
+                    }
+
+                    char *memory;
+                    size_t size;
+
+                } MemoryChunk;
+
+                int decomposeHeader(const char *header, std::vector<std::string> &headers);
+
+
+                int doInternalRequest(const std::string &url,
+                                      const std::string &method,
+                                      const std::vector<std::string> &inHeaders,
+                                      const std::string &request,
+                                      const std::string &username,
+                                      std::vector<std::string> &outHeaders,
+                                      std::string &response);
+
+                long m_lastResponseCode;
+        };
+    } // namespace Bridging
+}  // namespace OC
+#endif // _CURLCLIENT_H_
diff --git a/bridging/include/messageHandler.h b/bridging/include/messageHandler.h
new file mode 100644 (file)
index 0000000..65a7858
--- /dev/null
@@ -0,0 +1,167 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the plugin server implementation.  Most of this
+ * implementation is reused for other plugins.  There is NO customization
+ * required of functions in this file to accommodate plugin specific code.
+ */
+
+#ifndef _MESSAGEHANDLER_H
+#define _MESSAGEHANDLER_H
+
+#include <stdint.h>
+#include <stdio.h>
+#include "mpmErrorCode.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MPM_MAX_URI_LEN       256
+#define MPM_MAX_FILE_NAME_LENGTH           300
+#define MPM_MAX_LENGTH_32     32
+#define MPM_MAX_LENGTH_64     64
+#define MPM_MAX_LENGTH_256    256
+#define MPM_MAX_UNIQUE_ID_LEN 128
+#define MPM_MAX_METADATA_LEN  3000
+
+/* Enum to specify the action type*/
+typedef enum
+{
+    MPM_NOMSG = 0,
+    MPM_SCAN,
+    MPM_ADD,
+    MPM_DELETE,
+    MPM_REMOVE,
+    MPM_RECONNECT,
+    MPM_STOP,
+    MPM_DONE,
+    MPM_ERROR
+} MPMMessageType;
+
+/**
+ * This structure represents the format of the message exchanged
+ * between MPM and Plugin over the pipe.
+ */
+typedef struct
+{
+    /** size of the payload. */
+    size_t payloadSize;
+
+    /** type of the message [MPM_SCAN, MPM_ADD etc] */
+    MPMMessageType msgType;
+
+    /** Payload to be sent and received */
+    const uint8_t *payload;
+} MPMPipeMessage;
+
+/**
+ * This structure represents the add response message coming from
+ * the plugins to mpm library
+ */
+typedef struct
+{
+    char uri[MPM_MAX_URI_LEN];
+    char metadata[MPM_MAX_METADATA_LEN];
+} MPMAddResponse;
+
+/**
+ * This structure represents the resource list used for
+ * creating reconnect metadata
+ */
+typedef struct MPMResourceList
+{
+    char href[MPM_MAX_URI_LEN];
+    char relative[MPM_MAX_LENGTH_64];
+    char interfaces[MPM_MAX_LENGTH_64];
+    char rt[MPM_MAX_LENGTH_64];
+    int bitmap;
+    struct MPMResourceList *next;
+} MPMResourceList;
+
+/**
+ * This structure represents the device specific data of the plugin
+ * which is a part of reconnect metadata
+ */
+typedef struct
+{
+    char devName[MPM_MAX_LENGTH_64];
+    char devType[MPM_MAX_LENGTH_64];
+    char manufacturerName[MPM_MAX_LENGTH_256];
+} MPMDeviceSpecificData;
+
+/**
+ * This function writes messages to the pipe
+ * @param[in] fd            file descriptor
+ * @param[in] pipe_message  message to be written
+ *
+ * @return MPM_RESULT_OK on success, MPM_RESULT_INTERNAL_PARAMETER on failure
+*/
+MPMResult MPMWritePipeMessage(int fd, const MPMPipeMessage *pipe_message);
+
+/**
+ * This function reads messages from the pipe
+ * @param[in] fd                file descriptor
+ * @param[in,out] pipe_message  for storing the read message.
+ *
+ * @return number of bytes read
+*/
+ssize_t MPMReadPipeMessage(int fd, MPMPipeMessage *pipe_message);
+
+
+/**
+ * This function encodes the metadata received from the plugin
+ * @param[in] list            A list of resources supported by the device
+ * @param[in] deviceDetails   Plugin specific details to be encoded
+ * @param[in] buff            The metadata stream to be filled with encoded data
+ * @param[in] size            Size of the metadata stream to be encoded
+ * @param[in] details         To hold plugin specific device details to be encoded
+ * @param[in] payloadSize     Size of the plugin specific device details
+ *
+ * @return zero on no error, non-zero on occurrence of some error
+ */
+int64_t MPMFormMetaData(MPMResourceList *list, MPMDeviceSpecificData *deviceDetails, uint8_t *buff,
+                        size_t size, void *details, size_t payloadSize);
+
+/**
+ * This function decodes and parse the metadata received from
+ * the client as a part of reconnect request
+ * @param[in] buffer           The encoded metadata stream
+ * @param[in] size             Size of the encoded metadata stream
+ * @param[out] list            Reference to location of resource details
+ * @param[out] details         To hold plugin specific device details after parsing
+ */
+void MPMParseMetaData(const uint8_t *buffer, size_t size, MPMResourceList **list, void **details);
+
+/**
+ * This function sends response coming from the plugins to mpm library
+ * @param[in] response  Response to be sent
+ * @param[in] size      Size of the response.
+ * @param[in] type      Type of response (scan response, add response etc)
+ * @return MPM_RESULT_OK on success else MPM_RESULT_INTERNAL_ERROR
+ */
+MPMResult MPMSendResponse(const void *response, size_t size, MPMMessageType type);
+
+#ifdef __cplusplus
+}
+#endif // #ifdef __cplusplus
+
+#endif
diff --git a/bridging/include/mpmErrorCode.h b/bridging/include/mpmErrorCode.h
new file mode 100644 (file)
index 0000000..8e5e9a8
--- /dev/null
@@ -0,0 +1,82 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 _MPMERRORCODE_H_
+#define _MPMERRORCODE_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Definitions for error codes for the Bridging
+ */
+
+typedef enum
+{
+    MPM_CB_RESULT_KEEP   = 1,
+    MPM_CB_RESULT_DELETE = 2,
+    MPM_CB_RESULT_OK     = 3,
+    MPM_CB_RESULT_ERROR  = 4
+} MPMCbResult;
+
+typedef enum
+{
+    MPM_RESULT_OK = 0,
+    MPM_RESULT_CREATED_FAILED,            /*< Module has failed during create. */
+
+    MPM_RESULT_INVALID_HANDLE,            /*< The handle is invalid. */
+    MPM_RESULT_INVALID_PARAMETER,         /*< Parameters are invalid. */
+    MPM_RESULT_INTERNAL_ERROR,            /*< Some unknown error occurred. */
+    MPM_RESULT_INVALID_VERSION,           /*< Invalid plugin API version. */
+
+    MPM_RESULT_MISSING_API,               /*< One or more expected API is missing in the callback. */
+    MPM_RESULT_NOT_IMPLEMENTED,           /*< API function is not implemented. */
+    MPM_RESULT_OUT_OF_MEMORY,             /*< Memory allocation failure. */
+    MPM_RESULT_ERROR_ADD_MESSAGE_QUEUE,   /*< add message queue failure. */
+    MPM_RESULT_LOADLIBRARY_FAILED,
+    MPM_RESULT_INSUFFICIENT_BUFFER,
+    MPM_RESULT_DUPLICATE_API_CALL,
+    MPM_RESULT_FILE_NOT_OPEN,
+    MPM_RESULT_FILE_ALREADY_OPEN,
+    MPM_RESULT_FILE_NOT_CLOSED,
+    MPM_RESULT_NOT_STARTED,               /*< Module has not been started. */
+    MPM_RESULT_STARTED_FAILED,            /*< Module has failed during start. */
+    MPM_RESULT_ALREADY_STARTED,           /*< Module already started. */
+    MPM_RESULT_NOT_STOPPED,
+    MPM_RESULT_ALREADY_CREATED,           /*< Module already created. */
+    MPM_RESULT_NOT_AUTHORIZED,            /*< Not authorized */
+    MPM_RESULT_NOT_PRESENT,               /*< Not present or available */
+    MPM_RESULT_NETWORK_ERROR,
+    MPM_RESULT_JSON_ERROR,
+    MPM_RESULT_MEMORY_ERROR,
+    MPM_RESULT_INVALID_DATA,
+    MPM_RESULT_INDEX_OUT_OF_BOUNDS,       /*< Specified an index too great for array. */
+    MPM_RESULT_UNEXPECTED_RESULT          /*< Result code did not match expected response */
+} MPMResult;
+
+#ifdef __cplusplus
+}
+#endif // #ifdef __cpluscplus
+
+#endif /* _MPMERRORCODEH_ */
diff --git a/bridging/include/pluginIf.h b/bridging/include/pluginIf.h
new file mode 100644 (file)
index 0000000..97dbc3d
--- /dev/null
@@ -0,0 +1,106 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the "C" plugin interface definition.  This header file
+ * and its corresponding implementation file is intended to be shared among
+ * ALL plugins.  Modification of this file is not necessary for the construction
+ * of a new plugin.
+ */
+
+#ifndef _PLUGINIF_H_
+#define _PLUGINIF_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include "messageHandler.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * This is a clear way to store file descriptors for unnamed pipes.  Each pipe
+ * has a read end and a write end.
+ */
+struct MPMPipe
+{
+    int read_fd;
+    int write_fd;
+};
+
+/**
+ * structure to represent the plugin context
+ *  which is common between all the plugins.
+ */
+struct MPMCommonPluginCtx
+{
+    /**
+     * Unnamed pipes are used in this implementation of the common plugin code,
+     * however the unnamed pipes are NOT exposed outside of the common plugin code.
+     * One of the unnamed pipes is used by the child to tell parent that the
+     * child has created and started the plugin content section successfully or not.
+     * The other unnamed pipe is used by the parent to stop the child which in turn
+     * stops and destroys the plugin content. As you know the unnamed pipes may be
+     * only used with related processes (i.e. having the same parent). The pipes exist
+     * as long as their descriptors are open.
+     */
+    MPMPipe parent_reads_fds;
+    MPMPipe child_reads_fds;
+
+    /**
+     * The "started" variable is used by the parent process to not permit
+     * more than one fork to happen per instance of the plugin.
+     */
+    bool started;
+
+    /**
+     * The main thread in the child process needs to know when it should
+     * leave the OCF process loop.
+     */
+    bool exit_process_loop;
+    char reconnect_file_name[MPM_MAX_FILE_NAME_LENGTH];
+
+    /**
+     * Save the child's process handle to wait on it being signaled later
+     */
+    pid_t child_pid;
+
+};
+
+/** time out value */
+#define MPM_TIMEOUT_VAL_IN_SEC  60
+
+/**
+ * This function is a OCF server.  The function does not return unless there is an
+ * error or this main thread was signaled by the parent process main thread.
+ * @param[in] plugin_ctx            plugin specific context
+ *
+ * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if error
+ */
+MPMResult MPMPluginService(MPMCommonPluginCtx *plugin_ctx);
+
+#ifdef __cplusplus
+}
+#endif // #ifdef __cplusplus
+
+#endif /* __PLUGIN_IF_H__ */
diff --git a/bridging/include/pluginServer.h b/bridging/include/pluginServer.h
new file mode 100644 (file)
index 0000000..3d29953
--- /dev/null
@@ -0,0 +1,144 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the interface between the plugin's OCF server and the
+ * plugin specific implmentation
+ */
+
+#ifndef __PLUGINSERVER_H__
+#define __PLUGINSERVER_H__
+
+#include "messageHandler.h"
+#include "pluginIf.h"
+#include "mpmErrorCode.h"
+#include "ocstack.h"
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MPM_THREAD_PROCESS_SLEEPTIME     5
+
+/**
+ *  Plugin context is owned by the plugin specific content provider (you).
+ */
+struct MPMPluginCtx
+{
+    /**
+     * This plugin specific content provider has chosen to use a worker thread
+     * to manage all the devices.  Keeping track if the thread is started.
+     */
+    bool started;
+
+    /**
+     * The thread is staying around for the lifetime of the plugin instance.
+     * This state variable is required to tell the thread leave the thread
+     * function when that time comes.
+     */
+    bool stay_in_process_loop;
+
+    FILE *(*open)(const char *path, const char *mode);
+
+    /**
+     * The name of the plugin being started. This name must follow OCF specification
+     * for '/oic/d' device name.
+     */
+    const char *device_name;
+    char reconnect_file_name[MPM_MAX_FILE_NAME_LENGTH];
+
+    /**
+     * The resource type with device of the plugin being started. This name must follow OCF specification
+     * for /oic/d. Some examples would be 'oic.d.light' and 'oic.d.smartlock'.
+     */
+    const char *resource_type;
+
+    /**
+     *  Save the thread handle so there is a possibility to wait on thread
+     * handles for synchronization purposes.
+     */
+    pthread_t thread_handle;
+};
+
+/**
+ * These functions are called by the plugin's own Iotivity server which is
+ * common code for all plugins. There is one Iotivity server per plugin. The APIs
+ * shown below permit the plugin to initialize and to allow the plugin specific
+ * code to call other Iotivity csdk APIs. The implementation of these functions is found
+ * in the plugin specific code.
+ *
+ * @param[in] plugin_specific_ctx            Plugin specific context
+ * @param[in] message                        Message received from the client via mpm library
+ *
+ * @return MPM_RESULT_OK if no error, error specific value if any error occurs
+ */
+MPMResult pluginCreate(MPMPluginCtx **plugin_specific_ctx);
+
+MPMResult pluginStart(MPMPluginCtx *plugin_specific_ctx);
+
+MPMResult pluginStop(MPMPluginCtx *plugin_specific_ctx);
+
+MPMResult pluginDestroy(MPMPluginCtx *plugin_specific_ctx);
+
+MPMResult pluginScan(MPMPluginCtx *ctx, MPMPipeMessage *message);
+
+MPMResult pluginAdd(MPMPluginCtx *ctx, MPMPipeMessage *message);
+
+MPMResult pluginRemove(MPMPluginCtx *ctx, MPMPipeMessage *message);
+
+MPMResult pluginReconnect(MPMPluginCtx *ctx, MPMPipeMessage *message);
+
+/**
+ * This function is added for Zigbee and kept as empty function
+ * for all other plugins to avoid using ifdef(s)
+ */
+void MPMPluginSpecificProcess(void);
+
+/**
+ * This function handles the requests from mpm library to plugin
+ * @param[in] message        The message to received over the pipe from the mpm library
+ * @param[in] ctx            Context of the plugin to which the message has to be forwarded to
+ */
+void MPMRequestHandler(MPMPipeMessage *message, MPMPluginCtx *ctx);
+
+
+/**
+ * Stolen from: IOTIVITY/resource/csdk/stack/src/resource.c:ExtractFiltersFromQuery()
+ *
+ * Function will extract 0, 1 or 2 filters from query.
+ * More than 2 filters or unsupported filters will result in error.
+ * If both filters are of the same supported type, the 2nd one will be picked.
+ * Resource and device filters in the SAME query are NOT validated
+ * and resources will likely not clear filters.
+ *
+ * @param[in] query            The query string from which filters are to be extracted
+ * @param[in,out] filterOne    The first filter string
+ * @param[in,out] filterTwo    The second filter string
+ *
+ * @return MPM_RESULT_OK if no error, MPM_RESULT_INVALID_PARAMETER if any error
+ */
+MPMResult MPMExtractFiltersFromQuery(char *query, char **filterOne, char **filterTwo);
+
+#ifdef __cplusplus
+}
+#endif // #ifdef __cplusplus
+
+#endif /* __PLUGIN_SERVER_H__ */
diff --git a/bridging/mini_plugin_manager/SConscript b/bridging/mini_plugin_manager/SConscript
new file mode 100644 (file)
index 0000000..a6913e0
--- /dev/null
@@ -0,0 +1,75 @@
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# 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.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+##
+# Mini Plugin Manager build script
+##
+
+import os.path
+
+Import('env')
+
+target_os = env.get('TARGET_OS')
+src_dir = env.get('SRC_DIR')
+bridging_path = os.path.join(src_dir, 'bridging')
+
+print "Reading MPM script"
+
+######################################################################
+# Build flags
+######################################################################
+env.PrependUnique(CPPPATH = [ os.path.join(src_dir, 'resource', 'c_common', 'oic_string', 'include'),
+                              os.path.join(src_dir, 'extlibs', 'rapidjson', 'include', 'rapidjson')
+                            ])
+env.AppendUnique(CPPPATH = [ os.path.join(bridging_path, 'include')
+                           ])
+
+if target_os not in ['arduino', 'windows']:
+        env.AppendUnique(CPPDEFINES = ['WITH_POSIX'])
+        env.AppendUnique(CXXFLAGS = ['-std=c++0x', '-Wall', '-Wextra', '-Werror'])
+
+if target_os in ['darwin','ios']:
+        env.AppendUnique(CPPDEFINES = ['_DARWIN_C_SOURCE'])
+
+env.AppendUnique(RPATH = [env.get('BUILD_DIR')])
+env.AppendUnique(LIBPATH = [env.get('BUILD_DIR')])
+
+if env.get('LOGGING'):
+        env.AppendUnique(CPPDEFINES = ['TB_LOG'])
+
+
+env.PrependUnique(LIBS = ['mpmcommon'])
+#####################################################################
+# Source files and Target(s)
+######################################################################
+minipluginmanager_src = [
+         os.path.join(bridging_path, 'mini_plugin_manager', 'miniPluginManager.cpp'),
+        ]
+
+env.AppendUnique(MINIPLUGINMANAGER_SRC =minipluginmanager_src)
+
+print "Include path is %s" % env.get('CPPPATH')
+print "Files path is %s" % env.get('MINIPLUGINMANAGER_SRC')
+if target_os in ['android', 'tizen']:
+        mpmlib = env.SharedLibrary('minipluginmanager', env.get('MINIPLUGINMANAGER_SRC'))
+else:
+        mpmlib = env.StaticLibrary('minipluginmanager', env.get('MINIPLUGINMANAGER_SRC'))
+env.InstallTarget(mpmlib, 'minipluginmanager')
+env.UserInstallTargetLib(mpmlib, 'minipluginmanager')
+
diff --git a/bridging/mini_plugin_manager/miniPluginManager.cpp b/bridging/mini_plugin_manager/miniPluginManager.cpp
new file mode 100644 (file)
index 0000000..d0bebbe
--- /dev/null
@@ -0,0 +1,461 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 <stdio.h>
+#include <dlfcn.h>
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <ctype.h>
+#include <string.h>
+#include <stdint.h>
+#include <signal.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <iostream>
+#include <vector>
+
+#define TAG "MINI_PLUGIN_MANAGER"
+
+#include "oic_malloc.h"
+#include "pluginIf.h"
+#include "miniPluginManager.h"
+#include "messageHandler.h"
+#include "logger.h"
+
+pthread_t readResponsethreadhandle;
+
+bool exitResponseThread = false;
+
+/*******************************************************************************
+ * type defines and structure definitions go here
+ ******************************************************************************/
+
+typedef MPMCommonPluginCtx *(*create_t)();
+
+typedef int32_t (*start_t)(MPMCommonPluginCtx *ctx);
+
+typedef void (*stop_t)(MPMCommonPluginCtx *ctx);
+
+typedef void (*destroy_t)(MPMCommonPluginCtx *ctx);
+
+typedef struct plugin_runtime_tag
+{
+    create_t create;
+    start_t start;
+    stop_t stop;
+    destroy_t destroy;
+} MPMPluginRunTime;
+
+/**
+ * structure representing plugin context
+ */
+typedef struct plugin_context_tag
+{
+    /** function pointers to the plugin life cycle methods */
+    MPMPluginRunTime lifecycle;
+
+    /** plugin handle */
+    void *handle;
+
+    /** common plugin context */
+    MPMCommonPluginCtx *plugin_ctx;
+
+    /** plugin shared object name */
+    char shared_object_name[MAX_SHARED_OBJECT_NAME_LENGTH];
+
+    MPMCallback callbackClient;
+} MPMPluginContext;
+
+std::vector<MPMPluginContext> g_LoadedPlugins;
+
+/**
+ * This is MPM library API to start the thread for handling response
+ * messages from the plugins (child processes)
+ */
+
+void startReadResponseThread();
+
+/**
+ * This is MPM library API to terminate the thread for handling response
+ * messages from the plugins (child processes)
+ */
+void stopReadResponseThread();
+
+/**
+ * This function runs as a thread which is running to handle
+ * the response messages from the plugins(child processes)
+ */
+static void *readResponse(void *)
+{
+    int status = 0;
+    fd_set readfds;
+    struct timeval tv;
+
+    std::vector<MPMPluginContext>::iterator loadedPluginsItr;
+
+    std::vector<MPMPluginContext> *loadedPlugins = &g_LoadedPlugins;
+
+    tv.tv_sec = 5;
+    tv.tv_usec = 0;
+
+    while (true)
+    {
+        if (exitResponseThread == true)
+        {
+            OIC_LOG(DEBUG, TAG, "Exiting readResponse thread");
+            break;
+        }
+        int maxFd = -1;
+
+        FD_ZERO(&(readfds));
+
+        loadedPluginsItr = loadedPlugins->begin();
+
+        for ( ; loadedPluginsItr != loadedPlugins->end(); )
+        {
+            MPMCommonPluginCtx *ctx = (*loadedPluginsItr).plugin_ctx;
+            if (ctx->started)
+            {
+                FD_SET(ctx->parent_reads_fds.read_fd, &(readfds));
+                if (maxFd < ctx->parent_reads_fds.read_fd)
+                {
+                    maxFd = ctx->parent_reads_fds.read_fd;
+                }
+            }
+            loadedPluginsItr++;
+        }
+
+        if (-1 == select(maxFd + 1, &(readfds), NULL, NULL, &tv))
+        {
+            continue;
+        }
+
+        loadedPluginsItr = loadedPlugins->begin();
+
+        for ( ; loadedPluginsItr != loadedPlugins->end(); )
+        {
+            MPMCommonPluginCtx *ctx = (*loadedPluginsItr).plugin_ctx;
+            if (ctx == NULL)
+            {
+                loadedPluginsItr++;
+                continue;
+            }
+            if (ctx->started)
+            {
+                if (FD_ISSET(ctx->parent_reads_fds.read_fd, &(readfds)))
+                {
+                    pid_t childStat = waitpid(ctx->child_pid, &status, WNOHANG);
+                    MPMPipeMessage pipe_message;
+                    ssize_t readbytes = 0;
+
+                    pipe_message.payloadSize = 0;
+                    pipe_message.msgType = MPM_NOMSG;
+                    pipe_message.payload = NULL;
+                    readbytes = MPMReadPipeMessage(ctx->parent_reads_fds.read_fd, &pipe_message);
+                    if ((childStat != 0) || (readbytes <= 0))
+                    {
+                        OIC_LOG_V(DEBUG, TAG, "Plugin %s is exited",
+                                  (*loadedPluginsItr).shared_object_name);
+                        if (pipe_message.payloadSize > 0)
+                        {
+                            OICFree((void*)pipe_message.payload);
+                            pipe_message.payload = NULL;
+                            pipe_message.payloadSize = 0;
+                        }
+                        ctx->started = false;
+                    }
+                    else
+                    {
+                        (*loadedPluginsItr).callbackClient((uint32_t)pipe_message.msgType,
+                                                           (MPMMessage)pipe_message.payload,
+                                                           pipe_message.payloadSize,
+                                                           (*loadedPluginsItr).shared_object_name);
+
+                        pipe_message.msgType = MPM_NOMSG;
+                        if (pipe_message.payloadSize > 0)
+                        {
+                            OICFree((void*)pipe_message.payload);
+                            pipe_message.payload = NULL;
+                            pipe_message.payloadSize = 0;
+                        }
+
+                    }
+                }
+            }
+            loadedPluginsItr++;
+        }
+
+        sleep(1);
+    }
+
+    return (void *)loadedPlugins;
+}
+
+MPMResult MPMLoad(MPMPluginHandle *pluginHandle, const char *pluginName, MPMCallback callback,
+                  const char *reconnect_file_name)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    MPMPluginRunTime *functionSymbolTable;
+
+    if ((pluginName == NULL))
+    {
+        OIC_LOG(ERROR, TAG, "PluginName is NULL");
+        return result;
+    }
+
+    MPMPluginContext *plugin_instance = (MPMPluginContext *)OICCalloc(1, sizeof(MPMPluginContext));
+
+    if (plugin_instance == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Unable to allocate context.");
+        return MPM_RESULT_MEMORY_ERROR;
+    }
+    plugin_instance->plugin_ctx = NULL;
+
+    /*
+     * Now let's load the plugin and resolve the exported functions of the plugin
+     * The exported functions of interest: create, start, stop, destroy
+     */
+    plugin_instance->handle = dlopen(pluginName, RTLD_LAZY);
+
+    if (!plugin_instance->handle)
+    {
+        OIC_LOG_V(ERROR, TAG, "Error loading %s", pluginName);
+        OIC_LOG_V(ERROR, TAG, "Error message %s", dlerror());
+        goto CLEANUP;
+    }
+
+    strncpy(plugin_instance->shared_object_name, pluginName, MAX_SHARED_OBJECT_NAME_LENGTH-1);
+
+    OIC_LOG(DEBUG, TAG, "Resolving function symbols");
+    functionSymbolTable = (MPMPluginRunTime *) dlsym(plugin_instance->handle, "plugin_funcs");
+
+    if (functionSymbolTable == NULL)
+    {
+        OIC_LOG_V(ERROR, TAG, "Error loading function symbols from %s", pluginName);
+        goto CLEANUP;
+    }
+
+    plugin_instance->lifecycle.create  = functionSymbolTable->create;
+    plugin_instance->lifecycle.start   = functionSymbolTable->start;
+    plugin_instance->lifecycle.stop    = functionSymbolTable->stop;
+    plugin_instance->lifecycle.destroy = functionSymbolTable->destroy;
+
+    // Time to call the entry points (create and start)
+    OIC_LOG_V(INFO, TAG, "Calling create on \"%s\":", pluginName);
+
+    plugin_instance->plugin_ctx = (*(plugin_instance->lifecycle.create))();
+
+    if (NULL == plugin_instance->plugin_ctx)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to create %s", pluginName);
+        goto CLEANUP;
+    }
+    if (reconnect_file_name != NULL)
+    {
+        strncpy(plugin_instance->plugin_ctx->reconnect_file_name, reconnect_file_name,
+                MPM_MAX_FILE_NAME_LENGTH - 1);
+    }
+    else
+    {
+        plugin_instance->plugin_ctx->reconnect_file_name[0] = '\0';
+    }
+    OIC_LOG_V(INFO, TAG, "Calling start on \"%s\":", pluginName);
+
+    result = (MPMResult)(*(plugin_instance->lifecycle.start))(plugin_instance->plugin_ctx);
+    if (MPM_RESULT_OK == result)
+    {
+        OIC_LOG(INFO, TAG, "Plugin start successful.");
+        *pluginHandle = (MPMPluginHandle)(plugin_instance);
+
+        MPMPluginContext p_context;
+        p_context.lifecycle.create  = plugin_instance->lifecycle.create;
+        p_context.lifecycle.start   = plugin_instance->lifecycle.start;
+        p_context.lifecycle.stop    = plugin_instance->lifecycle.stop;
+        p_context.lifecycle.destroy = plugin_instance->lifecycle.destroy;
+        p_context.handle = plugin_instance->handle;
+
+        p_context.plugin_ctx = plugin_instance->plugin_ctx;
+
+        strncpy(p_context.shared_object_name, plugin_instance->shared_object_name,
+                sizeof(p_context.shared_object_name) - 1);
+        p_context.shared_object_name[sizeof(p_context.shared_object_name) - 1] = '\0';
+
+        p_context.callbackClient = callback;
+
+        g_LoadedPlugins.push_back(p_context);
+
+        if (g_LoadedPlugins.size() == 1)
+        {
+            startReadResponseThread();
+        }
+
+        return result;
+    }
+
+    OIC_LOG_V(ERROR, TAG, "Failed to start \"%s\":", pluginName);
+    OIC_LOG(ERROR, TAG, "Are the authorization files correct?");
+
+CLEANUP:
+    if (plugin_instance->plugin_ctx)
+    {
+        OICFree(plugin_instance->plugin_ctx);
+    }
+    OICFree(plugin_instance);
+    return MPM_RESULT_CREATED_FAILED;
+}
+
+void startReadResponseThread()
+{
+    //Create a thread to handle the responses from plugin processes
+    int error = pthread_create(&readResponsethreadhandle, NULL, readResponse, NULL);
+    if (error != 0)
+    {
+        OIC_LOG(ERROR, TAG, "readResponse thread could not be started");
+    }
+}
+
+void stopReadResponseThread()
+{
+    /* set this variable to terminate the readResponse thread */
+    exitResponseThread = true;
+    //terminate readResponse thread
+    pthread_join(readResponsethreadhandle, NULL);
+}
+
+MPMResult MPMUnload(MPMPluginHandle pluginHandle)
+{
+    if (pluginHandle == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "plugin_ctx in NULL");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    MPMPluginContext *plugin_instance = (MPMPluginContext *)(pluginHandle);
+
+    /* stop and destroy the plugin */
+    OIC_LOG_V(INFO, TAG, "Calling stop on \"%s\":", plugin_instance->shared_object_name);
+    (*(plugin_instance->lifecycle.stop))(plugin_instance->plugin_ctx);
+
+    OIC_LOG_V(INFO, TAG, "Calling destroy on \"%s\":", plugin_instance->shared_object_name);
+    (*(plugin_instance->lifecycle.destroy))(plugin_instance->plugin_ctx);
+    dlclose(plugin_instance->handle);
+
+    std::vector<MPMPluginContext>::iterator p_itr;
+    for (p_itr = g_LoadedPlugins.begin(); p_itr != g_LoadedPlugins.end(); )
+    {
+        if (strncmp((*p_itr).shared_object_name, plugin_instance->shared_object_name,
+                    strlen((*p_itr).shared_object_name)) == 0)
+        {
+            OIC_LOG_V(INFO, TAG, "plugin name %s", plugin_instance->shared_object_name);
+            p_itr = g_LoadedPlugins.erase(p_itr);
+            break;
+        }
+        else
+        {
+            p_itr++;
+        }
+    }
+
+    OICFree(plugin_instance);
+
+    if (g_LoadedPlugins.size() == 0)
+    {
+        stopReadResponseThread();
+    }
+
+    return MPM_RESULT_OK;
+}
+
+/**
+ * This function performs SCAN, ADD, REMOVE and RECONNECT functionality depending on
+ * MPMMessageType paramaeter.
+ * @param[in] pluginHandle            Handle of the plugin to which the message
+ *                                     is to be sent
+ * @param[in] message                  Message to be sent over the pipe to the plugin
+ * @param[in] size                     Size of the message in bytes
+ * @param[in] type                     Type of the message (MPM_SCAN, MPM_ADD, etc.)
+ *
+ * @return MPM_RESULT_OK if success, MPM_RESULT_INTERNAL_ERROR upon failure
+*/
+MPMResult MPMSendPipeMessage(MPMPluginHandle pluginHandle, MPMMessage message, size_t size,
+                             MPMMessageType type)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    if (pluginHandle == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "plugin_ctx in NULL");
+        return result;
+    }
+
+    MPMPluginContext *plugin_instance = (MPMPluginContext *)(pluginHandle);
+
+    if (plugin_instance->plugin_ctx->started)
+    {
+        OIC_LOG_V(DEBUG, TAG, "the plugin %s is started", plugin_instance->shared_object_name);
+        MPMPipeMessage pipe_message;
+
+        pipe_message.msgType = type;
+        pipe_message.payloadSize = size;
+        pipe_message.payload = (uint8_t *)message;
+        MPMCommonPluginCtx *ctx = (MPMCommonPluginCtx *) (plugin_instance->plugin_ctx);
+        result = MPMWritePipeMessage(ctx->child_reads_fds.write_fd, &pipe_message);
+
+        pipe_message.msgType = MPM_NOMSG;
+        pipe_message.payloadSize = 0;
+
+    }
+    else
+    {
+        OIC_LOG_V(ERROR, TAG, "the plugin %s is not yet started", plugin_instance->shared_object_name);
+    }
+    return result;
+}
+
+MPMResult MPMScan(MPMPluginHandle pluginHandle, MPMMessage message, size_t size)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    result = MPMSendPipeMessage(pluginHandle, message, size, MPM_SCAN);
+    return result;
+}
+
+MPMResult MPMAddDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    result = MPMSendPipeMessage(pluginHandle, message, size, MPM_ADD);
+    return result;
+}
+
+MPMResult MPMRemoveDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    result = MPMSendPipeMessage(pluginHandle, message, size, MPM_REMOVE);
+    return result;
+}
+
+MPMResult MPMReconnectDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    result = MPMSendPipeMessage(pluginHandle, message, size, MPM_RECONNECT);
+    return result;
+}
diff --git a/bridging/mini_plugin_manager/miniPluginManager.h b/bridging/mini_plugin_manager/miniPluginManager.h
new file mode 100644 (file)
index 0000000..3e92376
--- /dev/null
@@ -0,0 +1,140 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+
+/* This file contains the "C" plugin interface definition.  This header file
+ * and its corresponding implementation file is intended to be shared among
+ * ALL plugins.  Modification of this file is not necessary for the construction
+ * of a new plugin.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <pthread.h>
+#include "mpmErrorCode.h"
+#include <stdbool.h>
+
+#ifndef __MINI_PLUGIN_MANAGER_H__
+#define __MINI_PLUGIN_MANAGER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*******************************************************************************
+ * defines go here
+ ******************************************************************************/
+
+#define MAX_SHARED_OBJECT_NAME_LENGTH  300
+#define MAX_SHARED_OBJECTS_LOADED      20
+
+/*******************************************************************************
+ * type defines and structure definitions go here
+ ******************************************************************************/
+
+typedef void *MPMPluginHandle;
+typedef void *MPMMessage;
+
+/** MPMCallback - for receiving the responses from the plugin */
+typedef MPMCbResult (* MPMCallback)(uint32_t msgType, MPMMessage message, size_t size,
+                                    const char *pluginName);
+
+/**
+ * This method sends a scan request to the plugin
+ * @paran[in] pluginHandle            Plugin handle for the specific plugin
+ * @param[in] message                 Scan request to be passed from the client to
+ *                                    the mini_plugin_manager
+ * @param[in] size                    Size of the message
+ *
+ * @return MPM_RESULT_OK if no errors, else ERROR CODES defined in mpmErrorCode.h in case of error
+*/
+MPMResult MPMScan(MPMPluginHandle pluginHandle, MPMMessage message, size_t size);
+
+/**
+ * This method sends a add device request to the plugin
+ * @param[in] pluginHandle            Plugin handle for the specific plugin
+ * @param[in] message                 Add request to be passed from the client to
+ *                                    the mini_plugin_manager
+ * @param[in] size                    Size of the message
+ *
+ * @return MPM_RESULT_OK if no error, else ERROR CODES defined in mpmErrorCode.h in case of error
+*/
+MPMResult MPMAddDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size);
+
+/**
+ * This method sends a reconnect request to the plugin
+ * @param[in] pluginHandle            Plugin handle for the specific plugin
+ * @param[in] message                 Request request to be passed from the client to
+ *                                    the mini_plugin_manager
+ * @param[in] size                    Size of the message
+ *
+ * @return MPM_RESULT_OK if no error, else ERROR CODES defined in mpmErrorCode.h in case of error
+*/
+MPMResult MPMReconnectDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size);
+
+/**
+ * This method sends a remove device request to the plugin
+ * @param[in] pluginHandle            Plugin handle for the specific plugin
+ * @param[in] message                 Remove request to be passed from the client to
+ *                                    the mini_plugin_manager
+ * @param[in] size                    Size of the message
+ *
+ * @return MPM_RESULT_OK if no error, else ERROR CODES defined in mpmErrorCode.h in case of error
+*/
+MPMResult MPMRemoveDevice(MPMPluginHandle pluginHandle, MPMMessage message, size_t size);
+
+/**
+ * This is MPM library API to load the Plugins. It performs
+ * following functionality as:
+ * 1. Allocates memory to plugin_ctx
+ * 2. Opens shared library
+ * 3. Resolves the function symbols
+ * 4. Calls create() and start() for the plugins
+ *
+ * @param[out] pluginHandle            Plugin Handle to be handed back to
+ *                                     mpm_client application
+ * @param[in] pluginName               Name of the shared library to be loaded.
+ * @param[in] callback                 Callback function to be invoked upon SARR responses from
+ *                                     plugin to the client
+ * @param[in] filename                 Reconnect filename each plugin would maintain, currently not used
+ *
+ * @return MPM_RESULT_OK if no errors, else ERROR CODES defined in mpmErrorCode.h in case of error
+ */
+MPMResult MPMLoad(MPMPluginHandle *pluginHandle, const char *pluginName, MPMCallback callback,
+                  const char *filename);
+
+/**
+ * This is MPM library API to unload the Plugins. It performs
+ * following functionality as:
+ * 1. Calls stop() and destroy() for the plugin
+ * 2. Closes shared library
+ * 3. deallocates memory allocated to plugin_ctx
+ *
+ * @param[in] pluginHandle            Plugin handle sent by the
+ *                                    mpm_client application
+ *
+ * @return MPM_RESULT_OK if no errors, MPM_RESULT_INTERNAL_ERROR if stack process error
+ */
+MPMResult MPMUnload(MPMPluginHandle pluginHandle);
+
+#ifdef __cplusplus
+}
+#endif // #ifdef __cplusplus
+
+#endif /* __MINI_PLUGIN_MANAGER_H__ */
diff --git a/bridging/mpm_client/MPMSampleClient.cpp b/bridging/mpm_client/MPMSampleClient.cpp
new file mode 100644 (file)
index 0000000..08e2d8d
--- /dev/null
@@ -0,0 +1,606 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 <iostream>
+#include <unistd.h>
+#include <cstring>
+#include <string>
+#include <vector>
+#include <map>
+#include <thread>
+#include <sstream>
+#include <atomic>
+#include <csignal>
+#include <messageHandler.h>
+#include "sqlite3.h"
+#include <algorithm>
+
+#include "miniPluginManager.h"
+
+using namespace std;
+
+#define TERMINAL_BOLD_TEXT_RED            "\033[1;31m"
+#define TERMINAL_ATTRIBUTE_RESET          "\033[0m"
+
+#define BIND_INDEX_FIRST 1
+#define BIND_INDEX_SECOND 2
+#define BIND_INDEX_THIRD 3
+#define SQLITE_INSERT_T "INSERT INTO RECONNECT VALUES(?,?,?)"
+#define SQLITE_GET_RECONNECT_FILE "SELECT RECONNECT_FILE from RECONNECT where PLUGIN_NAME=?"
+#define SQLITE_DELETE_DEVICE "DELETE FROM RECONNECT WHERE URI=?"
+#define SQLITE_SELECT_RECONNECT_FILE "SELECT RECONNECT_FILE from RECONNECT where URI=?"
+
+#define RECONNECT_FILE_LENGTH 260
+
+map<string, MPMPluginHandle> g_loadedPlugins;
+std::atomic_bool keepEventLoop(true);
+bool autoAddMode = false;
+
+std::string rawUsageMessage =
+    R"(
+Usage: sample_app [OPTION]... PLUGINS_TO_LOAD...
+
+Load and unload plugins, send them commands after loading etc.
+Give valid inputs! This is just an example client and input is not sanitized to keep
+the client minimal.
+
+-n          Start plugins in NON secure mode. Default is secure.
+
+-a          Start this client in auto add mode.
+            This will cause the client send an ADD message with the same message as in
+            the scan response.
+
+-h          View this message.
+)";
+
+template <typename T>
+void log(T t)
+{
+    cout << TERMINAL_BOLD_TEXT_RED << t << TERMINAL_ATTRIBUTE_RESET;
+}
+template<typename T, typename... Args>
+void log(T t, Args... args)
+{
+    cout << TERMINAL_BOLD_TEXT_RED << t << TERMINAL_ATTRIBUTE_RESET;
+    log(args...);
+}
+
+enum pluginCommands { SCAN, ADD, REMOVE, UNLOAD, MAX_COMMANDS };
+
+/**
+ * Macro to verify sqlite success.
+ * eg: VERIFY_NON_NULL(TAG, ptrData, ERROR,OC_STACK_ERROR);
+ */
+#define PDM_VERIFY_SQLITE_OK(tag, arg, logLevel) do{ if (SQLITE_OK != (arg)) \
+            { log("Error in " #arg ", Error Message: ", \
+               sqlite3_errmsg(db), "\n"); return; }}while (0)
+
+sqlite3 *db;
+
+void printUsage()
+{
+    std::cout << rawUsageMessage << "\n\n";
+}
+
+static int sqlCallback(void *data, int argc, char **argv, char **azColName)
+{
+    int i;
+
+    log("Data: ", (const char*)data, "\n");
+    for(i=0; i<argc; i++)
+    {
+        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
+    }
+    printf("\n");
+    return 0;
+}
+
+void fileWriteAndDbUpdate(const char * uri, const char * pluginName, const char * metadata)
+{
+    FILE *fp = NULL;
+    char filename[RECONNECT_FILE_LENGTH];
+    const char* data = "Callback function called";
+    int rc;
+    char *zErrMsg = 0;
+    std::string sql;
+    std::string changedUri = uri;
+    sqlite3_stmt *stmt = 0;
+
+    replace(changedUri.begin(), changedUri.end(), '/', '_' );
+    changedUri.erase(0,1);
+
+    memset(filename, 0, RECONNECT_FILE_LENGTH);
+    strncpy(filename, changedUri.c_str(), changedUri.length());
+    strncat(filename, ".txt", strlen(".txt")+1);
+
+    printf("filename: %s\n", filename);
+    fp = fopen(filename, "w");
+    if (!fp)
+    {
+        printf("Unable to open file");
+        return;
+    }
+    fwrite(metadata, 1, MPM_MAX_METADATA_LEN, fp);
+    fputs("\n", fp);
+    fclose(fp);
+
+    /* Create SQL statement */
+    rc = sqlite3_prepare_v2(db, SQLITE_INSERT_T,
+                              strlen(SQLITE_INSERT_T) + 1, &stmt, NULL);
+    PDM_VERIFY_SQLITE_OK(TAG, rc, ERROR);
+
+    rc = sqlite3_bind_text(stmt, BIND_INDEX_FIRST, uri, MPM_MAX_URI_LEN, SQLITE_STATIC);
+    PDM_VERIFY_SQLITE_OK(TAG, rc, ERROR);
+
+    rc = sqlite3_bind_text(stmt, BIND_INDEX_SECOND, pluginName, MAX_SHARED_OBJECT_NAME_LENGTH, SQLITE_STATIC);
+    PDM_VERIFY_SQLITE_OK(TAG, rc, ERROR);
+
+    rc = sqlite3_bind_text(stmt, BIND_INDEX_THIRD, filename, RECONNECT_FILE_LENGTH, SQLITE_STATIC);
+    PDM_VERIFY_SQLITE_OK(TAG, rc, ERROR);
+
+    rc = sqlite3_step(stmt);
+    if (SQLITE_DONE != rc)
+    {
+        if (SQLITE_CONSTRAINT == rc)
+        {
+            //new OCStack result code
+            log("Error Occured: ",sqlite3_errmsg(db), "\n");
+            sqlite3_finalize(stmt);
+            return;
+        }
+        log("Error Occured: ",sqlite3_errmsg(db), "\n");
+        sqlite3_finalize(stmt);
+        return ;
+    }
+    sqlite3_finalize(stmt);
+
+    /* Create SQL statement */
+    sql = "SELECT * from RECONNECT";
+
+    /* Execute SQL statement */
+    rc = sqlite3_exec(db, sql.c_str(), sqlCallback, (void*)data, &zErrMsg);
+    if (rc != SQLITE_OK)
+    {
+        log("SQL error: ", zErrMsg, "\n");
+        sqlite3_free(zErrMsg);
+    }
+    else
+    {
+        log("Operation done successfully\n");
+    }
+
+}
+
+void deleteReconnectFile(char * uri)
+{
+    sqlite3_stmt *stmt = 0;
+    int res = 0;
+
+    res = sqlite3_prepare_v2(db, SQLITE_SELECT_RECONNECT_FILE,
+                              strlen(SQLITE_SELECT_RECONNECT_FILE) + 1, &stmt, NULL);
+    PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+    res = sqlite3_bind_text(stmt, BIND_INDEX_FIRST, uri, MPM_MAX_URI_LEN, SQLITE_STATIC);
+    PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+
+    if (SQLITE_ROW == sqlite3_step(stmt))
+    {
+        char reconnect_file[RECONNECT_FILE_LENGTH];
+        const unsigned char *ptr = sqlite3_column_text(stmt, 0);
+        if (ptr == NULL)
+        {
+            log("An internal error occured\n");
+            return;
+        }
+        memcpy(reconnect_file, ptr, RECONNECT_FILE_LENGTH);
+        log("reconnect file to be deleted is: ", reconnect_file, "\n");
+        remove(reconnect_file);
+    }
+}
+
+void deleteRecordFromDb(char * uri)
+{
+    int res = 0;
+    sqlite3_stmt *stmt = 0;
+
+    res = sqlite3_prepare_v2(db, SQLITE_DELETE_DEVICE, strlen(SQLITE_DELETE_DEVICE) + 1, &stmt, NULL);
+    PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+
+    res = sqlite3_bind_text(stmt, BIND_INDEX_FIRST, uri, MPM_MAX_URI_LEN, SQLITE_STATIC);
+    PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+
+    if (SQLITE_DONE != sqlite3_step(stmt))
+    {
+        log("Delete error: ", sqlite3_errmsg(db), "\n");
+        sqlite3_finalize(stmt);
+        return;
+    }
+    log("successful", "\n");
+    sqlite3_finalize(stmt);
+}
+
+void clearDevice(char * device)
+{
+    char * uri = (char *) calloc(1, MPM_MAX_URI_LEN);
+    if (!uri)
+    {
+        log(std::string("calloc failed"), "\n");
+        return;
+    }
+    memcpy(uri, device, strlen(device));
+
+    deleteReconnectFile(uri);
+    deleteRecordFromDb(uri);
+
+    free(uri);
+}
+
+MPMCbResult onCallback(uint32_t msgType, MPMMessage message, size_t size, const char * plugin_name)
+{
+    log("Message from:", std::string(plugin_name), "\n");
+    log("Interpreting payload as string:\n");
+
+    char* message_char = (char*) calloc(1, size+1);
+    if (message_char == NULL)
+    {
+        log("Message_char is null\n");
+        return MPM_CB_RESULT_ERROR;
+    }
+    memset(message_char, 0, size+1);
+    memcpy(message_char, message, size);
+    log(std::string(message_char), "\n");
+
+    if (msgType == MPM_SCAN && autoAddMode)
+    {
+        MPMAddDevice(g_loadedPlugins[std::string(plugin_name)], message, size);
+    }
+    else if (msgType == MPM_ADD)
+    {
+        MPMAddResponse addResponse;
+        memset(&addResponse, 0, sizeof(MPMAddResponse));
+        memcpy(&addResponse, message_char, sizeof(MPMAddResponse));
+
+        log("uri: ", addResponse.uri, "\n");
+
+        fileWriteAndDbUpdate(addResponse.uri, plugin_name, addResponse.metadata);
+    }
+    else if (msgType == MPM_REMOVE)
+    {
+        clearDevice(message_char);
+    }
+
+    free(message_char);
+
+    return MPM_CB_RESULT_OK;
+}
+
+void loadPlugin(const std::string pluginPath)
+{
+    MPMPluginHandle pluginHandle;
+
+    MPMResult result = MPMLoad(&pluginHandle, pluginPath.c_str(), onCallback, NULL);
+
+    if (result == MPM_RESULT_OK)
+    {
+        g_loadedPlugins[pluginPath] = pluginHandle;
+        printf("%p\n", pluginHandle);
+        log(pluginPath, " is loaded.\n");
+    }
+    else
+    {
+        std::cout << pluginPath << " failed to load." << std::endl;
+    }
+}
+
+void printPlugins()
+{
+    log("Plugins:\n");
+    int no = 0;
+    for (map<std::string, MPMPluginHandle >::iterator itr = g_loadedPlugins.begin(); itr != g_loadedPlugins.end();
+            ++itr)
+    {
+        log("\t", no, ":", itr->first, "\n");
+        ++no;
+    }
+}
+
+void printCommands()
+{
+    log("Commands:", "\t" , SCAN , ":" , "SCAN  ", ADD , ":" , "ADD  ", REMOVE , ":" , "REMOVE  "
+    , UNLOAD , ":" , "UNLOAD" , "\n");
+}
+
+void unloadCommand(MPMPluginHandle handle)
+{
+    std::string uri;
+    for (map<std::string, MPMPluginHandle >::iterator itr = g_loadedPlugins.begin(); itr != g_loadedPlugins.end();
+         ++itr)
+    {
+        if (itr->second == handle)
+        {
+            uri = itr->first;
+        }
+    }
+    log("Unloading ", uri, "\n");
+    MPMUnload(handle);
+    g_loadedPlugins.erase(uri);
+}
+
+void addCommand(MPMPluginHandle handle)
+{
+    std::string message;
+    log("What message to send to ADD (Usually URI of a \"scanned\" resource) : ");
+    std::cin >> message;
+
+    log("Sending ", message.c_str(), "\n");
+    MPMAddDevice(handle, const_cast<char *> (message.c_str()), message.size() + 1);
+}
+
+void removeCommand(MPMPluginHandle handle)
+{
+    std::string message;
+    log("What message to send to REMOVE (Usually URI of a \"added\" resource) : ");
+    std::cin >> message;
+
+    log("Sending ", message.c_str(), "\n");
+    MPMRemoveDevice(handle, const_cast<char *> (message.c_str()), message.size() + 1);
+}
+
+void scanCommand(MPMPluginHandle handle)
+{
+    MPMScan(handle, NULL, 0);
+}
+
+
+void processCommand (MPMPluginHandle handle, int commandNo)
+{
+    switch(commandNo)
+    {
+        case UNLOAD:
+            unloadCommand(handle);
+            break;
+         case SCAN:
+            scanCommand(handle);
+            break;
+         case ADD:
+            addCommand(handle);
+            break;
+        case REMOVE:
+            removeCommand(handle);
+            break;
+         default:
+            log("Invalid command\n");
+            break;
+    }
+}
+void getCommandEventLoop()
+{
+    while (keepEventLoop)
+    {
+        std::string input;
+        int plugin_no = -1, commandNo = -1;
+
+        std::cout << endl << endl;
+        if (g_loadedPlugins.empty())
+        {
+            log("No plugins loaded. We're done here. Bye!\n");
+            break;
+        }
+        printPlugins();
+        std::cout << "\n";
+        printCommands();
+        log("Enter blank line to see more logs.\n");
+        log("Enter plugin_no command_no : ");
+        while (getline(cin, input ))
+        {
+            if (input != "\n")
+            {
+                stringstream ss(input);
+                ss >> plugin_no >> commandNo;
+                break;
+            }
+        }
+
+        if (commandNo != -1 || plugin_no != -1)
+        {
+            MPMPluginHandle selectedPlugin = NULL;
+            int i = 0;
+
+            for (map<std::string, MPMPluginHandle >::iterator itr = g_loadedPlugins.begin();
+                 itr != g_loadedPlugins.end();
+                 ++itr)
+            {
+                if (i == plugin_no)
+                {
+                    selectedPlugin = itr->second;
+                    break;
+                }
+                ++i;
+            }
+            processCommand (selectedPlugin, commandNo);
+        }
+    }
+    for (map<std::string, MPMPluginHandle >::iterator itr = g_loadedPlugins.begin();
+         itr != g_loadedPlugins.end();
+         ++itr)
+    {
+        unloadCommand(itr->second);
+    }
+}
+
+void loadCommandLinePlugins (std::vector<string> &pluginsToLoad)
+{
+    for (uint32_t i = 0; i < pluginsToLoad.size(); ++i)
+    {
+        log("Loading ", pluginsToLoad[i], "\n");
+        loadPlugin(pluginsToLoad[i]);
+    }
+}
+
+void signalHandler(int signal)
+{
+    (void) signal;
+    keepEventLoop = false;
+}
+
+void openDatabase()
+{
+    int rc;
+    char *zErrMsg = 0;
+    std::string sql;
+    rc = sqlite3_open("reconnect.db", &db);
+    if (rc)
+    {
+        log("Can't open database: ", sqlite3_errmsg(db), "\n");
+        return;
+    }
+    else
+    {
+        log("Opened database successfully\n");
+    }
+    /* Create SQL statement */
+    sql = "CREATE TABLE IF NOT EXISTS RECONNECT("  \
+          "URI TEXT PRIMARY KEY NOT NULL," \
+          "PLUGIN_NAME TEXT NOT NULL," \
+          "RECONNECT_FILE TEXT NOT NULL);";
+
+    /* Execute SQL statement */
+    rc = sqlite3_exec(db, sql.c_str(), NULL, 0, &zErrMsg);
+    if (rc != SQLITE_OK)
+    {
+        log("SQL error: ", zErrMsg, "\n");
+        sqlite3_free(zErrMsg);
+    }
+    else
+    {
+       log("Table created successfully\n");
+    }
+}
+
+void reconnect()
+{
+    FILE *fp = NULL;
+
+    size_t len = -1;
+    int ret = 0;
+    char * metadata = NULL;
+    sqlite3_stmt *stmt = 0;
+    int res = 0;
+    for (map<std::string, MPMPluginHandle >::iterator itr = g_loadedPlugins.begin();
+             itr != g_loadedPlugins.end();
+             ++itr)
+    {
+        res = sqlite3_prepare_v2(db, SQLITE_GET_RECONNECT_FILE,
+                                      strlen(SQLITE_GET_RECONNECT_FILE) + 1, &stmt, NULL);
+        PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+
+        char * plugin_name = (char *) calloc( 1, MAX_SHARED_OBJECT_NAME_LENGTH);
+        if (plugin_name == NULL)
+        {
+            log("calloc failed","\n");
+            return ;
+        }
+        memcpy(plugin_name, (*itr).first.c_str(), (*itr).first.length());
+
+        res = sqlite3_bind_text(stmt, BIND_INDEX_FIRST, plugin_name, MAX_SHARED_OBJECT_NAME_LENGTH, SQLITE_STATIC);
+        PDM_VERIFY_SQLITE_OK(TAG, res, ERROR);
+        while (SQLITE_ROW == sqlite3_step(stmt))
+        {
+            char filename[RECONNECT_FILE_LENGTH];
+            const unsigned char *ptr = sqlite3_column_text(stmt, 0);
+            if (ptr == NULL)
+            {
+                log("An internal error occured\n");
+                continue;
+            }
+            memcpy(filename, ptr, RECONNECT_FILE_LENGTH);
+            log("reconnect filename is: ", filename, "\n");
+            fp = fopen(filename, "r");
+            if (fp == NULL)
+            {
+                log("File could not be opened\n");
+                continue;
+            }
+            ret = getline(&metadata, &len, fp);
+            if (ret == -1)
+            {
+                fclose(fp);
+                continue;
+            }
+            fclose(fp);
+            MPMReconnectDevice(itr->second, metadata, MPM_MAX_METADATA_LEN);
+        }
+        printf("no columns returned\n");
+        free(metadata);
+        metadata = NULL;
+        free(plugin_name);
+        sqlite3_finalize(stmt);
+    }
+}
+
+
+int main(int argc, char const *argv[])
+{
+    vector<string> pluginsToLoad;
+    pluginsToLoad.clear();
+
+    for (int i = 1; i < argc; ++i)
+    {
+        if (strcmp(argv[i], "-n") == 0)
+        {
+            setenv("NONSECURE", "true", 1);
+        }
+        else if (strcmp(argv[i], "-a") == 0)
+        {
+            autoAddMode = true;
+        }
+        else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0)
+        {
+            printUsage();
+            return 0;
+        }
+        else
+        {
+            pluginsToLoad.push_back(std::string(argv[i]));
+        }
+    }
+
+    if (!pluginsToLoad.empty())
+    {
+        loadCommandLinePlugins(pluginsToLoad);
+    }
+    else
+    {
+        log("At least one plugin needed\n");
+        return 0;
+    }
+
+    openDatabase();
+
+    std::signal(SIGINT, signalHandler);
+
+    reconnect();
+
+    std::thread eventLoopThread = std::thread(getCommandEventLoop);
+    eventLoopThread.join();
+
+    sqlite3_close(db);
+    return 0;
+}
diff --git a/bridging/mpm_client/README b/bridging/mpm_client/README
new file mode 100644 (file)
index 0000000..67f2309
--- /dev/null
@@ -0,0 +1,31 @@
+General:
+The "mpm_sample_client" is intended to show how you can
+programmatically control the functionality of the
+"minipluginmanager" library.
+
+1. "mpm_sample_client" awaits user input before doing
+    anything. You will be prompted with CLI controls
+    to flex the minipluginmanager library's APIs.
+
+    The only modes of operation are controlled via
+    command line arguments upon startup.
+
+    + You may issue the flag "-n" such that all
+      resources created by any plugin are created in
+      IoTivity's non-secure mode (i.e. no DTLS
+      encryption is used for the representation of
+      that particular resource).
+        (If this flag is not passed in, all
+         resources will be created in IoTivity's
+         secure mode (all requests/responses will
+         use DTLS encryption).
+
+    + Only one plugin will be loaded with the name
+      specified
+
+    + Operations performed by the "mpm_sample_client" are
+      as follows:
+        - Scan - Device Discovery
+        - Add - Create iotivity resource
+        - Remove - Delete iotivity resource
+        - Unload - to unload the plugin and exit mpm_sample_client
diff --git a/bridging/mpm_client/SConscript b/bridging/mpm_client/SConscript
new file mode 100644 (file)
index 0000000..16987e9
--- /dev/null
@@ -0,0 +1,83 @@
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# 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.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+Import('env')
+import os
+import os.path
+target_os = env.get('TARGET_OS')
+target_arch = env.get('TARGET_ARCH')
+mpmclient_env = env.Clone()
+src_dir = env.get('SRC_DIR')
+bridging_dir = os.path.join(src_dir, 'bridging')
+
+######################################################################
+# Build flags
+######################################################################
+
+def maskFlags(flags):
+    flags = [flags.replace('-Wl,--no-undefined', '' ) for flags in flags]
+    return flags
+mpmclient_env.PrependUnique(CPPPATH = [
+        os.path.join(bridging_dir, 'include'),
+        os.path.join(bridging_dir, 'mini_plugin_manager'),
+        os.path.join(bridging_dir, 'mpm_client')
+                ])
+
+#  Note: Each of the plugin names need to be listed here after 'minipluginmanager'.
+#  Otherwise you will need to prepend LD_LIBRARY_PATH to your execution command.
+mpmclient_env.PrependUnique(LIBS = ['minipluginmanager'])
+
+if target_arch in ['x86_64', 'arm64']:
+    mpmclient_env.AppendUnique(CPPFLAGS = ['-Llib64'])
+else:
+    mpmclient_env.AppendUnique(CPPFLAGS = ['-Llib'])
+mpmclient_env.AppendUnique(LIBS = ['pthread'])
+
+mpmclient_env.AppendUnique(CPPDEFINES = ['TB_LOG'])
+
+mpmclient_env.AppendUnique(LIBS = ['m',
+                               'octbstack',
+                               'ocsrm',
+                               'connectivity_abstraction',
+                               'coap',
+                               'curl' ])
+
+mpmclient_env.AppendUnique(RPATH = [os.path.join(env.get('BUILD_DIR'), 'bridging', 'plugins')])
+
+if target_os in ['linux', 'tizen']:
+    mpmclient_env.ParseConfig('pkg-config --cflags --libs sqlite3')
+else:
+    mpmclient_env.AppendUnique(CPPPATH = ['#/extlibs/sqlite3'])
+
+mpmclient_env['LINKFLAGS'] = maskFlags(env['LINKFLAGS'])
+mpmclient_env.AppendUnique(LINKFLAGS = ['-Wl,--allow-shlib-undefined'])
+######################################################################
+# Source files and Targets
+######################################################################
+mpm_sample_client = mpmclient_env.Program('mpm_sample_client',
+        ['MPMSampleClient.cpp'])
+
+list_of_samples = [mpm_sample_client]
+
+Alias("samples", list_of_samples)
+
+env.AppendTarget('samples')
+
+
diff --git a/bridging/plugins/lifx_plugin/README b/bridging/plugins/lifx_plugin/README
new file mode 100644 (file)
index 0000000..55c8b31
--- /dev/null
@@ -0,0 +1,66 @@
+General:
+To use this plugin, a config file "lifx.cnf" needs to be populated and placed in
+the same directory as the mpm client (i.e. "mpm_client" or "mpm_client_sarr")
+generated by IoTivity's build system.
+
+Note: A LifX light bulb with a non-proxied internet connection is required to use
+this plugin. All tests/verifications have been with a "LifX Color 1000 A19" bulb.
+This plugin will NOT provision your light bulb for you. You will need to perform
+the set-up steps through the LifX app (this can be found in either the iOS or
+Android app store) and your own LifX user account.
+
+What should this file look like?
+
+    See sample "lifx.cnf.sample" file with contents as shown
+    below(without spaces, tabs or quotes):
+
+    "
+
+        cdf1519ec9998380fff7abcaf03d404401063cec7a2972dc68d4eef3e6f328e2:
+
+    "
+
+Where to put this file?
+    The placement of the lifx.cnf file should be where
+    your mpm client is also:
+
+    <iotivity>/out/<TARGET_OS>/<TARGET_ARCH>/<BUILD>/bridging/src/mpm_client
+
+    Example: <iotivity>/out/linux/x86_64/release/bridging/src/mpm_client
+        Depending on your build configuration, the path may
+        look mildly different.
+
+What is this key or token?
+
+    The LifX mapping requires usage of the LifX
+    Developer's API. The usage of this API requires
+        that every user/developer has their own HTTP API
+    Token.This HTTP API Token allows the
+    liblifxplugin.so to perform actions within your
+    LifX cloud account on your behalf.
+
+Where can I obtain the token as shown in the above example?
+
+    Use the same username and password you used to set
+    up your LifX bulb at the following address to
+    obtain a token and select "Generate Token":
+
+    https://cloud.lifx.com/settings
+
+    Append a colon ":" to the end of the token and
+    place it in your lifx.cnf file. Do not share this
+    token with anyone. This token can give someone
+    complete access to your LifX account. Be careful.
+
+How can I start this plugin?
+
+    Use the binary executable 'mpm_sample_client' to load and 
+    control this plugin.
+
+    More information on these clients can be found at
+    <iotivity>/bridging/src/mpm_client/README.
+
+For proper documentation of this plugin, Mini Plugin
+Manager, the client applications, and other plugins, please
+perform a query on the "Bridging" or "Bridging Project" at
+wiki.iotivity.org.
diff --git a/bridging/plugins/lifx_plugin/SConscript b/bridging/plugins/lifx_plugin/SConscript
new file mode 100644 (file)
index 0000000..914aa8e
--- /dev/null
@@ -0,0 +1,98 @@
+#******************************************************************
+#
+# Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+#
+# 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.
+#
+#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+##
+# LifX Plugin build script
+##
+
+import os
+import os.path
+
+Import('env')
+
+target_os = env.get('TARGET_OS')
+src_dir = env.get('SRC_DIR')
+bridging_path = os.path.join(src_dir, 'bridging')
+
+lifx_env = env.Clone()
+
+print "Reading LifX Plugin script"
+
+######################################################################
+# Build flags
+######################################################################
+
+def maskFlags(flags):
+    flags = [flags.replace('-Wl,--no-undefined', '' ) for flags in flags]
+    return flags
+
+lifx_env.PrependUnique(CPPPATH = [ os.path.join(src_dir, 'resource', 'c_common', 'oic_malloc', 'include'),
+                              os.path.join(src_dir, 'resource', 'c_common', 'oic_string', 'include'),
+                              os.path.join(src_dir, 'resource', 'c_common'),
+                              os.path.join(src_dir, 'resource', 'oc_logger', 'include'),
+                              os.path.join(src_dir, 'resource', 'csdk', 'logger', 'include'),
+                              os.path.join(src_dir, 'resource', 'csdk', 'include'),
+                              os.path.join(src_dir, 'resource', 'csdk', 'stack', 'include'),
+                              os.path.join(src_dir, 'resource', 'include'),
+                              os.path.join(src_dir, 'extlibs', 'cjson'),
+                              os.path.join(src_dir, 'extlibs', 'tinycbor', 'src'),
+                              os.path.join(src_dir, 'extlibs', 'rapidjson', 'rapidjson', 'include', 'rapidjson')
+                              ])
+lifx_env.AppendUnique(CPPPATH = [ os.path.join(bridging_path, 'include'),
+                             os.path.join(bridging_path, 'plugins', 'lifx_plugin', 'lifx_objects')
+                             ])
+
+if target_os not in ['arduino', 'windows']:
+    lifx_env.AppendUnique(CPPDEFINES = ['WITH_POSIX'])
+
+if target_os in ['darwin','ios']:
+    lifx_env.AppendUnique(CPPDEFINES = ['_DARWIN_C_SOURCE'])
+
+if 'g++' in lifx_env.get('CXX'):
+    lifx_env.AppendUnique(CXXFLAGS = ['-std=c++0x', '-Wall', '-Wextra', '-Werror'])
+
+lifx_env.AppendUnique(RPATH = [lifx_env.get('BUILD_DIR')])
+lifx_env.AppendUnique(LIBPATH = [lifx_env.get('BUILD_DIR')])
+
+if lifx_env.get('LOGGING'):
+    lifx_env.AppendUnique(CPPDEFINES = ['TB_LOG'])
+
+lifx_env['LINKFLAGS'] = maskFlags(env['LINKFLAGS'])
+lifx_env.AppendUnique(LINKFLAGS = ['-Wl,--allow-shlib-undefined'])
+lifx_env.AppendUnique(LINKFLAGS = ['-Wl,--whole-archive', lifx_env.get('BUILD_DIR') +'libmpmcommon.a','-Wl,-no-whole-archive'])
+
+lifx_env.AppendUnique(LIBS = ['m',
+                               'octbstack',
+                               'ocsrm',
+                               'connectivity_abstraction',
+                               'coap',
+                               'curl' ])
+
+#####################################################################
+# Source files and Target(s)
+######################################################################
+lifx_src = [
+         os.path.join(bridging_path, 'plugins', 'lifx_plugin', 'lifxResource.cpp'),
+         os.path.join(bridging_path, 'plugins', 'lifx_plugin', 'lifx_objects', 'lifx.cpp'),
+         ]
+
+lifx_env.AppendUnique(LIFX_SRC = lifx_src)
+lifxlib = lifx_env.SharedLibrary('lifxplugin', lifx_env.get('LIFX_SRC'))
+lifx_env.InstallTarget(lifxlib, 'lifxplugin')
+lifx_env.UserInstallTargetLib(lifxlib, 'lifxplugin')
diff --git a/bridging/plugins/lifx_plugin/lifx.cnf.sample b/bridging/plugins/lifx_plugin/lifx.cnf.sample
new file mode 100644 (file)
index 0000000..5438990
--- /dev/null
@@ -0,0 +1 @@
+cdf1519ec9998380fff7abcaf03d404401063cec7a2972dc68d4eef3e6f328e2:
diff --git a/bridging/plugins/lifx_plugin/lifxResource.cpp b/bridging/plugins/lifx_plugin/lifxResource.cpp
new file mode 100644 (file)
index 0000000..e33bef5
--- /dev/null
@@ -0,0 +1,691 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 <stdio.h>
+#include <string.h>
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <set>
+#include <cmath>
+#include <mutex>
+#include "pluginServer.h"
+#include "ocpayload.h"
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "lifx.h"
+#include "logger.h"
+#include "ConcurrentIotivityUtils.h"
+
+MPMPluginCtx *g_pluginCtx = NULL;
+
+#define DEVICE_NAME "LIFX_BULB"
+#define DEVICE_TYPE "LIGHT_BULB"
+#define MANUFACTURER_NAME "LIFX"
+#define BM 3
+#define MAX_ACCESS_TOKEN_LENGTH 1024
+#define TAG "LIFX_RESOURCE"
+
+static const char* OIC_BINARY_SWITCH = "oic.r.switch.binary";
+static const char* OIC_BRIGHTNESS_LIGHT = "oic.r.light.brightness";
+
+const uint BINARY_SWITCH_CALLBACK = 0;
+const uint BRIGHTNESS_CALLBACK = 1;
+
+static const std::string BINARY_SWITCH_RELATIVE_URI = "/switch";
+static const std::string BRIGHTNESS_RELATIVE_URI = "/brightness";
+
+char accessToken[MAX_ACCESS_TOKEN_LENGTH];
+const static char CRED_FILE[] = "./oic_svr_db_lifx.dat";
+
+using namespace OC::Bridging;
+
+typedef struct
+{
+    char id[MPM_MAX_LENGTH_64];
+    char uuid[MPM_MAX_LENGTH_64];
+    char label[MPM_MAX_LENGTH_64];
+    char user[MPM_MAX_LENGTH_256];
+} LightDetails;
+
+std::map<std::string, LifxLightSharedPtr> uriToLifxLightMap;
+std::map<std::string, LifxLightSharedPtr> addedLights;
+std::mutex addedLightsLock;
+
+// Forward declarations.
+static void *lightMonitorThread(void *pointer);
+OCEntityHandlerResult resourceEntityHandler(OCEntityHandlerFlag flag,
+        OCEntityHandlerRequest *request, void *callbackParam);
+
+static LifxLightSharedPtr getLifXLightFromOCFResourceUri(std::string resourceUri);
+
+FILE *lifxSecurityFile(const char *, const char *mode)
+{
+    return fopen(CRED_FILE, mode);
+}
+
+MPMResult pluginCreate(MPMPluginCtx **pluginSpecificCtx)
+{
+    if (g_pluginCtx != NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Plugin is already created.");
+        return MPM_RESULT_ALREADY_CREATED;
+    }
+
+    MPMPluginCtx *ctx = (MPMPluginCtx *) OICCalloc(1, sizeof(MPMPluginCtx));
+
+    if (ctx == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Allocation of plugin context failed");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    *pluginSpecificCtx = ctx;
+    g_pluginCtx = ctx;
+
+    ctx->device_name = "Lifx Translator";
+    ctx->resource_type = "oic.d.light";
+    ctx->open = lifxSecurityFile;
+
+
+    FILE *fp = fopen("./lifx.cnf", "r");
+
+    if (NULL == fp)
+    {
+        OIC_LOG(ERROR, TAG, "error loading lifx.cnf file.");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    if (fgets(accessToken, MAX_ACCESS_TOKEN_LENGTH - 1, fp) == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to read ./lifx.cnf");
+        fclose(fp);
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+    accessToken[strlen(accessToken)-1] = '\0';
+    fclose(fp);
+
+    return MPM_RESULT_OK;
+}
+
+MPMResult pluginStart(MPMPluginCtx *ctx)
+{
+    MPMResult result = MPM_RESULT_INVALID_PARAMETER;
+    int error = 0;
+
+    if (ctx->started)
+    {
+        OIC_LOG(INFO, TAG, "Plugin is already started.");
+        return MPM_RESULT_ALREADY_STARTED;
+    }
+
+    ctx->stay_in_process_loop = true;
+
+    //@todo Maybe move this to plugin_add()? once the first light is added?
+    error = pthread_create(&(ctx->thread_handle), NULL, lightMonitorThread, ctx);
+    if (error == 0)
+    {
+        ctx->started = true;
+        result = MPM_RESULT_OK;
+    }
+    else
+    {
+        OIC_LOG_V(ERROR, TAG, "Can't create plugin specific thread :[%s]", strerror(errno));
+        pluginStop(ctx);
+        result = MPM_RESULT_STARTED_FAILED;
+    }
+
+    OIC_LOG_V(INFO, TAG, "Plugin start return value:%d.", result);
+    return (result);
+}
+
+MPMResult pluginScan(MPMPluginCtx *, MPMPipeMessage *)
+{
+    std::vector<LifxLightSharedPtr> lightsScanned;
+
+    MPMResult result = LifxLight::getLights(accessToken, lightsScanned);
+
+    for (uint32_t i = 0; i < lightsScanned.size(); ++i)
+    {
+        LifxLightSharedPtr light = lightsScanned[i];
+
+        OIC_LOG_V(INFO, TAG, "Found %s bulb %s(%s).", light->state.connected ? "CONNECTED" : "OFFLINE",
+                  light->config.id.c_str(), light->config.label.c_str());
+
+        if (!light->state.connected)
+        {
+            OIC_LOG(INFO, TAG, "Ignoring OFFLINE light");
+            continue;
+        }
+
+        std::string uri = "/lifx/" + light->config.id;
+
+        if (uriToLifxLightMap.find(uri) != uriToLifxLightMap.end())
+        {
+            OIC_LOG_V(INFO, TAG, "Already found %s. Ignoring", uri.c_str());
+            continue;
+        }
+
+        uriToLifxLightMap[uri] = light;
+
+        MPMSendResponse(uri.c_str(), uri.size(), MPM_SCAN);
+    }
+    if (result != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to fetch lights with error (%d)", result);
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    return MPM_RESULT_OK;
+
+}
+
+bool isSecureEnvironmentSet()
+{
+    char *non_secure_env = getenv("NONSECURE");
+
+    if (non_secure_env != NULL && (strcmp(non_secure_env, "true") == 0))
+    {
+        OIC_LOG(INFO, TAG, "Creating NON SECURE resources");
+        return false;
+    }
+    OIC_LOG(INFO, TAG, "Creating SECURE resources");
+    return true;
+}
+
+MPMResult createPayloadForMetadata(MPMResourceList **list, const char *uri, const char *res_type,
+                              const char *interface)
+{
+    MPMResourceList *tempPtr;
+    tempPtr = (MPMResourceList *) OICCalloc(1, sizeof(MPMResourceList));
+    if (!tempPtr)
+    {
+        OIC_LOG_V(ERROR, TAG, "failed to allocate memory for tempPtr");
+        return MPM_RESULT_OUT_OF_MEMORY;
+    }
+    strncpy(tempPtr->rt, res_type,  MPM_MAX_LENGTH_64);
+    strncpy(tempPtr->href, uri, MPM_MAX_URI_LEN);
+    strncpy(tempPtr->interfaces, interface, MPM_MAX_LENGTH_64);
+    tempPtr->bitmap = BM;
+    tempPtr->next = *list;
+    *list  = tempPtr;
+    return MPM_RESULT_OK;
+}
+
+/***
+ * Creates 2 OCF resources for a LifxLight. One for the switch to turn it on and off
+ * and one for brightness.
+ *
+ * @param[in] uri Base uri. Switch and brightness uris are baseUri appended with "/switch" & "/brightness"
+ * @return MPM_RESULT_OK
+ */
+MPMResult createOCFResources(const std::string &uri)
+{
+    uint8_t resourceProperties = (OC_OBSERVABLE | OC_DISCOVERABLE);
+    if (isSecureEnvironmentSet())
+    {
+        resourceProperties |= OC_SECURE;
+    }
+
+    std::string switchUri = uri + BINARY_SWITCH_RELATIVE_URI;
+    ConcurrentIotivityUtils::queueCreateResource(switchUri, OIC_BINARY_SWITCH, OC_RSRVD_INTERFACE_ACTUATOR,
+                                            resourceEntityHandler,
+                                            (void *) BINARY_SWITCH_CALLBACK, resourceProperties);
+
+    std::string brightnessUri = uri + BRIGHTNESS_RELATIVE_URI;
+    ConcurrentIotivityUtils::queueCreateResource(brightnessUri, OIC_BRIGHTNESS_LIGHT, OC_RSRVD_INTERFACE_ACTUATOR,
+                                            resourceEntityHandler,
+                                            (void *) BRIGHTNESS_CALLBACK, resourceProperties);
+
+    return MPM_RESULT_OK;
+}
+
+MPMResult deleteOCFResources(const std::string &uri)
+{
+    ConcurrentIotivityUtils::queueDeleteResource(uri + BINARY_SWITCH_RELATIVE_URI);
+    ConcurrentIotivityUtils::queueDeleteResource(uri + BRIGHTNESS_RELATIVE_URI);
+    return MPM_RESULT_OK;
+}
+
+MPMResult pluginAdd(MPMPluginCtx *, MPMPipeMessage * message)
+{
+    if (message->payloadSize <= 0 && message->payload == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "No payload received, failed to add device");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    uint8_t *buff = NULL;
+    MPMResourceList *list = NULL;
+    MPMDeviceSpecificData deviceConfiguration;
+    LightDetails pluginSpecificDetails;
+    std::string user;
+    memset(&pluginSpecificDetails, 0, sizeof(LightDetails));
+    memset(&deviceConfiguration, 0, sizeof(MPMDeviceSpecificData));
+
+    std::string uri = reinterpret_cast<const char*>(message->payload);
+
+    std::lock_guard<std::mutex> lock(addedLightsLock);
+    if (addedLights.find(uri) != addedLights.end())
+    {
+        OIC_LOG_V(ERROR, TAG, "%s already added", uri.c_str());
+        return MPM_RESULT_ALREADY_CREATED;
+    }
+    if (uriToLifxLightMap.find(uri) == uriToLifxLightMap.end())
+    {
+        OIC_LOG_V(ERROR, TAG, "%s was NOT discovered in a scan", uri.c_str());
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    createOCFResources(uri);
+
+    buff = (uint8_t *)OICCalloc(1, MPM_MAX_METADATA_LEN);
+    if (buff == NULL)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to allocate memory for reconnect buffer");
+        return MPM_RESULT_OUT_OF_MEMORY;
+    }
+
+    std::string switchUri = uri + BINARY_SWITCH_RELATIVE_URI;
+    result = createPayloadForMetadata(&list, switchUri.c_str(), OIC_BINARY_SWITCH,
+             OC_RSRVD_INTERFACE_ACTUATOR);
+
+    std::string brightnessUri = uri + BRIGHTNESS_RELATIVE_URI;
+    result = createPayloadForMetadata(&list, brightnessUri.c_str(), OIC_BRIGHTNESS_LIGHT,
+             OC_RSRVD_INTERFACE_ACTUATOR);
+    if (result == MPM_RESULT_OUT_OF_MEMORY)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to create payload for metadata");
+        return result;
+    }
+
+    LifxLightSharedPtr targetLight = uriToLifxLightMap[uri];
+    targetLight->getUser(user);
+
+    // filling plugin specific details
+    strncpy(pluginSpecificDetails.id, targetLight->config.id.c_str(), MPM_MAX_LENGTH_64);
+    strncpy(pluginSpecificDetails.label, targetLight->config.label.c_str(), MPM_MAX_LENGTH_64);
+    strncpy(pluginSpecificDetails.uuid, targetLight->config.uuid.c_str(), MPM_MAX_LENGTH_64);
+    strncpy(pluginSpecificDetails.user, user.c_str(), MPM_MAX_LENGTH_256);
+
+    // filling device specific details
+    strncpy(deviceConfiguration.devName, DEVICE_NAME, MPM_MAX_LENGTH_64);
+    strncpy(deviceConfiguration.devType, DEVICE_TYPE, MPM_MAX_LENGTH_64);
+    strncpy(deviceConfiguration.manufacturerName, MANUFACTURER_NAME, MPM_MAX_LENGTH_256);
+
+    MPMFormMetaData(list, &deviceConfiguration, buff, MPM_MAX_METADATA_LEN, &pluginSpecificDetails,
+                    sizeof(pluginSpecificDetails));
+
+    addedLights[uri] = uriToLifxLightMap[uri];
+
+    MPMAddResponse response;
+    strncpy(response.uri, uri.c_str(), MPM_MAX_URI_LEN);
+    memcpy(response.metadata, buff, MPM_MAX_METADATA_LEN);
+
+    size_t size = sizeof(MPMAddResponse);
+
+    MPMSendResponse(&response, size, MPM_ADD);
+
+    OICFree(buff);
+    return result;
+}
+
+MPMResult pluginRemove(MPMPluginCtx *, MPMPipeMessage *message)
+{
+    if (message->payloadSize <= 0 && message->payload == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "No paylaod received, failed to remove device");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+    std::string uri = reinterpret_cast<const char *>(message->payload);
+    OIC_LOG_V(DEBUG, TAG, "device uri to be removed - %s ", uri.c_str());
+
+    std::lock_guard<std::mutex> lock(addedLightsLock);
+    if (addedLights.find(uri) == addedLights.end())
+    {
+        OIC_LOG(ERROR, TAG, "Device to be removed is not added yet");
+        return MPM_RESULT_NOT_PRESENT;
+    }
+
+    deleteOCFResources(uri);
+
+    addedLights.erase(uri);
+    uriToLifxLightMap.erase(uri);
+
+    MPMSendResponse(uri.c_str(), uri.size(), MPM_REMOVE);
+    return MPM_RESULT_OK;
+}
+
+MPMResult pluginReconnect(MPMPluginCtx *, MPMPipeMessage *message)
+{
+    MPMResourceList *list = NULL, *temp = NULL;
+    void *pluginSpecificDetails = NULL;
+
+    if (message->payloadSize <= 0 && message->payload == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "No paylaod received, failed to reconnect");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    MPMParseMetaData(message->payload, MPM_MAX_METADATA_LEN, &list, &pluginSpecificDetails);
+
+    LightDetails *lightDetails = (LightDetails *)pluginSpecificDetails;
+
+    OIC_LOG_V(DEBUG, TAG, "\n Reconnect Details \nid - %s\nuuid - %s\nlabel - %s\nuser - %s\n",
+              lightDetails->id, lightDetails->uuid, lightDetails->label, lightDetails->user);
+
+    LifxLight::lightState state;
+    LifxLight::lightConfig cfg(lightDetails->id, lightDetails->uuid, lightDetails->label);
+    std::string uri = "/lifx/" + cfg.id;
+    std::shared_ptr<LifxLight> light = std::make_shared<LifxLight>(state, cfg, lightDetails->user);
+
+    createOCFResources(uri);
+    uriToLifxLightMap[uri] = light;
+    addedLights[uri] = uriToLifxLightMap[uri];
+
+    while (list)
+    {
+        temp = list;
+        list = list->next;
+        OICFree(temp);
+    }
+    free(lightDetails);
+    return MPM_RESULT_OK;
+}
+
+MPMResult pluginStop(MPMPluginCtx *pluginSpecificCtx)
+{
+    MPMResult result = MPM_RESULT_OK;
+
+    MPMPluginCtx *ctx = pluginSpecificCtx;
+
+    if (NULL != ctx && g_pluginCtx != NULL)
+    {
+        addedLights.clear();
+        uriToLifxLightMap.clear();
+
+        if (ctx->started)
+        {
+            ctx->stay_in_process_loop = false;
+            pthread_join(ctx->thread_handle, NULL);
+            ctx->started = false;
+        }
+    }
+
+    OIC_LOG_V(INFO, TAG, "Plugin stop's return value:%d", result);
+    return (result);
+
+}
+
+MPMResult pluginDestroy(MPMPluginCtx *pluginSpecificCtx)
+{
+    MPMResult result = MPM_RESULT_INTERNAL_ERROR;
+    MPMPluginCtx *ctx = (MPMPluginCtx *) pluginSpecificCtx;
+
+    if (ctx != NULL && g_pluginCtx != NULL)
+    {
+        if (ctx->started)
+        {
+            pluginStop(pluginSpecificCtx);
+        }
+        // freeing the resource allocated in create
+        OICFree(ctx);
+        g_pluginCtx = NULL;
+        result = MPM_RESULT_OK;
+    }
+
+    OIC_LOG_V(INFO, TAG, "Plugin destroy's return value:%d", result);
+
+    return (result);
+}
+
+OCRepPayload *addCommonLifXProperties(const LifxLightSharedPtr &l, OCRepPayload *payload)
+{
+    if (!OCRepPayloadSetPropString(payload, "x.com.intel.label", l->config.label.c_str()))
+    {
+        throw std::runtime_error("failed to set label");
+    }
+
+    if (!OCRepPayloadSetPropDouble(payload, "x.com.intel.secondsSinceLastSeen",
+                              l->state.secondsSinceLastSeen))
+    {
+        throw std::runtime_error("failed to set secondsSinceLastSeen");
+    }
+    return payload;
+}
+
+OCRepPayload *getBinarySwitchPayload(LifxLightSharedPtr l)
+{
+    std::unique_ptr<OCRepPayload , decltype(OCRepPayloadDestroy) *> payload {OCRepPayloadCreate(),
+         OCRepPayloadDestroy };
+
+    if (!payload)
+    {
+        throw std::runtime_error("payload cannot be NULL");
+    }
+
+    if (!OCRepPayloadSetPropBool(payload.get(), "value", l->state.power))
+    {
+        throw std::runtime_error("failed to set binary switch value in the payload");
+    }
+    return addCommonLifXProperties(l, payload.release());
+}
+
+OCRepPayload *getBrightnessPayload(LifxLightSharedPtr l)
+{
+    std::unique_ptr<OCRepPayload , decltype(OCRepPayloadDestroy) *> payload {OCRepPayloadCreate(),
+             OCRepPayloadDestroy };
+    if (!payload)
+    {
+        throw std::runtime_error("payload cannot be NULL");
+    }
+
+    // Convert LifX [0.0, 1.0] to OCF [0, 100].
+    if (!OCRepPayloadSetPropInt(payload.get(), "brightness", (int64_t) (l->state.brightness * 100.0)))
+    {
+         throw std::runtime_error("failed to set brightness");
+    }
+    return addCommonLifXProperties(l, payload.release());
+
+}
+
+OCRepPayload *processGetRequest(LifxLightSharedPtr l, uintptr_t resourceTypeInCallback)
+{
+    if (resourceTypeInCallback == BINARY_SWITCH_CALLBACK)
+    {
+        return getBinarySwitchPayload(l);
+    }
+    else if (resourceTypeInCallback == BRIGHTNESS_CALLBACK)
+    {
+        return getBrightnessPayload(l);
+    }
+    return NULL;
+}
+
+OCEntityHandlerResult processBinarySwitchUpdate(OCRepPayload *payload, LifxLightSharedPtr l)
+{
+    bool power = false;
+    if (!OCRepPayloadGetPropBool(payload, "value", &power))
+    {
+        throw std::runtime_error("Payload must contain \"value\"");
+    }
+
+    MPMResult result = l->setPower(power);
+
+    if (result != MPM_RESULT_OK)
+    {
+        throw std::runtime_error("Error setting power for PUT request");
+    }
+    return OC_EH_OK;
+}
+
+OCEntityHandlerResult processBrightnessUpdate(OCRepPayload *payload, LifxLightSharedPtr l)
+{
+    int64_t ocfBrightness = 0;
+    if (!OCRepPayloadGetPropInt(payload, "brightness", &ocfBrightness))
+    {
+        throw std::runtime_error("Payload must contain \"brightness\"");
+    }
+    // OCF brightness is [0, 100] and Lifx is [0.0, 1.00]
+    double lifxBrightness = ocfBrightness / 100.0;
+
+    MPMResult result = l->setBrightness(lifxBrightness);
+
+    if (result != MPM_RESULT_OK)
+    {
+        throw std::runtime_error("Error setting brightness for PUT request");
+    }
+    return OC_EH_OK;
+}
+
+OCEntityHandlerResult processPutRequest(OCRepPayload *payload, LifxLightSharedPtr l,
+                                        uintptr_t resourceTypeInCallback)
+{
+    if (payload == NULL)
+    {
+        throw std::runtime_error("PUT payload cannot be NULL");
+    }
+    if (resourceTypeInCallback == BINARY_SWITCH_CALLBACK)
+    {
+        return processBinarySwitchUpdate(payload, l);
+    }
+    else if (resourceTypeInCallback == BRIGHTNESS_CALLBACK)
+    {
+        return processBrightnessUpdate(payload, l);
+    }
+    return OC_EH_OK;
+}
+
+static LifxLightSharedPtr getLifXLightFromOCFResourceUri(std::string resourceUri)
+{
+    OIC_LOG_V(INFO, TAG, "Request for %s", resourceUri.c_str());
+    std::lock_guard<std::mutex> lock(addedLightsLock);
+    for (auto uriToLifXPair : addedLights)
+    {
+        if (resourceUri.find(uriToLifXPair.first) != std::string::npos)
+        {
+            return uriToLifXPair.second;
+        }
+    }
+    throw std::runtime_error("Resource " + resourceUri + " not found");
+}
+
+OCEntityHandlerResult resourceEntityHandler(OCEntityHandlerFlag ,
+        OCEntityHandlerRequest *request,
+        void *cb)
+{
+    uintptr_t callBackParamResourceType = (uintptr_t) cb;
+    OCEntityHandlerResult result = OC_EH_OK;
+    MPMResult res = MPM_RESULT_OK;
+
+    try
+    {
+        std::string uri;
+        ConcurrentIotivityUtils::getUriFromHandle(request->resource, uri);
+
+        LifxLightSharedPtr targetLight = getLifXLightFromOCFResourceUri(uri);
+
+        switch (request->method)
+        {
+            case OC_REST_GET:
+                // Empty GET case as actual request will be processed after the switch case.
+                break;
+
+            case OC_REST_PUT:
+            case OC_REST_POST:
+
+                res = (MPMResult)processPutRequest((OCRepPayload *) request->payload, targetLight,
+                                                   callBackParamResourceType);
+                if (res != MPM_RESULT_OK)
+                    result = OC_EH_ERROR;
+                break;
+
+            default:
+                OIC_LOG_V(INFO, TAG, "Unsupported method (%d) recieved", request->method);
+                ConcurrentIotivityUtils::respondToRequestWithError(request, "Unsupported method received",
+                        OC_EH_METHOD_NOT_ALLOWED);
+                return OC_EH_OK;
+        }
+
+        OCRepPayload *responsePayload = processGetRequest(targetLight, callBackParamResourceType);
+        ConcurrentIotivityUtils::respondToRequest(request, responsePayload, result);
+        OCRepPayloadDestroy(responsePayload);
+    }
+    catch (const std::exception &exp)
+    {
+        ConcurrentIotivityUtils::respondToRequestWithError(request, exp.what(), OC_EH_ERROR);
+        return OC_EH_OK;
+    }
+
+    return OC_EH_OK;
+}
+
+/* This function does not look for new lights. Only monitors existing lights.
+ * @param[in] pluginSpecificCtx        plugin specific context
+ */
+void *lightMonitorThread(void *pluginSpecificCtx)
+{
+    MPMPluginCtx *ctx = (MPMPluginCtx *) pluginSpecificCtx;
+    if (ctx != NULL)
+    {
+        while (ctx->stay_in_process_loop)
+        {
+            addedLightsLock.lock();
+
+            for (auto itr : addedLights)
+            {
+                LifxLightSharedPtr l = itr.second;
+                if (!l)
+                {
+                    continue;
+                }
+                LifxLight::lightState oldState = l->state;
+
+                l->refreshState();
+
+                LifxLight::lightState newState = l->state;
+
+                if (oldState.power != newState.power)
+                {
+                    ConcurrentIotivityUtils::queueNotifyObservers(itr.first + BINARY_SWITCH_RELATIVE_URI);
+                }
+                if (fabs(oldState.brightness - newState.brightness) >
+                    0.00001) // Lazy epsilon for double equals check.
+                {
+                    ConcurrentIotivityUtils::queueNotifyObservers(itr.first + BRIGHTNESS_RELATIVE_URI);
+                }
+                if (oldState.connected != newState.connected)
+                {
+                    OIC_LOG_V(INFO, TAG, "%s is %s", l->config.id.c_str(),
+                              l->state.connected ? "ONLINE" : "OFFLINE");
+                }
+            }
+
+            addedLightsLock.unlock();
+            sleep(MPM_THREAD_PROCESS_SLEEPTIME);
+        }
+        OIC_LOG(INFO, TAG, "Leaving LIFX monitor thread");
+    }
+    pthread_exit(NULL);
+}
diff --git a/bridging/plugins/lifx_plugin/lifx_objects/lifx.cpp b/bridging/plugins/lifx_plugin/lifx_objects/lifx.cpp
new file mode 100644 (file)
index 0000000..19babc7
--- /dev/null
@@ -0,0 +1,255 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 "stringbuffer.h"
+#include "writer.h"
+#include "curlClient.h"
+#include "iotivity_config.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include "document.h"
+#include <memory>
+#include <stdexcept>
+#include <iostream>
+#include "lifx.h"
+#include "logger.h"
+
+#define TAG "LIFX"
+using namespace OC::Bridging;
+
+MPMResult parseCloudResponse(const std::string response)
+{
+    rapidjson::Document doc;
+    doc.SetObject();
+
+    if (doc.Parse(response.c_str()).HasParseError())
+    {
+        OIC_LOG(ERROR, TAG, "Parse error in set Power");
+        return MPM_RESULT_JSON_ERROR;
+    }
+
+    if (doc.HasMember("error"))
+    {
+        throw std::runtime_error(doc["error"].GetString());
+    }
+
+    if (doc.HasMember("results"))
+    {
+        const rapidjson::Value &results = doc["results"];
+
+        if (results.Size() != 1)
+        {
+            OIC_LOG(INFO, TAG, "Multi element results. Only considering the first");
+        }
+
+        for (rapidjson::SizeType i = 0; i < results.Size(); ++i)
+        {
+            // This'll only consider the first one anyway as we only expect 1 element
+            // This'll have to be changed when we can change multiple light states at once.
+            std::string status = results[i]["status"].GetString();
+
+            return status == "ok" ? MPM_RESULT_OK : MPM_RESULT_INTERNAL_ERROR;
+        }
+    }
+    return MPM_RESULT_OK;
+}
+
+MPMResult static parseLightsFromCloudResponse(const std::string response, const std::string user,
+        std::vector<std::shared_ptr<LifxLight>> &parsedLights)
+{
+    rapidjson::Document lightsJsonResponse;
+    lightsJsonResponse.SetObject();
+
+    if (lightsJsonResponse.Parse<0>(response.c_str()).HasParseError())
+    {
+        OIC_LOG_V(ERROR, TAG, "Error parsing JSON:\n%s", response.c_str());
+        return MPM_RESULT_JSON_ERROR;
+    }
+
+    if (!lightsJsonResponse.IsArray())
+    {
+        OIC_LOG_V(ERROR, TAG, "Response is not an array.\n%s", response.c_str());
+        return MPM_RESULT_JSON_ERROR;
+    }
+    if (lightsJsonResponse.Empty())
+    {
+        OIC_LOG(ERROR, TAG, "Response is empty");
+        return MPM_RESULT_JSON_ERROR;
+    }
+
+    int i = 0;
+
+    for (rapidjson::Value::ConstValueIterator itr = lightsJsonResponse.Begin();
+         itr != lightsJsonResponse.End(); ++itr)
+    {
+        if (lightsJsonResponse[i].IsObject())
+        {
+            LifxLight::lightConfig cfg;
+            cfg.id = lightsJsonResponse[i]["id"].GetString();
+            cfg.uuid = lightsJsonResponse[i]["uuid"].GetString();
+            cfg.label = lightsJsonResponse[i]["label"].GetString();
+
+            LifxLight::lightState state;
+
+            std::string powerStr  = lightsJsonResponse[i]["power"].GetString();
+
+            state.power = powerStr == "on";
+
+            state.brightness = lightsJsonResponse[i]["brightness"].GetDouble();
+            state.connected = lightsJsonResponse[i]["connected"].GetBool();
+            state.secondsSinceLastSeen = lightsJsonResponse[i]["seconds_since_seen"].GetDouble();
+
+            std::shared_ptr<LifxLight> light = std::make_shared<LifxLight>(state, cfg, user);
+
+            parsedLights.push_back(light);
+
+            ++i;
+        }
+        else
+        {
+            OIC_LOG(ERROR, TAG, "Json blob doesn't have an expected object to be parsed.");
+            return MPM_RESULT_INTERNAL_ERROR;
+        }
+    }
+
+    return MPM_RESULT_OK;
+}
+
+MPMResult LifxLight::setState(std::string &stateRequest)
+{
+    if (this->user.empty())
+    {
+        throw std::runtime_error("Light not created in valid state by constructor. No \"user\" found");
+    }
+
+    CurlClient cc = CurlClient(CurlClient::CurlMethod::PUT, uri + "/state")
+                    .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+                    .setUserName(user)
+                    .setRequestBody(stateRequest);
+
+    OIC_LOG_V(INFO, TAG, "Request %s", stateRequest.c_str());
+    OIC_LOG_V(INFO, TAG, "Uri %s", uri.c_str());
+
+    int curlCode = cc.send();
+    std::string response = cc.getResponseBody();
+
+    OIC_LOG_V(INFO, TAG, "Response %s", response.c_str());
+
+    if (curlCode != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "PUT request for power failed. Error code %d", curlCode);
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+    // Small sleep to let the light inform the cloud of any state changes.
+    sleep(2);
+    refreshState();
+
+    return parseCloudResponse(response);
+}
+
+MPMResult LifxLight::setPower(bool power)
+{
+    std::string setPowerRequest = std::string("power=") + (power ? "on" : "off");
+    return setState(setPowerRequest);
+}
+
+MPMResult LifxLight::setBrightness(double brightness)
+{
+    if (brightness < 0.0) brightness = 0.0;
+    else if (brightness > 1.0) brightness = 1.0;
+
+    std::string setBrightnessRequest = std::string("brightness=") + std::to_string(brightness);
+
+    return setState(setBrightnessRequest);
+}
+
+// Limit calls to refreshState. LifX APIs limit calls
+// from an access token to 60 calls every 60 seconds
+// as made clear @ https://api.developer.lifx.com/docs/rate-limits
+MPMResult LifxLight::refreshState()
+{
+    if (this->user.empty())
+    {
+        throw std::runtime_error("Light not created in valid state by constructor. No \"user\" found");
+    }
+
+    CurlClient cc = CurlClient(CurlClient::CurlMethod::GET, uri)
+                    .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+                    .setUserName(user);
+
+    int curlCode = cc.send();
+
+    if (curlCode != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "GET request for light failed with error %d", curlCode);
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    std::string response = cc.getResponseBody();
+
+    std::vector<std::shared_ptr<LifxLight>> parsedLights;
+    MPMResult parseResult = parseLightsFromCloudResponse(response, this->user, parsedLights);
+
+    if (parseResult != MPM_RESULT_OK)
+    {
+        return parseResult;
+    }
+
+    if (parsedLights.size() != 1)
+    {
+        OIC_LOG_V(ERROR, TAG, "This is irregular! Instead of 1 light, returned %ld " , parsedLights.size());
+        return MPM_RESULT_JSON_ERROR;
+    }
+
+    std::shared_ptr<LifxLight> light = parsedLights[0]; // Only 1 light expected here.
+
+    if (light->config.uuid != this->config.uuid)
+    {
+        OIC_LOG_V(ERROR, TAG, "%s in response, when request made by %s", light->config.uuid.c_str(),
+                  this->config.uuid.c_str());
+        return MPM_RESULT_JSON_ERROR;
+    }
+
+    this->state = light->state;
+    return MPM_RESULT_OK;
+}
+
+MPMResult LifxLight::getLights(const std::string user,
+                               std::vector<std::shared_ptr<LifxLight> > &lights)
+{
+    CurlClient cc = CurlClient(CurlClient::CurlMethod::GET, LIFX_LIST_LIGHTS_URI)
+                    .addRequestHeader(CURL_HEADER_ACCEPT_JSON)
+                    .setUserName(user);
+
+    int curlCode = cc.send();
+
+    if (curlCode != MPM_RESULT_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "GET request for lights failed. Error code %d", curlCode);
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+
+    std::string response = cc.getResponseBody();
+
+    return parseLightsFromCloudResponse(response, user, lights);
+}
+
diff --git a/bridging/plugins/lifx_plugin/lifx_objects/lifx.h b/bridging/plugins/lifx_plugin/lifx_objects/lifx.h
new file mode 100644 (file)
index 0000000..aaf65fd
--- /dev/null
@@ -0,0 +1,106 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 __LIFX_H__
+#define __LIFX_H__
+
+
+#include <vector>
+#include <typeinfo>
+#include <mpmErrorCode.h>
+
+#define LIFX_BASE_URI           "https://api.lifx.com/v1/lights"
+#define LIFX_LIST_LIGHTS_URI    LIFX_BASE_URI "/all"
+
+class LifxLight
+{
+    public:
+
+        typedef struct _lightState
+        {
+            double brightness;
+            bool power;
+            bool connected;
+            double secondsSinceLastSeen;
+        } lightState;
+
+        typedef struct _lightConfig
+        {
+            std::string id;
+            std::string uuid;
+            std::string label;
+            _lightConfig() {}
+            _lightConfig(std::string light_id, std::string light_uuid, std::string light_label)
+            {
+                id = light_id;
+                uuid = light_uuid;
+                label = light_label;
+            }
+        } lightConfig;
+
+        LifxLight() {}
+        virtual ~LifxLight() {}
+
+        LifxLight(lightState &state, lightConfig &config, const std::string &user)
+        {
+            this->user = user;
+            this->state = state;
+            this->config = config;
+            this->uri = std::string(LIFX_BASE_URI).append("/").append(config.id);
+        }
+
+        /*Limit calls to refreshState. LifX API's limit calls
+         * from an access token and only allow 60 calls every 60 seconds
+         * as made clear @ https://api.developer.lifx.com/docs/rate-limits
+         */
+        MPMResult refreshState();
+
+        MPMResult setPower(bool power);
+
+        /**
+         * Sets the brightness for this bulb.
+         * @param[in] brightness Is in the range [0.0, 1.0].
+         *        Values outside the interval are set to the nearest endpoint.
+         * @return MPM_RESULT_OK if success, else appropriate error code on error
+         */
+        MPMResult setBrightness(double brightness);
+
+        MPMResult static getLights(const std::string user,
+                                   std::vector<std::shared_ptr<LifxLight> > &lights);
+
+        void getUser(std::string &userID)
+        {
+            userID = user;
+        }
+
+        lightState state;
+        lightConfig config;
+        std::string uri;
+
+    private:
+        std::string user;
+
+        MPMResult setState(std::string &setPowerRequest);
+};
+
+typedef std::shared_ptr<LifxLight> LifxLightSharedPtr;
+
+#endif // __LIFX_H__
diff --git a/bridging/plugins/stub_plugin/stub_plugin.cpp b/bridging/plugins/stub_plugin/stub_plugin.cpp
new file mode 100644 (file)
index 0000000..51de37b
--- /dev/null
@@ -0,0 +1,125 @@
+//******************************************************************
+//
+// Copyright 2017 Intel Mobile Communications GmbH All Rights Reserved.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 <stdio.h>
+#include <string.h>
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <set>
+#include <assert.h>
+#include <pluginServer.h>
+#include "logger.h"
+
+#define TAG "STUB_PLUGIN"
+
+MPMPluginCtx *g_plugin_ctx = NULL;
+static char CRED_FILE[] = "./oic_svr_db_sample.dat";
+
+FILE *sec_file(const char *, const char *mode)
+{
+    return fopen(CRED_FILE, mode);
+}
+
+MPMResult pluginCreate(MPMPluginCtx **pluginSpecificCtx)
+{
+    MPMPluginCtx *ctx = (MPMPluginCtx *) calloc(1, sizeof(MPMPluginCtx));
+    if (ctx == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Allocation of plugin context failed");
+        return MPM_RESULT_INTERNAL_ERROR;
+    }
+    *pluginSpecificCtx = ctx;
+    g_plugin_ctx = ctx;
+    ctx->device_name = "Stub plugin";
+    ctx->resource_type = "oic.d.stub";
+    ctx->open = sec_file;
+    return MPM_RESULT_OK;
+}
+
+MPMResult pluginStart(MPMPluginCtx *ctx)
+{
+    ctx->stay_in_process_loop = true;
+    OIC_LOG(INFO, TAG, "Plugin start called!");
+    return MPM_RESULT_OK;
+}
+
+void echoResponse(MPMPipeMessage *message, std::string type)
+{
+    std::string s = type + " response echo";
+    MPMSendResponse(s.c_str(), s.size(), message->msgType);
+}
+
+MPMResult pluginScan(MPMPluginCtx *, MPMPipeMessage *message)
+{
+    OIC_LOG(INFO, TAG, "Scan called!");
+    // Send back scan response to the client.
+    echoResponse(message, "SCAN");
+    return MPM_RESULT_OK;
+}
+
+MPMResult pluginAdd(MPMPluginCtx *, MPMPipeMessage *message)
+{
+    OIC_LOG(INFO, TAG, "Add called! Create Iotivity resources here based on what the client says");
+    echoResponse(message, "ADD");
+    return MPM_RESULT_OK;
+}
+
+MPMResult pluginRemove(MPMPluginCtx *, MPMPipeMessage *message)
+{
+    OIC_LOG(INFO, TAG, "Remove called! Remove iotivity resources here based on what the client says");
+    echoResponse(message, "REMOVE");
+    return MPM_RESULT_OK;
+}
+
+MPMResult pluginReconnect(MPMPluginCtx *, MPMPipeMessage *message)
+{
+    OIC_LOG(INFO, TAG,
+            "Reconnect called! Reconnect to devices, create resources from the message/cloud/db/file.");
+    echoResponse(message, "ADD");
+    return MPM_RESULT_OK;
+}
+
+MPMResult pluginStop(MPMPluginCtx *)
+{
+    OIC_LOG(INFO, TAG, "Stop called !");
+    return MPM_RESULT_OK;
+}
+
+
+MPMResult pluginDestroy(MPMPluginCtx *pluginSpecificCtx)
+{
+    OIC_LOG(INFO, TAG, "Destroy called");
+    if (!pluginSpecificCtx)
+    {
+        assert(g_plugin_ctx);
+
+        if (pluginSpecificCtx->started)
+        {
+            pluginStop(pluginSpecificCtx);
+        }
+        // freeing the resource allocated in create
+        free(pluginSpecificCtx);
+        g_plugin_ctx = NULL;
+    }
+
+    return (MPM_RESULT_OK);
+}
diff --git a/extlibs/rapidjson/SConscript b/extlibs/rapidjson/SConscript
new file mode 100644 (file)
index 0000000..5a1e708
--- /dev/null
@@ -0,0 +1,24 @@
+import os, string, sys
+
+Import('env')
+
+env = env.Clone()
+rapidjson_env = env.Clone()
+
+target_os = env.get('TARGET_OS')
+target_arch = env.get('TARGET_ARCH')
+
+host_os = sys.platform
+
+######################################################################
+# Build flags
+######################################################################
+src_dir = env.get('SRC_DIR')
+path = os.path.join(src_dir, 'extlibs', 'rapidjson', 'rapidjson')
+
+# check 'rapidjson' library, if it doesn't exits, ask user to download it
+if not os.path.exists(path):
+    rapidjson_env = Environment(ENV = os.environ)
+    rapidjson_zip = env.Download('v1.0.2.zip', 'https://github.com/miloyip/rapidjson/archive/v1.0.2.zip')
+    rapidjson_dir = env.UnpackAll('rapidjson', rapidjson_zip)
+    os.system("mv rapidjson-1.0.2 rapidjson")
index 08cbce4..a419707 100644 (file)
 #  define OC_CLOSE_SOCKET(s) close(s)
 #endif
 
+#ifndef SIZE_MAX
+/* Some systems fail to define SIZE_MAX in <stdint.h>, even though C99 requires it...
+ * Conversion from signed to unsigned is defined in 6.3.1.3 (Signed and unsigned integers) p2,
+ * which says: "the value is converted by repeatedly adding or subtracting one more than the
+ * maximum value that can be represented in the new type until the value is in the range of the
+ * new type."
+ * So -1 gets converted to size_t by adding SIZE_MAX + 1, which results in SIZE_MAX.
+ */
+#  define SIZE_MAX ((size_t)-1)
+#endif
+
 #endif