Application::get()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 6
rs 10
c 0
b 0
f 0
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\AppFramework\Core\Logger;
18
use OCA\Music\BusinessLayer\AlbumBusinessLayer;
19
use OCA\Music\BusinessLayer\TrackBusinessLayer;
20
use OCA\Music\Hooks\FileHooks;
21
use OCA\Music\Hooks\ShareHooks;
22
use OCA\Music\Hooks\UserHooks;
23
24
use OCA\Music\Middleware\AmpacheMiddleware;
25
use OCA\Music\Middleware\SubsonicMiddleware;
26
27
use OCA\Music\Service\AggregateScrobbler;
28
use OCA\Music\Service\ExternalScrobbler;
29
use OCA\Music\Service\Scrobbler;
30
use OCP\AppFramework\App;
31
use OCP\AppFramework\IAppContainer;
32
use OCP\Files\IMimeTypeLoader;
33
use OCP\IConfig;
34
use OCP\IRequest;
35
use OCP\IURLGenerator;
36
use OCP\Security\IContentSecurityPolicyManager;
37
use OCP\Security\ICrypto;
38
39
// The IBootstrap interface is not available on ownCloud. Create a thin base class to hide this difference
40
// from the actual Application class.
41
function useOwncloudBootstrapping() : bool {
42
	return (\OCA\Music\Utility\AppInfo::getVendor() == 'owncloud');
43
}
44
45
if (useOwncloudBootstrapping()) {
46
	class ApplicationBase extends App {}
47
} else {
48
	abstract class ApplicationBase extends App implements \OCP\AppFramework\Bootstrap\IBootstrap {}
49
}
50
51
class Application extends ApplicationBase {
52
	public function __construct(array $urlParams=[]) {
53
		parent::__construct('music', $urlParams);
54
55
		\mb_internal_encoding('UTF-8');
56
57
		// On ownCloud, the registrations must happen already within the constructor
58
		if (useOwncloudBootstrapping()) {
59
			$container = $this->getContainer();
60
			// this is not registered by the ownCloud core
61
			$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

61
			/** @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...
62
				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

62
				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

62
				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...
63
			});
64
65
			// Unlike Nextcloud, ownCloud is not able to autoload the classes directly within registerMiddleWare.
66
			// We have to fetch each middleware once so that the instances are already cached when registerMiddleWare is called.
67
			$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

67
			/** @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...
68
			$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

68
			/** @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...
69
70
			$this->registerMiddleWares($container);
71
			$this->registerScrobblers($container);
72
		}
73
	}
74
75
	/**
76
	 * @param mixed $context On Nextcloud, this is \OCP\AppFramework\Bootstrap\IRegistrationContext.
77
	 *                       On ownCloud, this is \OCP\AppFramework\IAppContainer.
78
	 */
79
	private function registerMiddleWares($context) : void {
80
		$context->registerMiddleWare(AmpacheMiddleware::class);
81
		$context->registerMiddleWare(SubsonicMiddleware::class);
82
	}
83
84
	/**
85
	 * This gets called on Nextcloud but not on ownCloud
86
	 * @param \OCP\AppFramework\Bootstrap\IRegistrationContext $context
87
	 */
88
	public function register($context) : void {
89
		$this->registerMiddleWares($context);
90
		$this->registerScrobblers($context);
91
		$context->registerDashboardWidget(\OCA\Music\Dashboard\MusicWidget::class);
92
	}
93
94
	/**
95
	 * This gets called on Nextcloud but not on ownCloud
96
	 * @param \OCP\AppFramework\Bootstrap\IBootContext $context
97
	 */
98
	public function boot($context) : void {
99
		$this->init();
100
		$this->registerEmbeddedPlayer();
101
	}
102
103
	public function init() : void {
104
		$this->registerHooks();
105
106
		// Adjust the CSP if loading the Music app proper or the NC dashboard
107
		$url = $this->getRequestUrl();
108
		if (\preg_match('%/apps/music/?$%', $url) || \preg_match('%/apps/dashboard/?$%', $url)) {
109
			$this->adjustCsp();
110
		}
111
	}
112
113
	/**
114
	 * Load embedded music player for Files and Sharing apps
115
	 */
116
	public function loadEmbeddedMusicPlayer() : void {
117
		\OCA\Music\Utility\HtmlUtil::addWebpackScript('files_music_player');
118
		\OCA\Music\Utility\HtmlUtil::addWebpackStyle('files_music_player');
119
		$this->adjustCsp();
120
	}
121
122
	/**
123
	 * Wrapper to get a service from the container, hiding the differences between the cloud versions.
124
	 * @param string $id A fully-qualified class name of an autoloadable class or other registered service ID
125
	 * @return mixed
126
	 */
127
	public function get(string $id) {
128
		$container = $this->getContainer();
129
		if (\method_exists($container, 'get')) { // @phpstan-ignore function.alreadyNarrowedType
130
			return $container->get($id); // IAppContainer::get exists on NC20+
131
		} else {
132
			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

132
			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...
133
		}
134
	}
135
136
	private function getRequestUrl() : string {
137
		$request = $this->get(IRequest::class);
138
		$url = $request->server['REQUEST_URI'] ?? '';
139
		$url = \explode('?', $url)[0]; // get rid of any query args
140
		$url = \explode('#', $url)[0]; // get rid of any hash part
141
		return $url;
142
	}
143
144
	private function registerHooks() : void {
145
		$this->get(FileHooks::class)->register();
146
		$this->get(ShareHooks::class)->register();
147
		$this->get(UserHooks::class)->register();
148
	}
149
150
	private function registerEmbeddedPlayer() : void {
151
		$dispatcher = $this->get(\OCP\EventDispatcher\IEventDispatcher::class);
152
153
		// Files app
154
		$dispatcher->addListener(\OCA\Files\Event\LoadAdditionalScriptsEvent::class, function () {
155
			$this->loadEmbeddedMusicPlayer();
156
		});
157
158
		// Files_Sharing app
159
		$dispatcher->addListener(\OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent::class, function (\OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent $event) {
160
			// don't load the embedded player on the authentication page of password-protected share, and only load it for shared folders (not individual files)
161
			if ($event->getScope() != \OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent::SCOPE_PUBLIC_SHARE_AUTH
162
					&& $event->getShare()->getNodeType() == 'folder') {
163
				$this->loadEmbeddedMusicPlayer();
164
			}
165
		});
166
	}
167
168
	/**
169
	 * Set content security policy to allow streaming media from the configured external sources
170
	 */
171
	private function adjustCsp() : void {
172
		/** @var IConfig $config */
173
		$config = $this->get(IConfig::class);
174
		$radioSources = $config->getSystemValue('music.allowed_stream_src', []);
175
176
		if (\is_string($radioSources)) {
177
			$radioSources = [$radioSources];
178
		}
179
180
		$policy = new \OCP\AppFramework\Http\ContentSecurityPolicy();
181
182
		foreach ($radioSources as $source) {
183
			$policy->addAllowedMediaDomain($source);
184
		}
185
186
		// The media sources 'data:' and 'blob:' are needed for HLS streaming
187
		if (self::hlsEnabled($config, $this->get('userId'))) {
188
			$policy->addAllowedMediaDomain('data:');
189
			$policy->addAllowedMediaDomain('blob:');
190
		}
191
192
		$this->get(IContentSecurityPolicyManager::class)->addDefaultPolicy($policy);
193
	}
194
195
	private static function hlsEnabled(IConfig $config, ?string $userId) : bool {
196
		$enabled = $config->getSystemValue('music.enable_radio_hls', true);
197
		if (empty($userId)) {
198
			$enabled = (bool)$config->getSystemValue('music.enable_radio_hls_on_share', $enabled);
199
		}
200
		return $enabled;
201
	}
202
203
	/**
204
	 * @param mixed $context On Nextcloud, this is \OCP\AppFramework\Bootstrap\IRegistrationContext.
205
	 *                       On ownCloud, this is \OCP\AppFramework\IAppContainer.
206
	 */
207
	private function registerScrobblers($context) : void {
208
		$context->registerService('externalScrobblers', function () {
209
			return [
210
				new ExternalScrobbler(
211
					$this->get(IConfig::class),
212
					$this->get(Logger::class),
213
					$this->get(IURLGenerator::class),
214
					$this->get(TrackBusinessLayer::class),
215
					$this->get(AlbumBusinessLayer::class),
216
					$this->get(ICrypto::class),
217
					'Last.fm',
218
					'lastfm',
219
					'http://ws.audioscrobbler.com/2.0/',
220
					'http://www.last.fm/api/auth/',
221
					$this->get('appName')
222
				)
223
			];
224
		});
225
226
		$context->registerService(Scrobbler::class, function () {
227
			$scrobblers = $this->get('externalScrobblers');
228
			$scrobblers[] = $this->get(TrackBusinessLayer::class);
229
			return new AggregateScrobbler($scrobblers);
230
		});
231
	}
232
}
233