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