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) {
124 if (request_uri.empty())
125 return info_uri.empty();
127 std::string info_scheme = utils::SchemeName(info_uri);
129 // if has only scheme or scheme+star. ex) http, http://, http://*
130 if (!info_scheme.empty() &&
131 (info_uri == info_scheme || utils::EndsWith(info_uri, "://")
132 || utils::EndsWith(info_uri, "://*"))) {
133 return utils::SchemeName(request_uri) == info_scheme;
136 if (utils::EndsWith(info_uri, "*")) {
137 return utils::StartsWith(request_uri, info_uri.substr(0,
138 info_uri.length()-1));
140 return request_uri == info_uri;
144 static std::string InsertPrefixPath(const std::string& start_uri) {
145 if (start_uri.find("://") != std::string::npos)
148 return std::string(kSchemeTypeFile) + "/" + start_uri;
153 ResourceManager::Resource::Resource(const std::string& uri)
154 : uri_(uri), should_reset_(true), encoding_(kDefaultEncoding) {}
156 ResourceManager::Resource::Resource(const std::string& uri, bool should_reset)
157 : uri_(uri), should_reset_(should_reset), encoding_(kDefaultEncoding) {}
159 ResourceManager::Resource::Resource(const std::string& uri,
160 const std::string& mime,
161 const std::string& encoding)
162 : uri_(uri), mime_(mime), should_reset_(true), encoding_(encoding) {}
164 ResourceManager::Resource::Resource(const ResourceManager::Resource& res) {
168 ResourceManager::Resource& ResourceManager::Resource::operator=(
169 const ResourceManager::Resource& res) {
170 this->uri_ = res.uri();
171 this->mime_ = res.mime();
172 this->should_reset_ = res.should_reset();
173 this->encoding_ = res.encoding();
177 bool ResourceManager::Resource::operator==(const Resource& res) {
178 if (this->uri_ == res.uri() && this->mime_ == res.mime()
179 && this->should_reset_ == res.should_reset()
180 && this->encoding_ == res.encoding()) {
187 ResourceManager::ResourceManager(ApplicationData* application_data,
188 LocaleManager* locale_manager)
189 : application_data_(application_data),
190 locale_manager_(locale_manager),
191 security_model_version_(0) {
192 if (application_data != NULL) {
193 appid_ = application_data->tizen_application_info()->id();
194 if (application_data->csp_info() != NULL ||
195 application_data->csp_report_info() != NULL ||
196 application_data->allowed_navigation_info() != NULL) {
197 security_model_version_ = 2;
199 security_model_version_ = 1;
204 std::unique_ptr<ResourceManager::Resource>
205 ResourceManager::GetDefaultResource() {
208 std::string encoding = kDefaultEncoding;
210 std::shared_ptr<const wgt::parse::ContentInfo> content_info;
211 if (application_data_) {
212 content_info = application_data_->content_info();
214 src = content_info->src();
215 type = content_info->type();
216 encoding = content_info->encoding();
217 LOGGER(DEBUG) << "src: " << src;
218 LOGGER(DEBUG) << "type: " << type;
219 LOGGER(DEBUG) << "encoding: " << encoding;
221 LOGGER(DEBUG) << "content_info is null";
225 // Check that tizen:content src is external page
226 if (utils::StartsWith(src, kSchemeTypeHttp) ||
227 utils::StartsWith(src, kSchemeTypeHttps)) {
228 LOGGER(DEBUG) << "tizen content_info's src is an external page";
229 return std::unique_ptr<Resource>(new Resource(src, type, encoding));
232 // Find based on default start files list, if src is empty or invald
233 if (!content_info || !utils::Exists(resource_base_path_+src)) {
234 for (auto& start_file : kDefaultStartFiles) {
235 if (utils::Exists(resource_base_path_ + start_file)) {
236 src = InsertPrefixPath(start_file);
237 LOGGER(DEBUG) << "start file: " << src;
238 return std::unique_ptr<Resource>(new Resource(src, type, encoding));
241 // shouldn't be entered here
242 LOGGER(ERROR) << "it can't enter here. can't find any default start file";
243 return std::unique_ptr<Resource>(new Resource(src, type, encoding));
246 return std::unique_ptr<Resource>(new Resource(InsertPrefixPath(src),
250 std::unique_ptr<ResourceManager::Resource> ResourceManager::GetMatchedResource(
251 const AppControlInfo& app_control_info) {
252 if (!app_control_info.src().empty()) {
253 return std::unique_ptr<Resource>(new Resource(
254 InsertPrefixPath(app_control_info.src()),
255 app_control_info.reload() == "disable" ? false : true));
257 return GetDefaultResource();
260 std::unique_ptr<ResourceManager::Resource> ResourceManager::GetStartResource(
261 const AppControl* app_control) {
262 std::string operation = app_control->operation();
263 if (operation.empty()) {
264 LOGGER(ERROR) << "operation(mandatory) is NULL";
265 return GetDefaultResource();
268 std::string mime = app_control->mime();
269 std::string uri = app_control->uri();
270 if (mime.empty() && !uri.empty()) {
271 mime = GetMimeFromUri(uri);
274 LOGGER(DEBUG) << "Passed AppControl data";
275 LOGGER(DEBUG) << " - operation : " << operation;
276 LOGGER(DEBUG) << " - mimetype : " << mime;
277 LOGGER(DEBUG) << " - uri : " << uri;
279 if (application_data_ == NULL ||
280 application_data_->app_control_info_list() == NULL) {
281 return GetDefaultResource();
284 auto app_control_list =
285 application_data_->app_control_info_list()->controls;
287 AppControlList::const_iterator iter = std::find_if(
288 app_control_list.begin(), app_control_list.end(),
289 [&operation, &uri, &mime](AppControlInfo& info) -> bool {
290 return (info.operation() == operation)
291 && CompareMime(info.mime(), mime) && CompareUri(info.uri(), uri); });
293 if (iter != app_control_list.end()) {
294 return GetMatchedResource(*iter);
296 return GetDefaultResource();
300 std::string ResourceManager::GetLocalizedPath(const std::string& origin) {
301 std::string file_scheme = std::string() + kSchemeTypeFile + "/";
302 std::string app_scheme = std::string() + kSchemeTypeApp;
303 std::string locale_path = "locales/";
304 auto find = locale_cache_.find(origin);
305 if (find != locale_cache_.end()) {
308 std::string& result = locale_cache_[origin];
310 std::string url = origin;
313 size_t pos = url.find_first_of("#?");
314 if (pos != std::string::npos) {
315 suffix = url.substr(pos);
319 if (utils::StartsWith(url, app_scheme)) {
321 url.erase(0, app_scheme.length());
324 std::string check = appid_ + "/";
325 if (utils::StartsWith(url, check)) {
326 url.erase(0, check.length());
328 LOGGER(ERROR) << "Invalid uri: {scheme:app} uri=" << origin;
331 } else if (utils::StartsWith(url, file_scheme)) {
333 url.erase(0, file_scheme.length());
336 if (!url.empty() && url[url.length()-1] == '/') {
337 url.erase(url.length()-1, 1);
341 LOGGER(ERROR) << "Invalid uri: uri=" << origin;
345 std::string file_path = utils::UrlDecode(RemoveLocalePath(url));
346 for (auto& locales : locale_manager_->system_locales()) {
348 std::string app_locale_path = resource_base_path_ + locale_path;
349 if (!Exists(app_locale_path)) {
353 // check locale path ../locales/en_us/
354 std::string app_localized_path = app_locale_path + locales + "/";
355 if (!Exists(app_localized_path)) {
358 std::string resource_path = app_localized_path + file_path;
359 if (Exists(resource_path)) {
360 result = "file://" + resource_path + suffix;
365 std::string default_locale = resource_base_path_ + file_path;
366 if (Exists(default_locale)) {
367 result = "file://" + default_locale + suffix;
371 LOGGER(ERROR) << "Invalid uri: uri=" << origin << ", decoded=" << file_path;
375 std::string ResourceManager::RemoveLocalePath(const std::string& path) {
376 std::string locale_path = "locales/";
377 std::string result_path = path.at(0) == '/' ? path : "/" + path;
378 if (!utils::StartsWith(result_path, resource_base_path_)) {
382 result_path = result_path.substr(resource_base_path_.length());
383 if (!utils::StartsWith(result_path, locale_path)) {
387 size_t found = result_path.find_first_of('/', locale_path.length());
388 if (found != std::string::npos) {
389 result_path = result_path.substr(found+1);
394 void ResourceManager::set_base_resource_path(const std::string& path) {
399 resource_base_path_ = path;
400 if (resource_base_path_[resource_base_path_.length()-1] != '/') {
401 resource_base_path_ += "/";
405 bool ResourceManager::Exists(const std::string& path) {
406 auto find = file_existed_cache_.find(path);
407 if (find != file_existed_cache_.end()) {
410 bool ret = file_existed_cache_[path] = utils::Exists(path);
414 bool ResourceManager::AllowNavigation(const std::string& url) {
415 if (security_model_version_ == 2)
416 return CheckAllowNavigation(url);
417 return CheckWARP(url);
420 bool ResourceManager::AllowedResource(const std::string& url) {
421 if (security_model_version_ == 2)
423 return CheckWARP(url);
426 bool ResourceManager::CheckWARP(const std::string& url) {
427 // allow non-external resource
428 if (!utils::StartsWith(url, kSchemeTypeHttp) &&
429 !utils::StartsWith(url, kSchemeTypeHttps)) {
433 auto warp = application_data_->warp_info();
434 if (warp.get() == NULL)
437 auto find = warp_cache_.find(url);
438 if (find != warp_cache_.end()) {
442 bool& result = warp_cache_[url];
447 // if didn't have a scheme, it means local resource
448 if (url_info.scheme().empty()) {
452 for (auto& allow : warp->access_map()) {
453 if (allow.first == "*") {
455 } else if (allow.first.empty()) {
459 URL allow_url(allow.first);
461 // should be match the scheme and port
462 if (allow_url.scheme() != url_info.scheme() ||
463 allow_url.port() != url_info.port()) {
467 // if domain alos was matched, allow resource
468 if (allow_url.domain() == url_info.domain()) {
470 } else if (allow.second) {
471 // if does not match domain, should be check sub domain
473 // filter : test.com , subdomain=true
474 // url : aaa.test.com
475 // check url was ends with ".test.com"
476 if (utils::EndsWith(url_info.domain(), "." + allow_url.domain())) {
482 return result = false;
485 bool ResourceManager::CheckAllowNavigation(const std::string& url) {
486 // allow non-external resource
487 if (!utils::StartsWith(url, kSchemeTypeHttp) &&
488 !utils::StartsWith(url, kSchemeTypeHttps)) {
492 auto allow = application_data_->allowed_navigation_info();
493 if (allow.get() == NULL)
496 auto find = warp_cache_.find(url);
497 if (find != warp_cache_.end()) {
501 bool& result = warp_cache_[url];
506 // if didn't have a scheme, it means local resource
507 if (url_info.scheme().empty()) {
511 for (auto& allow_domain : allow->GetAllowedDomains()) {
512 URL a_domain_info(allow_domain);
515 if (a_domain_info.domain() == "*") {
519 bool prefix_wild = false;
520 bool suffix_wild = false;
521 std::string a_domain = a_domain_info.domain();
522 if (utils::StartsWith(a_domain, "*.")) {
524 // *.domain.com -> .domain.com
525 a_domain = a_domain.substr(1);
527 if (utils::EndsWith(a_domain, ".*")) {
529 // domain.* -> domain.
530 a_domain = a_domain.substr(0, a_domain.length() - 1);
533 if (!prefix_wild && !suffix_wild) {
534 // if no wildcard, should be exactly matched
535 if (url_info.domain() == a_domain) {
538 } else if (prefix_wild && !suffix_wild) {
539 // *.domain.com : it shoud be "domain.com" or end with ".domain.com"
540 if (url_info.domain() == a_domain.substr(1) ||
541 utils::EndsWith(url_info.domain(), a_domain)) {
544 } else if (!prefix_wild && suffix_wild) {
545 // www.sample.* : it should be starts with "www.sample."
546 if (utils::StartsWith(url_info.domain(), a_domain)) {
549 } else if (prefix_wild && suffix_wild) {
550 // *.sample.* : it should be starts with sample. or can find ".sample."
552 if (utils::StartsWith(url_info.domain(), a_domain.substr(1)) ||
553 std::string::npos != url_info.domain().find(a_domain)) {
559 return result = false;
562 bool ResourceManager::IsEncrypted(const std::string& path) {
563 auto setting = application_data_->setting_info();
564 if (setting.get() == NULL)
567 if (setting->encryption_enabled()) {
568 std::string ext = utils::ExtName(path);
569 if (kEncryptedFileExtensions.count(ext) > 0) {
576 std::string ResourceManager::DecryptResource(const std::string& path) {
577 // read file and make a buffer
578 std::string src_path(path);
579 if (utils::StartsWith(src_path, kSchemeTypeFile)) {
580 src_path.erase(0, strlen(kSchemeTypeFile));
583 // Remove the parameters at the end of an href attribute
584 size_t end_of_path = src_path.find_first_of("?#");
585 if (end_of_path != std::string::npos)
586 src_path = src_path.substr(0, end_of_path);
588 // checking web app type
589 static bool inited = false;
590 static bool is_global = false;
591 static bool is_preload = false;
593 std::string pkg_id = application_data_->pkg_id();
596 pkgmgrinfo_pkginfo_h handle;
597 int ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkg_id.c_str(), getuid(), &handle);
598 if (ret != PMINFO_R_OK) {
599 LOGGER(ERROR) << "Could not get handle for pkginfo : pkg_id = "
603 ret = pkgmgrinfo_pkginfo_is_global(handle, &is_global);
604 if (ret != PMINFO_R_OK) {
605 LOGGER(ERROR) << "Could not check is_global : pkg_id = "
607 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
610 ret = pkgmgrinfo_pkginfo_is_preload(handle, &is_preload);
611 if (ret != PMINFO_R_OK) {
612 LOGGER(ERROR) << "Could not check is_preload : pkg_id = "
614 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
617 pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
622 memset(&buf, 0, sizeof(buf));
623 if (stat(src_path.c_str(), &buf) == 0) {
624 const std::size_t file_size = buf.st_size;
625 std::unique_ptr<unsigned char[]> in_chunk;
627 if (0 == file_size) {
628 LOGGER(ERROR) << src_path.c_str() << " size is 0, so decryption is skiped";
632 FILE *src = fopen(src_path.c_str(), "rb");
634 LOGGER(ERROR) << "Cannot open file for decryption: " << path;
638 // Read buffer from the source file
639 std::unique_ptr<unsigned char[]> decrypted_str(new unsigned char[file_size]);
640 int decrypted_size = 0;
643 unsigned char get_dec_size[5];
644 memset(get_dec_size, 0x00, sizeof(get_dec_size));
647 fread(get_dec_size, 1, 4, src);
648 if (0 != read_size) {
649 unsigned int read_buf_size = 0;
650 std::istringstream(std::string((char*)get_dec_size)) >> read_buf_size;
651 if (read_buf_size == 0) {
652 LOGGER(ERROR) << "Failed to read resource";
656 in_chunk.reset(new unsigned char[read_buf_size]);
658 size_t dec_read_size =
659 fread(in_chunk.get(), 1, read_buf_size, src);
660 if (0 != dec_read_size) {
661 unsigned char* decrypted_data = nullptr;
662 size_t decrypted_len = 0;
665 ret = wae_decrypt_global_web_application(pkg_id.c_str(),
672 ret = wae_decrypt_web_application(getuid(),
680 if (WAE_ERROR_NONE != ret) {
681 LOGGER(ERROR) << "Error during decryption: ";
683 case WAE_ERROR_INVALID_PARAMETER:
684 LOGGER(ERROR) << "WAE_ERROR_INVALID_PARAMETER";
686 case WAE_ERROR_PERMISSION_DENIED:
687 LOGGER(ERROR) << "WAE_ERROR_PERMISSION_DENIED";
689 case WAE_ERROR_NO_KEY:
690 LOGGER(ERROR) << "WAE_ERROR_NO_KEY";
692 case WAE_ERROR_KEY_MANAGER:
693 LOGGER(ERROR) << "WAE_ERROR_KEY_MANAGER";
695 case WAE_ERROR_CRYPTO:
696 LOGGER(ERROR) << "WAE_ERROR_CRYPTO";
698 case WAE_ERROR_UNKNOWN:
699 LOGGER(ERROR) << "WAE_ERROR_UNKNOWN";
702 LOGGER(ERROR) << "UNKNOWN";
709 memcpy(decrypted_str.get() + decrypted_size, decrypted_data, decrypted_len);
710 decrypted_size += decrypted_len;
711 std::free(decrypted_data);
714 } while(0 == std::feof(src));
716 memset(decrypted_str.get() + decrypted_size, '\n', file_size - decrypted_size);
718 // change to data schem
719 std::stringstream dst_str;
720 std::string content_type = GetMimeFromUri(path);
721 std::string encoded = utils::Base64Encode(decrypted_str.get(), decrypted_size);
722 dst_str << "data:" << content_type << ";base64," << encoded;
724 decrypted_str.reset(new unsigned char[file_size]);
726 return dst_str.str();
731 } // namespace common