1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
6 #include "xwalk/application/browser/application_protocols.h"
15 #include "base/files/file_path.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/string_util.h"
19 #include "base/threading/thread_restrictions.h"
20 #include "base/threading/worker_pool.h"
21 #include "base/threading/sequenced_worker_pool.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/resource_request_info.h"
24 #include "url/url_util.h"
25 #include "net/base/net_errors.h"
26 #include "net/http/http_response_headers.h"
27 #include "net/http/http_response_info.h"
28 #include "net/url_request/url_request_error_job.h"
29 #include "net/url_request/url_request_file_job.h"
30 #include "net/url_request/url_request_simple_job.h"
31 #include "xwalk/application/browser/application_service.h"
32 #include "xwalk/application/common/application_data.h"
33 #include "xwalk/application/common/application_file_util.h"
34 #include "xwalk/application/common/application_manifest_constants.h"
35 #include "xwalk/application/common/application_resource.h"
36 #include "xwalk/application/common/constants.h"
37 #include "xwalk/application/common/manifest_handlers/csp_handler.h"
38 #include "xwalk/runtime/common/xwalk_system_locale.h"
41 #include <ss_manager.h>
43 #include "base/files/file_util.h"
44 #include "base/task_runner.h"
45 #include "net/base/file_stream.h"
46 #include "net/base/io_buffer.h"
47 #include "net/base/mime_util.h"
48 #include "net/url_request/url_request.h"
49 #include "net/url_request/url_request_job.h"
50 #include "net/url_request/url_request_status.h"
52 #include "xwalk/application/common/manifest_handlers/tizen_setting_handler.h"
53 #include "xwalk/application/common/tizen/encryption.h"
56 using content::BrowserThread;
57 using content::ResourceRequestInfo;
61 namespace keys = application_manifest_keys;
63 namespace application {
67 net::HttpResponseHeaders* BuildHttpHeaders(
68 const std::string& content_security_policy,
69 const std::string& mime_type, const std::string& method,
70 const base::FilePath& file_path, const base::FilePath& relative_path,
71 bool is_authority_match) {
72 std::string raw_headers;
73 if (method == "GET") {
74 if (relative_path.empty())
75 raw_headers.append("HTTP/1.1 400 Bad Request");
76 else if (!is_authority_match)
77 raw_headers.append("HTTP/1.1 403 Forbidden");
78 else if (file_path.empty())
79 raw_headers.append("HTTP/1.1 404 Not Found");
81 raw_headers.append("HTTP/1.1 200 OK");
83 raw_headers.append("HTTP/1.1 501 Not Implemented");
86 if (!content_security_policy.empty()) {
87 raw_headers.append(1, '\0');
88 raw_headers.append("Content-Security-Policy: ");
89 raw_headers.append(content_security_policy);
92 raw_headers.append(1, '\0');
93 raw_headers.append("Access-Control-Allow-Origin: *");
95 if (!mime_type.empty()) {
96 raw_headers.append(1, '\0');
97 raw_headers.append("Content-Type: ");
98 raw_headers.append(mime_type);
101 raw_headers.append(2, '\0');
102 return new net::HttpResponseHeaders(raw_headers);
105 void ReadResourceFilePath(
106 const ApplicationResource& resource,
107 base::FilePath* file_path) {
108 *file_path = resource.GetFilePath();
111 class URLRequestApplicationJob : public net::URLRequestFileJob {
113 URLRequestApplicationJob(
114 net::URLRequest* request,
115 net::NetworkDelegate* network_delegate,
116 const scoped_refptr<base::TaskRunner>& file_task_runner,
117 const std::string& application_id,
118 const base::FilePath& directory_path,
119 const base::FilePath& relative_path,
120 const std::string& content_security_policy,
121 const std::list<std::string>& locales,
122 bool is_authority_match)
123 : net::URLRequestFileJob(
124 request, network_delegate, base::FilePath(), file_task_runner),
125 content_security_policy_(content_security_policy),
127 resource_(application_id, directory_path, relative_path),
128 relative_path_(relative_path),
129 is_authority_match_(is_authority_match),
130 weak_factory_(this) {
133 void GetResponseInfo(net::HttpResponseInfo* info) override {
134 std::string mime_type;
135 GetMimeType(&mime_type);
136 std::string method = request()->method();
137 response_info_.headers = BuildHttpHeaders(
138 content_security_policy_, mime_type, method, file_path_,
139 relative_path_, is_authority_match_);
140 *info = response_info_;
143 void Start() override {
144 base::FilePath* read_file_path = new base::FilePath;
146 resource_.SetLocales(locales_);
147 bool posted = base::WorkerPool::PostTaskAndReply(
149 base::Bind(&ReadResourceFilePath, resource_,
150 base::Unretained(read_file_path)),
151 base::Bind(&URLRequestApplicationJob::OnFilePathRead,
152 weak_factory_.GetWeakPtr(),
153 base::Owned(read_file_path)),
154 true /* task is slow */);
159 virtual ~URLRequestApplicationJob() {}
161 std::string content_security_policy_;
162 std::list<std::string> locales_;
163 ApplicationResource resource_;
164 base::FilePath relative_path_;
167 void OnFilePathRead(base::FilePath* read_file_path) {
168 file_path_ = *read_file_path;
169 if (file_path_.empty())
170 NotifyHeadersComplete();
172 URLRequestFileJob::Start();
175 net::HttpResponseInfo response_info_;
176 bool is_authority_match_;
177 base::WeakPtrFactory<URLRequestApplicationJob> weak_factory_;
180 #if defined(OS_TIZEN)
181 class URLRequestApplicationJobTizen : public URLRequestApplicationJob {
183 URLRequestApplicationJobTizen(
184 net::URLRequest* request,
185 net::NetworkDelegate* network_delegate,
186 const scoped_refptr<base::TaskRunner>& file_task_runner,
187 const std::string& application_id,
188 const base::FilePath& directory_path,
189 const base::FilePath& relative_path,
190 const std::string& content_security_policy,
191 const std::list<std::string>& locales,
192 bool is_authority_match,
194 : URLRequestApplicationJob(request, network_delegate, file_task_runner,
195 application_id, directory_path, relative_path,
196 content_security_policy, locales, is_authority_match),
197 file_task_runner_(file_task_runner),
198 stream_(new net::FileStream(file_task_runner)),
199 encrypted_(encrypted),
200 weak_ptr_factory_(this) {
203 void Start() override {
205 return URLRequestApplicationJob::Start();
206 base::FilePath* read_file_path = new base::FilePath;
207 resource_.SetLocales(locales_);
208 bool posted = base::WorkerPool::PostTaskAndReply(
210 base::Bind(&URLRequestApplicationJobTizen::ReadFilePath,
211 weak_ptr_factory_.GetWeakPtr(), resource_,
212 base::Unretained(read_file_path)),
213 base::Bind(&URLRequestApplicationJobTizen::DidReadFilePath,
214 weak_ptr_factory_.GetWeakPtr(), base::Owned(read_file_path)),
215 true /* task is slow */);
219 void Kill() override {
221 return URLRequestApplicationJob::Kill();
222 weak_ptr_factory_.InvalidateWeakPtrs();
223 URLRequestJob::Kill();
226 bool ReadRawData(net::IOBuffer* buf, int buf_size,
227 int* bytes_read) override {
229 return URLRequestApplicationJob::ReadRawData(buf, buf_size, bytes_read);
230 int remaining = plain_buffer_->size() - plain_buffer_offset_;
231 if (buf_size > remaining)
232 buf_size = remaining;
237 memcpy(buf->data(), plain_buffer_->data() + plain_buffer_offset_, buf_size);
238 plain_buffer_offset_ += buf_size;
239 *bytes_read = buf_size;
243 bool GetMimeType(std::string* mime_type) const override {
246 return URLRequestApplicationJob::GetMimeType(mime_type);
247 if (!mime_type_.empty()) {
248 *mime_type = mime_type_;
255 void ReadFilePath(const ApplicationResource& resource,
256 base::FilePath* file_path) {
257 *file_path = resource.GetFilePath();
260 void DidReadFilePath(
261 base::FilePath* read_file_path) {
262 file_path_ = *read_file_path;
263 if (file_path_.empty()) {
264 NotifyHeadersComplete();
267 base::File::Info* file_info = new base::File::Info();
268 file_task_runner_->PostTaskAndReply(FROM_HERE,
269 base::Bind(&URLRequestApplicationJobTizen::FetchFileInfo,
270 weak_ptr_factory_.GetWeakPtr(), file_path_,
271 base::Unretained(file_info)),
272 base::Bind(&URLRequestApplicationJobTizen::DidFetchFileInfo,
273 weak_ptr_factory_.GetWeakPtr(), base::Owned(file_info)));
276 void FetchFileInfo(const base::FilePath& file_path,
277 base::File::Info* file_info) {
278 base::GetFileInfo(file_path, file_info);
281 void DidFetchFileInfo(const base::File::Info* file_info) {
282 if (!file_info->size) {
283 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
284 net::ERR_FILE_NOT_FOUND));
288 net::GetMimeTypeFromFile(file_path_, &mime_type_);
289 cipher_buffer_ = new net::IOBufferWithSize(file_info->size);
290 int flags = base::File::FLAG_OPEN |
291 base::File::FLAG_READ |
292 base::File::FLAG_ASYNC;
293 int rv = stream_->Open(file_path_, flags,
294 base::Bind(&URLRequestApplicationJobTizen::DidOpen,
295 weak_ptr_factory_.GetWeakPtr()));
296 if (rv != net::ERR_IO_PENDING)
300 void DidOpen(int result) {
302 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
305 int rv = stream_->Read(
306 cipher_buffer_.get(),
307 cipher_buffer_->size(),
308 base::Bind(&URLRequestApplicationJobTizen::DidReadEncryptedData,
309 weak_ptr_factory_.GetWeakPtr(), cipher_buffer_));
310 if (rv != net::ERR_IO_PENDING)
311 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, rv));
314 void DidReadEncryptedData(scoped_refptr<net::IOBufferWithSize> buf,
317 std::string plain_text;
319 const char* filename = resource_.application_id().c_str();
321 ssm_getinfo(filename, &sfi, SSM_FLAG_DATA, filename);
322 char* data = static_cast<char*>(
323 malloc(sizeof(char) * (sfi.originSize + 1)));
324 memset(data, 0x00, (sfi.originSize + 1));
325 int ret = ssm_read(filename, data, sfi.originSize, &read_len,
326 SSM_FLAG_SECRET_OPERATION, filename);
327 std::string key_str(data, read_len);
329 if (!ret && !DecryptData(buf->data(), buf->size(), key_str, &plain_text)) {
330 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
331 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
334 plain_buffer_ = new net::StringIOBuffer(plain_text);
335 set_expected_content_size(plain_buffer_->size());
336 plain_buffer_offset_ = 0;
337 NotifyHeadersComplete();
340 const scoped_refptr<base::TaskRunner> file_task_runner_;
341 scoped_ptr<net::FileStream> stream_;
343 scoped_refptr<net::IOBufferWithSize> cipher_buffer_;
344 scoped_refptr<net::StringIOBuffer> plain_buffer_;
345 int plain_buffer_offset_;
346 std::string mime_type_;
347 base::WeakPtrFactory<URLRequestApplicationJobTizen> weak_ptr_factory_;
351 // This class is a thread-safe cache of active application's data.
352 // This class is used by ApplicationProtocolHandler as it lives on IO thread
353 // and hence cannot access ApplicationService directly.
354 class ApplicationDataCache : public ApplicationService::Observer {
356 scoped_refptr<ApplicationData> GetApplicationData(
357 const std::string& application_id) const {
358 base::AutoLock lock(lock_);
359 ApplicationData::ApplicationDataMap::const_iterator it =
360 cache_.find(application_id);
361 if (it != cache_.end()) {
367 void DidLaunchApplication(Application* app) override {
368 base::AutoLock lock(lock_);
369 cache_.insert(std::pair<std::string, scoped_refptr<ApplicationData> >(
370 app->id(), app->data()));
373 void WillDestroyApplication(Application* app) override {
374 base::AutoLock lock(lock_);
375 cache_.erase(app->id());
379 ApplicationData::ApplicationDataMap cache_;
380 mutable base::Lock lock_;
383 class ApplicationProtocolHandler
384 : public net::URLRequestJobFactory::ProtocolHandler {
386 explicit ApplicationProtocolHandler(ApplicationService* service) {
388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
389 // ApplicationProtocolHandler lives longer than ApplicationService,
390 // so we do not need to remove cache_ from ApplicationService
392 service->AddObserver(&cache_);
395 virtual ~ApplicationProtocolHandler() {}
397 net::URLRequestJob* MaybeCreateJob(
398 net::URLRequest* request,
399 net::NetworkDelegate* network_delegate) const override;
402 ApplicationDataCache cache_;
403 DISALLOW_COPY_AND_ASSIGN(ApplicationProtocolHandler);
406 // The |locale| should be expanded to user agent locale.
407 // Such as, "en-us" will be expaned as "en-us, en".
408 void GetUserAgentLocales(const std::string& sys_locale,
409 std::list<std::string>& ua_locales) { // NOLINT
410 if (sys_locale.empty())
413 std::string locale = base::StringToLowerASCII(sys_locale);
416 ua_locales.push_back(locale);
417 position = locale.find_last_of("-");
418 locale = locale.substr(0, position);
419 } while (position != std::string::npos);
423 ApplicationProtocolHandler::MaybeCreateJob(
424 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
425 const std::string& application_id = request->url().host();
426 scoped_refptr<ApplicationData> application =
427 cache_.GetApplicationData(application_id);
429 if (!application.get())
430 return new net::URLRequestErrorJob(
431 request, network_delegate, net::ERR_FILE_NOT_FOUND);
433 base::FilePath relative_path =
434 ApplicationURLToRelativeFilePath(request->url());
435 base::FilePath directory_path;
436 std::string content_security_policy;
437 if (application.get()) {
438 directory_path = application->path();
440 const char* csp_key = GetCSPKey(application->manifest_type());
441 const CSPInfo* csp_info = static_cast<CSPInfo*>(
442 application->GetManifestData(csp_key));
444 const std::map<std::string, std::vector<std::string> >& policies =
445 csp_info->GetDirectives();
446 std::map<std::string, std::vector<std::string> >::const_iterator it =
448 for (; it != policies.end(); ++it) {
449 content_security_policy.append(
450 it->first + ' ' + JoinString(it->second, ' ') + ';');
455 std::list<std::string> locales;
456 if (application.get() &&
457 application->manifest_type() == Manifest::TYPE_WIDGET) {
458 GetUserAgentLocales(GetSystemLocale(), locales);
459 GetUserAgentLocales(application->GetManifest()->default_locale(), locales);
462 #if defined(OS_TIZEN)
463 TizenSettingInfo* info = static_cast<TizenSettingInfo*>(
464 application->GetManifestData(application_widget_keys::kTizenSettingKey));
465 bool encrypted = info &&
466 info->encryption_enabled() &&
467 RequiresEncryption(relative_path);
468 return new URLRequestApplicationJobTizen(
471 content::BrowserThread::GetBlockingPool()->
472 GetTaskRunnerWithShutdownBehavior(
473 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
477 content_security_policy,
482 return new URLRequestApplicationJob(
485 content::BrowserThread::GetBlockingPool()->
486 GetTaskRunnerWithShutdownBehavior(
487 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
491 content_security_policy,
499 linked_ptr<net::URLRequestJobFactory::ProtocolHandler>
500 CreateApplicationProtocolHandler(ApplicationService* service) {
501 return linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
502 new ApplicationProtocolHandler(service));
505 } // namespace application