Issues (40)

lib/AppInfo/Application.php (6 issues)

1
<?php declare(strict_types=1);
2
3
/**
4
 * ownCloud - Music app
5
 *
6
 * This file is licensed under the Affero General Public License version 3 or
7
 * later. See the COPYING file.
8
 *
9
 * @author Morris Jobke <[email protected]>
10
 * @author Pauli Järvinen <[email protected]>
11
 * @copyright Morris Jobke 2014
12
 * @copyright Pauli Järvinen 2017 - 2025
13
 */
14
15
namespace OCA\Music\AppInfo;
16
17
use OCA\Music\Hooks\FileHooks;
18
use OCA\Music\Hooks\ShareHooks;
19
use OCA\Music\Hooks\UserHooks;
20
21
use OCA\Music\Middleware\AmpacheMiddleware;
22
use OCA\Music\Middleware\SubsonicMiddleware;
23
24
use OCP\AppFramework\App;
25
use OCP\AppFramework\IAppContainer;
26
use OCP\Files\IMimeTypeLoader;
27
use OCP\IConfig;
28
use OCP\IRequest;
29
use OCP\Security\IContentSecurityPolicyManager;
30
31
// The IBootstrap interface is not available on ownCloud. Create a thin base class to hide this difference
32
// from the actual Application class.
33
function useOwncloudBootstrapping() : bool {
34
	return (\OCA\Music\Utility\AppInfo::getVendor() == 'owncloud');
35
}
36
37
if (useOwncloudBootstrapping()) {
38
	class ApplicationBase extends App {}
39
} else {
40
	abstract class ApplicationBase extends App implements \OCP\AppFramework\Bootstrap\IBootstrap {}
41
}
42
43
class Application extends ApplicationBase {
44
	public function __construct(array $urlParams=[]) {
45
		parent::__construct('music', $urlParams);
46
47
		\mb_internal_encoding('UTF-8');
48
49
		// On ownCloud, the registrations must happen already within the constructor
50
		if (useOwncloudBootstrapping()) {
51
			$container = $this->getContainer();
52
			// this is not registered by the ownCloud core
53
			$container->registerService(IMimeTypeLoader::class, function (IAppContainer $c) {
0 ignored issues
show
Deprecated Code introduced by
The function OCP\IContainer::registerService() has been deprecated: 20.0.0 use \OCP\AppFramework\Bootstrap\IRegistrationContext::registerService ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

53
			/** @scrutinizer ignore-deprecated */ $container->registerService(IMimeTypeLoader::class, function (IAppContainer $c) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
54
				return $c->getServer()->getMimeTypeLoader();
0 ignored issues
show
Deprecated Code introduced by
The function OCP\IServerContainer::getMimeTypeLoader() has been deprecated: 20.0.0 have it injected or fetch it through \Psr\Container\ContainerInterface::get ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

54
				return /** @scrutinizer ignore-deprecated */ $c->getServer()->getMimeTypeLoader();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Deprecated Code introduced by
The function OCP\AppFramework\IAppContainer::getServer() has been deprecated: 20.0.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

54
				return /** @scrutinizer ignore-deprecated */ $c->getServer()->getMimeTypeLoader();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
55
			});
56
57
			// Unlike Nextcloud, ownCloud is not able to autoload the classes directly within registerMiddleWare.
58
			// We have to fetch each middleware once so that the instances are already cached when registerMiddleWare is called.
59
			$container->query(AmpacheMiddleware::class);
0 ignored issues
show
Deprecated Code introduced by
The function OCP\IContainer::query() has been deprecated: 20.0.0 use \Psr\Container\ContainerInterface::get ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

59
			/** @scrutinizer ignore-deprecated */ $container->query(AmpacheMiddleware::class);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
60
			$container->query(SubsonicMiddleware::class);
0 ignored issues
show
Deprecated Code introduced by
The function OCP\IContainer::query() has been deprecated: 20.0.0 use \Psr\Container\ContainerInterface::get ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

60
			/** @scrutinizer ignore-deprecated */ $container->query(SubsonicMiddleware::class);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
61
62
			$this->registerMiddleWares($container);
63
		}
64
	}
65
66
	/**
67
	 * @param mixed $context On Nextcloud, this is \OCP\AppFramework\Bootstrap\IRegistrationContext.
68
	 *                       On ownCloud, this is \OCP\AppFramework\IAppContainer.
69
	 */
70
	private function registerMiddleWares($context) : void {
71
		$context->registerMiddleWare(AmpacheMiddleware::class);
72
		$context->registerMiddleWare(SubsonicMiddleware::class);
73
	}
74
75
	/**
76
	 * This gets called on Nextcloud but not on ownCloud
77
	 * @param \OCP\AppFramework\Bootstrap\IRegistrationContext $context
78
	 */
79
	public function register($context) : void {
80
		$this->registerMiddleWares($context);
81
		$context->registerDashboardWidget(\OCA\Music\Dashboard\MusicWidget::class);
82
	}
83
84
	/**
85
	 * This gets called on Nextcloud but not on ownCloud
86
	 * @param \OCP\AppFramework\Bootstrap\IBootContext $context
87
	 */
88
	public function boot($context) : void {
89
		$this->init();
90
		$this->registerEmbeddedPlayer();
91
	}
92
93
	public function init() : void {
94
		$this->registerHooks();
95
96
		// Adjust the CSP if loading the Music app proper or the NC dashboard
97
		$url = $this->getRequestUrl();
98
		if (\preg_match('%/apps/music/?$%', $url) || \preg_match('%/apps/dashboard/?$%', $url)) {
99
			$this->adjustCsp();
100
		}
101
	}
102
103
	/**
104
	 * Load embedded music player for Files and Sharing apps
105
	 */
106
	public function loadEmbeddedMusicPlayer() : void {
107
		\OCA\Music\Utility\HtmlUtil::addWebpackScript('files_music_player');
108
		\OCA\Music\Utility\HtmlUtil::addWebpackStyle('files_music_player');
109
		$this->adjustCsp();
110
	}
111
112
	/**
113
	 * Wrapper to get a service from the container, hiding the differences between the cloud versions.
114
	 * @param string $id A fully-qualified class name of an autoloadable class or other registered service ID
115
	 * @return mixed
116
	 */
117
	public function get(string $id) {
118
		$container = $this->getContainer();
119
		if (\method_exists($container, 'get')) { // @phpstan-ignore function.alreadyNarrowedType
120
			return $container->get($id); // IAppContainer::get exists on NC20+
121
		} else {
122
			return $container->query($id); // On ownCloud, we use IAppContainer::query which is deprecated on NC20+
0 ignored issues
show
Deprecated Code introduced by
The function OCP\IContainer::query() has been deprecated: 20.0.0 use \Psr\Container\ContainerInterface::get ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

122
			return /** @scrutinizer ignore-deprecated */ $container->query($id); // On ownCloud, we use IAppContainer::query which is deprecated on NC20+

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
123
		}
124
	}
125
126
	private function getRequestUrl() : string {
127
		$request = $this->get(IRequest::class);
128
		$url = $request->server['REQUEST_URI'] ?? '';
129
		$url = \explode('?', $url)[0]; // get rid of any query args
130
		$url = \explode('#', $url)[0]; // get rid of any hash part
131
		return $url;
132
	}
133
134
	private function registerHooks() : void {
135
		$this->get(FileHooks::class)->register();
136
		$this->get(ShareHooks::class)->register();
137
		$this->get(UserHooks::class)->register();
138
	}
139
140
	private function registerEmbeddedPlayer() : void {
141
		$dispatcher = $this->get(\OCP\EventDispatcher\IEventDispatcher::class);
142
143
		// Files app
144
		$dispatcher->addListener(\OCA\Files\Event\LoadAdditionalScriptsEvent::class, function() {
145
			$this->loadEmbeddedMusicPlayer();
146
		});
147
148
		// Files_Sharing app
149
		$dispatcher->addListener(\OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent::class, function(\OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent $event) {
150
			// don't load the embedded player on the authentication page of password-protected share, and only load it for shared folders (not individual files)
151
			if ($event->getScope() != \OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH
152
					&& $event->getShare()->getNodeType() == 'folder') {
153
				$this->loadEmbeddedMusicPlayer();
154
			}
155
		});
156
	}
157
158
	/**
159
	 * Set content security policy to allow streaming media from the configured external sources
160
	 */
161
	private function adjustCsp() : void {
162
		/** @var IConfig $config */
163
		$config = $this->get(IConfig::class);
164
		$radioSources = $config->getSystemValue('music.allowed_stream_src', []);
165
166
		if (\is_string($radioSources)) {
167
			$radioSources = [$radioSources];
168
		}
169
170
		$policy = new \OCP\AppFramework\Http\ContentSecurityPolicy();
171
172
		foreach ($radioSources as $source) {
173
			$policy->addAllowedMediaDomain($source);
174
		}
175
176
		// The media sources 'data:' and 'blob:' are needed for HLS streaming
177
		if (self::hlsEnabled($config, $this->get('userId'))) {
178
			$policy->addAllowedMediaDomain('data:');
179
			$policy->addAllowedMediaDomain('blob:');
180
		}
181
182
		$this->get(IContentSecurityPolicyManager::class)->addDefaultPolicy($policy);
183
	}
184
185
	private static function hlsEnabled(IConfig $config, ?string $userId) : bool {
186
		$enabled = $config->getSystemValue('music.enable_radio_hls', true);
187
		if (empty($userId)) {
188
			$enabled = (bool)$config->getSystemValue('music.enable_radio_hls_on_share', $enabled);
189
		}
190
		return $enabled;
191
	}
192
}
193