1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/pepper/url_request_info_util.h"
6
7#include "base/logging.h"
8#include "base/strings/string_util.h"
9#include "content/common/fileapi/file_system_messages.h"
10#include "content/renderer/pepper/common.h"
11#include "content/renderer/pepper/plugin_module.h"
12#include "content/renderer/pepper/ppb_file_ref_impl.h"
13#include "content/renderer/render_thread_impl.h"
14#include "net/http/http_util.h"
15#include "ppapi/shared_impl/url_request_info_data.h"
16#include "ppapi/shared_impl/var.h"
17#include "ppapi/thunk/enter.h"
18#include "third_party/WebKit/public/platform/WebData.h"
19#include "third_party/WebKit/public/platform/WebHTTPBody.h"
20#include "third_party/WebKit/public/platform/WebURL.h"
21#include "third_party/WebKit/public/platform/WebURLRequest.h"
22#include "third_party/WebKit/public/web/WebDocument.h"
23#include "third_party/WebKit/public/web/WebFrame.h"
24#include "url/gurl.h"
25#include "url/url_util.h"
26#include "webkit/child/weburlrequest_extradata_impl.h"
27
28using ppapi::URLRequestInfoData;
29using ppapi::Resource;
30using ppapi::thunk::EnterResourceNoLock;
31using ppapi::thunk::PPB_FileRef_API;
32using WebKit::WebData;
33using WebKit::WebHTTPBody;
34using WebKit::WebString;
35using WebKit::WebFrame;
36using WebKit::WebURL;
37using WebKit::WebURLRequest;
38
39namespace content {
40
41namespace {
42
43// Appends the file ref given the Resource pointer associated with it to the
44// given HTTP body, returning true on success.
45bool AppendFileRefToBody(
46    Resource* file_ref_resource,
47    int64_t start_offset,
48    int64_t number_of_bytes,
49    PP_Time expected_last_modified_time,
50    WebHTTPBody *http_body) {
51  // Get the underlying file ref impl.
52  if (!file_ref_resource)
53    return false;
54  PPB_FileRef_API* file_ref_api = file_ref_resource->AsPPB_FileRef_API();
55  if (!file_ref_api)
56    return false;
57  const PPB_FileRef_Impl* file_ref =
58      static_cast<PPB_FileRef_Impl*>(file_ref_api);
59
60  base::FilePath platform_path;
61  switch (file_ref->GetFileSystemType()) {
62    case PP_FILESYSTEMTYPE_LOCALTEMPORARY:
63    case PP_FILESYSTEMTYPE_LOCALPERSISTENT:
64      // TODO(kinuko): remove this sync IPC when we fully support
65      // AppendURLRange for FileSystem URL.
66      RenderThreadImpl::current()->Send(
67          new FileSystemHostMsg_SyncGetPlatformPath(
68              file_ref->GetFileSystemURL(), &platform_path));
69      break;
70    case PP_FILESYSTEMTYPE_EXTERNAL:
71      platform_path = file_ref->GetSystemPath();
72      break;
73    default:
74      NOTREACHED();
75  }
76  http_body->appendFileRange(
77      platform_path.AsUTF16Unsafe(),
78      start_offset,
79      number_of_bytes,
80      expected_last_modified_time);
81  return true;
82}
83
84// Checks that the request data is valid. Returns false on failure. Note that
85// method and header validation is done by the URL loader when the request is
86// opened, and any access errors are returned asynchronously.
87bool ValidateURLRequestData(const ppapi::URLRequestInfoData& data) {
88  if (data.prefetch_buffer_lower_threshold < 0 ||
89      data.prefetch_buffer_upper_threshold < 0 ||
90      data.prefetch_buffer_upper_threshold <=
91      data.prefetch_buffer_lower_threshold) {
92    return false;
93  }
94  return true;
95}
96
97// Ensures that the file_ref members of the given request info data are
98// populated from the resource IDs. Returns true on success.
99bool EnsureFileRefObjectsPopulated(ppapi::URLRequestInfoData* data) {
100  // Get the Resource objects for any file refs with only host resource (this
101  // is the state of the request as it comes off IPC).
102  for (size_t i = 0; i < data->body.size(); ++i) {
103    URLRequestInfoData::BodyItem& item = data->body[i];
104    if (item.is_file && !item.file_ref.get()) {
105      EnterResourceNoLock<PPB_FileRef_API> enter(
106          item.file_ref_host_resource.host_resource(), false);
107      if (!enter.succeeded())
108        return false;
109      item.file_ref = enter.resource();
110    }
111  }
112  return true;
113}
114
115}  // namespace
116
117bool CreateWebURLRequest(ppapi::URLRequestInfoData* data,
118                         WebFrame* frame,
119                         WebURLRequest* dest) {
120  // In the out-of-process case, we've received the URLRequestInfoData
121  // from the untrusted plugin and done no validation on it. We need to be
122  // sure it's not being malicious by checking everything for consistency.
123  if (!ValidateURLRequestData(*data) || !EnsureFileRefObjectsPopulated(data))
124    return false;
125
126  dest->initialize();
127  dest->setURL(frame->document().completeURL(WebString::fromUTF8(
128      data->url)));
129  dest->setDownloadToFile(data->stream_to_file);
130  dest->setReportUploadProgress(data->record_upload_progress);
131
132  if (!data->method.empty())
133    dest->setHTTPMethod(WebString::fromUTF8(data->method));
134
135  dest->setFirstPartyForCookies(frame->document().firstPartyForCookies());
136
137  const std::string& headers = data->headers;
138  if (!headers.empty()) {
139    net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n\r");
140    while (it.GetNext()) {
141      dest->addHTTPHeaderField(
142          WebString::fromUTF8(it.name()),
143          WebString::fromUTF8(it.values()));
144    }
145  }
146
147  // Append the upload data.
148  if (!data->body.empty()) {
149    WebHTTPBody http_body;
150    http_body.initialize();
151    for (size_t i = 0; i < data->body.size(); ++i) {
152      const URLRequestInfoData::BodyItem& item = data->body[i];
153      if (item.is_file) {
154        if (!AppendFileRefToBody(item.file_ref.get(),
155                                 item.start_offset,
156                                 item.number_of_bytes,
157                                 item.expected_last_modified_time,
158                                 &http_body))
159          return false;
160      } else {
161        DCHECK(!item.data.empty());
162        http_body.appendData(WebData(item.data));
163      }
164    }
165    dest->setHTTPBody(http_body);
166  }
167
168  // Add the "Referer" header if there is a custom referrer. Such requests
169  // require universal access. For all other requests, "Referer" will be set
170  // after header security checks are done in AssociatedURLLoader.
171  if (data->has_custom_referrer_url && !data->custom_referrer_url.empty())
172    frame->setReferrerForRequest(*dest, GURL(data->custom_referrer_url));
173
174  if (data->has_custom_content_transfer_encoding &&
175      !data->custom_content_transfer_encoding.empty()) {
176    dest->addHTTPHeaderField(
177        WebString::fromUTF8("Content-Transfer-Encoding"),
178        WebString::fromUTF8(data->custom_content_transfer_encoding));
179  }
180
181  if (data->has_custom_user_agent) {
182    bool was_after_preconnect_request = false;
183    dest->setExtraData(new webkit_glue::WebURLRequestExtraDataImpl(
184        WebKit::WebReferrerPolicyDefault,  // Ignored.
185        WebString::fromUTF8(data->custom_user_agent),
186        was_after_preconnect_request));
187  }
188
189  return true;
190}
191
192bool URLRequestRequiresUniversalAccess(const ppapi::URLRequestInfoData& data) {
193  return
194      data.has_custom_referrer_url ||
195      data.has_custom_content_transfer_encoding ||
196      data.has_custom_user_agent ||
197      url_util::FindAndCompareScheme(data.url, "javascript", NULL);
198}
199
200}  // namespace content
201