Skip an asterisk in appcontrol uri data
[platform/framework/web/crosswalk-tizen.git] / common / resource_manager.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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 #include "common/resource_manager.h"
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <aul.h>
22 #include <pkgmgr-info.h>
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <web_app_enc.h>
26
27 #include <algorithm>
28 #include <functional>
29 #include <memory>
30 #include <set>
31 #include <sstream>
32 #include <vector>
33
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"
41
42 using wgt::parse::AppControlInfo;
43
44 namespace common {
45
46 namespace {
47
48 typedef std::vector<AppControlInfo> AppControlList;
49
50 // Scheme type
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://";
59
60 // Default Start Files
61 const char* kDefaultStartFiles[] = {
62     "index.htm",
63     "index.html",
64     "index.svg",
65     "index.xhtml",
66     "index.xht"};
67
68 // Default Encoding
69 const char* kDefaultEncoding = "UTF-8";
70
71 // EncryptedFileExtensions
72 const std::set<std::string> kEncryptedFileExtensions{
73     ".html", ".htm", ".css", ".js"};
74
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));
89   }
90
91   if (ret == AUL_R_OK) {
92     return std::string(mimetype);
93   } else {
94     return std::string();
95   }
96 }
97
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 == "*/*")
102     return true;
103
104   if (request_mime.empty())
105     return info_mime.empty();
106
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, '/')))
113     return false;
114
115   return info_type == request_type &&
116          (info_sub == "*") ? true : (info_sub == request_sub);
117 }
118
119 static bool CompareUri(const std::string& info_uri,
120                        const std::string& request_uri) {
121   if (info_uri == "*")
122     return true;
123
124   if (request_uri.empty())
125     return info_uri.empty();
126
127   std::string info_scheme = utils::SchemeName(info_uri);
128
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;
134   }
135
136   if (utils::EndsWith(info_uri, "*")) {
137     return utils::StartsWith(request_uri, info_uri.substr(0,
138                                                           info_uri.length()-1));
139   } else {
140     return request_uri == info_uri;
141   }
142 }
143
144 static std::string InsertPrefixPath(const std::string& start_uri) {
145   if (start_uri.find("://") != std::string::npos)
146     return start_uri;
147   else
148     return std::string(kSchemeTypeFile) + "/" + start_uri;
149 }
150
151 }  // namespace
152
153 ResourceManager::Resource::Resource(const std::string& uri)
154   : uri_(uri), should_reset_(true), encoding_(kDefaultEncoding) {}
155
156 ResourceManager::Resource::Resource(const std::string& uri, bool should_reset)
157   : uri_(uri), should_reset_(should_reset), encoding_(kDefaultEncoding) {}
158
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) {}
163
164 ResourceManager::Resource::Resource(const ResourceManager::Resource& res) {
165   *this = res;
166 }
167
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();
174   return *this;
175 }
176
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()) {
181     return true;
182   } else {
183     return false;
184   }
185 }
186
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;
198     } else {
199       security_model_version_ = 1;
200     }
201   }
202 }
203
204 std::unique_ptr<ResourceManager::Resource>
205 ResourceManager::GetDefaultResource() {
206   std::string src;
207   std::string type;
208   std::string encoding = kDefaultEncoding;
209
210   std::shared_ptr<const wgt::parse::ContentInfo> content_info;
211   if (application_data_) {
212     content_info = application_data_->content_info();
213     if (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;
220     } else {
221       LOGGER(DEBUG) << "content_info is null";
222     }
223   }
224
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));
230   }
231
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));
239       }
240     }
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));
244   }
245
246   return std::unique_ptr<Resource>(new Resource(InsertPrefixPath(src),
247                                                 type, encoding));
248 }
249
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));
256   }
257   return GetDefaultResource();
258 }
259
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();
266   }
267
268   std::string mime = app_control->mime();
269   std::string uri = app_control->uri();
270   if (mime.empty() && !uri.empty()) {
271     mime = GetMimeFromUri(uri);
272   }
273
274   LOGGER(DEBUG) << "Passed AppControl data";
275   LOGGER(DEBUG) << " - operation : " << operation;
276   LOGGER(DEBUG) << " - mimetype  : " << mime;
277   LOGGER(DEBUG) << " - uri       : " << uri;
278
279   if (application_data_ == NULL ||
280       application_data_->app_control_info_list() == NULL) {
281     return GetDefaultResource();
282   }
283
284   auto app_control_list =
285     application_data_->app_control_info_list()->controls;
286
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); });
292
293   if (iter != app_control_list.end()) {
294     return GetMatchedResource(*iter);
295   } else {
296   return GetDefaultResource();
297   }
298 }
299
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()) {
306     return find->second;
307   }
308   std::string& result = locale_cache_[origin];
309   result = origin;
310   std::string url = origin;
311
312   std::string suffix;
313   size_t pos = url.find_first_of("#?");
314   if (pos != std::string::npos) {
315     suffix = url.substr(pos);
316     url.resize(pos);
317   }
318
319   if (utils::StartsWith(url, app_scheme)) {
320     // remove "app://"
321     url.erase(0, app_scheme.length());
322
323     // remove app id + /
324     std::string check = appid_ + "/";
325     if (utils::StartsWith(url, check)) {
326       url.erase(0, check.length());
327     } else {
328       LOGGER(ERROR) << "Invalid uri: {scheme:app} uri=" << origin;
329       return result;
330     }
331   } else if (utils::StartsWith(url, file_scheme)) {
332     // remove "file:///"
333     url.erase(0, file_scheme.length());
334   }
335
336   if (!url.empty() && url[url.length()-1] == '/') {
337       url.erase(url.length()-1, 1);
338   }
339
340   if (url.empty()) {
341     LOGGER(ERROR) << "Invalid uri: uri=" << origin;
342     return result;
343   }
344
345   std::string file_path = utils::UrlDecode(RemoveLocalePath(url));
346   for (auto& locales : locale_manager_->system_locales()) {
347     // check ../locales/
348     std::string app_locale_path = resource_base_path_ + locale_path;
349     if (!Exists(app_locale_path)) {
350       break;
351     }
352
353     // check locale path ../locales/en_us/
354     std::string app_localized_path = app_locale_path + locales + "/";
355     if (!Exists(app_localized_path)) {
356       continue;
357     }
358     std::string resource_path = app_localized_path + file_path;
359     if (Exists(resource_path)) {
360       result = "file://" + resource_path + suffix;
361       return result;
362     }
363   }
364
365   std::string default_locale = resource_base_path_ + file_path;
366   if (Exists(default_locale)) {
367     result = "file://" + default_locale + suffix;
368     return result;
369   }
370
371   LOGGER(ERROR) << "Invalid uri: uri=" << origin << ", decoded=" << file_path;
372   return result;
373 }
374
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_)) {
379     return path;
380   }
381
382   result_path = result_path.substr(resource_base_path_.length());
383   if (!utils::StartsWith(result_path, locale_path)) {
384     return result_path;
385   }
386
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);
390   }
391   return result_path;
392 }
393
394 void ResourceManager::set_base_resource_path(const std::string& path) {
395   if (path.empty()) {
396     return;
397   }
398
399   resource_base_path_ = path;
400   if (resource_base_path_[resource_base_path_.length()-1] != '/') {
401     resource_base_path_ += "/";
402   }
403 }
404
405 bool ResourceManager::Exists(const std::string& path) {
406   auto find = file_existed_cache_.find(path);
407   if (find != file_existed_cache_.end()) {
408     return find->second;
409   }
410   bool ret = file_existed_cache_[path] = utils::Exists(path);
411   return ret;
412 }
413
414 bool ResourceManager::AllowNavigation(const std::string& url) {
415   if (security_model_version_ == 2)
416     return CheckAllowNavigation(url);
417   return CheckWARP(url);
418 }
419
420 bool ResourceManager::AllowedResource(const std::string& url) {
421   if (security_model_version_ == 2)
422     return true;
423   return CheckWARP(url);
424 }
425
426 bool ResourceManager::CheckWARP(const std::string& url) {
427   // allow non-external resource
428   if (!utils::StartsWith(url, kSchemeTypeHttp) &&
429       !utils::StartsWith(url, kSchemeTypeHttps)) {
430     return true;
431   }
432
433   auto warp = application_data_->warp_info();
434   if (warp.get() == NULL)
435     return false;
436
437   auto find = warp_cache_.find(url);
438   if (find != warp_cache_.end()) {
439     return find->second;
440   }
441
442   bool& result = warp_cache_[url];
443   result = true;
444
445   URL url_info(url);
446
447   // if didn't have a scheme, it means local resource
448   if (url_info.scheme().empty()) {
449     return true;
450   }
451
452   for (auto& allow : warp->access_map()) {
453     if (allow.first == "*") {
454       return true;
455     } else if (allow.first.empty()) {
456       continue;
457     }
458
459     URL allow_url(allow.first);
460
461     // should be match the scheme and port
462     if (allow_url.scheme() != url_info.scheme() ||
463         allow_url.port() != url_info.port()) {
464       continue;
465     }
466
467     // if domain alos was matched, allow resource
468     if (allow_url.domain() == url_info.domain()) {
469       return true;
470     } else if (allow.second) {
471       // if does not match domain, should be check sub domain
472
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())) {
477         return true;
478       }
479     }
480   }
481
482   return result = false;
483 }
484
485 bool ResourceManager::CheckAllowNavigation(const std::string& url) {
486   // allow non-external resource
487   if (!utils::StartsWith(url, kSchemeTypeHttp) &&
488       !utils::StartsWith(url, kSchemeTypeHttps)) {
489     return true;
490   }
491
492   auto allow = application_data_->allowed_navigation_info();
493   if (allow.get() == NULL)
494     return false;
495
496   auto find = warp_cache_.find(url);
497   if (find != warp_cache_.end()) {
498     return find->second;
499   }
500
501   bool& result = warp_cache_[url];
502   result = true;
503
504   URL url_info(url);
505
506   // if didn't have a scheme, it means local resource
507   if (url_info.scheme().empty()) {
508     return true;
509   }
510
511   for (auto& allow_domain : allow->GetAllowedDomains()) {
512     URL a_domain_info(allow_domain);
513
514     // check wildcard *
515     if (a_domain_info.domain() == "*") {
516       return true;
517     }
518
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, "*.")) {
523       prefix_wild = true;
524       // *.domain.com -> .domain.com
525       a_domain = a_domain.substr(1);
526     }
527     if (utils::EndsWith(a_domain, ".*")) {
528       suffix_wild = true;
529       // domain.* -> domain.
530       a_domain = a_domain.substr(0, a_domain.length() - 1);
531     }
532
533     if (!prefix_wild && !suffix_wild) {
534       // if no wildcard, should be exactly matched
535       if (url_info.domain() == a_domain) {
536         return true;
537       }
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)) {
542         return true;
543       }
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)) {
547         return true;
548       }
549     } else if (prefix_wild && suffix_wild) {
550       // *.sample.* : it should be starts with sample. or can find ".sample."
551       // in url
552       if (utils::StartsWith(url_info.domain(), a_domain.substr(1)) ||
553           std::string::npos != url_info.domain().find(a_domain)) {
554         return true;
555       }
556     }
557   }
558
559   return result = false;
560 }
561
562 bool ResourceManager::IsEncrypted(const std::string& path) {
563   auto setting = application_data_->setting_info();
564   if (setting.get() == NULL)
565     return false;
566
567   if (setting->encryption_enabled()) {
568     std::string ext = utils::ExtName(path);
569     if (kEncryptedFileExtensions.count(ext) > 0) {
570       return true;
571     }
572   }
573   return false;
574 }
575
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));
581   }
582
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);
587
588   // checking web app type
589   static bool inited = false;
590   static bool is_global = false;
591   static bool is_preload = false;
592
593   std::string pkg_id = application_data_->pkg_id();
594   if (!inited) {
595     inited = true;
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 = "
600                     << pkg_id;
601       return path;
602     } else {
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 = "
606                       << pkg_id;
607         pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
608         return path;
609       }
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 = "
613                       << pkg_id;
614         pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
615         return path;
616       }
617       pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
618     }
619   }
620
621   struct stat buf;
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;
626
627     if (0 == file_size) {
628       LOGGER(ERROR) << src_path.c_str() << " size is 0, so decryption is skiped";
629       return path;
630     }
631
632     FILE *src = fopen(src_path.c_str(), "rb");
633     if (src == NULL) {
634       LOGGER(ERROR) << "Cannot open file for decryption: " << path;
635       return path;
636     }
637
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;
641
642     do {
643       unsigned char get_dec_size[5];
644       memset(get_dec_size, 0x00, sizeof(get_dec_size));
645
646       size_t read_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";
653           fclose(src);
654           return path;
655         }
656         in_chunk.reset(new unsigned char[read_buf_size]);
657
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;
663           int ret;
664           if (is_global) {
665             ret = wae_decrypt_global_web_application(pkg_id.c_str(),
666                                                      is_preload,
667                                                      in_chunk.get(),
668                                                      (int)dec_read_size,
669                                                      &decrypted_data,
670                                                      &decrypted_len);
671           } else {
672             ret = wae_decrypt_web_application(getuid(),
673                                               pkg_id.c_str(),
674                                               in_chunk.get(),
675                                               (int)dec_read_size,
676                                               &decrypted_data,
677                                               &decrypted_len);
678           }
679
680           if (WAE_ERROR_NONE != ret) {
681             LOGGER(ERROR) << "Error during decryption: ";
682             switch (ret) {
683             case WAE_ERROR_INVALID_PARAMETER:
684               LOGGER(ERROR) << "WAE_ERROR_INVALID_PARAMETER";
685               break;
686             case WAE_ERROR_PERMISSION_DENIED:
687               LOGGER(ERROR) << "WAE_ERROR_PERMISSION_DENIED";
688               break;
689             case WAE_ERROR_NO_KEY:
690               LOGGER(ERROR) << "WAE_ERROR_NO_KEY";
691               break;
692             case WAE_ERROR_KEY_MANAGER:
693               LOGGER(ERROR) << "WAE_ERROR_KEY_MANAGER";
694               break;
695             case WAE_ERROR_CRYPTO:
696               LOGGER(ERROR) << "WAE_ERROR_CRYPTO";
697               break;
698             case WAE_ERROR_UNKNOWN:
699               LOGGER(ERROR) << "WAE_ERROR_UNKNOWN";
700               break;
701             default:
702               LOGGER(ERROR) << "UNKNOWN";
703               break;
704             }
705             fclose(src);
706             return path;
707           }
708
709           memcpy(decrypted_str.get() + decrypted_size, decrypted_data, decrypted_len);
710           decrypted_size += decrypted_len;
711           std::free(decrypted_data);
712         }
713       }
714     } while(0 == std::feof(src));
715     fclose(src);
716     memset(decrypted_str.get() + decrypted_size, '\n', file_size - decrypted_size);
717
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;
723
724     decrypted_str.reset(new unsigned char[file_size]);
725
726     return dst_str.str();
727   }
728   return path;
729 }
730
731 }  // namespace common