blob: 4509f446a4de4630dce99b6c589cf80b34fc77b4 [file] [log] [blame]
<?php
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* The ProxyHandler class does the actual proxy'ing work. it deals both with
* GET and POST based input, and peforms a request based on the input, headers and
* httpmethod params.
*
*/
class ProxyHandler extends ProxyBase {
/**
* Fetches the content and returns it as-is using the headers as returned
* by the remote host.
*
* @param string $url the url to retrieve
*/
public function fetch($url) {
// TODO: Check to see if we can just use MakeRequestOptions::fromCurrentRequest
$st = isset($_GET['st']) ? $_GET['st'] : (isset($_POST['st']) ? $_POST['st'] : false);
$body = isset($_GET['postData']) ? $_GET['postData'] : (isset($_POST['postData']) ? $_POST['postData'] : false);
$authz = isset($_GET['authz']) ? $_GET['authz'] : (isset($_POST['authz']) ? $_POST['authz'] : null);
$headers = isset($_GET['headers']) ? $_GET['headers'] : (isset($_POST['headers']) ? $_POST['headers'] : null);
$params = new MakeRequestOptions($url);
$params->setSecurityTokenString($st)
->setAuthz($authz)
->setRequestBody($body)
->setHttpMethod('GET')
->setFormEncodedRequestHeaders($headers)
->setNoCache($this->context->getIgnoreCache());
$result = $this->makeRequest->fetch($this->context, $params);
$httpCode = (int)$result->getHttpCode();
$cleanedResponseHeaders = $this->makeRequest->cleanResponseHeaders($result->getResponseHeaders());
$isShockwaveFlash = false;
foreach ($cleanedResponseHeaders as $key => $val) {
header("$key: $val", true);
if (strtoupper($key) == 'CONTENT-TYPE' && strtolower($val) == 'application/x-shockwave-flash') {
// We're skipping the content disposition header for flash due to an issue with Flash player 10
// This does make some sites a higher value phishing target, but this can be mitigated by
// additional referer checks.
$isShockwaveFlash = true;
}
}
if (! $isShockwaveFlash && !Config::get('debug')) {
header('Content-Disposition: attachment;filename=p.txt');
}
$lastModified = $result->getResponseHeader('Last-Modified') != null ? $result->getResponseHeader('Last-Modified') : gmdate('D, d M Y H:i:s', $result->getCreated()) . ' GMT';
$notModified = false;
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $lastModified && ! isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
$if_modified_since = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
// Use the request's Last-Modified, otherwise fall back on our internal time keeping (the time the request was created)
$lastModified = strtotime($lastModified);
if ($lastModified <= $if_modified_since) {
$notModified = true;
}
}
if ($httpCode == 200) {
// only set caching headers if the result was 'OK'
$this->setCachingHeaders($lastModified);
// was the &gadget=<gadget url> specified in the request? if so parse it and check the rewrite settings
if (isset($_GET['gadget'])) {
$this->rewriteContent($_GET['gadget'], $result);
}
}
// If the cached file time is within the refreshInterval params value, return not-modified
if ($notModified) {
header('HTTP/1.0 304 Not Modified', true);
header('Content-Length: 0', true);
} else {
header("HTTP/1.1 $httpCode ".$result->getHttpCodeMsg());
// then echo the content
echo $result->getResponseContent();
}
}
private function rewriteContent($gadgetUrl, RemoteContentRequest &$result) {
try {
// At the moment we're only able to rewrite CSS files, so check the content type and/or the file extension before rewriting
$headers = $result->getResponseHeaders();
$isCss = false;
if (isset($headers['Content-Type']) && strtolower($headers['Content-Type'] == 'text/csss')) {
$isCss = true;
} else {
$ext = substr($_GET['url'], strrpos($_GET['url'], '.') + 1);
$isCss = strtolower($ext) == 'css';
}
if ($isCss) {
$gadget = $this->createGadget($gadgetUrl);
$rewrite = $gadget->gadgetSpec->rewrite;
if (is_array($rewrite)) {
$contentRewriter = new ContentRewriter($this->context, $gadget);
$result->setResponseContent($contentRewriter->rewriteCSS($result->getResponseContent()));
}
}
} catch (Exception $e) {
// ignore, not being able to rewrite anything isn't fatal
}
}
/**
* Uses the GadgetFactory to instrance the specified gadget
*
* @param string $gadgetUrl
*/
private function createGadget($gadgetUrl) {
// Only include these files if appropiate, else it would slow down the entire proxy way to much
require_once 'src/gadgets/GadgetSpecParser.php';
require_once 'src/gadgets/GadgetBlacklist.php';
require_once 'src/gadgets/sample/BasicGadgetBlacklist.php';
require_once 'src/gadgets/GadgetContext.php';
require_once 'src/gadgets/GadgetFactory.php';
require_once 'src/gadgets/GadgetSpec.php';
require_once 'src/gadgets/Gadget.php';
require_once 'src/gadgets/GadgetException.php';
require_once 'src/gadgets/rewrite/GadgetRewriter.php';
require_once 'src/gadgets/rewrite/DomRewriter.php';
require_once 'src/gadgets/rewrite/ContentRewriter.php';
// make sure our context returns the gadget url and not the proxied document url
$this->context->setUrl($gadgetUrl);
// and create & return the gadget
$gadgetSpecFactory = new GadgetFactory($this->context, null);
$gadget = $gadgetSpecFactory->createGadget();
return $gadget;
}
}