Merge "Update deprecated libprivilege-control API functions." into tizen
[platform/framework/native/appfw.git] / src / app / FApp_AppResourceString.cpp
1 //
2 // Copyright (c) 2012 Samsung Electronics Co., Ltd.
3 //
4 // Licensed under the Apache License, Version 2.0 (the License);
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 /**
18  * @file        FApp_AppResourceString.cpp
19  * @brief       This is the implementation for the _AppResourceString class.
20  */
21
22 #include <libxml/parser.h>
23 #include <libxml/xpath.h>
24 #include <libxml/tree.h>
25 #include <unicode/locid.h>
26
27 #include <unique_ptr.h>
28 #include <runtime_info.h>
29
30 #include <FBaseResult.h>
31 #include <FBaseSysLog.h>
32 #include <FBase_StringConverter.h>
33 #include <FBaseColHashMap.h>
34
35 #include <FIoFile.h>
36 #include <FIoDirectory.h>
37
38 #include <FApp_AppInfo.h>
39 #include <FAppPkgPackageInfo.h>
40 #include "FAppPkg_PackageManagerImpl.h"
41 #include "FAppPkg_PackageInfoImpl.h"
42
43 #include "FApp_AppResourceImpl.h"
44 #include "FApp_AppResourceString.h"
45
46 using namespace Tizen::App::Package;
47 using namespace Tizen::Base;
48 using namespace Tizen::Base::Collection;
49 using namespace Tizen::Io;
50
51 static const char* AR_ROOT_NODE_NAME = "string_table";
52 static const char* AR_CHILD_NODE_1 = "text";
53 static const char* AR_ATTRIBUTE = "id";
54
55 struct FreeXmlDoc
56 {
57         void operator ()(_xmlDoc* p)
58         {
59                 if (p != null)
60                 {
61                         xmlFreeDoc(p);
62                 }
63         }
64 };
65
66 namespace Tizen { namespace App
67 {
68
69 _AppResourceString::_AppResourceString(void)
70         : __pDoc(null)
71         , __pLockOfParser(null)
72         , __pXmlNodeMap(null)
73 {
74 }
75
76 _AppResourceString::~_AppResourceString(void)
77 {
78         if (__pDoc != null)
79         {
80                 xmlFreeDoc(__pDoc);
81         }
82         if (__pXmlNodeMap)
83         {
84                 __pXmlNodeMap->RemoveAll(true);
85                 delete __pXmlNodeMap;
86         }
87         delete __pLockOfParser;
88 }
89
90 // Exception: E_OUT_OF_MEMORY, E_APP_NOT_INSTALLED, E_SYSTEM
91 _AppResourceString*
92 _AppResourceString::Get_AppResourceStringN(int type, const Tizen::Base::String& value)
93 {
94         std::unique_ptr< _AppResourceString > pAppResourceString(new (std::nothrow) _AppResourceString);
95         SysTryReturn(NID_APP, pAppResourceString != null, null,
96                         E_OUT_OF_MEMORY, "[%s] Unable to allocate memory for _AppResourceString", GetErrorMessage(E_OUT_OF_MEMORY));
97
98         String resourceFolder;
99         switch(type)
100         {
101         case APP_RESOURCE_DEFAULT:
102         case APP_RESOURCE_BY_LIBRARY_NAME:
103         default:
104                 resourceFolder = _AppInfo::GetAppRootPath();
105                 resourceFolder.Append(L"res/");
106                 resourceFolder.Append(value);
107                 resourceFolder.Append(L"/");
108                 break;
109
110         case APP_RESOURCE_BY_APP_ID:
111                 std::unique_ptr< PackageInfo> pPkgInfo(_PackageManagerImpl::GetInstance()->GetPackageInfoN(value));
112                 SysTryReturn(NID_APP, pPkgInfo != null, null,
113                                 E_APP_NOT_INSTALLED, "[%s] Failed to get the package info", GetErrorMessage(E_APP_NOT_INSTALLED));
114
115                 _PackageInfoImpl* pPkgInfoImpl = _PackageInfoImpl::GetInstance(pPkgInfo.get());
116                 SysTryReturn(NID_APP, pPkgInfoImpl != null, null,
117                                 E_APP_NOT_INSTALLED, "[%s] Failed to get the package info impl", GetErrorMessage(E_APP_NOT_INSTALLED));
118
119                 resourceFolder = pPkgInfoImpl->GetAppRootPath();
120                 resourceFolder.Append(L"/res/");
121                 break;
122         }
123
124         result r = pAppResourceString->Initialize(resourceFolder);
125         SysTryReturn(NID_APP, !IsFailed(r), null,
126                                 r, "[%s] Failed to initialize AppResourceString", GetErrorMessage(r));
127
128         ClearLastResult();
129         return pAppResourceString.release();
130 }
131
132 result
133 _AppResourceString::Initialize(String& resourceFolder)
134 {
135         result r = E_SUCCESS;
136         if (__pLockOfParser == null)
137         {
138                 __pLockOfParser = new (std::nothrow) Tizen::Base::Runtime::Mutex();
139                 SysTryReturnResult(NID_APP, __pLockOfParser != null, E_OUT_OF_MEMORY, "Failed to initialize resource parser.");
140
141                 r = __pLockOfParser->Create();
142                 SysTryCatch(NID_APP, !IsFailed(r), , r, "[%s] Failed to Create Mutex.", GetErrorMessage(r));
143         }
144
145         r = __pLockOfParser->Acquire();
146         SysTryCatch(NID_APP, !IsFailed(r), r = E_SYSTEM, E_SYSTEM, "[E_SYSTEM] Failed to Acquire Mutex [%s].", GetErrorMessage(r));
147
148         r = InitializeStringInfo(resourceFolder);
149         __pLockOfParser->Release();
150
151         SysTryCatch(NID_APP, !IsFailed(r), , r, "[%s] Failed to InitializeStringInfo.", GetErrorMessage(r));
152         return E_SUCCESS;
153
154 CATCH:
155         delete __pLockOfParser;
156         __pLockOfParser = null;
157
158         return r;
159 }
160
161 result
162 _AppResourceString::InitializeStringInfo(String& resourceFolder)
163 {
164         char* pVal = null;
165         int ret = runtime_info_get_value_string(RUNTIME_INFO_KEY_LANGUAGE, &pVal);
166         SysTryReturnResult(NID_APP, ret == 0, E_SYSTEM, "runtime_info_get_value_string returns %d.", ret);
167
168         U_ICU_NAMESPACE::Locale icuLcl(pVal);
169         String language(icuLcl.getISO3Language());
170         String country(icuLcl.getCountry());
171         if (pVal)
172         {
173                 free(pVal);
174         }
175
176         String resFilename;
177         result r = resFilename.Format(256 * 2 + 10, L"%ls-%ls.xml", language.GetPointer(), country.GetPointer());
178         SysTryReturn(NID_APP, !IsFailed(r), E_SYSTEM, r, "[E_SYSTEM] Failed to format path string [%s]", GetErrorMessage(r));
179
180         if (!resourceFolder.EndsWith(L'/'))
181         {
182                 resourceFolder.Append(L'/');
183         }
184
185         std::unique_ptr< _xmlDoc, FreeXmlDoc > pDoc(ParseXmlFile(resourceFolder + resFilename));
186         if (pDoc == null)
187         {
188                 // open directory
189                 Directory dir;
190                 r = dir.Construct(resourceFolder);
191                 if (r == E_SUCCESS)
192                 {
193                         // read all directory entries
194                         DirEnumerator* pDirEnum = dir.ReadN();
195                         if (pDirEnum != null)
196                         {
197                                 const String starts = language + L"-";
198                                 const String ends = L".xml";
199
200                                 // loop through all directory entries
201                                 while (pDirEnum->MoveNext() == E_SUCCESS)
202                                 {
203                                         // get several properties of each directory entry.
204                                         resFilename = pDirEnum->GetCurrentDirEntry().GetName();
205                                         if (resFilename.StartsWith(starts, 0) && resFilename.EndsWith(ends))
206                                         {
207                                                 pDoc.reset(ParseXmlFile(resourceFolder + resFilename));
208                                                 break;
209                                         }
210                                 }
211                                 // Delete enumerator
212                                 delete pDirEnum;
213                         }
214                 }
215         }
216
217         if (pDoc == null)
218         {
219                 pDoc.reset(ParseXmlFile(resourceFolder + L"eng-GB.xml"));
220         }
221
222         if (pDoc == null)
223         {
224                 pDoc.reset(ParseXmlFile(resourceFolder + L"eng-US.xml"));
225         }
226
227         if (pDoc == null)
228         {
229                 pDoc.reset(ParseXmlFile(resourceFolder + L"English.xml"));
230         }
231
232         SysTryLogReturn(NID_APP, pDoc != null, E_SUCCESS, "Can't find xml resource file.");
233
234         xmlNodePtr cur = xmlDocGetRootElement(pDoc.get());
235         SysTryReturnResult(NID_APP, cur != null, E_SYSTEM, "Empty document.");
236
237         ret = xmlStrcmp(cur->name, (const xmlChar*) AR_ROOT_NODE_NAME);
238         SysTryReturnResult(NID_APP, ret == 0, E_SYSTEM, "Document root node is not <string_table>.");
239
240         std::unique_ptr< HashMap, AllElementsDeleter > pXmlNodeMap (new (std::nothrow) HashMap());
241         SysTryReturnResult(NID_APP, pXmlNodeMap != null, E_OUT_OF_MEMORY, " Memory allocation failed.");
242
243         r = pXmlNodeMap->Construct();
244         SysTryReturnResult(NID_APP, !IsFailed(r), E_SYSTEM, "Unable to construct xml node map [%s]", GetErrorMessage(r));
245
246         xmlChar* pxmlValue = null;
247         cur = cur->xmlChildrenNode;
248         while (cur != null)
249         {
250                 if ((!xmlStrcmp(cur->name, (const xmlChar*) AR_CHILD_NODE_1)))
251                 {
252                         pxmlValue = xmlGetProp(cur, (const xmlChar*) AR_ATTRIBUTE);
253                         if (cur->type == (xmlElementType)  XML_ELEMENT_NODE)
254                         {
255                                 xmlChar* pContent = xmlNodeListGetString( pDoc.get(), cur->xmlChildrenNode, 1);
256
257                                 std::unique_ptr< String > pKey(new (std::nothrow) String);
258                                 SysTryReturnResult(NID_APP, pKey != null, E_OUT_OF_MEMORY, " Memory allocation failed.");
259                                 Tizen::Base::Utility::StringUtil::Utf8ToString((char*) pxmlValue, *pKey);
260
261                                 std::unique_ptr< String > pValue(new (std::nothrow) String);
262                                 SysTryReturnResult(NID_APP, pValue != null, E_OUT_OF_MEMORY, " Memory allocation failed.");
263                                 Tizen::Base::Utility::StringUtil::Utf8ToString((char*) pContent, *pValue);
264
265                                 pXmlNodeMap->Add(*pKey, *pValue);
266                                 pKey.release();
267                                 pValue.release();
268
269                                 if (pContent)
270                                 {
271                                         xmlFree(pContent);
272                                 }
273                         }
274
275                         if (pxmlValue)
276                         {
277                                 xmlFree(pxmlValue);
278                                 pxmlValue = null;
279                         }
280
281                 }
282                 cur =  cur->next;
283         }
284
285         if (__pDoc != null)
286         {
287                 xmlFreeDoc(__pDoc);
288                 __pDoc = null;
289         }
290
291         if (__pXmlNodeMap)
292         {
293                 __pXmlNodeMap->RemoveAll(true);
294                 delete __pXmlNodeMap;
295         }
296
297         __pDoc = pDoc.release();
298         __pXmlNodeMap = pXmlNodeMap.release();
299         return E_SUCCESS;
300 }
301
302 xmlDocPtr
303 _AppResourceString::ParseXmlFile(const String& path)
304 {
305         xmlDocPtr pDoc = null;
306         if (File::IsFileExist(path))
307         {
308                 std::unique_ptr<char[]> pBuf(_StringConverter::CopyToCharArrayN(path));
309                 pDoc = xmlParseFile(pBuf.get());
310         }
311         return pDoc;
312 }
313
314
315 result
316 _AppResourceString::GetString(const String resourceId, String& loadedString)
317 {
318 #if defined(ENABLE_XPATH)
319         SysTryReturnResult(NID_APP, __pDoc != null, E_FAILURE, "Can't find xml resource file.");
320
321         xmlXPathContextPtr __pXpathCtx = null;
322         xmlXPathObjectPtr __pXpathObj = null;
323
324         String key;
325         key.Format(1024, L"/string_table/text[@id='%ls']", resourceId.GetPointer());
326
327         xmlChar* pXpathExpr = (xmlChar*) (_StringConverter::CopyToCharArrayN(key));
328         xmlChar* pContent = null;
329         // Create xpath evaluation context
330         __pXpathCtx = xmlXPathNewContext(__pDoc);
331         if (__pXpathCtx == null)
332         {
333                 SysLogException(NID_APP, E_INVALID_STATE, "[E_INVALID_STATE] XPathNewContext must not be null.");
334
335                 delete[] pXpathExpr;
336                 return E_INVALID_STATE;
337         }
338
339         // Evaluate xpath expression
340         __pXpathObj = xmlXPathEvalExpression(pXpathExpr, __pXpathCtx);
341         if (null == __pXpathObj ||
342                 null == __pXpathObj->nodesetval ||
343                 null == __pXpathObj->nodesetval->nodeTab ||
344                 null == __pXpathObj->nodesetval->nodeTab[0]->children
345                 )
346         {
347                 SysLog(NID_APP, "xpath expression is invalid.");
348
349                 xmlXPathFreeContext(__pXpathCtx);
350                 __pXpathCtx = null;
351                 delete[] pXpathExpr;
352                 return E_INVALID_ARG;
353         }
354         delete[] pXpathExpr;
355
356         pContent = __pXpathObj->nodesetval->nodeTab[0]->children->content;
357         loadedString = String((char*) pContent);
358
359         SysLog(NID_APP, "content : %s.", pContent);
360
361         // file close & open
362         // Cleanup of XPath data
363         xmlXPathFreeObject(__pXpathObj);
364         __pXpathObj = null;
365         xmlXPathFreeContext(__pXpathCtx);
366         __pXpathCtx = null;
367
368         SysLog(NID_APP, "Free xpath data.");
369
370         return E_SUCCESS;
371
372 #else
373         SysTryReturnResult(NID_APP, !resourceId.IsEmpty(), E_INVALID_ARG, "Wrong resource Id.");
374         SysTryReturnResult(NID_APP, __pDoc != null, E_FAILURE, "Application string resource was not initialized.");
375         SysTryReturnResult(NID_APP, __pLockOfParser != null, E_FAILURE, "__pLockOfParser was not initialized.");
376
377         result r = __pLockOfParser->Acquire();
378         SysTryReturnResult(NID_APP, !IsFailed(r), E_FAILURE, "Failed to Acquire Mutex [%s].", GetErrorMessage(r));
379
380         String* pkeyValue = static_cast< String* > (__pXmlNodeMap->GetValue(resourceId));
381         r = E_FAILURE; //GetLastResult();
382         if (pkeyValue)
383         {
384                 loadedString = *pkeyValue;
385                 if (HasSpecialString(loadedString))
386                 {
387                         String rawString(loadedString);
388                         ConvertToCstyleString(rawString, loadedString);
389                 }
390                 r = E_SUCCESS;
391         }
392
393         __pLockOfParser->Release();
394         return r;
395 #endif
396 }
397
398
399 bool
400 _AppResourceString::HasSpecialString(const String& resourceStr)
401 {
402         int foundIndex = 0;
403         return((resourceStr.IndexOf("&lt;", 0, foundIndex) == E_SUCCESS)
404                 || (resourceStr.IndexOf("&amp;", 0, foundIndex) == E_SUCCESS)
405                 || (resourceStr.IndexOf("&gt;", 0, foundIndex) == E_SUCCESS)
406                 || (resourceStr.IndexOf("&apos;", 0, foundIndex) == E_SUCCESS)
407                 || (resourceStr.IndexOf("&quot", 0, foundIndex) == E_SUCCESS)
408                 || (resourceStr.IndexOf("&amp;", 0, foundIndex) == E_SUCCESS)
409                 || (resourceStr.IndexOf("\\", 0, foundIndex) == E_SUCCESS));
410 }
411
412
413 result
414 _AppResourceString::ConvertToCstyleString(const String& resourceStr, String& convertedStr)
415 {
416         SysTryReturnResult(NID_APP, resourceStr.GetLength() > 0, E_INVALID_ARG, "resource string must be greater than 0.");
417
418         std::unique_ptr<char[]> pBuffer(_StringConverter::CopyToCharArrayN(resourceStr));
419         SysTryReturnResult(NID_APP, pBuffer != null, E_OUT_OF_MEMORY, "Memory allocation failure.");
420
421         int i = 0;
422         int j = 0;
423         int len = strlen(pBuffer.get());//      len = String_length((MString)pBuffer);
424         while (i < len)
425         {
426                 if (pBuffer[i] == '\\')
427                 {
428                         if (pBuffer[i + 1] == 'n')
429                         {
430                                 pBuffer[j] = '\n';
431                                 i += 2;
432                                 j++;
433                         }
434                         else if (pBuffer[i + 1] == 't')
435                         {
436                                 pBuffer[j] = '\t';
437                                 i += 2;
438                                 j++;
439                         }
440                         else if (pBuffer[i + 1] == '\\')
441                         {
442                                 pBuffer[j] = '\\';
443                                 i += 2;
444                                 j++;
445                         }
446                         else
447                         {
448                                 i++;
449                         }
450                 }
451                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'l') && (pBuffer[i + 2] == 't') &&
452                                  (pBuffer[i + 3] == ';'))
453                 {
454                         pBuffer[j] = '<';
455                         i += 4;
456                         j++;
457                 }
458                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'g') && (pBuffer[i + 2] == 't') &&
459                                  (pBuffer[i + 3] == ';'))
460                 {
461                         pBuffer[j] = '>';
462                         i += 4;
463                         j++;
464                 }
465                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'a') && (pBuffer[i + 2] == 'm') &&
466                                  (pBuffer[i + 3] == 'p') && (pBuffer[i + 4] == ';'))
467                 {
468                         pBuffer[j] = '&';
469                         i += 5;
470                         j++;
471                 }
472                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'a') && (pBuffer[i + 2] == 'p') &&
473                                  (pBuffer[i + 3] == 'o') && (pBuffer[i + 4] == 's') && (pBuffer[i + 5] == ';'))
474                 {
475                         pBuffer[j] = 39; // '
476                         i += 6;
477                         j++;
478                 }
479                 else if ((pBuffer[i] == '&') && (pBuffer[i + 1] == 'q') && (pBuffer[i + 2] == 'u') &&
480                                  (pBuffer[i + 3] == 'o') && (pBuffer[i + 4] == 't') && (pBuffer[i + 5] == ';'))
481                 {
482                         pBuffer[j] = 34; // "
483                         i += 6;
484                         j++;
485                 }
486                 else
487                 {
488                         pBuffer[j] = pBuffer[i];
489                         i++;
490                         j++;
491                 }
492         }
493         if (j != i)
494         {
495                 for (; j <= i; j++)
496                 {
497                         pBuffer[j] = '\0';
498                 }
499         }
500
501         convertedStr = pBuffer.get();
502         return E_SUCCESS;
503 }
504
505
506 } } // Tizen::App