Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / xwalk / application / browser / application_protocols.cc
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.
5
6 #include "xwalk/application/browser/application_protocols.h"
7
8 #include <algorithm>
9 #include <map>
10 #include <list>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
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"
39
40 #if defined(OS_TIZEN)
41 #include <ss_manager.h>
42
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"
51
52 #include "xwalk/application/common/manifest_handlers/tizen_setting_handler.h"
53 #include "xwalk/application/common/tizen/encryption.h"
54 #endif
55
56 using content::BrowserThread;
57 using content::ResourceRequestInfo;
58
59 namespace xwalk {
60
61 namespace keys = application_manifest_keys;
62
63 namespace application {
64
65 namespace {
66
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");
80     else
81       raw_headers.append("HTTP/1.1 200 OK");
82   } else {
83     raw_headers.append("HTTP/1.1 501 Not Implemented");
84   }
85
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);
90   }
91
92   raw_headers.append(1, '\0');
93   raw_headers.append("Access-Control-Allow-Origin: *");
94
95   if (!mime_type.empty()) {
96     raw_headers.append(1, '\0');
97     raw_headers.append("Content-Type: ");
98     raw_headers.append(mime_type);
99   }
100
101   raw_headers.append(2, '\0');
102   return new net::HttpResponseHeaders(raw_headers);
103 }
104
105 void ReadResourceFilePath(
106     const ApplicationResource& resource,
107     base::FilePath* file_path) {
108   *file_path = resource.GetFilePath();
109 }
110
111 class URLRequestApplicationJob : public net::URLRequestFileJob {
112  public:
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),
126         locales_(locales),
127         resource_(application_id, directory_path, relative_path),
128         relative_path_(relative_path),
129         is_authority_match_(is_authority_match),
130         weak_factory_(this) {
131   }
132
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_;
141   }
142
143   void Start() override {
144     base::FilePath* read_file_path = new base::FilePath;
145
146     resource_.SetLocales(locales_);
147     bool posted = base::WorkerPool::PostTaskAndReply(
148         FROM_HERE,
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 */);
155     DCHECK(posted);
156   }
157
158  protected:
159   virtual ~URLRequestApplicationJob() {}
160
161   std::string content_security_policy_;
162   std::list<std::string> locales_;
163   ApplicationResource resource_;
164   base::FilePath relative_path_;
165
166  private:
167   void OnFilePathRead(base::FilePath* read_file_path) {
168     file_path_ = *read_file_path;
169     if (file_path_.empty())
170       NotifyHeadersComplete();
171     else
172       URLRequestFileJob::Start();
173   }
174
175   net::HttpResponseInfo response_info_;
176   bool is_authority_match_;
177   base::WeakPtrFactory<URLRequestApplicationJob> weak_factory_;
178 };
179
180 #if defined(OS_TIZEN)
181 class URLRequestApplicationJobTizen : public URLRequestApplicationJob {
182  public:
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,
193       bool encrypted)
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) {
201   }
202
203   void Start() override {
204     if (!encrypted_)
205       return URLRequestApplicationJob::Start();
206     base::FilePath* read_file_path = new base::FilePath;
207     resource_.SetLocales(locales_);
208     bool posted = base::WorkerPool::PostTaskAndReply(
209         FROM_HERE,
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 */);
216     DCHECK(posted);
217   }
218
219   void Kill() override {
220     if (!encrypted_)
221       return URLRequestApplicationJob::Kill();
222     weak_ptr_factory_.InvalidateWeakPtrs();
223     URLRequestJob::Kill();
224   }
225
226   bool ReadRawData(net::IOBuffer* buf, int buf_size,
227       int* bytes_read) override {
228     if (!encrypted_)
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;
233     if (!buf_size) {
234       *bytes_read = 0;
235       return true;
236     }
237     memcpy(buf->data(), plain_buffer_->data() + plain_buffer_offset_, buf_size);
238     plain_buffer_offset_ += buf_size;
239     *bytes_read = buf_size;
240     return true;
241   }
242
243   bool GetMimeType(std::string* mime_type) const override {
244     DCHECK(mime_type);
245     if (!encrypted_)
246       return URLRequestApplicationJob::GetMimeType(mime_type);
247     if (!mime_type_.empty()) {
248       *mime_type = mime_type_;
249       return true;
250     }
251     return false;
252   }
253
254  private:
255   void ReadFilePath(const ApplicationResource& resource,
256       base::FilePath* file_path) {
257     *file_path = resource.GetFilePath();
258   }
259
260   void DidReadFilePath(
261       base::FilePath* read_file_path) {
262     file_path_ = *read_file_path;
263     if (file_path_.empty()) {
264       NotifyHeadersComplete();
265       return;
266     }
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)));
274   }
275
276   void FetchFileInfo(const base::FilePath& file_path,
277       base::File::Info* file_info) {
278     base::GetFileInfo(file_path, file_info);
279   }
280
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));
285       return;
286     }
287
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)
297       DidOpen(rv);
298   }
299
300   void DidOpen(int result) {
301     if (result) {
302       NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, result));
303       return;
304     }
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));
312   }
313
314   void DidReadEncryptedData(scoped_refptr<net::IOBufferWithSize> buf,
315       int result) {
316     stream_.reset();
317     std::string plain_text;
318     size_t read_len = 0;
319     const char* filename = resource_.application_id().c_str();
320     ssm_file_info_t sfi;
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);
328     free(data);
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));
332       return;
333     }
334     plain_buffer_ = new net::StringIOBuffer(plain_text);
335     set_expected_content_size(plain_buffer_->size());
336     plain_buffer_offset_ = 0;
337     NotifyHeadersComplete();
338   }
339
340   const scoped_refptr<base::TaskRunner> file_task_runner_;
341   scoped_ptr<net::FileStream> stream_;
342   bool encrypted_;
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_;
348 };
349 #endif
350
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 {
355  public:
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()) {
362       return it->second;
363     }
364     return NULL;
365   }
366
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()));
371   }
372
373   void WillDestroyApplication(Application* app) override {
374     base::AutoLock lock(lock_);
375     cache_.erase(app->id());
376   }
377
378  private:
379   ApplicationData::ApplicationDataMap cache_;
380   mutable base::Lock lock_;
381 };
382
383 class ApplicationProtocolHandler
384     : public net::URLRequestJobFactory::ProtocolHandler {
385  public:
386   explicit ApplicationProtocolHandler(ApplicationService* service) {
387     DCHECK(service);
388     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
389     // ApplicationProtocolHandler lives longer than ApplicationService,
390     // so we do not need to remove cache_ from ApplicationService
391     // observers list.
392     service->AddObserver(&cache_);
393   }
394
395   virtual ~ApplicationProtocolHandler() {}
396
397   net::URLRequestJob* MaybeCreateJob(
398       net::URLRequest* request,
399       net::NetworkDelegate* network_delegate) const override;
400
401  private:
402   ApplicationDataCache cache_;
403   DISALLOW_COPY_AND_ASSIGN(ApplicationProtocolHandler);
404 };
405
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())
411     return;
412
413   std::string locale = base::StringToLowerASCII(sys_locale);
414   size_t position;
415   do {
416     ua_locales.push_back(locale);
417     position = locale.find_last_of("-");
418     locale = locale.substr(0, position);
419   } while (position != std::string::npos);
420 }
421
422 net::URLRequestJob*
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);
428
429   if (!application.get())
430     return new net::URLRequestErrorJob(
431         request, network_delegate, net::ERR_FILE_NOT_FOUND);
432
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();
439
440     const char* csp_key = GetCSPKey(application->manifest_type());
441     const CSPInfo* csp_info = static_cast<CSPInfo*>(
442           application->GetManifestData(csp_key));
443     if (csp_info) {
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 =
447           policies.begin();
448       for (; it != policies.end(); ++it) {
449         content_security_policy.append(
450             it->first + ' ' + JoinString(it->second, ' ') + ';');
451       }
452     }
453   }
454
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);
460   }
461
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(
469       request,
470       network_delegate,
471       content::BrowserThread::GetBlockingPool()->
472       GetTaskRunnerWithShutdownBehavior(
473           base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
474       application_id,
475       directory_path,
476       relative_path,
477       content_security_policy,
478       locales,
479       application.get(),
480       encrypted);
481 #else
482     return new URLRequestApplicationJob(
483         request,
484         network_delegate,
485         content::BrowserThread::GetBlockingPool()->
486         GetTaskRunnerWithShutdownBehavior(
487             base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
488         application_id,
489         directory_path,
490         relative_path,
491         content_security_policy,
492         locales,
493         application.get());
494 #endif
495 }
496
497 }  // namespace
498
499 linked_ptr<net::URLRequestJobFactory::ProtocolHandler>
500 CreateApplicationProtocolHandler(ApplicationService* service) {
501   return linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
502       new ApplicationProtocolHandler(service));
503 }
504
505 }  // namespace application
506 }  // namespace xwalk