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/child/request_extra_data.h"
10#include "content/common/fileapi/file_system_messages.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/c/pp_bool.h"
19#include "ppapi/c/pp_var.h"
20#include "ppapi/proxy/ppapi_messages.h"
21#include "ppapi/shared_impl/url_request_info_data.h"
22#include "ppapi/shared_impl/var.h"
23#include "ppapi/thunk/enter.h"
24#include "third_party/WebKit/public/platform/WebData.h"
25#include "third_party/WebKit/public/platform/WebHTTPBody.h"
26#include "third_party/WebKit/public/platform/WebURL.h"
27#include "third_party/WebKit/public/platform/WebURLRequest.h"
28#include "third_party/WebKit/public/web/WebDocument.h"
29#include "third_party/WebKit/public/web/WebFrame.h"
30#include "url/gurl.h"
31#include "url/url_util.h"
32
33using ppapi::Resource;
34using ppapi::URLRequestInfoData;
35using ppapi::thunk::EnterResourceNoLock;
36using blink::WebData;
37using blink::WebHTTPBody;
38using blink::WebString;
39using blink::WebFrame;
40using blink::WebURL;
41using blink::WebURLRequest;
42
43namespace content {
44
45namespace {
46
47// Appends the file ref given the Resource pointer associated with it to the
48// given HTTP body, returning true on success.
49bool AppendFileRefToBody(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(platform_path.AsUTF16Unsafe(),
87                             start_offset,
88                             number_of_bytes,
89                             expected_last_modified_time);
90  return true;
91}
92
93// Checks that the request data is valid. Returns false on failure. Note that
94// method and header validation is done by the URL loader when the request is
95// opened, and any access errors are returned asynchronously.
96bool ValidateURLRequestData(const URLRequestInfoData& data) {
97  if (data.prefetch_buffer_lower_threshold < 0 ||
98      data.prefetch_buffer_upper_threshold < 0 ||
99      data.prefetch_buffer_upper_threshold <=
100          data.prefetch_buffer_lower_threshold) {
101    return false;
102  }
103  return true;
104}
105
106std::string FilterStringForXRequestedWithValue(const std::string& s) {
107  std::string rv;
108  rv.reserve(s.length());
109  for (size_t i = 0; i < s.length(); i++) {
110    char c = s[i];
111    // Allow ASCII digits, letters, periods, commas, and underscores. (Ignore
112    // all other characters.)
113    if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
114        (c >= 'a' && c <= 'z') || (c == '.') || (c == ',') || (c == '_'))
115      rv.push_back(c);
116  }
117  return rv;
118}
119
120// Returns an appropriate value for the X-Requested-With header for plugins that
121// present an X-Requested-With header. Returns a blank string for other plugins.
122// We produce a user-agent-like string (eating spaces and other undesired
123// characters) like "ShockwaveFlash/11.5.31.135" from the plugin name and
124// version.
125std::string MakeXRequestedWithValue(const std::string& name,
126                                    const std::string& version) {
127  std::string rv = FilterStringForXRequestedWithValue(name);
128  if (rv.empty())
129    return std::string();
130
131  // Apply to a narrow list of plugins only.
132  if (rv != "ShockwaveFlash" && rv != "PPAPITests")
133    return std::string();
134
135  std::string filtered_version = FilterStringForXRequestedWithValue(version);
136  if (!filtered_version.empty())
137    rv += "/" + filtered_version;
138
139  return rv;
140}
141
142}  // namespace
143
144bool CreateWebURLRequest(PP_Instance instance,
145                         URLRequestInfoData* data,
146                         WebFrame* frame,
147                         WebURLRequest* dest) {
148  // In the out-of-process case, we've received the URLRequestInfoData
149  // from the untrusted plugin and done no validation on it. We need to be
150  // sure it's not being malicious by checking everything for consistency.
151  if (!ValidateURLRequestData(*data))
152    return false;
153
154   std::string name_version;
155
156   // Allow instance to be 0 or -1 for testing purposes.
157   if (instance && instance != -1) {
158     PepperPluginInstanceImpl* instance_impl =
159         HostGlobals::Get()->GetInstance(instance);
160     if (instance_impl) {
161       name_version = MakeXRequestedWithValue(
162           instance_impl->module()->name(),
163           instance_impl->module()->version());
164     }
165   } else {
166     name_version = "internal_testing_only";
167   }
168
169  dest->initialize();
170  dest->setURL(frame->document().completeURL(WebString::fromUTF8(data->url)));
171  dest->setDownloadToFile(data->stream_to_file);
172  dest->setReportUploadProgress(data->record_upload_progress);
173
174  if (!data->method.empty())
175    dest->setHTTPMethod(WebString::fromUTF8(data->method));
176
177  dest->setFirstPartyForCookies(frame->document().firstPartyForCookies());
178
179  const std::string& headers = data->headers;
180  if (!headers.empty()) {
181    net::HttpUtil::HeadersIterator it(headers.begin(), headers.end(), "\n\r");
182    while (it.GetNext()) {
183      dest->addHTTPHeaderField(WebString::fromUTF8(it.name()),
184                               WebString::fromUTF8(it.values()));
185    }
186  }
187
188  // Append the upload data.
189  if (!data->body.empty()) {
190    WebHTTPBody http_body;
191    http_body.initialize();
192    int file_index = 0;
193    for (size_t i = 0; i < data->body.size(); ++i) {
194      const URLRequestInfoData::BodyItem& item = data->body[i];
195      if (item.is_file) {
196        if (!AppendFileRefToBody(instance,
197                                 item.file_ref_pp_resource,
198                                 item.start_offset,
199                                 item.number_of_bytes,
200                                 item.expected_last_modified_time,
201                                 &http_body))
202          return false;
203        file_index++;
204      } else {
205        DCHECK(!item.data.empty());
206        http_body.appendData(WebData(item.data));
207      }
208    }
209    dest->setHTTPBody(http_body);
210  }
211
212  // Add the "Referer" header if there is a custom referrer. Such requests
213  // require universal access. For all other requests, "Referer" will be set
214  // after header security checks are done in AssociatedURLLoader.
215  if (data->has_custom_referrer_url && !data->custom_referrer_url.empty())
216    frame->setReferrerForRequest(*dest, GURL(data->custom_referrer_url));
217
218  if (data->has_custom_content_transfer_encoding &&
219      !data->custom_content_transfer_encoding.empty()) {
220    dest->addHTTPHeaderField(
221        WebString::fromUTF8("Content-Transfer-Encoding"),
222        WebString::fromUTF8(data->custom_content_transfer_encoding));
223  }
224
225  if (data->has_custom_user_agent || !name_version.empty()) {
226    RequestExtraData* extra_data = new RequestExtraData();
227    if (data->has_custom_user_agent) {
228      extra_data->set_custom_user_agent(
229          WebString::fromUTF8(data->custom_user_agent));
230    }
231    if (!name_version.empty()) {
232      extra_data->set_requested_with(WebString::fromUTF8(name_version));
233    }
234    dest->setExtraData(extra_data);
235  }
236
237  return true;
238}
239
240bool URLRequestRequiresUniversalAccess(const URLRequestInfoData& data) {
241  return data.has_custom_referrer_url ||
242         data.has_custom_content_transfer_encoding ||
243         data.has_custom_user_agent ||
244         url::FindAndCompareScheme(data.url, url::kJavaScriptScheme, NULL);
245}
246
247}  // namespace content
248