2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "common/resource_manager.h"
19 #include <sys/types.h>
21 #include <pkgmgr-info.h>
24 #include <web_app_enc.h>
33 #include "common/application_data.h"
34 #include "common/app_control.h"
35 #include "common/file_utils.h"
36 #include "common/locale_manager.h"
37 #include "common/logger.h"
38 #include "common/string_utils.h"
39 #include "common/url.h"
41 using wgt::parse::AppControlInfo;
47 typedef std::vector<AppControlInfo> AppControlList;
50 const char* kSchemeTypeApp = "app://";
51 const char* kSchemeTypeFile = "file://";
52 const char* kSchemeTypeHttp = "http://";
53 const char* kSchemeTypeHttps = "https://";
54 // lendth of scheme identifier ://
55 const int kSchemeIdLen = 3;
56 // TODO(wy80.choi): comment out below unused const variables if needed.
57 // const char* kSchemeTypeWidget = "widget://";
59 // Default Start Files
60 const char* kDefaultStartFiles[] = {
68 const char* kDefaultEncoding = "UTF-8";
70 // EncryptedFileExtensions
71 const std::set<std::string> kEncryptedFileExtensions{
72 ".html", ".htm", ".css", ".js"};
74 static std::string GetMimeFromUri(const std::string& uri) {
75 // checking passed uri is local file
76 std::string file_uri_case(kSchemeTypeFile);
77 int ret = AUL_R_EINVAL;
78 char mimetype[128] = {0, };
79 size_t pos = std::string::npos;
80 if (utils::StartsWith(uri, file_uri_case)) {
81 // case 1. uri = file:///xxxx
82 ret = aul_get_mime_from_file(uri.substr(pos+file_uri_case.length()).c_str(),
83 mimetype, sizeof(mimetype));
84 } else if (utils::StartsWith(uri, "/")) {
85 // case 2. uri = /xxxx
86 ret = aul_get_mime_from_file(uri.c_str(),
87 mimetype, sizeof(mimetype));
90 if (ret == AUL_R_OK) {
91 return std::string(mimetype);
97 static bool CompareMime(const std::string& info_mime,
98 const std::string& request_mime) {
99 // suppose that these mimetypes are valid expressions ('type'/'sub-type')
100 if (info_mime == "*" || info_mime == "*/*")
103 if (request_mime.empty())
104 return info_mime.empty();
106 std::string info_type;
107 std::string info_sub;
108 std::string request_type;
109 std::string request_sub;
110 if (!(utils::SplitString(info_mime, &info_type, &info_sub, '/') &&
111 utils::SplitString(request_mime, &request_type, &request_sub, '/')))
114 return info_type == request_type &&
115 (info_sub == "*") ? true : (info_sub == request_sub);
118 static bool CompareUri(const std::string& info_uri,
119 const std::string& request_uri) {
120 if (request_uri.empty())
121 return info_uri.empty();
123 std::string info_scheme = utils::SchemeName(info_uri);
125 // if has only scheme or scheme+star. ex) http, http://, http://*
126 if (!info_scheme.empty() &&
127 (info_uri == info_scheme || utils::EndsWith(info_uri, "://")
128 || utils::EndsWith(info_uri, "://*"))) {
129 return utils::SchemeName(request_uri) == info_scheme;
132 if (utils::EndsWith(info_uri, "*")) {
133 return utils::StartsWith(request_uri, info_uri.substr(0,
134 info_uri.length()-1));
136 return request_uri == info_uri;
140 static std::string InsertPrefixPath(const std::string& start_uri) {
141 if (start_uri.find("://") != std::string::npos)
144 return std::string(kSchemeTypeFile) + "/" + start_uri;
149 ResourceManager::Resource::Resource(const std::string& uri)
150 : uri_(uri), should_reset_(true), encoding_(kDefaultEncoding) {}
152 ResourceManager::Resource::Resource(const std::string& uri, bool should_reset)
153 : uri_(uri), should_reset_(should_reset), encoding_(kDefaultEncoding) {}
155 ResourceManager::Resource::Resource(const std::string& uri,
156 const std::string& mime,
157 const std::string& encoding)
158 : uri_(uri), mime_(mime), should_reset_(true), encoding_(encoding) {}
160 ResourceManager::Resource::Resource(const ResourceManager::Resource& res) {
164 ResourceManager::Resource& ResourceManager::Resource::operator=(
165 const ResourceManager::Resource& res) {
166 this->uri_ = res.uri();
167 this->mime_ = res.mime();
168 this->should_reset_ = res.should_reset();
169 this->encoding_ = res.encoding();
173 bool ResourceManager::Resource::operator==(const Resource& res) {
174 if (this->uri_ == res.uri() && this->mime_ == res.mime()
175 && this->should_reset_ == res.should_reset()
176 && this->encoding_ == res.encoding()) {
183 ResourceManager::ResourceManager(ApplicationData* application_data,
184 LocaleManager* locale_manager)
185 : application_data_(application_data), locale_manager_(locale_manager) {
186 if (application_data != NULL) {
187 appid_ = application_data->tizen_application_info()->id();
188 if (application_data->csp_info() != NULL ||
189 application_data->csp_report_info() != NULL ||
190 application_data->allowed_navigation_info() != NULL) {
191 security_model_version_ = 2;
193 security_model_version_ = 1;
198 std::unique_ptr<ResourceManager::Resource>
199 ResourceManager::GetDefaultResource() {
202 std::string encoding = kDefaultEncoding;
203 auto content_info = application_data_->content_info();
205 src = content_info->src();
206 type = content_info->type();
207 encoding = content_info->encoding();
208 LOGGER(DEBUG) << "src: " << src;
209 LOGGER(DEBUG) << "type: " << type;
210 LOGGER(DEBUG) << "encoding: " << encoding;
212 LOGGER(DEBUG) << "content_info is null";
215 // Check that tizen:content src is external page
216 if (content_info && content_info->is_tizen_content()
217 && (utils::StartsWith(src, kSchemeTypeHttp) ||
218 utils::StartsWith(src, kSchemeTypeHttps))) {
219 LOGGER(DEBUG) << "tizen content_info's src is an external page";
220 return std::unique_ptr<Resource>(new Resource(src, type, encoding));
223 // Find based on default start files list, if src is empty or invald
224 if (!content_info || !utils::Exists(resource_base_path_+src)) {
225 for (auto& start_file : kDefaultStartFiles) {
226 if (utils::Exists(resource_base_path_ + start_file)) {
227 src = InsertPrefixPath(start_file);
228 LOGGER(DEBUG) << "start file: " << src;
229 return std::unique_ptr<Resource>(new Resource(src, type, encoding));
232 // shouldn't be entered here
233 LOGGER(ERROR) << "it can't enter here. can't find any default start file";
234 return std::unique_ptr<Resource>(new Resource(src, type, encoding));
237 return std::unique_ptr<Resource>(new Resource(InsertPrefixPath(src),
241 std::unique_ptr<ResourceManager::Resource> ResourceManager::GetMatchedResource(
242 const AppControlInfo& app_control_info) {
243 if (!app_control_info.src().empty()) {
244 return std::unique_ptr<Resource>(new Resource(
245 InsertPrefixPath(app_control_info.src()),
246 app_control_info.reload() == "disable" ? false : true));
248 return GetDefaultResource();
251 std::unique_ptr<ResourceManager::Resource> ResourceManager::GetStartResource(
252 const AppControl* app_control) {
253 std::string operation = app_control->operation();
254 if (operation.empty()) {
255 LOGGER(ERROR) << "operation(mandatory) is NULL";
256 return GetDefaultResource();
259 std::string mime = app_control->mime();
260 std::string uri = app_control->uri();
261 if (mime.empty() && !uri.empty()) {
262 mime = GetMimeFromUri(uri);
265 LOGGER(DEBUG) << "Passed AppControl data";
266 LOGGER(DEBUG) << " - operation : " << operation;
267 LOGGER(DEBUG) << " - mimetype : " << mime;
268 LOGGER(DEBUG) << " - uri : " << uri;
270 if (application_data_ == NULL ||
271 application_data_->app_control_info_list() == NULL) {
272 return GetDefaultResource();
275 auto app_control_list =
276 application_data_->app_control_info_list()->controls;
278 AppControlList::const_iterator iter = std::find_if(
279 app_control_list.begin(), app_control_list.end(),
280 [&operation, &uri, &mime](AppControlInfo& info) -> bool {
281 return (info.operation() == operation)
282 && CompareMime(info.mime(), mime) && CompareUri(info.uri(), uri); });
284 if (iter != app_control_list.end()) {
285 return GetMatchedResource(*iter);
287 return GetDefaultResource();
291 std::string ResourceManager::GetLocalizedPath(const std::string& origin) {
292 std::string file_scheme = std::string() + kSchemeTypeFile + "/";
293 std::string app_scheme = std::string() + kSchemeTypeApp;
294 std::string locale_path = "locales/";
295 auto find = locale_cache_.find(origin);
296 if (find != locale_cache_.end()) {
299 std::string& result = locale_cache_[origin];
301 std::string url = origin;
304 size_t pos = url.find_first_of("#?");
305 if (pos != std::string::npos) {
306 suffix = url.substr(pos);
310 if (utils::StartsWith(url, app_scheme)) {
312 url.erase(0, app_scheme.length());
315 std::string check = appid_ + "/";
316 if (utils::StartsWith(url, check)) {
317 url.erase(0, check.length());
319 LOGGER(ERROR) << "Invalid appid";
322 } else if (utils::StartsWith(url, file_scheme)) {
324 url.erase(0, file_scheme.length());
327 if (!url.empty() && url[url.length()-1] == '/') {
328 url.erase(url.length()-1, 1);
332 LOGGER(ERROR) << "URL Localization error";
336 std::string file_path = utils::UrlDecode(RemoveLocalePath(url));
337 for (auto& locales : locale_manager_->system_locales()) {
339 std::string app_locale_path = resource_base_path_ + locale_path;
340 if (!Exists(app_locale_path)) {
344 // check locale path ../locales/en_us/
345 std::string app_localized_path = app_locale_path + locales + "/";
346 if (!Exists(app_localized_path)) {
349 std::string resource_path = app_localized_path + file_path;
350 if (Exists(resource_path)) {
351 result = "file://" + resource_path + suffix;
356 std::string default_locale = resource_base_path_ + file_path;
357 if (Exists(default_locale)) {
358 result = "file://" + default_locale + suffix;
362 LOGGER(ERROR) << "URL Localization error";
366 std::string ResourceManager::RemoveLocalePath(const std::string& path) {
367 std::string locale_path = "locales/";
368 std::string result_path = path.at(0) == '/' ? path : "/" + path;
369 if (!utils::StartsWith(result_path, resource_base_path_)) {
373 result_path = result_path.substr(resource_base_path_.length());
374 if (!utils::StartsWith(result_path, locale_path)) {
378 size_t found = result_path.find_first_of('/', locale_path.length());
379 if (found != std::string::npos) {
380 result_path = result_path.substr(found+1);
385 void ResourceManager::set_base_resource_path(const std::string& path) {
390 resource_base_path_ = path;
391 if (resource_base_path_[resource_base_path_.length()-1] != '/') {
392 resource_base_path_ += "/";
396 bool ResourceManager::Exists(const std::string& path) {
397 auto find = file_existed_cache_.find(path);
398 if (find != file_existed_cache_.end()) {
401 bool ret = file_existed_cache_[path] = utils::Exists(path);
405 bool ResourceManager::AllowNavigation(const std::string& url) {
406 if (security_model_version_ == 2)
407 return CheckAllowNavigation(url);
408 return CheckWARP(url);
411 bool ResourceManager::AllowedResource(const std::string& url) {
412 if (security_model_version_ == 2)
414 return CheckWARP(url);
417 bool ResourceManager::CheckWARP(const std::string& url) {
418 // allow non-external resource
419 if (!utils::StartsWith(url, kSchemeTypeHttp) &&
420 !utils::StartsWith(url, kSchemeTypeHttps)) {
424 auto warp = application_data_->warp_info();
425 if (warp.get() == NULL)
428 auto find = warp_cache_.find(url);
429 if (find != warp_cache_.end()) {
433 bool& result = warp_cache_[url];
438 // if didn't have a scheme, it means local resource
439 if (url_info.scheme().empty()) {
443 for (auto& allow : warp->access_map()) {
444 if (allow.first == "*") {
446 } else if (allow.first.empty()) {
450 URL allow_url(allow.first);
452 // should be match the scheme and port
453 if (allow_url.scheme() != url_info.scheme() ||
454 allow_url.port() != url_info.port()) {
458 // if domain alos was matched, allow resource
459 if (allow_url.domain() == url_info.domain()) {
461 } else if (allow.second) {
462 // if does not match domain, should be check sub domain
464 // filter : test.com , subdomain=true
465 // url : aaa.test.com
466 // check url was ends with ".test.com"
467 if (utils::EndsWith(url_info.domain(), "." + allow_url.domain())) {
473 return result = false;
476 bool ResourceManager::CheckAllowNavigation(const std::string& url) {
477 // allow non-external resource
478 if (!utils::StartsWith(url, kSchemeTypeHttp) &&
479 !utils::StartsWith(url, kSchemeTypeHttps)) {
483 auto allow = application_data_->allowed_navigation_info();
484 if (allow.get() == NULL)
487 auto find = warp_cache_.find(url);
488 if (find != warp_cache_.end()) {
492 bool& result = warp_cache_[url];
497 // if didn't have a scheme, it means local resource
498 if (url_info.scheme().empty()) {
502 for (auto& allow_domain : allow->GetAllowedDomains()) {
503 URL a_domain_info(allow_domain);
506 if (a_domain_info.domain() == "*") {
510 bool prefix_wild = false;
511 bool suffix_wild = false;
512 std::string a_domain = a_domain_info.domain();
513 if (utils::StartsWith(a_domain, "*.")) {
515 // *.domain.com -> .domain.com
516 a_domain = a_domain.substr(1);
518 if (utils::EndsWith(a_domain, ".*")) {
520 // domain.* -> domain.
521 a_domain = a_domain.substr(0, a_domain.length() - 1);
524 if (!prefix_wild && !suffix_wild) {
525 // if no wildcard, should be exactly matched
526 if (url_info.domain() == a_domain) {
529 } else if (prefix_wild && !suffix_wild) {
530 // *.domain.com : it shoud be "domain.com" or end with ".domain.com"
531 if (url_info.domain() == a_domain.substr(1) ||
532 utils::EndsWith(url_info.domain(), a_domain)) {
535 } else if (!prefix_wild && suffix_wild) {
536 // www.sample.* : it should be starts with "www.sample."
537 if (utils::StartsWith(url_info.domain(), a_domain)) {
540 } else if (prefix_wild && suffix_wild) {
541 // *.sample.* : it should be starts with sample. or can find ".sample."
543 if (utils::StartsWith(url_info.domain(), a_domain.substr(1)) ||
544 std::string::npos != url_info.domain().find(a_domain)) {
550 return result = false;
553 bool ResourceManager::IsEncrypted(const std::string& path) {
554 auto setting = application_data_->setting_info();
555 if (setting.get() == NULL)
558 if (setting->encryption_enabled()) {
559 std::string ext = utils::ExtName(path);
560 if (kEncryptedFileExtensions.count(ext) > 0) {
567 std::string ResourceManager::DecryptResource(const std::string& path) {
568 // read file and make a buffer
569 std::string src_path(path);
570 if (utils::StartsWith(src_path, kSchemeTypeFile)) {
571 src_path.erase(0, strlen(kSchemeTypeFile));
574 FILE *src = fopen(src_path.c_str(), "rb");
576 LOGGER(ERROR) << "Cannot open file for decryption: " << src_path;
581 fseek(src, 0, SEEK_END);
582 size_t src_len = ftell(src);
584 // if the file exists and is empty, bypass it
590 // Read buffer from the source file
591 std::unique_ptr<char[]> src_buf(new char[src_len]);
592 if (src_len != fread(src_buf.get(), sizeof(char), src_len, src)) {
593 LOGGER(ERROR) << "Read error, file: " << src_path;
599 // checking web app type
600 static bool inited = false;
601 static bool is_global = false;
602 static bool is_preload = false;
605 std::string pkg_id = application_data_->pkg_id();
608 pkgmgrinfo_pkginfo_h handle;
609 ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkg_id.c_str(), getuid(), &handle);
610 if (ret != PMINFO_R_OK) {
611 LOGGER(ERROR) << "Could not get handle for pkginfo : pkg_id = "
615 ret = pkgmgrinfo_pkginfo_is_global(handle, &is_global);
616 if (ret != PMINFO_R_OK) {
617 LOGGER(ERROR) << "Could not check is_global : pkg_id = "
619 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
622 ret = pkgmgrinfo_pkginfo_is_preload(handle, &is_preload);
623 if (ret != PMINFO_R_OK) {
624 LOGGER(ERROR) << "Could not check is_preload : pkg_id = "
626 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
629 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
633 wae_app_type_e app_type = WAE_DOWNLOADED_NORMAL_APP;
635 app_type = WAE_DOWNLOADED_GLOBAL_APP;
636 } else if (is_preload) {
637 app_type = WAE_PRELOADED_APP;
640 // decrypt buffer with wae functions
641 uint8_t* dst_buf = nullptr;
643 ret = wae_decrypt_web_application(pkg_id.c_str(),
645 reinterpret_cast<uint8_t*>(src_buf.get()),
649 if (WAE_ERROR_NONE != ret) {
651 case WAE_ERROR_INVALID_PARAMETER:
652 LOGGER(ERROR) << "Error during decryption: WAE_ERROR_INVALID_PARAMETER";
654 case WAE_ERROR_PERMISSION_DENIED:
655 LOGGER(ERROR) << "Error during decryption: WAE_ERROR_PERMISSION_DENIED";
657 case WAE_ERROR_NO_KEY:
658 LOGGER(ERROR) << "Error during decryption: WAE_ERROR_NO_KEY";
660 case WAE_ERROR_KEY_MANAGER:
661 LOGGER(ERROR) << "Error during decryption: WAE_ERROR_KEY_MANAGER";
663 case WAE_ERROR_CRYPTO:
664 LOGGER(ERROR) << "Error during decryption: WAE_ERROR_CRYPTO";
666 case WAE_ERROR_UNKNOWN:
667 LOGGER(ERROR) << "Error during decryption: WAE_ERROR_UNKNOWN";
670 LOGGER(ERROR) << "Error during decryption: UNKNOWN";
676 // change to data schem
677 std::stringstream dst_str;
678 std::string content_type = GetMimeFromUri(path);
679 std::string encoded = utils::Base64Encode(dst_buf, dst_len);
680 dst_str << "data:" << content_type << ";base64," << encoded;
684 return dst_str.str();
687 } // namespace common