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>
22 #include <pkgmgr-info.h>
25 #include <web_app_enc.h>
34 #include "common/application_data.h"
35 #include "common/app_control.h"
36 #include "common/file_utils.h"
37 #include "common/locale_manager.h"
38 #include "common/logger.h"
39 #include "common/string_utils.h"
40 #include "common/url.h"
42 using wgt::parse::AppControlInfo;
48 typedef std::vector<AppControlInfo> AppControlList;
51 const char* kSchemeTypeApp = "app://";
52 const char* kSchemeTypeFile = "file://";
53 const char* kSchemeTypeHttp = "http://";
54 const char* kSchemeTypeHttps = "https://";
55 // lendth of scheme identifier ://
56 const int kSchemeIdLen = 3;
57 // TODO(wy80.choi): comment out below unused const variables if needed.
58 // const char* kSchemeTypeWidget = "widget://";
60 // Default Start Files
61 const char* kDefaultStartFiles[] = {
69 const char* kDefaultEncoding = "UTF-8";
71 // EncryptedFileExtensions
72 const std::set<std::string> kEncryptedFileExtensions{
73 ".html", ".htm", ".css", ".js"};
75 static std::string GetMimeFromUri(const std::string& uri) {
76 // checking passed uri is local file
77 std::string file_uri_case(kSchemeTypeFile);
78 int ret = AUL_R_EINVAL;
79 char mimetype[128] = {0, };
80 size_t pos = std::string::npos;
81 if (utils::StartsWith(uri, file_uri_case)) {
82 // case 1. uri = file:///xxxx
83 ret = aul_get_mime_from_file(uri.substr(pos+file_uri_case.length()).c_str(),
84 mimetype, sizeof(mimetype));
85 } else if (utils::StartsWith(uri, "/")) {
86 // case 2. uri = /xxxx
87 ret = aul_get_mime_from_file(uri.c_str(),
88 mimetype, sizeof(mimetype));
91 if (ret == AUL_R_OK) {
92 return std::string(mimetype);
98 static bool CompareMime(const std::string& info_mime,
99 const std::string& request_mime) {
100 // suppose that these mimetypes are valid expressions ('type'/'sub-type')
101 if (info_mime == "*" || info_mime == "*/*")
104 if (request_mime.empty())
105 return info_mime.empty();
107 std::string info_type;
108 std::string info_sub;
109 std::string request_type;
110 std::string request_sub;
111 if (!(utils::SplitString(info_mime, &info_type, &info_sub, '/') &&
112 utils::SplitString(request_mime, &request_type, &request_sub, '/')))
115 return info_type == request_type &&
116 (info_sub == "*") ? true : (info_sub == request_sub);
119 static bool CompareUri(const std::string& info_uri,
120 const std::string& request_uri) {
121 if (request_uri.empty())
122 return info_uri.empty();
124 std::string info_scheme = utils::SchemeName(info_uri);
126 // if has only scheme or scheme+star. ex) http, http://, http://*
127 if (!info_scheme.empty() &&
128 (info_uri == info_scheme || utils::EndsWith(info_uri, "://")
129 || utils::EndsWith(info_uri, "://*"))) {
130 return utils::SchemeName(request_uri) == info_scheme;
133 if (utils::EndsWith(info_uri, "*")) {
134 return utils::StartsWith(request_uri, info_uri.substr(0,
135 info_uri.length()-1));
137 return request_uri == info_uri;
141 static std::string InsertPrefixPath(const std::string& start_uri) {
142 if (start_uri.find("://") != std::string::npos)
145 return std::string(kSchemeTypeFile) + "/" + start_uri;
150 ResourceManager::Resource::Resource(const std::string& uri)
151 : uri_(uri), should_reset_(true), encoding_(kDefaultEncoding) {}
153 ResourceManager::Resource::Resource(const std::string& uri, bool should_reset)
154 : uri_(uri), should_reset_(should_reset), encoding_(kDefaultEncoding) {}
156 ResourceManager::Resource::Resource(const std::string& uri,
157 const std::string& mime,
158 const std::string& encoding)
159 : uri_(uri), mime_(mime), should_reset_(true), encoding_(encoding) {}
161 ResourceManager::Resource::Resource(const ResourceManager::Resource& res) {
165 ResourceManager::Resource& ResourceManager::Resource::operator=(
166 const ResourceManager::Resource& res) {
167 this->uri_ = res.uri();
168 this->mime_ = res.mime();
169 this->should_reset_ = res.should_reset();
170 this->encoding_ = res.encoding();
174 bool ResourceManager::Resource::operator==(const Resource& res) {
175 if (this->uri_ == res.uri() && this->mime_ == res.mime()
176 && this->should_reset_ == res.should_reset()
177 && this->encoding_ == res.encoding()) {
184 ResourceManager::ResourceManager(ApplicationData* application_data,
185 LocaleManager* locale_manager)
186 : application_data_(application_data),
187 locale_manager_(locale_manager),
188 security_model_version_(0) {
189 if (application_data != NULL) {
190 appid_ = application_data->tizen_application_info()->id();
191 if (application_data->csp_info() != NULL ||
192 application_data->csp_report_info() != NULL ||
193 application_data->allowed_navigation_info() != NULL) {
194 security_model_version_ = 2;
196 security_model_version_ = 1;
201 std::unique_ptr<ResourceManager::Resource>
202 ResourceManager::GetDefaultResource() {
205 std::string encoding = kDefaultEncoding;
207 std::shared_ptr<const wgt::parse::ContentInfo> content_info;
208 if (application_data_) {
209 content_info = application_data_->content_info();
211 src = content_info->src();
212 type = content_info->type();
213 encoding = content_info->encoding();
214 LOGGER(DEBUG) << "src: " << src;
215 LOGGER(DEBUG) << "type: " << type;
216 LOGGER(DEBUG) << "encoding: " << encoding;
218 LOGGER(DEBUG) << "content_info is null";
222 // Check that tizen:content src is external page
223 if (utils::StartsWith(src, kSchemeTypeHttp) ||
224 utils::StartsWith(src, kSchemeTypeHttps)) {
225 LOGGER(DEBUG) << "tizen content_info's src is an external page";
226 return std::unique_ptr<Resource>(new Resource(src, type, encoding));
229 // Find based on default start files list, if src is empty or invald
230 if (!content_info || !utils::Exists(resource_base_path_+src)) {
231 for (auto& start_file : kDefaultStartFiles) {
232 if (utils::Exists(resource_base_path_ + start_file)) {
233 src = InsertPrefixPath(start_file);
234 LOGGER(DEBUG) << "start file: " << src;
235 return std::unique_ptr<Resource>(new Resource(src, type, encoding));
238 // shouldn't be entered here
239 LOGGER(ERROR) << "it can't enter here. can't find any default start file";
240 return std::unique_ptr<Resource>(new Resource(src, type, encoding));
243 return std::unique_ptr<Resource>(new Resource(InsertPrefixPath(src),
247 std::unique_ptr<ResourceManager::Resource> ResourceManager::GetMatchedResource(
248 const AppControlInfo& app_control_info) {
249 if (!app_control_info.src().empty()) {
250 return std::unique_ptr<Resource>(new Resource(
251 InsertPrefixPath(app_control_info.src()),
252 app_control_info.reload() == "disable" ? false : true));
254 return GetDefaultResource();
257 std::unique_ptr<ResourceManager::Resource> ResourceManager::GetStartResource(
258 const AppControl* app_control) {
259 std::string operation = app_control->operation();
260 if (operation.empty()) {
261 LOGGER(ERROR) << "operation(mandatory) is NULL";
262 return GetDefaultResource();
265 std::string mime = app_control->mime();
266 std::string uri = app_control->uri();
267 if (mime.empty() && !uri.empty()) {
268 mime = GetMimeFromUri(uri);
271 LOGGER(DEBUG) << "Passed AppControl data";
272 LOGGER(DEBUG) << " - operation : " << operation;
273 LOGGER(DEBUG) << " - mimetype : " << mime;
274 LOGGER(DEBUG) << " - uri : " << uri;
276 if (application_data_ == NULL ||
277 application_data_->app_control_info_list() == NULL) {
278 return GetDefaultResource();
281 auto app_control_list =
282 application_data_->app_control_info_list()->controls;
284 AppControlList::const_iterator iter = std::find_if(
285 app_control_list.begin(), app_control_list.end(),
286 [&operation, &uri, &mime](AppControlInfo& info) -> bool {
287 return (info.operation() == operation)
288 && CompareMime(info.mime(), mime) && CompareUri(info.uri(), uri); });
290 if (iter != app_control_list.end()) {
291 return GetMatchedResource(*iter);
293 return GetDefaultResource();
297 std::string ResourceManager::GetLocalizedPath(const std::string& origin) {
298 std::string file_scheme = std::string() + kSchemeTypeFile + "/";
299 std::string app_scheme = std::string() + kSchemeTypeApp;
300 std::string locale_path = "locales/";
301 auto find = locale_cache_.find(origin);
302 if (find != locale_cache_.end()) {
305 std::string& result = locale_cache_[origin];
307 std::string url = origin;
310 size_t pos = url.find_first_of("#?");
311 if (pos != std::string::npos) {
312 suffix = url.substr(pos);
316 if (utils::StartsWith(url, app_scheme)) {
318 url.erase(0, app_scheme.length());
321 std::string check = appid_ + "/";
322 if (utils::StartsWith(url, check)) {
323 url.erase(0, check.length());
325 LOGGER(ERROR) << "Invalid uri: {scheme:app} uri=" << origin;
328 } else if (utils::StartsWith(url, file_scheme)) {
330 url.erase(0, file_scheme.length());
333 if (!url.empty() && url[url.length()-1] == '/') {
334 url.erase(url.length()-1, 1);
338 LOGGER(ERROR) << "Invalid uri: uri=" << origin;
342 std::string file_path = utils::UrlDecode(RemoveLocalePath(url));
343 for (auto& locales : locale_manager_->system_locales()) {
345 std::string app_locale_path = resource_base_path_ + locale_path;
346 if (!Exists(app_locale_path)) {
350 // check locale path ../locales/en_us/
351 std::string app_localized_path = app_locale_path + locales + "/";
352 if (!Exists(app_localized_path)) {
355 std::string resource_path = app_localized_path + file_path;
356 if (Exists(resource_path)) {
357 result = "file://" + resource_path + suffix;
362 std::string default_locale = resource_base_path_ + file_path;
363 if (Exists(default_locale)) {
364 result = "file://" + default_locale + suffix;
368 LOGGER(ERROR) << "Invalid uri: uri=" << origin << ", decoded=" << file_path;
372 std::string ResourceManager::RemoveLocalePath(const std::string& path) {
373 std::string locale_path = "locales/";
374 std::string result_path = path.at(0) == '/' ? path : "/" + path;
375 if (!utils::StartsWith(result_path, resource_base_path_)) {
379 result_path = result_path.substr(resource_base_path_.length());
380 if (!utils::StartsWith(result_path, locale_path)) {
384 size_t found = result_path.find_first_of('/', locale_path.length());
385 if (found != std::string::npos) {
386 result_path = result_path.substr(found+1);
391 void ResourceManager::set_base_resource_path(const std::string& path) {
396 resource_base_path_ = path;
397 if (resource_base_path_[resource_base_path_.length()-1] != '/') {
398 resource_base_path_ += "/";
402 bool ResourceManager::Exists(const std::string& path) {
403 auto find = file_existed_cache_.find(path);
404 if (find != file_existed_cache_.end()) {
407 bool ret = file_existed_cache_[path] = utils::Exists(path);
411 bool ResourceManager::AllowNavigation(const std::string& url) {
412 if (security_model_version_ == 2)
413 return CheckAllowNavigation(url);
414 return CheckWARP(url);
417 bool ResourceManager::AllowedResource(const std::string& url) {
418 if (security_model_version_ == 2)
420 return CheckWARP(url);
423 bool ResourceManager::CheckWARP(const std::string& url) {
424 // allow non-external resource
425 if (!utils::StartsWith(url, kSchemeTypeHttp) &&
426 !utils::StartsWith(url, kSchemeTypeHttps)) {
430 auto warp = application_data_->warp_info();
431 if (warp.get() == NULL)
434 auto find = warp_cache_.find(url);
435 if (find != warp_cache_.end()) {
439 bool& result = warp_cache_[url];
444 // if didn't have a scheme, it means local resource
445 if (url_info.scheme().empty()) {
449 for (auto& allow : warp->access_map()) {
450 if (allow.first == "*") {
452 } else if (allow.first.empty()) {
456 URL allow_url(allow.first);
458 // should be match the scheme and port
459 if (allow_url.scheme() != url_info.scheme() ||
460 allow_url.port() != url_info.port()) {
464 // if domain alos was matched, allow resource
465 if (allow_url.domain() == url_info.domain()) {
467 } else if (allow.second) {
468 // if does not match domain, should be check sub domain
470 // filter : test.com , subdomain=true
471 // url : aaa.test.com
472 // check url was ends with ".test.com"
473 if (utils::EndsWith(url_info.domain(), "." + allow_url.domain())) {
479 return result = false;
482 bool ResourceManager::CheckAllowNavigation(const std::string& url) {
483 // allow non-external resource
484 if (!utils::StartsWith(url, kSchemeTypeHttp) &&
485 !utils::StartsWith(url, kSchemeTypeHttps)) {
489 auto allow = application_data_->allowed_navigation_info();
490 if (allow.get() == NULL)
493 auto find = warp_cache_.find(url);
494 if (find != warp_cache_.end()) {
498 bool& result = warp_cache_[url];
503 // if didn't have a scheme, it means local resource
504 if (url_info.scheme().empty()) {
508 for (auto& allow_domain : allow->GetAllowedDomains()) {
509 URL a_domain_info(allow_domain);
512 if (a_domain_info.domain() == "*") {
516 bool prefix_wild = false;
517 bool suffix_wild = false;
518 std::string a_domain = a_domain_info.domain();
519 if (utils::StartsWith(a_domain, "*.")) {
521 // *.domain.com -> .domain.com
522 a_domain = a_domain.substr(1);
524 if (utils::EndsWith(a_domain, ".*")) {
526 // domain.* -> domain.
527 a_domain = a_domain.substr(0, a_domain.length() - 1);
530 if (!prefix_wild && !suffix_wild) {
531 // if no wildcard, should be exactly matched
532 if (url_info.domain() == a_domain) {
535 } else if (prefix_wild && !suffix_wild) {
536 // *.domain.com : it shoud be "domain.com" or end with ".domain.com"
537 if (url_info.domain() == a_domain.substr(1) ||
538 utils::EndsWith(url_info.domain(), a_domain)) {
541 } else if (!prefix_wild && suffix_wild) {
542 // www.sample.* : it should be starts with "www.sample."
543 if (utils::StartsWith(url_info.domain(), a_domain)) {
546 } else if (prefix_wild && suffix_wild) {
547 // *.sample.* : it should be starts with sample. or can find ".sample."
549 if (utils::StartsWith(url_info.domain(), a_domain.substr(1)) ||
550 std::string::npos != url_info.domain().find(a_domain)) {
556 return result = false;
559 bool ResourceManager::IsEncrypted(const std::string& path) {
560 auto setting = application_data_->setting_info();
561 if (setting.get() == NULL)
564 if (setting->encryption_enabled()) {
565 std::string ext = utils::ExtName(path);
566 if (kEncryptedFileExtensions.count(ext) > 0) {
573 std::string ResourceManager::DecryptResource(const std::string& path) {
574 // read file and make a buffer
575 std::string src_path(path);
576 if (utils::StartsWith(src_path, kSchemeTypeFile)) {
577 src_path.erase(0, strlen(kSchemeTypeFile));
580 // Remove the parameters at the end of an href attribute
581 size_t end_of_path = src_path.find_first_of("?#");
582 if (end_of_path != std::string::npos)
583 src_path = src_path.substr(0, end_of_path);
585 // checking web app type
586 static bool inited = false;
587 static bool is_global = false;
588 static bool is_preload = false;
590 std::string pkg_id = application_data_->pkg_id();
593 pkgmgrinfo_pkginfo_h handle;
594 int ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkg_id.c_str(), getuid(), &handle);
595 if (ret != PMINFO_R_OK) {
596 LOGGER(ERROR) << "Could not get handle for pkginfo : pkg_id = "
600 ret = pkgmgrinfo_pkginfo_is_global(handle, &is_global);
601 if (ret != PMINFO_R_OK) {
602 LOGGER(ERROR) << "Could not check is_global : pkg_id = "
604 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
607 ret = pkgmgrinfo_pkginfo_is_preload(handle, &is_preload);
608 if (ret != PMINFO_R_OK) {
609 LOGGER(ERROR) << "Could not check is_preload : pkg_id = "
611 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
614 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
619 memset(&buf, 0, sizeof(buf));
620 if (stat(src_path.c_str(), &buf) == 0) {
621 const std::size_t file_size = buf.st_size;
622 std::unique_ptr<unsigned char[]> in_chunk;
624 if (0 == file_size) {
625 LOGGER(ERROR) << src_path.c_str() << " size is 0, so decryption is skiped";
629 FILE *src = fopen(src_path.c_str(), "rb");
631 LOGGER(ERROR) << "Cannot open file for decryption: " << path;
635 // Read buffer from the source file
636 std::unique_ptr<unsigned char[]> decrypted_str(new unsigned char[file_size]);
637 int decrypted_size = 0;
640 unsigned char get_dec_size[5];
641 memset(get_dec_size, 0x00, sizeof(get_dec_size));
644 fread(get_dec_size, 1, 4, src);
645 if (0 != read_size) {
646 unsigned int read_buf_size = 0;
647 std::istringstream(std::string((char*)get_dec_size)) >> read_buf_size;
648 if (read_buf_size == 0) {
649 LOGGER(ERROR) << "Failed to read resource";
653 in_chunk.reset(new unsigned char[read_buf_size]);
655 size_t dec_read_size =
656 fread(in_chunk.get(), 1, read_buf_size, src);
657 if (0 != dec_read_size) {
658 unsigned char* decrypted_data = nullptr;
659 size_t decrypted_len = 0;
662 ret = wae_decrypt_global_web_application(pkg_id.c_str(),
669 ret = wae_decrypt_web_application(getuid(),
677 if (WAE_ERROR_NONE != ret) {
678 LOGGER(ERROR) << "Error during decryption: ";
680 case WAE_ERROR_INVALID_PARAMETER:
681 LOGGER(ERROR) << "WAE_ERROR_INVALID_PARAMETER";
683 case WAE_ERROR_PERMISSION_DENIED:
684 LOGGER(ERROR) << "WAE_ERROR_PERMISSION_DENIED";
686 case WAE_ERROR_NO_KEY:
687 LOGGER(ERROR) << "WAE_ERROR_NO_KEY";
689 case WAE_ERROR_KEY_MANAGER:
690 LOGGER(ERROR) << "WAE_ERROR_KEY_MANAGER";
692 case WAE_ERROR_CRYPTO:
693 LOGGER(ERROR) << "WAE_ERROR_CRYPTO";
695 case WAE_ERROR_UNKNOWN:
696 LOGGER(ERROR) << "WAE_ERROR_UNKNOWN";
699 LOGGER(ERROR) << "UNKNOWN";
706 memcpy(decrypted_str.get() + decrypted_size, decrypted_data, decrypted_len);
707 decrypted_size += decrypted_len;
708 std::free(decrypted_data);
711 } while(0 == std::feof(src));
713 memset(decrypted_str.get() + decrypted_size, '\n', file_size - decrypted_size);
715 // change to data schem
716 std::stringstream dst_str;
717 std::string content_type = GetMimeFromUri(path);
718 std::string encoded = utils::Base64Encode(decrypted_str.get(), decrypted_size);
719 dst_str << "data:" << content_type << ";base64," << encoded;
721 decrypted_str.reset(new unsigned char[file_size]);
723 return dst_str.str();
728 } // namespace common