Home | History | Annotate | Download | only in pepper
      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 
     33 using ppapi::Resource;
     34 using ppapi::URLRequestInfoData;
     35 using ppapi::thunk::EnterResourceNoLock;
     36 using blink::WebData;
     37 using blink::WebHTTPBody;
     38 using blink::WebString;
     39 using blink::WebFrame;
     40 using blink::WebURL;
     41 using blink::WebURLRequest;
     42 
     43 namespace content {
     44 
     45 namespace {
     46 
     47 // Appends the file ref given the Resource pointer associated with it to the
     48 // given HTTP body, returning true on success.
     49 bool 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.
     96 bool 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 
    106 std::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.
    125 std::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 
    144 bool 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 
    240 bool 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