[IOT-2259] Windows: Handle Persistent Storage for UWP Apps
[platform/upstream/iotivity.git] / resource / c_common / oic_platform / src / windows / oic_winplatform.cpp
1 /* *****************************************************************
2 *
3 * Copyright 2017 Microsoft
4 *
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *      http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 ******************************************************************/
19 #include "iotivity_config.h"
20
21 #ifdef HAVE_STRING_H
22 #include <string.h>
23 #endif
24
25 #include <windows.h>
26 #include <string>
27 #include <assert.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <inttypes.h>
31 #include "logger.h"
32 #include "iotivity_debug.h"
33 #include "oic_platform.h"
34 #include "oic_string.h"
35 #include "oic_malloc.h"
36
37 #ifdef UWP_APP
38 #include <cstdint>
39 #include <Windows.Foundation.h>
40 #include <windows.storage.h>
41 #include <wrl.h>
42
43 using namespace ABI::Windows::Storage;
44 using namespace Microsoft::WRL;
45 using namespace Microsoft::WRL::Wrappers;
46 #endif // UWP_APP
47
48 #define TAG "OIC_PLATFORM"
49
50 #define IOTIVITY_FOLDER_NAME "\\iotivity\\"
51
52 using namespace std;
53
54 #ifdef UWP_APP
55 // This function converts a wide char string to a standard char string.
56 static std::string ConvertWStrtoStr(PCWSTR wstr)
57 {
58     std::string strRet;
59     char* str = nullptr;
60
61     int strLength = WideCharToMultiByte(
62         CP_UTF8,
63         WC_ERR_INVALID_CHARS,
64         wstr,
65         -1,
66         nullptr,
67         0,
68         nullptr,
69         nullptr);
70
71     if (strLength == 0)
72     {
73         OIC_LOG_V(ERROR, TAG, "Failed WideCharToMultiByte(), GetLastError(): %u", GetLastError());
74         goto exit;
75     }
76
77     // strLength includes null char
78     str = static_cast<char*>(OICCalloc(1, strLength));
79     if (str == nullptr)
80     {
81         OIC_LOG(ERROR, TAG, "Failed to create str buffer");
82         goto exit;
83     }
84
85     int retLen = WideCharToMultiByte(
86         CP_UTF8,
87         0,
88         wstr,
89         -1,
90         str,
91         strLength,
92         nullptr,
93         nullptr);
94
95     if (retLen != strLength)
96     {
97         OIC_LOG_V(ERROR, TAG, "WideCharToMultiByte failed to convert WSTR, GetLastError(): %u",
98             GetLastError());
99         goto exit;
100     }
101
102     strRet = str;
103
104 exit:
105     if (str != nullptr)
106     {
107         OICFree(str);
108     }
109     return strRet;
110 }
111 #endif // UWP_APP
112
113 static bool VerifyOrCreateDir(LPCSTR dir)
114 {
115     if (!CreateDirectory(dir, nullptr))
116     {
117         DWORD err = GetLastError();
118         if (err != ERROR_ALREADY_EXISTS)
119         {
120             OIC_LOG_V(ERROR, TAG, "Failed to CreateDirectory, GetLastError(): %u", err);
121             return false;
122         }
123     }
124
125     return true;
126 }
127
128 static OICPlatformResult_t GetSysLocalAppDataPath(std::string &path, size_t &sysPathLen, bool getTempDir)
129 {
130     OICPlatformResult_t ret = OIC_PLATFORM_OK;
131
132 #ifdef UWP_APP
133     HRESULT hr = S_OK;
134
135     // Since we are running in a UWP app, we don't need to initialize the Windows Runtime
136     // with RoInitialize
137     ComPtr<IApplicationDataStatics> appDataStatics;
138     hr = RoGetActivationFactory(HStringReference(L"Windows.Storage.ApplicationData").Get(),
139         __uuidof(appDataStatics), &appDataStatics);
140     if (FAILED(hr))
141     {
142         OIC_LOG_V(ERROR, TAG, "Failed to get the ActivationFactory, hr: %#x", hr);
143         return OIC_PLATFORM_ERROR;
144     }
145
146     ComPtr<IApplicationData> appData;
147     hr = appDataStatics->get_Current(&appData);
148     if (FAILED(hr))
149     {
150         OIC_LOG_V(ERROR, TAG, "Failed to get ApplicationData, hr: %#x", hr);
151         return OIC_PLATFORM_ERROR;
152     }
153
154     ComPtr<IStorageFolder> folder;
155     if (!getTempDir)
156     {
157         hr = appData->get_LocalFolder(&folder);
158         if (FAILED(hr))
159         {
160             OIC_LOG_V(ERROR, TAG, "Failed to get Local App StorageFolder, hr: %#x", hr);
161             return OIC_PLATFORM_ERROR;
162         }
163     }
164     else
165     {
166         hr = appData->get_TemporaryFolder(&folder);
167         if (FAILED(hr))
168         {
169             OIC_LOG_V(ERROR, TAG, "Failed to get Temp App StorageFolder, hr: %#x", hr);
170             return OIC_PLATFORM_ERROR;
171         }
172     }
173
174     ComPtr<IStorageItem> folderItem;
175     hr = folder.As(&folderItem);
176     if (FAILED(hr))
177     {
178         OIC_LOG_V(ERROR, TAG, "Failed to get StorageItem, hr: %#x", hr);
179         return OIC_PLATFORM_ERROR;
180     }
181
182     HString folderPathHString;
183     hr = folderItem->get_Path(folderPathHString.GetAddressOf());
184     if (FAILED(hr))
185     {
186         OIC_LOG_V(ERROR, TAG, "Failed to get FolderPath, hr: %#x", hr);
187         return OIC_PLATFORM_ERROR;
188     }
189
190     uint32_t wstrPathLength;
191     PCWSTR folderPathWStr = folderPathHString.GetRawBuffer(&wstrPathLength);
192     path = ConvertWStrtoStr(folderPathWStr);
193     if (path.empty())
194     {
195         OIC_LOG(ERROR, TAG, "Failed to ConvertWStrtoStr");
196         return OIC_PLATFORM_ERROR;
197     }
198
199     // The length of the string that the system returned. All the folders up to this point
200     // should exist.
201     sysPathLen = path.length();
202 #else // UWP_APP
203     // Unsupported for win32 apps
204     OIC_LOG(WARNING, TAG, "Unsupported platform.");
205     OC_UNUSED(path);
206     OC_UNUSED(sysPathLen);
207     OC_UNUSED(getTempDir);
208     ret = OIC_PLATFORM_NOTIMPL;
209 #endif // UWP_APP
210
211     return ret;
212 }
213
214 // This function returns the platform and application specific local or temp application data path.
215 // This path will be used to store the iotivity metadata.For example, the security databases.
216 // The path will contain the trailing backslash(\) and the null terminating character.
217 // To get the needed buffer size, call this function with buffer NULL.
218 static OICPlatformResult_t GetLocalAppDataPath(std::string &path, bool getTempDir, char* buffer, size_t* bufferLen)
219 {
220     if (bufferLen == nullptr)
221     {
222         OIC_LOG(ERROR, TAG, "bufferLen is NULL");
223         return OIC_PLATFORM_INVALID_PARAM;
224     }
225
226     if (path.empty())
227     {
228         // The length of the string that the system returned. All the folders up to this point
229         // should exist.
230         size_t sysPathLen;
231         OICPlatformResult_t ret = GetSysLocalAppDataPath(path, sysPathLen, getTempDir);
232         // Set path to the appropriate system local or temp application data path
233         if (ret != OIC_PLATFORM_OK)
234         {
235             OIC_LOG_V(ERROR, TAG, "Failed to GetSysLocalAppDataPath, ret: %"PRIuPTR,
236                 static_cast<size_t>(ret));
237             // On failure, path should be cleared in GetSysLocalAppDataPath
238             return ret;
239         }
240
241         // Append \iotivity\ for UWP and Win32
242         path.append(IOTIVITY_FOLDER_NAME);
243
244         if (path[path.length() - 1] != '\\')
245         {
246             path.append("\\");
247         }
248
249         // At this point, the path should end with backslash (\)
250         // Verify/Create all the folders in the path.
251         // Start searching from after the path retrieved from the system APIs
252         size_t prevSlashIdx = 0;
253         size_t slashIdx = path.find("\\", sysPathLen);
254         while (slashIdx != string::npos)
255         {
256             std::string dir = path.substr(0, slashIdx);
257             if (!VerifyOrCreateDir(dir.c_str()))
258             {
259                 OIC_LOG_V(ERROR, TAG, "Failed to VerifyOrCreateDir %s", dir.c_str());
260                 // Revert path back to default state as we cannot create an
261                 // intermediate folder
262                 path.clear();
263                 return OIC_PLATFORM_ERROR;
264             }
265             prevSlashIdx = slashIdx;
266             slashIdx = path.find("\\", slashIdx + 1);
267         }
268
269         if (prevSlashIdx != (path.length() - 1))
270         {
271             // Verify/Create the last folder
272             if (!VerifyOrCreateDir(path.c_str()))
273             {
274                 OIC_LOG_V(ERROR, TAG, "Failed to VerifyOrCreateDir %s", path.c_str());
275                 // Revert path back to default state as we cannot create an
276                 // intermediate folder
277                 path.clear();
278                 return OIC_PLATFORM_ERROR;
279             }
280         }
281     }
282
283     if (path.empty())
284     {
285         // Path shouldn't be empty at this point
286         OIC_LOG(ERROR, TAG, "Path is empty");
287         return OIC_PLATFORM_ERROR;
288     }
289
290     if (buffer != nullptr)
291     {
292         if (*bufferLen < (path.length() + 1))
293         {
294             // Insufficient buffer size for path string
295             OIC_LOG(ERROR, TAG, "Insufficient buffer size for path string");
296             *bufferLen = path.length() + 1; // + 1 for null-character
297             return OIC_PLATFORM_INVALID_PARAM;
298         }
299         OICStrcpy(buffer, *bufferLen, path.c_str());
300         *bufferLen = path.length() + 1; // + 1 for null-character
301     }
302     else
303     {
304         *bufferLen = path.length() + 1; // + 1 for null-character
305     }
306
307     return OIC_PLATFORM_OK;
308 }
309
310 // This function returns the platform and application specific local application data path.
311 // This path will be used to store the iotivity metadata.For example, the security databases.
312 // The path will contain the trailing backslash(\) and the null terminating character.
313 // To get the needed buffer size, call this function with buffer NULL.
314 OICPlatformResult_t OICGetLocalAppDataPath(char* buffer, size_t* bufferLen)
315 {
316     static std::string localAppDataPath;
317     return GetLocalAppDataPath(localAppDataPath, false, buffer, bufferLen);
318 }
319
320 // This function returns the platform and application specific temp application data path.
321 // This path will be used to store the iotivity metadata.For example, the security databases.
322 // The path will contain the trailing backslash(\) and the null terminating character.
323 // To get the needed buffer size, call this function with buffer NULL.
324 OICPlatformResult_t OICGetTempAppDataPath(char* buffer, size_t* bufferLen)
325 {
326     static std::string tempAppDataPath;
327     return GetLocalAppDataPath(tempAppDataPath, true, buffer, bufferLen);
328 }