[IOT-2259] Windows: Handle Persistent Storage for UWP Apps
authorIbrahim Esmat <iesmat@microsoft.com>
Thu, 27 Apr 2017 16:13:35 +0000 (09:13 -0700)
committerDan Mihai <Daniel.Mihai@microsoft.com>
Fri, 19 May 2017 22:48:41 +0000 (22:48 +0000)
Changes to allow UWP apps that use IPCA to handle Persistent
Storage:
- Updated the SConscripts to point to store LIBS
- Added a UWP_APP env variable to enable building store DLLs
- Disabled gtest and other tests when building store DLLs
  because gtest is heavily dependent on win32 APIs and DLLs
- Add code to set the sqlite3 temporary directory when building
  for store DLLs (as required by sqlite3). This temporary
  directory will be the appropriate temp directory.
- Add a platform helper function to get the best application
  path to store the Persistent Storage files. IPCA uses this
  helper function to get the path when opening PS files.
- Add capability to run.bat to build UWP DLLs

Change-Id: Iac3a2c9e9e2fd994b06e964254f2d394b15526aa
Signed-off-by: Ibrahim Esmat <iesmat@microsoft.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/19845
Reviewed-by: Alex Kelley <alexke@microsoft.com>
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
Reviewed-by: Dan Mihai <Daniel.Mihai@microsoft.com>
18 files changed:
build_common/SConscript
build_common/windows/SConscript
extlibs/libcoap/SConscript
extlibs/sqlite3/SConscript
resource/IPCA/SConscript
resource/IPCA/src/SConscript
resource/IPCA/src/ocfframework.cpp
resource/c_common/SConscript
resource/c_common/oic_platform/include/oic_platform.h
resource/c_common/oic_platform/src/others/oic_otherplatforms.c [new file with mode: 0644]
resource/c_common/oic_platform/src/windows/oic_winplatform.cpp [new file with mode: 0644]
resource/csdk/stack/SConscript
resource/csdk/stack/include/internal/ocsqlite3helper.h [new file with mode: 0644]
resource/csdk/stack/src/ocsqlite3helper.c [new file with mode: 0644]
resource/csdk/stack/src/ocstack.c
resource/examples/SConscript
resource/unit_tests.scons
run.bat

index 20a5801..ebd9674 100755 (executable)
@@ -137,6 +137,7 @@ if target_os == 'windows':
        # For VS2013, MSVC_VERSION is '12.0'. For VS2015, MSVC_VERSION is '14.0'.
        # Default value is None, meaning that SCons has to choose automatically a VS version.
        help_vars.Add(EnumVariable('MSVC_VERSION', 'MSVC compiler version - Windows', None, allowed_values=('12.0', '14.0')))
+       help_vars.Add(EnumVariable('UWP_APP', 'Build for a Universal Windows Platform (UWP) Application', '0', allowed_values=('0', '1')))
 
 help_vars.Add(BoolVariable('BUILD_JAVA', 'Build Java bindings', False))
 help_vars.Add(PathVariable('JAVA_HOME', 'JDK directory', os.environ.get('JAVA_HOME'), PathVariable.PathAccept))
@@ -172,6 +173,7 @@ else:
             PREFIX = GetOption('prefix'),
             LIB_INSTALL_DIR = ARGUMENTS.get('LIB_INSTALL_DIR') #for 64bit build
             )
+
 Help(help_vars.GenerateHelpText(env))
 
 if env.get('WITH_ENV'):
@@ -254,6 +256,8 @@ env.SConscriptChdir(1)
 # Set the source directory and build directory
 #   Source directory: 'dir'
 #   Build directory: 'dir'/out/<target_os>/<target_arch>/<release or debug>/
+#   On windows, the build directory will be:
+#     'dir'/out/windows/<win32 or uwp>/<target_arch>/<release or debug>/
 #
 # You can get the directory as following:
 #   env.get('SRC_DIR')
@@ -269,10 +273,21 @@ def __set_dir(env, dir):
 ''' % dir
         Exit(1)
 
+    build_dir = dir + '/out/' + target_os + '/'
+
+    if target_os == 'windows':
+        if env.get('UWP_APP') == '1':
+            build_dir = build_dir + 'uwp/'
+        else:
+            build_dir = build_dir + 'win32/'
+
+    build_dir = build_dir + target_arch
+
     if env.get('RELEASE'):
-        build_dir = dir + '/out/' + target_os + '/' + target_arch + '/release/'
+        build_dir = build_dir + '/release/'
     else:
-        build_dir = dir + '/out/' + target_os + '/' + target_arch + '/debug/'
+        build_dir = build_dir + '/debug/'
+
     env.VariantDir(build_dir, dir, duplicate=0)
 
     env.Replace(BUILD_DIR = build_dir)
index 3c91339..075c759 100644 (file)
@@ -6,6 +6,14 @@ import os.path
 
 # Set common flags
 if env['CC'] == 'cl':
+    if env.get('UWP_APP') == '1':
+        # Currently only supports VS2015 (14.0)
+        supported_uwp_msvc_versions = ['14.0']
+        # If MSVC_VERSION is not supported for UWP, exit on error
+        if env.get('MSVC_VERSION') not in supported_uwp_msvc_versions:
+            print '\nError: Trying to Build UWP binaries with unsupported Visual Studio version\n'
+            Exit(1)
+
     #  - warning C4133: incompatible type conversion
     env.AppendUnique(CCFLAGS=['/we4133'])
 
@@ -56,9 +64,6 @@ if env['CC'] == 'cl':
     env['PDB'] = '${TARGET.base}.pdb'
     env.Append(LINKFLAGS=['/PDB:${TARGET.base}.pdb'])
 
-    # Add Windows-specific libraries
-    env.AppendUnique(LIBS = ['bcrypt', 'ws2_32', 'advapi32', 'iphlpapi', 'crypt32', 'kernel32'])
-
     # Visual Studio compiler complains that functions like strncpy are unsafe. We
     # are aware that it's possible to create a non-null terminated string using the
     # strncpy function.  However, the str*_s functions are not standard and thus
@@ -67,6 +72,17 @@ if env['CC'] == 'cl':
     # See https://msdn.microsoft.com/en-us/library/ttcz0bys.aspx for more details.
     env.AppendUnique(CPPDEFINES=['_CRT_SECURE_NO_WARNINGS', '_CRT_NONSTDC_NO_WARNINGS'])
 
+    if env.get('UWP_APP') != '1':
+        # Add Desktop specific libraries
+        env.AppendUnique(LIBS = ['bcrypt', 'ws2_32', 'advapi32', 'iphlpapi', 'crypt32', 'kernel32'])
+    else:
+        # Add Windows Universal Platform specific libraries and flags
+        # Note: We technically should set WINAPI_FAMILY=WINAPI_FAMILY_APP, but cannot
+        #       due to [IOT-2312]. All APIs used are store/UWP compatible at this time.
+        env.AppendUnique(CPPDEFINES=['UWP_APP', '__WRL_NO_DEFAULT_LIB__'])
+        env.AppendUnique(LINKFLAGS=['/MANIFEST:NO', '/WINMD:NO', '/APPCONTAINER'])
+        env.AppendUnique(LIBS = ['WindowsApp', 'bcrypt', 'ws2_32', 'iphlpapi', 'crypt32'])
+
 elif env['CC'] == 'gcc':
     print "\nError: gcc not supported on Windows.  Use Visual Studio!\n"
     Exit(1);
index db2bdbe..c16d433 100644 (file)
@@ -182,8 +182,6 @@ if with_upstream_libcoap == '1':
 
 ''' % (str(target_os), str(target_arch))
 
-        config_h_body = ''
-
         config_h_footer = '''
 
 /* Define to the full name of this package. */
@@ -208,54 +206,87 @@ if with_upstream_libcoap == '1':
 
     ''' % (str(lib_prefix + 'coap'), str(lib_prefix + 'coap ' + libcoap_version))
 
-        cxx_headers = ['arpa/inet.h',
-                       'assert.h',
-                       'limits.h',
-                       'netinet/in.h',
-                       'stdio.h',
-                       'strings.h',
-                       'sys/select.h',
-                       'sys/socket.h',
-                       'sys/time.h',
-                       'sys/types.h',
-                       'sys/uio.h',
-                       'sys/unistd.h',
-                       'syslog.h',
-                       'time.h',
-                       'unistd.h',
-                       'winsock2.h',
-                       'ws2tcpip.h']
-
-        cxx_functions = ['malloc',
-                         'snprintf',
-                         'strnlen',
-                         'vprintf']
-
-        if target_os == 'arduino':
-            # Detection of headers on the Arduino platform is currently broken.
-            cxx_headers = []
-
-        def get_define_from_string(string):
-            string_converted = string.replace("/","_").replace(".","_").upper()
-            return "HAVE_" + string_converted
-
-        for header_file_name in cxx_headers:
-            if conf.CheckCXXHeader(header_file_name):
-                config_h_body += "#define %s 1\n\n" % get_define_from_string(header_file_name)
-
-        for function_name in cxx_functions:
-            if conf.CheckFunc(function_name):
-                config_h_body += "#define %s 1\n\n" % get_define_from_string(function_name)
-
-        if conf.CheckCXXHeader('windows.h'):
-            config_h_body += "#define ssize_t SSIZE_T\n\n"
-            config_h_body += "#define in_port_t uint16_t\n\n"
-
-        conf.Finish()
-
-        # Autoconf feature doesn't work with Jenkins' arduino toolchain, so hardcode it here.
-        if target_os == 'arduino':
-            config_h_body += "#define HAVE_ARDUINO_TIME_H\n\n"
+        config_h_body = ''
+
+        if ((target_os == 'windows') and (libcoap_env.get('UWP_APP') == '1')):
+            # Workaround for libcoap config [Investigation in IOT-2234]:
+            # libcoap builds its config file by trying to create a small program to see if an API is
+            # available. However, when building with store libraries on windows, it doesn't seem
+            # to find malloc or strnlen APIs. On Windows, those APIs are guaranteed to be there,
+            # therefore, create the libcoap config_h_body with what is needed and expected to be
+            # there.
+            config_h_body = '''
+#define HAVE_ASSERT_H 1
+
+#define HAVE_LIMITS_H 1
+
+#define HAVE_STDIO_H 1
+
+#define HAVE_SYS_TYPES_H 1
+
+#define HAVE_TIME_H 1
+
+#define HAVE_WINSOCK2_H 1
+
+#define HAVE_WS2TCPIP_H 1
+
+#define HAVE_MALLOC 1
+
+#define HAVE_STRNLEN 1
+
+#define ssize_t SSIZE_T
+
+#define in_port_t uint16_t
+'''
+        else:
+            cxx_headers = ['arpa/inet.h',
+                           'assert.h',
+                           'limits.h',
+                           'netinet/in.h',
+                           'stdio.h',
+                           'strings.h',
+                           'sys/select.h',
+                           'sys/socket.h',
+                           'sys/time.h',
+                           'sys/types.h',
+                           'sys/uio.h',
+                           'sys/unistd.h',
+                           'syslog.h',
+                           'time.h',
+                           'unistd.h',
+                           'winsock2.h',
+                           'ws2tcpip.h']
+
+            cxx_functions = ['malloc',
+                             'snprintf',
+                             'strnlen',
+                             'vprintf']
+
+            if target_os == 'arduino':
+                # Detection of headers on the Arduino platform is currently broken.
+                cxx_headers = []
+
+            def get_define_from_string(string):
+                string_converted = string.replace("/","_").replace(".","_").upper()
+                return "HAVE_" + string_converted
+
+            for header_file_name in cxx_headers:
+                if conf.CheckCXXHeader(header_file_name):
+                    config_h_body += "#define %s 1\n\n" % get_define_from_string(header_file_name)
+
+            for function_name in cxx_functions:
+                if conf.CheckFunc(function_name):
+                    config_h_body += "#define %s 1\n\n" % get_define_from_string(function_name)
+
+            if conf.CheckCXXHeader('windows.h'):
+                config_h_body += "#define ssize_t SSIZE_T\n\n"
+                config_h_body += "#define in_port_t uint16_t\n\n"
+
+            conf.Finish()
+
+            # Autoconf feature doesn't work with Jenkins' arduino toolchain, so hardcode it here.
+            if target_os == 'arduino':
+                config_h_body += "#define HAVE_ARDUINO_TIME_H\n\n"
 
         # Generate the file
         if os.path.exists(config_h_file_path):
index 97e4c67..30e2184 100755 (executable)
@@ -41,6 +41,8 @@ if target_os in targets_need_sqlite:
 
 if target_os in ['windows']:
     sqlite_env.AppendUnique(CCFLAGS = ['/W4', '/WX'])
+    if sqlite_env.get('UWP_APP') == '1':
+        sqlite_env.AppendUnique(CPPDEFINES=['SQLITE_OS_WINRT'])
     libsqlite3 = sqlite_env.StaticLibrary('sqlite3', sqlite_c)
     # In multi-threaded builds, SCons appears to proceed to using sqlite3.lib
     # before finishing InstallTarget(libsqlite3, 'sqlite3') here. So, use the
index 3078e1a..4769d03 100644 (file)
@@ -30,6 +30,9 @@ ipca_env = env.Clone()
 if ipca_env['CC'] == 'cl':
     ipca_env.AppendUnique(CCFLAGS=['/W4'])
 
+# c_common calls into mbedcrypto and ipca calls into c_common
+ipca_env.AppendUnique(LIBS = ['mbedcrypto'])
+
 # Build ipca lib
 SConscript('src/SConscript', 'ipca_env')
 
index cdc163c..0881eb9 100644 (file)
@@ -74,11 +74,10 @@ ipca_lib_env.PrependUnique(LIBS = [
         'oc'
         ])
 
-if ipca_env.get('SECURED') == '1':
+if ipca_lib_env.get('SECURED') == '1':
     ipca_lib_env.PrependUnique(LIBS = [
             'mbedtls',
             'mbedx509',
-            'mbedcrypto',
             'ocprovision'
             ])
 
index e295a99..63fc954 100644 (file)
 using namespace std;
 using namespace std::placeholders;
 
+#include <assert.h>
+#include <inttypes.h>
 #include "oic_malloc.h"
 #include "oic_time.h"
 #include "OCApi.h"
 #include "pinoxmcommon.h"
 #include "srmutility.h"
 #include "ocrandom.h"
+#include "oic_platform.h"
 
 #define TAG                "IPCA_OcfFramework"
 #define DO_DEBUG           0
 
+#define PROVISIONING_DB    "PDM.db"
+
 const unsigned short c_discoveryTimeout = 5;  // Max number of seconds to discover
                                               // security information for a device
 
+// Path for Persistent Storage (Ends with backslash (\) or forward slash (/))
+std::string  g_psPath;
+
 // Initialize Persistent Storage for security database
 FILE* server_fopen(const char *path, const char *mode)
 {
-    return fopen(path, mode);
+    // At this point, the persistent storage path should have been found, otherwise
+    // Start() should have failed.
+    std::string filePath(g_psPath);
+    // g_psPath ends with trailing backslash (\) or forward slash (/)
+    // so don't have to worry about adding it.
+    filePath.append(path);
+
+    return fopen(filePath.c_str(), mode);
 }
 
 OCPersistentStorage ps = {server_fopen, fread, fwrite, fclose, unlink};
@@ -203,6 +218,47 @@ IPCAStatus OCFFramework::Start(const IPCAAppInfoInternal& appInfo, bool isUnitTe
         return IPCA_OK;
     }
 
+    char* psPath = nullptr;
+    size_t psPathLen = 0;
+    OICPlatformResult_t ret = OICGetLocalAppDataPath(nullptr, &psPathLen);
+    if (ret == OIC_PLATFORM_OK)
+    {
+        psPath = static_cast<char*>(OICCalloc(1, psPathLen));
+        if (psPath == nullptr)
+        {
+            OIC_LOG(FATAL, TAG, "Could not allocate persistent storage path buffer");
+            return IPCA_OUT_OF_MEMORY;
+        }
+
+        ret = OICGetLocalAppDataPath(psPath, &psPathLen);
+        if (ret != OIC_PLATFORM_OK)
+        {
+            OIC_LOG_V(FATAL, TAG,
+                "Failed to get persistent storage path from OICGetLocalAppDataPath, ret: %"PRIuPTR,
+                static_cast<size_t>(ret));
+            OICFree(psPath);
+            return IPCA_FAIL;
+        }
+
+        g_psPath.assign(psPath);
+
+        OICFree(psPath);
+        psPath = nullptr;
+    }
+    else
+    {
+        // Continue if not implemented returned, g_psPath by default is an empty string.
+        // Otherwise, fail
+        if (ret != OIC_PLATFORM_NOTIMPL)
+        {
+            OIC_LOG_V(FATAL, TAG,
+                "Failed to get path length from OICGetLocalAppDataPath, ret: %"PRIuPTR,
+                static_cast<size_t>(ret));
+            // An error occurred, fail
+            return IPCA_FAIL;
+        }
+    }
+
     PlatformConfig Configuration {
                         ServiceType::InProc,
                         ModeType::Both,  // Server mode is required for security provisioning.
@@ -216,8 +272,14 @@ IPCAStatus OCFFramework::Start(const IPCAAppInfoInternal& appInfo, bool isUnitTe
         return IPCA_FAIL;
     }
 
-    // Initialize the database that will be used for provisioning
-    if (OCSecure::provisionInit("") != OC_STACK_OK)
+
+    // Initialize the database that will be used for provisioning.
+    // Initialize it with the default PDM.db file name.
+    std::string pdmDbFile(g_psPath);
+    // g_psPath ends with trailing backslash (\) or forward slash (/)
+    // so don't have to worry about adding it.
+    pdmDbFile.append(PROVISIONING_DB);
+    if (OCSecure::provisionInit(pdmDbFile) != OC_STACK_OK)
     {
         OIC_LOG(FATAL, TAG, "Failed provisionInit()");
         return IPCA_FAIL;
index 8cd962c..43d5758 100644 (file)
@@ -197,6 +197,11 @@ elif target_os in ['arduino']:
 else:
     common_src.append('ocatomic/src/others/ocatomic.c')
 
+if target_os in ['windows']:
+    common_src.append('oic_platform/src/windows/oic_winplatform.cpp')
+else:
+    common_src.append('oic_platform/src/others/oic_otherplatforms.c')
+
 # C++ Arduino's <Timer.h> is included so use C++ compiler/flags
 if target_os in ['arduino']:
     octimer_env = common_env.Clone()
index a7b102c..e99e7cc 100644 (file)
@@ -28,6 +28,24 @@ extern "C"
 #define OIC_UUID_LENGTH     16
 
 /**
+ * Declares OIC Platform Results & Errors.
+ */
+typedef enum
+{
+    /** Success status code.*/
+    OIC_PLATFORM_OK = 0,
+
+    /** Error status code - START HERE.*/
+    OIC_PLATFORM_INVALID_PARAM,
+    OIC_PLATFORM_NO_MEMORY,
+    OIC_PLATFORM_NOTIMPL,
+
+    /** Generic ERROR.*/
+    OIC_PLATFORM_ERROR = 255
+    /** Error status code - END HERE.*/
+} OICPlatformResult_t;
+
+/**
  * Function returns a UUID that can be used for platform ID.
  *
  * @param[in] platformUuid  The generated UUID.
@@ -36,6 +54,40 @@ extern "C"
  */
 bool OICGetPlatformUuid(uint8_t platformUuid[OIC_UUID_LENGTH]);
 
+/**
+ * This function returns the platform and application specific local application data path.
+ * This path will be used to store the iotivity metadata. For example, the security databases.
+ * The path will contain the trailing backslash (\) or forward slash (/) and the null terminating
+ * character.
+ * To get the needed buffer size, call this function with buffer NULL.
+ *
+ * @param[out]    buffer    The buffer to store the path.
+ * @param[in,out] bufferLen The size of the buffer passed in. If buffer is NULL, the function will
+ *                          return the buffer size needed including the null character. If buffer
+ *                          passed in doesn't have sufficient size, then bufferLen will also be set
+ *                          to the buffer size needed.
+ *
+ * @return OIC_PLATFORM_OK on success, appropriate error code on failure.
+ */
+OICPlatformResult_t OICGetLocalAppDataPath(char* buffer, size_t* bufferLen);
+
+/**
+ * This function returns the platform and application specific data path for temporary files.
+ * This path will be used to store sqlite3 metadata that might need to be created temporarily.
+ * The path will contain the trailing backslash (\) or forward slash (/) and the null terminating
+ * character.
+ * To get the needed buffer size, call this function with buffer NULL.
+ *
+ * @param[out]    buffer    The buffer to store the path.
+ * @param[in,out] bufferLen The size of the buffer passed in. If buffer is NULL, the function will
+ *                          return the buffer size needed including the null character. If buffer
+ *                          passed in doesn't have sufficient size, then bufferLen will also be set
+ *                          to the buffer size needed.
+ *
+ * @return OIC_PLATFORM_OK on success, appropriate error code on failure.
+ */
+OICPlatformResult_t OICGetTempAppDataPath(char* buffer, size_t* bufferLen);
+
 #ifdef __cplusplus
 }
 #endif // __cplusplus
diff --git a/resource/c_common/oic_platform/src/others/oic_otherplatforms.c b/resource/c_common/oic_platform/src/others/oic_otherplatforms.c
new file mode 100644 (file)
index 0000000..bd2cbe2
--- /dev/null
@@ -0,0 +1,40 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* 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 "iotivity_config.h"
+#include "iotivity_debug.h"
+#include "logger.h"
+#include "oic_platform.h"
+
+#define TAG "OIC_PLATFORM"
+
+OICPlatformResult_t OICGetLocalAppDataPath(char* buffer, size_t* bufferLen)
+{
+    OIC_LOG(WARNING, TAG, "Unsupported platform.");
+    OC_UNUSED(buffer);
+    OC_UNUSED(bufferLen);
+    return OIC_PLATFORM_NOTIMPL;
+}
+
+OICPlatformResult_t OICGetTempAppDataPath(char* buffer, size_t* bufferLen)
+{
+    OIC_LOG(WARNING, TAG, "Unsupported platform.");
+    OC_UNUSED(buffer);
+    OC_UNUSED(bufferLen);
+    return OIC_PLATFORM_NOTIMPL;
+}
diff --git a/resource/c_common/oic_platform/src/windows/oic_winplatform.cpp b/resource/c_common/oic_platform/src/windows/oic_winplatform.cpp
new file mode 100644 (file)
index 0000000..b42b0d9
--- /dev/null
@@ -0,0 +1,328 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* 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 "iotivity_config.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include <windows.h>
+#include <string>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "logger.h"
+#include "iotivity_debug.h"
+#include "oic_platform.h"
+#include "oic_string.h"
+#include "oic_malloc.h"
+
+#ifdef UWP_APP
+#include <cstdint>
+#include <Windows.Foundation.h>
+#include <windows.storage.h>
+#include <wrl.h>
+
+using namespace ABI::Windows::Storage;
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+#endif // UWP_APP
+
+#define TAG "OIC_PLATFORM"
+
+#define IOTIVITY_FOLDER_NAME "\\iotivity\\"
+
+using namespace std;
+
+#ifdef UWP_APP
+// This function converts a wide char string to a standard char string.
+static std::string ConvertWStrtoStr(PCWSTR wstr)
+{
+    std::string strRet;
+    char* str = nullptr;
+
+    int strLength = WideCharToMultiByte(
+        CP_UTF8,
+        WC_ERR_INVALID_CHARS,
+        wstr,
+        -1,
+        nullptr,
+        0,
+        nullptr,
+        nullptr);
+
+    if (strLength == 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed WideCharToMultiByte(), GetLastError(): %u", GetLastError());
+        goto exit;
+    }
+
+    // strLength includes null char
+    str = static_cast<char*>(OICCalloc(1, strLength));
+    if (str == nullptr)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to create str buffer");
+        goto exit;
+    }
+
+    int retLen = WideCharToMultiByte(
+        CP_UTF8,
+        0,
+        wstr,
+        -1,
+        str,
+        strLength,
+        nullptr,
+        nullptr);
+
+    if (retLen != strLength)
+    {
+        OIC_LOG_V(ERROR, TAG, "WideCharToMultiByte failed to convert WSTR, GetLastError(): %u",
+            GetLastError());
+        goto exit;
+    }
+
+    strRet = str;
+
+exit:
+    if (str != nullptr)
+    {
+        OICFree(str);
+    }
+    return strRet;
+}
+#endif // UWP_APP
+
+static bool VerifyOrCreateDir(LPCSTR dir)
+{
+    if (!CreateDirectory(dir, nullptr))
+    {
+        DWORD err = GetLastError();
+        if (err != ERROR_ALREADY_EXISTS)
+        {
+            OIC_LOG_V(ERROR, TAG, "Failed to CreateDirectory, GetLastError(): %u", err);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static OICPlatformResult_t GetSysLocalAppDataPath(std::string &path, size_t &sysPathLen, bool getTempDir)
+{
+    OICPlatformResult_t ret = OIC_PLATFORM_OK;
+
+#ifdef UWP_APP
+    HRESULT hr = S_OK;
+
+    // Since we are running in a UWP app, we don't need to initialize the Windows Runtime
+    // with RoInitialize
+    ComPtr<IApplicationDataStatics> appDataStatics;
+    hr = RoGetActivationFactory(HStringReference(L"Windows.Storage.ApplicationData").Get(),
+        __uuidof(appDataStatics), &appDataStatics);
+    if (FAILED(hr))
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to get the ActivationFactory, hr: %#x", hr);
+        return OIC_PLATFORM_ERROR;
+    }
+
+    ComPtr<IApplicationData> appData;
+    hr = appDataStatics->get_Current(&appData);
+    if (FAILED(hr))
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to get ApplicationData, hr: %#x", hr);
+        return OIC_PLATFORM_ERROR;
+    }
+
+    ComPtr<IStorageFolder> folder;
+    if (!getTempDir)
+    {
+        hr = appData->get_LocalFolder(&folder);
+        if (FAILED(hr))
+        {
+            OIC_LOG_V(ERROR, TAG, "Failed to get Local App StorageFolder, hr: %#x", hr);
+            return OIC_PLATFORM_ERROR;
+        }
+    }
+    else
+    {
+        hr = appData->get_TemporaryFolder(&folder);
+        if (FAILED(hr))
+        {
+            OIC_LOG_V(ERROR, TAG, "Failed to get Temp App StorageFolder, hr: %#x", hr);
+            return OIC_PLATFORM_ERROR;
+        }
+    }
+
+    ComPtr<IStorageItem> folderItem;
+    hr = folder.As(&folderItem);
+    if (FAILED(hr))
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to get StorageItem, hr: %#x", hr);
+        return OIC_PLATFORM_ERROR;
+    }
+
+    HString folderPathHString;
+    hr = folderItem->get_Path(folderPathHString.GetAddressOf());
+    if (FAILED(hr))
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to get FolderPath, hr: %#x", hr);
+        return OIC_PLATFORM_ERROR;
+    }
+
+    uint32_t wstrPathLength;
+    PCWSTR folderPathWStr = folderPathHString.GetRawBuffer(&wstrPathLength);
+    path = ConvertWStrtoStr(folderPathWStr);
+    if (path.empty())
+    {
+        OIC_LOG(ERROR, TAG, "Failed to ConvertWStrtoStr");
+        return OIC_PLATFORM_ERROR;
+    }
+
+    // The length of the string that the system returned. All the folders up to this point
+    // should exist.
+    sysPathLen = path.length();
+#else // UWP_APP
+    // Unsupported for win32 apps
+    OIC_LOG(WARNING, TAG, "Unsupported platform.");
+    OC_UNUSED(path);
+    OC_UNUSED(sysPathLen);
+    OC_UNUSED(getTempDir);
+    ret = OIC_PLATFORM_NOTIMPL;
+#endif // UWP_APP
+
+    return ret;
+}
+
+// This function returns the platform and application specific local or temp application data path.
+// This path will be used to store the iotivity metadata.For example, the security databases.
+// The path will contain the trailing backslash(\) and the null terminating character.
+// To get the needed buffer size, call this function with buffer NULL.
+static OICPlatformResult_t GetLocalAppDataPath(std::string &path, bool getTempDir, char* buffer, size_t* bufferLen)
+{
+    if (bufferLen == nullptr)
+    {
+        OIC_LOG(ERROR, TAG, "bufferLen is NULL");
+        return OIC_PLATFORM_INVALID_PARAM;
+    }
+
+    if (path.empty())
+    {
+        // The length of the string that the system returned. All the folders up to this point
+        // should exist.
+        size_t sysPathLen;
+        OICPlatformResult_t ret = GetSysLocalAppDataPath(path, sysPathLen, getTempDir);
+        // Set path to the appropriate system local or temp application data path
+        if (ret != OIC_PLATFORM_OK)
+        {
+            OIC_LOG_V(ERROR, TAG, "Failed to GetSysLocalAppDataPath, ret: %"PRIuPTR,
+                static_cast<size_t>(ret));
+            // On failure, path should be cleared in GetSysLocalAppDataPath
+            return ret;
+        }
+
+        // Append \iotivity\ for UWP and Win32
+        path.append(IOTIVITY_FOLDER_NAME);
+
+        if (path[path.length() - 1] != '\\')
+        {
+            path.append("\\");
+        }
+
+        // At this point, the path should end with backslash (\)
+        // Verify/Create all the folders in the path.
+        // Start searching from after the path retrieved from the system APIs
+        size_t prevSlashIdx = 0;
+        size_t slashIdx = path.find("\\", sysPathLen);
+        while (slashIdx != string::npos)
+        {
+            std::string dir = path.substr(0, slashIdx);
+            if (!VerifyOrCreateDir(dir.c_str()))
+            {
+                OIC_LOG_V(ERROR, TAG, "Failed to VerifyOrCreateDir %s", dir.c_str());
+                // Revert path back to default state as we cannot create an
+                // intermediate folder
+                path.clear();
+                return OIC_PLATFORM_ERROR;
+            }
+            prevSlashIdx = slashIdx;
+            slashIdx = path.find("\\", slashIdx + 1);
+        }
+
+        if (prevSlashIdx != (path.length() - 1))
+        {
+            // Verify/Create the last folder
+            if (!VerifyOrCreateDir(path.c_str()))
+            {
+                OIC_LOG_V(ERROR, TAG, "Failed to VerifyOrCreateDir %s", path.c_str());
+                // Revert path back to default state as we cannot create an
+                // intermediate folder
+                path.clear();
+                return OIC_PLATFORM_ERROR;
+            }
+        }
+    }
+
+    if (path.empty())
+    {
+        // Path shouldn't be empty at this point
+        OIC_LOG(ERROR, TAG, "Path is empty");
+        return OIC_PLATFORM_ERROR;
+    }
+
+    if (buffer != nullptr)
+    {
+        if (*bufferLen < (path.length() + 1))
+        {
+            // Insufficient buffer size for path string
+            OIC_LOG(ERROR, TAG, "Insufficient buffer size for path string");
+            *bufferLen = path.length() + 1; // + 1 for null-character
+            return OIC_PLATFORM_INVALID_PARAM;
+        }
+        OICStrcpy(buffer, *bufferLen, path.c_str());
+        *bufferLen = path.length() + 1; // + 1 for null-character
+    }
+    else
+    {
+        *bufferLen = path.length() + 1; // + 1 for null-character
+    }
+
+    return OIC_PLATFORM_OK;
+}
+
+// This function returns the platform and application specific local application data path.
+// This path will be used to store the iotivity metadata.For example, the security databases.
+// The path will contain the trailing backslash(\) and the null terminating character.
+// To get the needed buffer size, call this function with buffer NULL.
+OICPlatformResult_t OICGetLocalAppDataPath(char* buffer, size_t* bufferLen)
+{
+    static std::string localAppDataPath;
+    return GetLocalAppDataPath(localAppDataPath, false, buffer, bufferLen);
+}
+
+// This function returns the platform and application specific temp application data path.
+// This path will be used to store the iotivity metadata.For example, the security databases.
+// The path will contain the trailing backslash(\) and the null terminating character.
+// To get the needed buffer size, call this function with buffer NULL.
+OICPlatformResult_t OICGetTempAppDataPath(char* buffer, size_t* bufferLen)
+{
+    static std::string tempAppDataPath;
+    return GetLocalAppDataPath(tempAppDataPath, true, buffer, bufferLen);
+}
index 7cd8486..2e6011d 100644 (file)
@@ -230,6 +230,9 @@ if 'SERVER' in rd_mode:
     if target_os not in ['linux', 'tizen', 'windows']:
         liboctbstack_src.append('#extlibs/sqlite3/sqlite3.c')
 
+if ((target_os in ['windows']) and (liboctbstack_env.get('UWP_APP') == '1')):
+    liboctbstack_src.append(OCTBSTACK_SRC + 'ocsqlite3helper.c')
+
 internal_liboctbstack = liboctbstack_env.StaticLibrary('octbstack_internal', liboctbstack_src)
 octbstack_libs = Flatten(internal_liboctbstack)
 
diff --git a/resource/csdk/stack/include/internal/ocsqlite3helper.h b/resource/csdk/stack/include/internal/ocsqlite3helper.h
new file mode 100644 (file)
index 0000000..e9d7286
--- /dev/null
@@ -0,0 +1,38 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* 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 SQLITE3_HELPER_H__
+#define SQLITE3_HELPER_H__
+
+#include "octypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef UWP_APP
+OCStackResult InitSqlite3TempDir();
+#endif // UWP_APP
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SQLITE3_HELPER_H__
+
diff --git a/resource/csdk/stack/src/ocsqlite3helper.c b/resource/csdk/stack/src/ocsqlite3helper.c
new file mode 100644 (file)
index 0000000..0336bf8
--- /dev/null
@@ -0,0 +1,78 @@
+/* *****************************************************************
+*
+* Copyright 2017 Microsoft
+*
+*
+* 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.
+*
+******************************************************************/
+
+#ifdef UWP_APP
+#include <inttypes.h>
+#include "ocsqlite3helper.h"
+#include "logger.h"
+#include "oic_platform.h"
+#include "oic_malloc.h"
+#include "sqlite3.h"
+
+OCStackResult InitSqlite3TempDir()
+{
+    OCStackResult result = OC_STACK_OK;
+
+    size_t tempSqlite3DirLen = 0;
+    char *tempSqlite3Dir = NULL;
+    OICPlatformResult_t ret = OICGetTempAppDataPath(NULL, &tempSqlite3DirLen);
+    if (ret == OIC_PLATFORM_OK)
+    {
+        tempSqlite3Dir = (char*)OICCalloc(1, tempSqlite3DirLen);
+        if (tempSqlite3Dir == NULL)
+        {
+            OIC_LOG(FATAL, TAG, "Could not allocate temp path buffer");
+            result = OC_STACK_NO_MEMORY;
+            goto exit;
+        }
+
+        ret = OICGetTempAppDataPath(tempSqlite3Dir, &tempSqlite3DirLen);
+        if (ret != OIC_PLATFORM_OK)
+        {
+            OIC_LOG_V(FATAL, TAG,
+                "Failed to get temp path from OICGetTempAppDataPath, ret: %"PRIuPTR, (size_t)ret);
+            result = OC_STACK_ERROR;
+            goto exit;
+        }
+
+        // Set the temp directory for sqlite3
+        sqlite3_temp_directory = sqlite3_mprintf("%s", tempSqlite3Dir);
+    }
+    else
+    {
+        // Return success if not implemented returned, otherwise, fail
+        if (ret != OIC_PLATFORM_NOTIMPL)
+        {
+            // An error occurred, fail
+            OIC_LOG_V(FATAL, TAG,
+                "Failed to get temp path length from OICGetTempAppDataPath, ret: %"PRIuPTR,
+                (size_t)ret);
+            result = OC_STACK_ERROR;
+        }
+    }
+
+exit:
+    if (tempSqlite3Dir != NULL)
+    {
+        OICFree(tempSqlite3Dir);
+    }
+
+    return result;
+}
+#endif // UWP_APP
index faed96c..29248dd 100644 (file)
 #include "platform_features.h"
 #include "oic_platform.h"
 
+#ifdef UWP_APP
+#include "ocsqlite3helper.h"
+#endif // UWP_APP
+
 #if defined(TCP_ADAPTER) && defined(WITH_CLOUD)
 #include "occonnectionmanager.h"
 #endif
@@ -2743,6 +2747,11 @@ OCStackResult OCInitializeInternal(OCMode mode, OCTransportFlags serverFlags,
     defaultDeviceHandler = NULL;
     defaultDeviceHandlerCallbackParameter = NULL;
 
+#ifdef UWP_APP
+    result = InitSqlite3TempDir();
+    VERIFY_SUCCESS(result, OC_STACK_OK);
+#endif // UWP_APP
+
     result = InitializeScheduleResourceList();
     VERIFY_SUCCESS(result, OC_STACK_OK);
 
index 65a2344..ddf80f2 100644 (file)
@@ -127,8 +127,12 @@ examples += [clientjson, serverjson, directpairingdat]
 
 if target_os in ['msys_nt', 'windows']:
     winUIClient = examples_env.Program('winUIClient', ['winuiclientgui.cpp', 'winuiclient.cpp'])
-    mediaserver = examples_env.Program('mediaserver', 'mediaserver.cpp')
-    examples += [winUIClient, mediaserver]
+    examples += [winUIClient]
+    # mediaserver uses GetVersionEx which is a windows native/desktop only API.
+    # Only build for Native/Win32
+    if env.get('UWP_APP') != '1':
+        mediaserver = examples_env.Program('mediaserver', 'mediaserver.cpp')
+        examples += [mediaserver]
 
 Alias("examples", examples)
 examples_env.AppendTarget('examples')
index 50cbade..0d4c3df 100644 (file)
@@ -27,7 +27,17 @@ Import('env')
 
 target_os = env.get('TARGET_OS')
 
-if target_os in ['linux', 'windows', 'darwin', 'msys_nt']:
+build_tests = False
+
+if target_os in ['windows']:
+    # gtest has a lot of windows native/desktop only APIs. As all unit tests depend on gtest,
+    # only enable unit tests if building for Win32.
+    if env.get('UWP_APP') != '1':
+        build_tests = True
+elif target_os in ['darwin', 'linux', 'msys_nt']:
+    build_tests = True
+
+if build_tests:
     # Verify that 'google unit test' library is installed.  If not,
     # get it and install it
     test_env = SConscript('#extlibs/gtest/SConscript')
diff --git a/run.bat b/run.bat
index 0fb581e..386258a 100644 (file)
--- a/run.bat
+++ b/run.bat
@@ -51,6 +51,11 @@ if "%MULTIPLE_OWNER%" == "" (
   set MULTIPLE_OWNER=1
 )
 
+if "%UWP_APP%" == "" (
+  REM Set it to build Win32 app by default
+  set UWP_APP=0
+)
+
 set THREAD_COUNT=%NUMBER_OF_PROCESSORS%
 
 set ROUTING=EP
@@ -97,6 +102,9 @@ IF NOT "%1"=="" (
   IF /I "%1"=="-automaticUpdate" (
     set AUTOMATIC_UPDATE=1
   )
+  IF /I "%1"=="-uwp" (
+    set UWP_APP=1
+  )
 
   SHIFT
   GOTO :processArgs
@@ -106,16 +114,21 @@ IF %RELEASE% == 1 (
   set BINDIR=release
 )
 
+set BUILD_VARIANT=win32
+if "%UWP_APP%" == "1" (
+  set BUILD_VARIANT=uwp
+)
+
 REM We need to append the "PATH" so the octbstack.dll can be found by executables
 IF "%BUILD_MSYS%" == "" (
-  set BUILD_DIR=out\windows\%TARGET_ARCH%\%BINDIR%
+  set BUILD_DIR=out\windows\%BUILD_VARIANT%\%TARGET_ARCH%\%BINDIR%
   set PATH=!PATH!;!IOTIVITY_DIR!!BUILD_DIR!;
 ) ELSE (
   set BUILD_DIR=out\msys_nt\x86_64\%BINDIR%
   set PATH=!PATH!;!BUILD_DIR!;C:\msys64\mingw64\bin
 )
 
-set BUILD_OPTIONS= TARGET_OS=%TARGET_OS% TARGET_ARCH=%TARGET_ARCH% RELEASE=%RELEASE% WITH_RA=0 TARGET_TRANSPORT=IP SECURED=%SECURED% WITH_TCP=%WITH_TCP% BUILD_SAMPLE=ON LOGGING=%LOGGING% TEST=%TEST% RD_MODE=%RD_MODE% ROUTING=%ROUTING% WITH_UPSTREAM_LIBCOAP=%WITH_UPSTREAM_LIBCOAP% MULTIPLE_OWNER=%MULTIPLE_OWNER% -j %THREAD_COUNT% AUTOMATIC_UPDATE=%AUTOMATIC_UPDATE%
+set BUILD_OPTIONS= TARGET_OS=%TARGET_OS% TARGET_ARCH=%TARGET_ARCH% UWP_APP=%UWP_APP% RELEASE=%RELEASE% WITH_RA=0 TARGET_TRANSPORT=IP SECURED=%SECURED% WITH_TCP=%WITH_TCP% BUILD_SAMPLE=ON LOGGING=%LOGGING% TEST=%TEST% RD_MODE=%RD_MODE% ROUTING=%ROUTING% WITH_UPSTREAM_LIBCOAP=%WITH_UPSTREAM_LIBCOAP% MULTIPLE_OWNER=%MULTIPLE_OWNER% -j %THREAD_COUNT% AUTOMATIC_UPDATE=%AUTOMATIC_UPDATE%
 
 REM Use MSVC_VERSION=12.0 for VS2013, or MSVC_VERSION=14.0 for VS2015.
 REM If MSVC_VERSION has not been defined here, SCons chooses automatically a VS version.
@@ -173,6 +186,8 @@ if "!RUN_ARG!"=="server" (
   echo Starting IoTivity build with these options:
   echo   TARGET_OS=%TARGET_OS%
   echo   TARGET_ARCH=%TARGET_ARCH%
+  echo   UWP_APP=%UWP_APP%
+  echo   BUILD_DIR=%BUILD_DIR%
   echo   SECURED=%SECURED%
   echo   RELEASE=%RELEASE%
   echo   TEST=%TEST%
@@ -189,21 +204,22 @@ if "!RUN_ARG!"=="server" (
   if ERRORLEVEL 1 (
     echo SCons failed - exiting run.bat with code 3
     exit /B 3
-    )
+  )
 ) else if "!RUN_ARG!"=="clean" (
+  echo Cleaning IoTivity build
   del /S *.ilk
   call scons.bat VERBOSE=1 %BUILD_OPTIONS% -c
   if ERRORLEVEL 1 (
     echo SCons failed - exiting run.bat with code 2
     exit /B 2
-    )
+  )
 ) else if "!RUN_ARG!"=="cleangtest" (
   rd /s /q extlibs\gtest\googletest-release-1.7.0
   del extlibs\gtest\release-1.7.0.zip
 ) else (
-    echo.%0 - Script requires a valid argument!
-    echo Exiting run.bat with code 1
-    exit /B 1
+  echo.%0 - Script requires a valid argument!
+  echo Exiting run.bat with code 1
+  exit /B 1
 )
 
 cd %IOTIVITY_DIR%
@@ -244,6 +260,8 @@ echo   -noMOT                       - Remove Multiple Ownership Transfer support
 echo.
 echo   -automaticUpdate             - Automatically update libcoap to required version.
 echo.
+echo   -uwp                         - Build for the Universal Windows Platform (UWP).
+echo.
 echo.
 echo. Usage examples:
 echo.