Passed
Push — master ( 2fd7fe...f5932e )
by Robin
14:46 queued 13s
created

OC_EventSource::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 2
rs 10
1
<?php
2
3
use OCP\IRequest;
4
5
/**
6
 * @copyright Copyright (c) 2016, ownCloud, Inc.
7
 *
8
 * @author Bart Visscher <[email protected]>
9
 * @author Christian Oliff <[email protected]>
10
 * @author Christoph Wurst <[email protected]>
11
 * @author Felix Moeller <[email protected]>
12
 * @author Lukas Reschke <[email protected]>
13
 * @author Morris Jobke <[email protected]>
14
 * @author Robin Appelman <[email protected]>
15
 * @author Thomas Müller <[email protected]>
16
 *
17
 * @license AGPL-3.0
18
 *
19
 * This code is free software: you can redistribute it and/or modify
20
 * it under the terms of the GNU Affero General Public License, version 3,
21
 * as published by the Free Software Foundation.
22
 *
23
 * This program is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26
 * GNU Affero General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU Affero General Public License, version 3,
29
 * along with this program. If not, see <http://www.gnu.org/licenses/>
30
 *
31
 */
32
class OC_EventSource implements \OCP\IEventSource {
33
	/**
34
	 * @var bool
35
	 */
36
	private $fallback;
37
38
	/**
39
	 * @var int
40
	 */
41
	private $fallBackId = 0;
42
43
	/**
44
	 * @var bool
45
	 */
46
	private $started = false;
47
48
	private IRequest $request;
49
50
	public function __construct(IRequest $request) {
51
		$this->request = $request;
52
	}
53
54
	protected function init() {
55
		if ($this->started) {
56
			return;
57
		}
58
		$this->started = true;
59
60
		// prevent php output buffering, caching and nginx buffering
61
		OC_Util::obEnd();
62
		header('Cache-Control: no-cache');
63
		header('X-Accel-Buffering: no');
64
		$this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true';
65
		if ($this->fallback) {
66
			$this->fallBackId = (int)$_GET['fallback_id'];
67
			/**
68
			 * FIXME: The default content-security-policy of ownCloud forbids inline
69
			 * JavaScript for security reasons. IE starting on Windows 10 will
70
			 * however also obey the CSP which will break the event source fallback.
71
			 *
72
			 * As a workaround thus we set a custom policy which allows the execution
73
			 * of inline JavaScript.
74
			 *
75
			 * @link https://github.com/owncloud/core/issues/14286
76
			 */
77
			header("Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'");
78
			header("Content-Type: text/html");
79
			echo str_repeat('<span></span>' . PHP_EOL, 10); //dummy data to keep IE happy
80
		} else {
81
			header("Content-Type: text/event-stream");
82
		}
83
		if (!$this->request->passesStrictCookieCheck()) {
84
			header('Location: '.\OC::$WEBROOT);
85
			exit();
86
		}
87
		if (!$this->request->passesCSRFCheck()) {
88
			$this->send('error', 'Possible CSRF attack. Connection will be closed.');
89
			$this->close();
90
			exit();
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
91
		}
92
		flush();
93
	}
94
95
	/**
96
	 * send a message to the client
97
	 *
98
	 * @param string $type
99
	 * @param mixed $data
100
	 *
101
	 * @throws \BadMethodCallException
102
	 * if only one parameter is given, a typeless message will be send with that parameter as data
103
	 * @suppress PhanDeprecatedFunction
104
	 */
105
	public function send($type, $data = null) {
106
		if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) {
107
			throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')');
108
		}
109
		$this->init();
110
		if (is_null($data)) {
111
			$data = $type;
112
			$type = null;
113
		}
114
		if ($this->fallback) {
115
			$response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack('
116
				. $this->fallBackId . ',"' . $type . '",' . OC_JSON::encode($data) . ')</script>' . PHP_EOL;
117
			echo $response;
118
		} else {
119
			if ($type) {
120
				echo 'event: ' . $type . PHP_EOL;
121
			}
122
			echo 'data: ' . OC_JSON::encode($data) . PHP_EOL;
123
		}
124
		echo PHP_EOL;
125
		flush();
126
	}
127
128
	/**
129
	 * close the connection of the event source
130
	 */
131
	public function close() {
132
		$this->send('__internal__', 'close'); //server side closing can be an issue, let the client do it
133
	}
134
}
135