667db3f1dc7207a86d4a7eb343672a4added3983
[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 (request_uri.empty())
122     return info_uri.empty();
123
124   std::string info_scheme = utils::SchemeName(info_uri);
125
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;
131   }
132
133   if (utils::EndsWith(info_uri, "*")) {
134     return utils::StartsWith(request_uri, info_uri.substr(0,
135                                                           info_uri.length()-1));
136   } else {
137     return request_uri == info_uri;
138   }
139 }
140
141 static std::string InsertPrefixPath(const std::string& start_uri) {
142   if (start_uri.find("://") != std::string::npos)
143     return start_uri;
144   else
145     return std::string(kSchemeTypeFile) + "/" + start_uri;
146 }
147
148 }  // namespace
149
150 ResourceManager::Resource::Resource(const std::string& uri)
151   : uri_(uri), should_reset_(true), encoding_(kDefaultEncoding) {}
152
153 ResourceManager::Resource::Resource(const std::string& uri, bool should_reset)
154   : uri_(uri), should_reset_(should_reset), encoding_(kDefaultEncoding) {}
155
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) {}
160
161 ResourceManager::Resource::Resource(const ResourceManager::Resource& res) {
162   *this = res;
163 }
164
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();
171   return *this;
172 }
173
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()) {
178     return true;
179   } else {
180     return false;
181   }
182 }
183
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;
195     } else {
196       security_model_version_ = 1;
197     }
198   }
199 }
200
201 std::unique_ptr<ResourceManager::Resource>
202 ResourceManager::GetDefaultResource() {
203   std::string src;
204   std::string type;
205   std::string encoding = kDefaultEncoding;
206
207   std::shared_ptr<const wgt::parse::ContentInfo> content_info;
208   if (application_data_) {
209     content_info = application_data_->content_info();
210     if (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;
217     } else {
218       LOGGER(DEBUG) << "content_info is null";
219     }
220   }
221
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));
227   }
228
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));
236       }
237     }
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));
241   }
242
243   return std::unique_ptr<Resource>(new Resource(InsertPrefixPath(src),
244                                                 type, encoding));
245 }
246
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));
253   }
254   return GetDefaultResource();
255 }
256
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();
263   }
264
265   std::string mime = app_control->mime();
266   std::string uri = app_control->uri();
267   if (mime.empty() && !uri.empty()) {
268     mime = GetMimeFromUri(uri);
269   }
270
271   LOGGER(DEBUG) << "Passed AppControl data";
272   LOGGER(DEBUG) << " - operation : " << operation;
273   LOGGER(DEBUG) << " - mimetype  : " << mime;
274   LOGGER(DEBUG) << " - uri       : " << uri;
275
276   if (application_data_ == NULL ||
277       application_data_->app_control_info_list() == NULL) {
278     return GetDefaultResource();
279   }
280
281   auto app_control_list =
282     application_data_->app_control_info_list()->controls;
283
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); });
289
290   if (iter != app_control_list.end()) {
291     return GetMatchedResource(*iter);
292   } else {
293   return GetDefaultResource();
294   }
295 }
296
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()) {
303     return find->second;
304   }
305   std::string& result = locale_cache_[origin];
306   result = origin;
307   std::string url = origin;
308
309   std::string suffix;
310   size_t pos = url.find_first_of("#?");
311   if (pos != std::string::npos) {
312     suffix = url.substr(pos);
313     url.resize(pos);
314   }
315
316   if (utils::StartsWith(url, app_scheme)) {
317     // remove "app://"
318     url.erase(0, app_scheme.length());
319
320     // remove app id + /
321     std::string check = appid_ + "/";
322     if (utils::StartsWith(url, check)) {
323       url.erase(0, check.length());
324     } else {
325       LOGGER(ERROR) << "Invalid uri: {scheme:app} uri=" << origin;
326       return result;
327     }
328   } else if (utils::StartsWith(url, file_scheme)) {
329     // remove "file:///"
330     url.erase(0, file_scheme.length());
331   }
332
333   if (!url.empty() && url[url.length()-1] == '/') {
334       url.erase(url.length()-1, 1);
335   }
336
337   if (url.empty()) {
338     LOGGER(ERROR) << "Invalid uri: uri=" << origin;
339     return result;
340   }
341
342   std::string file_path = utils::UrlDecode(RemoveLocalePath(url));
343   for (auto& locales : locale_manager_->system_locales()) {
344     // check ../locales/
345     std::string app_locale_path = resource_base_path_ + locale_path;
346     if (!Exists(app_locale_path)) {
347       break;
348     }
349
350     // check locale path ../locales/en_us/
351     std::string app_localized_path = app_locale_path + locales + "/";
352     if (!Exists(app_localized_path)) {
353       continue;
354     }
355     std::string resource_path = app_localized_path + file_path;
356     if (Exists(resource_path)) {
357       result = "file://" + resource_path + suffix;
358       return result;
359     }
360   }
361
362   std::string default_locale = resource_base_path_ + file_path;
363   if (Exists(default_locale)) {
364     result = "file://" + default_locale + suffix;
365     return result;
366   }
367
368   LOGGER(ERROR) << "Invalid uri: uri=" << origin << ", decoded=" << file_path;
369   return result;
370 }
371
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_)) {
376     return path;
377   }
378
379   result_path = result_path.substr(resource_base_path_.length());
380   if (!utils::StartsWith(result_path, locale_path)) {
381     return result_path;
382   }
383
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);
387   }
388   return result_path;
389 }
390
391 void ResourceManager::set_base_resource_path(const std::string& path) {
392   if (path.empty()) {
393     return;
394   }
395
396   resource_base_path_ = path;
397   if (resource_base_path_[resource_base_path_.length()-1] != '/') {
398     resource_base_path_ += "/";
399   }
400 }
401
402 bool ResourceManager::Exists(const std::string& path) {
403   auto find = file_existed_cache_.find(path);
404   if (find != file_existed_cache_.end()) {
405     return find->second;
406   }
407   bool ret = file_existed_cache_[path] = utils::Exists(path);
408   return ret;
409 }
410
411 bool ResourceManager::AllowNavigation(const std::string& url) {
412   if (security_model_version_ == 2)
413     return CheckAllowNavigation(url);
414   return CheckWARP(url);
415 }
416
417 bool ResourceManager::AllowedResource(const std::string& url) {
418   if (security_model_version_ == 2)
419     return true;
420   return CheckWARP(url);
421 }
422
423 bool ResourceManager::CheckWARP(const std::string& url) {
424   // allow non-external resource
425   if (!utils::StartsWith(url, kSchemeTypeHttp) &&
426       !utils::StartsWith(url, kSchemeTypeHttps)) {
427     return true;
428   }
429
430   auto warp = application_data_->warp_info();
431   if (warp.get() == NULL)
432     return false;
433
434   auto find = warp_cache_.find(url);
435   if (find != warp_cache_.end()) {
436     return find->second;
437   }
438
439   bool& result = warp_cache_[url];
440   result = true;
441
442   URL url_info(url);
443
444   // if didn't have a scheme, it means local resource
445   if (url_info.scheme().empty()) {
446     return true;
447   }
448
449   for (auto& allow : warp->access_map()) {
450     if (allow.first == "*") {
451       return true;
452     } else if (allow.first.empty()) {
453       continue;
454     }
455
456     URL allow_url(allow.first);
457
458     // should be match the scheme and port
459     if (allow_url.scheme() != url_info.scheme() ||
460         allow_url.port() != url_info.port()) {
461       continue;
462     }
463
464     // if domain alos was matched, allow resource
465     if (allow_url.domain() == url_info.domain()) {
466       return true;
467     } else if (allow.second) {
468       // if does not match domain, should be check sub domain
469
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())) {
474         return true;
475       }
476     }
477   }
478
479   return result = false;
480 }
481
482 bool ResourceManager::CheckAllowNavigation(const std::string& url) {
483   // allow non-external resource
484   if (!utils::StartsWith(url, kSchemeTypeHttp) &&
485       !utils::StartsWith(url, kSchemeTypeHttps)) {
486     return true;
487   }
488
489   auto allow = application_data_->allowed_navigation_info();
490   if (allow.get() == NULL)
491     return false;
492
493   auto find = warp_cache_.find(url);
494   if (find != warp_cache_.end()) {
495     return find->second;
496   }
497
498   bool& result = warp_cache_[url];
499   result = true;
500
501   URL url_info(url);
502
503   // if didn't have a scheme, it means local resource
504   if (url_info.scheme().empty()) {
505     return true;
506   }
507
508   for (auto& allow_domain : allow->GetAllowedDomains()) {
509     URL a_domain_info(allow_domain);
510
511     // check wildcard *
512     if (a_domain_info.domain() == "*") {
513       return true;
514     }
515
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, "*.")) {
520       prefix_wild = true;
521       // *.domain.com -> .domain.com
522       a_domain = a_domain.substr(1);
523     }
524     if (utils::EndsWith(a_domain, ".*")) {
525       suffix_wild = true;
526       // domain.* -> domain.
527       a_domain = a_domain.substr(0, a_domain.length() - 1);
528     }
529
530     if (!prefix_wild && !suffix_wild) {
531       // if no wildcard, should be exactly matched
532       if (url_info.domain() == a_domain) {
533         return true;
534       }
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)) {
539         return true;
540       }
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)) {
544         return true;
545       }
546     } else if (prefix_wild && suffix_wild) {
547       // *.sample.* : it should be starts with sample. or can find ".sample."
548       // in url
549       if (utils::StartsWith(url_info.domain(), a_domain.substr(1)) ||
550           std::string::npos != url_info.domain().find(a_domain)) {
551         return true;
552       }
553     }
554   }
555
556   return result = false;
557 }
558
559 bool ResourceManager::IsEncrypted(const std::string& path) {
560   auto setting = application_data_->setting_info();
561   if (setting.get() == NULL)
562     return false;
563
564   if (setting->encryption_enabled()) {
565     std::string ext = utils::ExtName(path);
566     if (kEncryptedFileExtensions.count(ext) > 0) {
567       return true;
568     }
569   }
570   return false;
571 }
572
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));
578   }
579
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);
584
585   // checking web app type
586   static bool inited = false;
587   static bool is_global = false;
588   static bool is_preload = false;
589
590   std::string pkg_id = application_data_->pkg_id();
591   if (!inited) {
592     inited = true;
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 = "
597                     << pkg_id;
598       return path;
599     } else {
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 = "
603                       << pkg_id;
604         pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
605         return path;
606       }
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 = "
610                       << pkg_id;
611         pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
612         return path;
613       }
614       pkgmgrinfo_pkginfo_destroy_pkginfo(handle);
615     }
616   }
617
618   struct stat buf;
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;
623
624     if (0 == file_size) {
625       LOGGER(ERROR) << src_path.c_str() << " size is 0, so decryption is skiped";
626       return path;
627     }
628
629     FILE *src = fopen(src_path.c_str(), "rb");
630     if (src == NULL) {
631       LOGGER(ERROR) << "Cannot open file for decryption: " << path;
632       return path;
633     }
634
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;
638
639     do {
640       unsigned char get_dec_size[5];
641       memset(get_dec_size, 0x00, sizeof(get_dec_size));
642
643       size_t read_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";
650           fclose(src);
651           return path;
652         }
653         in_chunk.reset(new unsigned char[read_buf_size]);
654
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;
660           int ret;
661           if (is_global) {
662             ret = wae_decrypt_global_web_application(pkg_id.c_str(),
663                                                      is_preload,
664                                                      in_chunk.get(),
665                                                      (int)dec_read_size,
666                                                      &decrypted_data,
667                                                      &decrypted_len);
668           } else {
669             ret = wae_decrypt_web_application(getuid(),
670                                               pkg_id.c_str(),
671                                               in_chunk.get(),
672                                               (int)dec_read_size,
673                                               &decrypted_data,
674                                               &decrypted_len);
675           }
676
677           if (WAE_ERROR_NONE != ret) {
678             LOGGER(ERROR) << "Error during decryption: ";
679             switch (ret) {
680             case WAE_ERROR_INVALID_PARAMETER:
681               LOGGER(ERROR) << "WAE_ERROR_INVALID_PARAMETER";
682               break;
683             case WAE_ERROR_PERMISSION_DENIED:
684               LOGGER(ERROR) << "WAE_ERROR_PERMISSION_DENIED";
685               break;
686             case WAE_ERROR_NO_KEY:
687               LOGGER(ERROR) << "WAE_ERROR_NO_KEY";
688               break;
689             case WAE_ERROR_KEY_MANAGER:
690               LOGGER(ERROR) << "WAE_ERROR_KEY_MANAGER";
691               break;
692             case WAE_ERROR_CRYPTO:
693               LOGGER(ERROR) << "WAE_ERROR_CRYPTO";
694               break;
695             case WAE_ERROR_UNKNOWN:
696               LOGGER(ERROR) << "WAE_ERROR_UNKNOWN";
697               break;
698             default:
699               LOGGER(ERROR) << "UNKNOWN";
700               break;
701             }
702             fclose(src);
703             return path;
704           }
705
706           memcpy(decrypted_str.get() + decrypted_size, decrypted_data, decrypted_len);
707           decrypted_size += decrypted_len;
708           std::free(decrypted_data);
709         }
710       }
711     } while(0 == std::feof(src));
712     fclose(src);
713     memset(decrypted_str.get() + decrypted_size, '\n', file_size - decrypted_size);
714
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;
720
721     decrypted_str.reset(new unsigned char[file_size]);
722
723     return dst_str.str();
724   }
725   return path;
726 }
727
728 }  // namespace common