1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @copyright Copyright (c) 2016 Lukas Reschke <[email protected]> |
4
|
|
|
* |
5
|
|
|
* @author Lukas Reschke <[email protected]> |
6
|
|
|
* @author Roeland Jago Douma <[email protected]> |
7
|
|
|
* |
8
|
|
|
* @license GNU AGPL version 3 or any later version |
9
|
|
|
* |
10
|
|
|
* This program is free software: you can redistribute it and/or modify |
11
|
|
|
* it under the terms of the GNU Affero General Public License as |
12
|
|
|
* published by the Free Software Foundation, either version 3 of the |
13
|
|
|
* License, or (at your option) any later version. |
14
|
|
|
* |
15
|
|
|
* This program is distributed in the hope that it will be useful, |
16
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
17
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18
|
|
|
* GNU Affero General Public License for more details. |
19
|
|
|
* |
20
|
|
|
* You should have received a copy of the GNU Affero General Public License |
21
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
22
|
|
|
* |
23
|
|
|
*/ |
24
|
|
|
|
25
|
|
|
namespace OCA\Richdocuments\AppInfo; |
26
|
|
|
|
27
|
|
|
use OC\EventDispatcher\SymfonyAdapter; |
28
|
|
|
use OC\Files\Type\Detection; |
29
|
|
|
use OC\Security\CSP\ContentSecurityPolicy; |
30
|
|
|
use OCA\Federation\TrustedServers; |
31
|
|
|
use OCA\Richdocuments\AppConfig; |
32
|
|
|
use OCA\Richdocuments\Capabilities; |
33
|
|
|
use OCA\Richdocuments\PermissionManager; |
34
|
|
|
use OCA\Richdocuments\Preview\MSExcel; |
35
|
|
|
use OCA\Richdocuments\Preview\MSWord; |
36
|
|
|
use OCA\Richdocuments\Preview\OOXML; |
37
|
|
|
use OCA\Richdocuments\Preview\OpenDocument; |
38
|
|
|
use OCA\Richdocuments\Preview\Pdf; |
39
|
|
|
use OCA\Richdocuments\Service\CapabilitiesService; |
40
|
|
|
use OCA\Richdocuments\Service\FederationService; |
41
|
|
|
use OCA\Richdocuments\Template\CollaboraTemplateProvider; |
42
|
|
|
use OCA\Richdocuments\WOPI\DiscoveryManager; |
43
|
|
|
use OCA\Viewer\Event\LoadViewer; |
44
|
|
|
use OCP\AppFramework\App; |
45
|
|
|
use OCP\AppFramework\Bootstrap\IBootContext; |
46
|
|
|
use OCP\AppFramework\Bootstrap\IBootstrap; |
47
|
|
|
use OCP\AppFramework\Bootstrap\IRegistrationContext; |
48
|
|
|
use OCP\EventDispatcher\IEventDispatcher; |
49
|
|
|
use OCP\Files\Template\ITemplateManager; |
50
|
|
|
use OCP\Files\Template\TemplateFileCreator; |
51
|
|
|
use OCP\GlobalScale\IConfig; |
52
|
|
|
use OCP\IPreview; |
53
|
|
|
|
54
|
|
|
class Application extends App implements IBootstrap { |
55
|
|
|
|
56
|
|
|
public const APPNAME = 'richdocuments'; |
57
|
|
|
|
58
|
|
|
public function __construct(array $urlParams = array()) { |
59
|
|
|
parent::__construct(self::APPNAME, $urlParams); |
60
|
|
|
$this->getContainer()->registerCapability(Capabilities::class); |
|
|
|
|
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
|
64
|
|
|
public function register(IRegistrationContext $context): void { |
65
|
|
|
$context->registerTemplateProvider(CollaboraTemplateProvider::class); |
66
|
|
|
$context->registerCapability(Capabilities::class); |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
public function boot(IBootContext $context): void { |
70
|
|
|
$currentUser = \OC::$server->getUserSession()->getUser(); |
71
|
|
|
if($currentUser !== null) { |
72
|
|
|
/** @var PermissionManager $permissionManager */ |
73
|
|
|
$permissionManager = \OC::$server->query(PermissionManager::class); |
74
|
|
|
if(!$permissionManager->isEnabledForUser($currentUser)) { |
75
|
|
|
return; |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** @var IEventDispatcher $eventDispatcher */ |
80
|
|
|
$eventDispatcher = $this->getContainer()->getServer()->query(IEventDispatcher::class); |
|
|
|
|
81
|
|
|
$eventDispatcher->addListener(LoadViewer::class, function () { |
82
|
|
|
\OCP\Util::addScript('richdocuments', 'viewer'); |
83
|
|
|
}); |
84
|
|
|
|
85
|
|
|
$context->injectFn(function(ITemplateManager $templateManager) { |
86
|
|
|
$templateManager->registerTemplateFileCreator(function () { |
87
|
|
|
$odtType = new TemplateFileCreator('richdocuments', 'New document', '.odt'); |
88
|
|
|
$odtType->addMimetype('application/vnd.oasis.opendocument.text'); |
89
|
|
|
$odtType->addMimetype('application/vnd.oasis.opendocument.text-template'); |
90
|
|
|
$odtType->setIconClass('icon-filetype-document'); |
91
|
|
|
$odtType->setRatio(21/29.7); |
92
|
|
|
return $odtType; |
93
|
|
|
}); |
94
|
|
|
$templateManager->registerTemplateFileCreator(function () { |
95
|
|
|
$odsType = new TemplateFileCreator('richdocuments', 'New spreadsheet', '.ods'); |
96
|
|
|
$odsType->addMimetype('application/vnd.oasis.opendocument.spreadsheet'); |
97
|
|
|
$odsType->addMimetype('application/vnd.oasis.opendocument.spreadsheet-template'); |
98
|
|
|
$odsType->setIconClass('icon-filetype-spreadsheet'); |
99
|
|
|
$odsType->setRatio(16/9); |
100
|
|
|
return $odsType; |
101
|
|
|
}); |
102
|
|
|
$templateManager->registerTemplateFileCreator(function () { |
103
|
|
|
$odpType = new TemplateFileCreator('richdocuments', 'New presentation', '.odp'); |
104
|
|
|
$odpType->addMimetype('application/vnd.oasis.opendocument.presentation'); |
105
|
|
|
$odpType->addMimetype('application/vnd.oasis.opendocument.presentation-template'); |
106
|
|
|
$odpType->setIconClass('icon-filetype-presentation'); |
107
|
|
|
$odpType->setRatio(16/9); |
108
|
|
|
return $odpType; |
109
|
|
|
}); |
110
|
|
|
}); |
111
|
|
|
|
112
|
|
|
$context->injectFn(function (SymfonyAdapter $eventDispatcher) { |
113
|
|
|
$eventDispatcher->addListener('OCA\Files::loadAdditionalScripts', |
114
|
|
|
function() { |
115
|
|
|
\OCP\Util::addScript('richdocuments', 'files'); |
116
|
|
|
} |
117
|
|
|
); |
118
|
|
|
$eventDispatcher->addListener('OCA\Files_Sharing::loadAdditionalScripts', |
119
|
|
|
function() { |
120
|
|
|
\OCP\Util::addScript('richdocuments', 'files'); |
121
|
|
|
} |
122
|
|
|
); |
123
|
|
|
|
124
|
|
|
if (class_exists('\OC\Files\Type\TemplateManager')) { |
125
|
|
|
$manager = \OC_Helper::getFileTemplateManager(); |
126
|
|
|
|
127
|
|
|
$manager->registerTemplate('application/vnd.openxmlformats-officedocument.wordprocessingml.document', dirname(__DIR__) . '/assets/docxtemplate.docx'); |
128
|
|
|
$manager->registerTemplate('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', dirname(__DIR__) . '/assets/xlsxtemplate.xlsx'); |
129
|
|
|
$manager->registerTemplate('application/vnd.openxmlformats-officedocument.presentationml.presentation', dirname(__DIR__) . '/assets/pptxtemplate.pptx'); |
130
|
|
|
$manager->registerTemplate('application/vnd.oasis.opendocument.presentation', dirname(__DIR__) . '/assets/template.odp'); |
131
|
|
|
$manager->registerTemplate('application/vnd.oasis.opendocument.text', dirname(__DIR__) . '/assets/template.odt'); |
132
|
|
|
$manager->registerTemplate('application/vnd.oasis.opendocument.spreadsheet', dirname(__DIR__) . '/assets/template.ods'); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
$this->registerProvider(); |
136
|
|
|
$this->updateCSP(); |
137
|
|
|
$this->checkAndEnableCODEServer(); |
138
|
|
|
}); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
public function registerProvider() { |
142
|
|
|
$container = $this->getContainer(); |
143
|
|
|
|
144
|
|
|
// Register mimetypes |
145
|
|
|
/** @var Detection $detector */ |
146
|
|
|
$detector = $container->query(\OCP\Files\IMimeTypeDetector::class); |
|
|
|
|
147
|
|
|
$detector->getAllMappings(); |
148
|
|
|
$detector->registerType('ott','application/vnd.oasis.opendocument.text-template'); |
149
|
|
|
$detector->registerType('ots', 'application/vnd.oasis.opendocument.spreadsheet-template'); |
150
|
|
|
$detector->registerType('otp', 'application/vnd.oasis.opendocument.presentation-template'); |
151
|
|
|
|
152
|
|
|
/** @var IPreview $previewManager */ |
153
|
|
|
$previewManager = $container->query(IPreview::class); |
|
|
|
|
154
|
|
|
|
155
|
|
|
$previewManager->registerProvider('/application\/vnd.ms-excel/', function() use ($container) { |
156
|
|
|
return $container->query(MSExcel::class); |
|
|
|
|
157
|
|
|
}); |
158
|
|
|
|
159
|
|
|
$previewManager->registerProvider('/application\/msword/', function() use ($container) { |
160
|
|
|
return $container->query(MSWord::class); |
|
|
|
|
161
|
|
|
}); |
162
|
|
|
|
163
|
|
|
$previewManager->registerProvider('/application\/vnd.openxmlformats-officedocument.*/', function() use ($container) { |
164
|
|
|
return $container->query(OOXML::class); |
|
|
|
|
165
|
|
|
}); |
166
|
|
|
|
167
|
|
|
// \OC::$server->getLogger()->debug('==== Richdocuments Application registerProvider: calling manager registerProvider:'); |
168
|
|
|
$previewManager->registerProvider('/application\/vnd.oasis.opendocument.*/', function() use ($container) { |
169
|
|
|
// \OC::$server->getLogger()->debug('==== Richdocuments Application registerProvider lambda. OpenDocument::class=' . OpenDocument::class); |
170
|
|
|
return $container->query(OpenDocument::class); |
|
|
|
|
171
|
|
|
}); |
172
|
|
|
|
173
|
|
|
$previewManager->registerProvider('/application\/pdf/', function() use ($container) { |
174
|
|
|
return $container->query(Pdf::class); |
|
|
|
|
175
|
|
|
}); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
public function updateCSP() { |
179
|
|
|
$container = $this->getContainer(); |
180
|
|
|
|
181
|
|
|
$publicWopiUrl = $container->getServer()->getConfig()->getAppValue('richdocuments', 'public_wopi_url', ''); |
|
|
|
|
182
|
|
|
$publicWopiUrl = $publicWopiUrl === '' ? \OC::$server->getConfig()->getAppValue('richdocuments', 'wopi_url') : $publicWopiUrl; |
183
|
|
|
$cspManager = $container->getServer()->getContentSecurityPolicyManager(); |
|
|
|
|
184
|
|
|
$policy = new ContentSecurityPolicy(); |
185
|
|
|
if ($publicWopiUrl !== '') { |
186
|
|
|
$policy->addAllowedFrameDomain('\'self\''); |
187
|
|
|
$policy->addAllowedFrameDomain($this->domainOnly($publicWopiUrl)); |
188
|
|
|
if (method_exists($policy, 'addAllowedFormActionDomain')) { |
189
|
|
|
$policy->addAllowedFormActionDomain($this->domainOnly($publicWopiUrl)); |
190
|
|
|
} |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* Dynamically add CSP for federated editing |
195
|
|
|
*/ |
196
|
|
|
$path = ''; |
197
|
|
|
try { |
198
|
|
|
$path = $container->getServer()->getRequest()->getPathInfo(); |
|
|
|
|
199
|
|
|
} catch (\Exception $e) {} |
|
|
|
|
200
|
|
|
if (strpos($path, '/apps/files/') === 0 && $container->getServer()->getAppManager()->isEnabledForUser('federation')) { |
|
|
|
|
201
|
|
|
/** @var FederationService $federationService */ |
202
|
|
|
$federationService = \OC::$server->query(FederationService::class); |
203
|
|
|
|
204
|
|
|
// Always add trusted servers on global scale |
205
|
|
|
/** @var IConfig $globalScale */ |
206
|
|
|
$globalScale = $container->query(IConfig::class); |
|
|
|
|
207
|
|
|
if ($globalScale->isGlobalScaleEnabled()) { |
208
|
|
|
$trustedList = \OC::$server->getConfig()->getSystemValue('gs.trustedHosts', []); |
209
|
|
|
foreach ($trustedList as $server) { |
210
|
|
|
$this->addTrustedRemote($policy, $server); |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
$remoteAccess = $container->getServer()->getRequest()->getParam('richdocuments_remote_access'); |
|
|
|
|
214
|
|
|
|
215
|
|
|
if ($remoteAccess && $federationService->isTrustedRemote($remoteAccess)) { |
216
|
|
|
$this->addTrustedRemote($policy, $remoteAccess); |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
$cspManager->addDefaultPolicy($policy); |
|
|
|
|
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
private function addTrustedRemote($policy, $url) { |
224
|
|
|
/** @var FederationService $federationService */ |
225
|
|
|
$federationService = \OC::$server->query(FederationService::class); |
226
|
|
|
try { |
227
|
|
|
$remoteCollabora = $federationService->getRemoteCollaboraURL($url); |
228
|
|
|
$policy->addAllowedFrameDomain($url); |
229
|
|
|
$policy->addAllowedFrameDomain($remoteCollabora); |
230
|
|
|
} catch (\Exception $e) { |
231
|
|
|
// We can ignore this exception for adding predefined domains to the CSP as it it would then just |
232
|
|
|
// reload the page to set a proper allowed frame domain if we don't have a fixed list of trusted |
233
|
|
|
// remotes in a global scale scenario |
234
|
|
|
} |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
public function checkAndEnableCODEServer() { |
238
|
|
|
// Supported only on Linux OS, and x86_64 & ARM64 platforms |
239
|
|
|
$supportedArchs = array('x86_64', 'aarch64'); |
240
|
|
|
$osFamily = PHP_VERSION_ID >= 70200 ? PHP_OS_FAMILY : PHP_OS; |
241
|
|
|
if ($osFamily !== 'Linux' || !in_array(php_uname('m'), $supportedArchs)) |
242
|
|
|
return; |
243
|
|
|
|
244
|
|
|
$CODEAppID = (php_uname('m') === 'x86_64') ? 'richdocumentscode' : 'richdocumentscode_arm64'; |
245
|
|
|
|
246
|
|
|
if ($this->getContainer()->getServer()->getAppManager()->isEnabledForUser($CODEAppID)) { |
|
|
|
|
247
|
|
|
$appConfig = $this->getContainer()->query(AppConfig::class); |
|
|
|
|
248
|
|
|
$wopi_url = $appConfig->getAppValue('wopi_url'); |
249
|
|
|
$isCODEEnabled = strpos($wopi_url, 'proxy.php?req=') !== false; |
250
|
|
|
|
251
|
|
|
// Check if we have the wopi_url set to custom currently |
252
|
|
|
if ($wopi_url !== null && $wopi_url !== '' && $isCODEEnabled === false) { |
253
|
|
|
return; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
$urlGenerator = \OC::$server->getURLGenerator(); |
257
|
|
|
$relativeUrl = $urlGenerator->linkTo($CODEAppID, '') . 'proxy.php'; |
258
|
|
|
$absoluteUrl = $urlGenerator->getAbsoluteURL($relativeUrl); |
259
|
|
|
$new_wopi_url = $absoluteUrl . '?req='; |
260
|
|
|
|
261
|
|
|
// Check if the wopi url needs to be updated |
262
|
|
|
if ($isCODEEnabled && $wopi_url === $new_wopi_url) { |
263
|
|
|
return; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
$appConfig->setAppValue('wopi_url', $new_wopi_url); |
267
|
|
|
$appConfig->setAppValue('disable_certificate_verification', 'yes'); |
268
|
|
|
|
269
|
|
|
$discoveryManager = $this->getContainer()->query(DiscoveryManager::class); |
|
|
|
|
270
|
|
|
$capabilitiesService = $this->getContainer()->query(CapabilitiesService::class); |
|
|
|
|
271
|
|
|
|
272
|
|
|
$discoveryManager->refetch(); |
273
|
|
|
$capabilitiesService->clear(); |
274
|
|
|
$capabilitiesService->refetch(); |
275
|
|
|
} |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
/** |
279
|
|
|
* Strips the path and query parameters from the URL. |
280
|
|
|
* |
281
|
|
|
* @param string $url |
282
|
|
|
* @return string |
283
|
|
|
*/ |
284
|
|
|
private function domainOnly($url) { |
285
|
|
|
$parsed_url = parse_url($url); |
286
|
|
|
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : ''; |
287
|
|
|
$host = isset($parsed_url['host']) ? $parsed_url['host'] : ''; |
288
|
|
|
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : ''; |
289
|
|
|
return "$scheme$host$port"; |
290
|
|
|
} |
291
|
|
|
} |
292
|
|
|
|
This method has been deprecated. The supplier of the class has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.