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
|
|||||||
54 | return $c->getServer()->getMimeTypeLoader(); |
||||||
0 ignored issues
–
show
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
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. ![]() 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
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. ![]() |
|||||||
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
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
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. ![]() |
|||||||
60 | $container->query(SubsonicMiddleware::class); |
||||||
0 ignored issues
–
show
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
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. ![]() |
|||||||
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
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
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. ![]() |
|||||||
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 |
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.