public function testAddCacheHeaders()
 {
     $body = "<html><head></head><body><h1>Mysite</h1></body></html>";
     $response = new HTTPResponse($body, 200);
     $this->assertEmpty($response->getHeader('Cache-Control'));
     HTTP::set_cache_age(30);
     HTTP::add_cache_headers($response);
     $this->assertNotEmpty($response->getHeader('Cache-Control'));
     // Ensure max-age is zero for development.
     Director::config()->update('environment_type', 'dev');
     $response = new HTTPResponse($body, 200);
     HTTP::add_cache_headers($response);
     $this->assertContains('max-age=0', $response->getHeader('Cache-Control'));
     // Ensure max-age setting is respected in production.
     Director::config()->update('environment_type', 'live');
     $response = new HTTPResponse($body, 200);
     HTTP::add_cache_headers($response);
     $this->assertContains('max-age=30', explode(', ', $response->getHeader('Cache-Control')));
     $this->assertNotContains('max-age=0', $response->getHeader('Cache-Control'));
     // Still "live": Ensure header's aren't overridden if already set (using purposefully different values).
     $headers = array('Vary' => '*', 'Pragma' => 'no-cache', 'Cache-Control' => 'max-age=0, no-cache, no-store');
     $response = new HTTPResponse($body, 200);
     foreach ($headers as $name => $value) {
         $response->addHeader($name, $value);
     }
     HTTP::add_cache_headers($response);
     foreach ($headers as $name => $value) {
         $this->assertEquals($value, $response->getHeader($name));
     }
 }
 /**
  * @param HTTPResponse|string $body Either the plaintext content of the error
  * message, or an HTTPResponse object representing it. In either case, the
  * $statusCode and $statusDescription will be the HTTP status of the resulting
  * response.
  * @param int $statusCode
  * @param string $statusDescription
  * @see HTTPResponse::__construct();
  */
 public function __construct($body = null, $statusCode = null, $statusDescription = null)
 {
     if ($body instanceof HTTPResponse) {
         // statusCode and statusDescription should override whatever is passed in the body
         if ($statusCode) {
             $body->setStatusCode($statusCode);
         }
         if ($statusDescription) {
             $body->setStatusDescription($statusDescription);
         }
         $this->setResponse($body);
     } else {
         $response = new HTTPResponse($body, $statusCode, $statusDescription);
         // Error responses should always be considered plaintext, for security reasons
         $response->addHeader('Content-Type', 'text/plain');
         $this->setResponse($response);
     }
     parent::__construct($this->getResponse()->getBody(), $this->getResponse()->getStatusCode());
 }
 /**
  * @param array $record
  * @return bool
  */
 protected function write(array $record)
 {
     ini_set('display_errors', 0);
     // TODO: This coupling isn't ideal
     // See https://github.com/silverstripe/silverstripe-framework/issues/4484
     if (Controller::has_curr()) {
         $response = Controller::curr()->getResponse();
     } else {
         $response = new HTTPResponse();
     }
     // If headers have been sent then these won't be used, and may throw errors that we wont' want to see.
     if (!headers_sent()) {
         $response->setStatusCode($this->statusCode);
         $response->addHeader("Content-Type", $this->contentType);
     } else {
         // To supress errors aboot errors
         $response->setStatusCode(200);
     }
     $response->setBody($record['formatted']);
     $response->output();
     return false === $this->bubble;
 }
 /**
  * Generate an {@see HTTPResponse} for the given file from the source filesystem
  * @param FilesystemInterface $flysystem
  * @param string $fileID
  * @return HTTPResponse
  */
 protected function createResponseFor(FilesystemInterface $flysystem, $fileID)
 {
     // Build response body
     // @todo: gzip / buffer response?
     $body = $flysystem->read($fileID);
     $mime = $flysystem->getMimetype($fileID);
     $response = new HTTPResponse($body, 200);
     // Add headers
     $response->addHeader('Content-Type', $mime);
     $headers = Config::inst()->get(get_class($this), 'file_response_headers');
     foreach ($headers as $header => $value) {
         $response->addHeader($header, $value);
     }
     return $response;
 }
 /**
  * Returns the appropriate response up the controller chain
  * if {@link validate()} fails (which is checked prior to executing any form actions).
  * By default, returns different views for ajax/non-ajax request, and
  * handles 'application/json' requests with a JSON object containing the error messages.
  * Behaviour can be influenced by setting {@link $redirectToFormOnValidationError},
  * and can be overruled by setting {@link $validationResponseCallback}.
  *
  * @return HTTPResponse|string
  */
 protected function getValidationErrorResponse()
 {
     $callback = $this->getValidationResponseCallback();
     if ($callback && ($callbackResponse = $callback())) {
         return $callbackResponse;
     }
     $request = $this->getRequest();
     if ($request->isAjax()) {
         // Special case for legacy Validator.js implementation
         // (assumes eval'ed javascript collected through FormResponse)
         $acceptType = $request->getHeader('Accept');
         if (strpos($acceptType, 'application/json') !== FALSE) {
             // Send validation errors back as JSON with a flag at the start
             $response = new HTTPResponse(Convert::array2json($this->validator->getErrors()));
             $response->addHeader('Content-Type', 'application/json');
         } else {
             $this->setupFormErrors();
             // Send the newly rendered form tag as HTML
             $response = new HTTPResponse($this->forTemplate());
             $response->addHeader('Content-Type', 'text/html');
         }
         return $response;
     } else {
         if ($this->getRedirectToFormOnValidationError()) {
             if ($pageURL = $request->getHeader('Referer')) {
                 if (Director::is_site_url($pageURL)) {
                     // Remove existing pragmas
                     $pageURL = preg_replace('/(#.*)/', '', $pageURL);
                     $pageURL = Director::absoluteURL($pageURL, true);
                     return $this->controller->redirect($pageURL . '#' . $this->FormName());
                 }
             }
         }
         return $this->controller->redirectBack();
     }
 }
 /**
  * Construct an HTTPResponse that will deliver a file to the client.
  * Caution: Since it requires $fileData to be passed as binary data (no stream support),
  * it's only advisable to send small files through this method.
  *
  * @static
  * @param $fileData
  * @param $fileName
  * @param null $mimeType
  * @return HTTPResponse
  */
 public static function send_file($fileData, $fileName, $mimeType = null)
 {
     if (!$mimeType) {
         $mimeType = HTTP::get_mime_type($fileName);
     }
     $response = new HTTPResponse($fileData);
     $response->addHeader("Content-Type", "{$mimeType}; name=\"" . addslashes($fileName) . "\"");
     // Note a IE-only fix that inspects this header in HTTP::add_cache_headers().
     $response->addHeader("Content-Disposition", "attachment; filename=\"" . addslashes($fileName) . "\"");
     $response->addHeader("Content-Length", strlen($fileData));
     return $response;
 }
    /**
     * Generate a secure token which can be used as a crypto key.
     * Returns the token and suggests PHP configuration to set it.
     */
    public function generatesecuretoken()
    {
        $generator = Injector::inst()->create('SilverStripe\\Security\\RandomGenerator');
        $token = $generator->randomToken('sha1');
        $body = <<<TXT
Generated new token. Please add the following code to your YAML configuration:

Security:
  token: {$token}

TXT;
        $response = new HTTPResponse($body);
        return $response->addHeader('Content-Type', 'text/plain');
    }
 /**
  * Save  handler
  *
  * @param array $data
  * @param Form $form
  * @return HTTPResponse
  */
 public function save($data, $form)
 {
     $request = $this->getRequest();
     $className = $this->stat('tree_class');
     // Existing or new record?
     $id = $data['ID'];
     if (is_numeric($id) && $id > 0) {
         $record = DataObject::get_by_id($className, $id);
         if ($record && !$record->canEdit()) {
             return Security::permissionFailure($this);
         }
         if (!$record || !$record->ID) {
             $this->httpError(404, "Bad record ID #" . (int) $id);
         }
     } else {
         if (!singleton($this->stat('tree_class'))->canCreate()) {
             return Security::permissionFailure($this);
         }
         $record = $this->getNewItem($id, false);
     }
     // save form data into record
     $form->saveInto($record, true);
     $record->write();
     $this->extend('onAfterSave', $record);
     $this->setCurrentPageID($record->ID);
     $message = _t('LeftAndMain.SAVEDUP', 'Saved.');
     if ($request->getHeader('X-Formschema-Request')) {
         $schemaId = Controller::join_links($this->Link('schema/DetailEditForm'), $id);
         // Ensure that newly created records have all their data loaded back into the form.
         $form->loadDataFrom($record);
         $form->setMessage($message, 'good');
         $data = $this->getSchemaForForm($form, $schemaId);
         $response = new HTTPResponse(Convert::raw2json($data));
         $response->addHeader('Content-Type', 'application/json');
     } else {
         $response = $this->getResponseNegotiator()->respond($request);
     }
     $response->addHeader('X-Status', rawurlencode($message));
     return $response;
 }
 /**
  * Require basic authentication.  Will request a username and password if none is given.
  *
  * Used by {@link Controller::init()}.
  *
  * @throws HTTPResponse_Exception
  *
  * @param string $realm
  * @param string|array $permissionCode Optional
  * @param boolean $tryUsingSessionLogin If true, then the method with authenticate against the
  *  session log-in if those credentials are disabled.
  * @return Member|bool $member
  */
 public static function requireLogin($realm, $permissionCode = null, $tryUsingSessionLogin = true)
 {
     $isRunningTests = class_exists('SilverStripe\\Dev\\SapphireTest', false) && SapphireTest::is_running_test();
     if (!Security::database_is_ready() || Director::is_cli() && !$isRunningTests) {
         return true;
     }
     /*
      * Enable HTTP Basic authentication workaround for PHP running in CGI mode with Apache
      * Depending on server configuration the auth header may be in HTTP_AUTHORIZATION or
      * REDIRECT_HTTP_AUTHORIZATION
      *
      * The follow rewrite rule must be in the sites .htaccess file to enable this workaround
      * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
      */
     $authHeader = isset($_SERVER['HTTP_AUTHORIZATION']) ? $_SERVER['HTTP_AUTHORIZATION'] : (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) ? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] : null);
     $matches = array();
     if ($authHeader && preg_match('/Basic\\s+(.*)$/i', $authHeader, $matches)) {
         list($name, $password) = explode(':', base64_decode($matches[1]));
         $_SERVER['PHP_AUTH_USER'] = strip_tags($name);
         $_SERVER['PHP_AUTH_PW'] = strip_tags($password);
     }
     $member = null;
     if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
         $member = MemberAuthenticator::authenticate(array('Email' => $_SERVER['PHP_AUTH_USER'], 'Password' => $_SERVER['PHP_AUTH_PW']), null);
     }
     if (!$member && $tryUsingSessionLogin) {
         $member = Member::currentUser();
     }
     // If we've failed the authentication mechanism, then show the login form
     if (!$member) {
         $response = new HTTPResponse(null, 401);
         $response->addHeader('WWW-Authenticate', "Basic realm=\"{$realm}\"");
         if (isset($_SERVER['PHP_AUTH_USER'])) {
             $response->setBody(_t('BasicAuth.ERRORNOTREC', "That username / password isn't recognised"));
         } else {
             $response->setBody(_t('BasicAuth.ENTERINFO', "Please enter a username and password."));
         }
         // Exception is caught by RequestHandler->handleRequest() and will halt further execution
         $e = new HTTPResponse_Exception(null, 401);
         $e->setResponse($response);
         throw $e;
     }
     if ($permissionCode && !Permission::checkMember($member->ID, $permissionCode)) {
         $response = new HTTPResponse(null, 401);
         $response->addHeader('WWW-Authenticate', "Basic realm=\"{$realm}\"");
         if (isset($_SERVER['PHP_AUTH_USER'])) {
             $response->setBody(_t('BasicAuth.ERRORNOTADMIN', "That user is not an administrator."));
         }
         // Exception is caught by RequestHandler->handleRequest() and will halt further execution
         $e = new HTTPResponse_Exception(null, 401);
         $e->setResponse($response);
         throw $e;
     }
     return $member;
 }
 /**
  * Determines if a specified file exists
  *
  * @param HTTPRequest $request
  * @return HTTPResponse
  */
 public function fileexists(HTTPRequest $request)
 {
     // Assert that requested filename doesn't attempt to escape the directory
     $originalFile = $request->requestVar('filename');
     if ($originalFile !== basename($originalFile)) {
         $return = array('error' => _t('File.NOVALIDUPLOAD', 'File is not a valid upload'));
     } else {
         $return = array('exists' => $this->checkFileExists($originalFile));
     }
     // Encode and present response
     $response = new HTTPResponse(Convert::raw2json($return));
     $response->addHeader('Content-Type', 'application/json');
     if (!empty($return['error'])) {
         $response->setStatusCode(400);
     }
     return $response;
 }
 /**
  * REST endpoint to get a campaign.
  *
  * @param HTTPRequest $request
  *
  * @return HTTPResponse
  */
 public function readCampaign(HTTPRequest $request)
 {
     $response = new HTTPResponse();
     if ($request->getHeader('Accept') == 'text/json') {
         $response->addHeader('Content-Type', 'application/json');
         if (!$request->param('Name')) {
             return new HTTPResponse(null, 400);
         }
         /** @var ChangeSet $changeSet */
         $changeSet = ChangeSet::get()->byID($request->param('ID'));
         if (!$changeSet) {
             return new HTTPResponse(null, 404);
         }
         if (!$changeSet->canView()) {
             return new HTTPResponse(null, 403);
         }
         $body = Convert::raw2json($this->getChangeSetResource($changeSet));
         return (new HTTPResponse($body, 200))->addHeader('Content-Type', 'application/json');
     } else {
         return $this->index($request);
     }
 }
 /**
  * Check if this action has a confirmation step
  *
  * @param HTTPRequest $request
  * @return HTTPResponse
  */
 public function handleConfirmation($request)
 {
     // Find the action handler
     $action = $request->param('BatchAction');
     $actionHandler = $this->actionByName($action);
     // Sanitise ID list and query the database for apges
     $csvIDs = $request->requestVar('csvIDs');
     $ids = $this->cleanIDs($csvIDs);
     // Check dialog
     if ($actionHandler->hasMethod('confirmationDialog')) {
         $response = new HTTPResponse(json_encode($actionHandler->confirmationDialog($ids)));
     } else {
         $response = new HTTPResponse(json_encode(array('alert' => false)));
     }
     $response->addHeader("Content-type", "application/json");
     return $response;
 }
 /**
  * Performs the actual action of adding the object to the ChangeSet, once the ChangeSet ID is known
  *
  * @param DataObject $object The object to add to the ChangeSet
  * @param int $campaignID The ID of the ChangeSet to add $object to
  * @return HTTPResponse
  * @throws HTTPResponse_Exception
  */
 public function addToCampaign($object, $campaignID)
 {
     /** @var ChangeSet $changeSet */
     $changeSet = ChangeSet::get()->byID($campaignID);
     if (!$changeSet) {
         $this->controller->httpError(404, _t('AddToCampaign.ErrorNotFound', 'That {Type} couldn\'t be found', '', ['Type' => 'Campaign']));
         return null;
     }
     if (!$changeSet->canEdit()) {
         $this->controller->httpError(403, _t('AddToCampaign.ErrorCampaignPermissionDenied', 'It seems you don\'t have the necessary permissions to add {ObjectTitle} to {CampaignTitle}', '', ['ObjectTitle' => $object->Title, 'CampaignTitle' => $changeSet->Title]));
         return null;
     }
     $changeSet->addObject($object);
     $request = $this->controller->getRequest();
     $message = _t('AddToCampaign.Success', 'Successfully added {ObjectTitle} to {CampaignTitle}', '', ['ObjectTitle' => $object->Title, 'CampaignTitle' => $changeSet->Title]);
     if ($request->getHeader('X-Formschema-Request')) {
         return $message;
     } elseif (Director::is_ajax()) {
         $response = new HTTPResponse($message, 200);
         $response->addHeader('Content-Type', 'text/plain; charset=utf-8');
         return $response;
     } else {
         return $this->controller->getController()->redirectBack();
     }
 }
 /**
  * Performs the following replacements:
  * - Check user defined content type and use it, if it's empty use the text/html.
  * - If find a XML header replaces it and existing doctypes with HTML4.01 Strict.
  * - Replaces self-closing tags like <img /> with unclosed solitary tags like <img>.
  * - Replaces all occurrences of "application/xhtml+xml" with "text/html" in the template.
  * - Removes "xmlns" attributes and any <?xml> Pragmas.
  *
  * @param HTTPResponse $response
  */
 public function html(HTTPResponse $response)
 {
     $encoding = $this->config()->get('encoding');
     $contentType = $this->config()->get('content_type');
     if (empty($contentType)) {
         $response->addHeader("Content-Type", "text/html; charset=" . $encoding);
     } else {
         $response->addHeader("Content-Type", $contentType . "; charset=" . $encoding);
     }
     $response->addHeader("Vary", "Accept");
     $content = $response->getBody();
     $hasXMLHeader = substr($content, 0, 5) == '<' . '?xml';
     // Fix base tag
     $content = preg_replace('/<base href="([^"]*)" \\/>/', '<base href="$1"><!--[if lte IE 6]></base><![endif]-->', $content);
     $content = preg_replace("#<\\?xml[^>]+\\?>\n?#", '', $content);
     $content = str_replace(array('/>', 'xml:lang', 'application/xhtml+xml'), array('>', 'lang', 'text/html'), $content);
     // Only replace the doctype in templates with the xml header
     if ($hasXMLHeader) {
         $content = preg_replace('/<!DOCTYPE[^>]+>/', '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">', $content);
     }
     $content = preg_replace('/<html xmlns="[^"]+"/', '<html ', $content);
     $response->setBody($content);
 }
 /**
  * Action to handle upload of a single file
  *
  * @param HTTPRequest $request
  * @return HTTPResponse
  */
 public function upload(HTTPRequest $request)
 {
     if ($this->isDisabled() || $this->isReadonly() || !$this->canUpload()) {
         return $this->httpError(403);
     }
     // Protect against CSRF on destructive action
     $token = $this->getForm()->getSecurityToken();
     if (!$token->checkRequest($request)) {
         return $this->httpError(400);
     }
     // Get form details
     $name = $this->getName();
     $postVars = $request->postVar($name);
     // Extract uploaded files from Form data
     $uploadedFile = $this->extractUploadedFileData($postVars);
     if (!$uploadedFile) {
         return $this->httpError(400);
     }
     // Save the temporary files into a File objects
     // and save data/error on a per file basis
     $result = $this->saveTemporaryFile($uploadedFile, $error);
     if (empty($result)) {
         $return = array('error' => $error);
     } else {
         $return = $this->encodeAssetAttributes($result['Filename'], $result['Hash'], $result['Variant']);
     }
     $this->getUpload()->clearErrors();
     // Format response with json
     $response = new HTTPResponse(Convert::raw2json(array($return)));
     $response->addHeader('Content-Type', 'text/plain');
     return $response;
 }
 /**
  * @param HTTPRequest $request
  *
  * @return HTTPResponse
  */
 public function apiSearch(HTTPRequest $request)
 {
     $params = $request->getVars();
     $list = $this->getList($params);
     $response = new HTTPResponse();
     $response->addHeader('Content-Type', 'application/json');
     $response->setBody(json_encode(["files" => array_map(function ($file) {
         return $this->getObjectFromData($file);
     }, $list->toArray()), "count" => $list->count()]));
     return $response;
 }