Security update for Core, with self-updated composer
[yaffs-website] / web / core / lib / Drupal / Core / EventSubscriber / AjaxResponseSubscriber.php
1 <?php
2
3 namespace Drupal\Core\EventSubscriber;
4
5 use Drupal\Component\Utility\Html;
6 use Drupal\Core\Ajax\AjaxResponse;
7 use Drupal\Core\Render\AttachmentsResponseProcessorInterface;
8 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
9 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
10 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
11 use Symfony\Component\HttpKernel\KernelEvents;
12
13 /**
14  * Response subscriber to handle AJAX responses.
15  */
16 class AjaxResponseSubscriber implements EventSubscriberInterface {
17
18   /**
19    * The AJAX response attachments processor service.
20    *
21    * @var \Drupal\Core\Render\AttachmentsResponseProcessorInterface
22    */
23   protected $ajaxResponseAttachmentsProcessor;
24
25   /**
26    * Constructs an AjaxResponseSubscriber object.
27    *
28    * @param \Drupal\Core\Render\AttachmentsResponseProcessorInterface $ajax_response_attachments_processor
29    *   The AJAX response attachments processor service.
30    */
31   public function __construct(AttachmentsResponseProcessorInterface $ajax_response_attachments_processor) {
32     $this->ajaxResponseAttachmentsProcessor = $ajax_response_attachments_processor;
33   }
34
35   /**
36    * Request parameter to indicate that a request is a Drupal Ajax request.
37    */
38   const AJAX_REQUEST_PARAMETER = '_drupal_ajax';
39
40   /**
41    * Sets the AJAX parameter from the current request.
42    *
43    * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
44    *   The response event, which contains the current request.
45    */
46   public function onRequest(GetResponseEvent $event) {
47     // Pass to the Html class that the current request is an Ajax request.
48     if ($event->getRequest()->request->get(static::AJAX_REQUEST_PARAMETER)) {
49       Html::setIsAjax(TRUE);
50     }
51   }
52
53   /**
54    * Renders the ajax commands right before preparing the result.
55    *
56    * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
57    *   The response event, which contains the possible AjaxResponse object.
58    */
59   public function onResponse(FilterResponseEvent $event) {
60     $response = $event->getResponse();
61     if ($response instanceof AjaxResponse) {
62       $this->ajaxResponseAttachmentsProcessor->processAttachments($response);
63
64       // IE 9 does not support XHR 2 (http://caniuse.com/#feat=xhr2), so
65       // for that browser, jquery.form submits requests containing a file upload
66       // via an IFRAME rather than via XHR. Since the response is being sent to
67       // an IFRAME, it must be formatted as HTML. Specifically:
68       // - It must use the text/html content type or else the browser will
69       //   present a download prompt. Note: This applies to both file uploads
70       //   as well as any ajax request in a form with a file upload form.
71       // - It must place the JSON data into a textarea to prevent browser
72       //   extensions such as Linkification and Skype's Browser Highlighter
73       //   from applying HTML transformations such as URL or phone number to
74       //   link conversions on the data values.
75       //
76       // Since this affects the format of the output, it could be argued that
77       // this should be implemented as a separate Accept MIME type. However,
78       // that would require separate variants for each type of AJAX request
79       // (e.g., drupal-ajax, drupal-dialog, drupal-modal), so for expediency,
80       // this browser workaround is implemented via a GET or POST parameter.
81       //
82       // @see http://malsup.com/jquery/form/#file-upload
83       // @see https://www.drupal.org/node/1009382
84       // @see https://www.drupal.org/node/2339491
85       // @see Drupal.ajax.prototype.beforeSend()
86       $accept = $event->getRequest()->headers->get('accept');
87
88       if (strpos($accept, 'text/html') !== FALSE) {
89         $response->headers->set('Content-Type', 'text/html; charset=utf-8');
90
91         // Browser IFRAMEs expect HTML. Browser extensions, such as Linkification
92         // and Skype's Browser Highlighter, convert URLs, phone numbers, etc.
93         // into links. This corrupts the JSON response. Protect the integrity of
94         // the JSON data by making it the value of a textarea.
95         // @see http://malsup.com/jquery/form/#file-upload
96         // @see https://www.drupal.org/node/1009382
97         $response->setContent('<textarea>' . $response->getContent() . '</textarea>');
98       }
99
100       // User-uploaded files cannot set any response headers, so a custom header
101       // is used to indicate to ajax.js that this response is safe. Note that
102       // most Ajax requests bound using the Form API will be protected by having
103       // the URL flagged as trusted in Drupal.settings, so this header is used
104       // only for things like custom markup that gets Ajax behaviors attached.
105       $response->headers->set('X-Drupal-Ajax-Token', 1);
106     }
107   }
108
109   /**
110    * {@inheritdoc}
111    */
112   public static function getSubscribedEvents() {
113     $events[KernelEvents::RESPONSE][] = ['onResponse', -100];
114     $events[KernelEvents::REQUEST][] = ['onRequest', 50];
115
116     return $events;
117   }
118
119 }