ShareController.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
  6. * @author Bjoern Schiessle <bjoern@schiessle.org>
  7. * @author Björn Schießle <bjoern@schiessle.org>
  8. * @author Georg Ehrke <oc.list@georgehrke.com>
  9. * @author Joas Schilling <coding@schilljs.com>
  10. * @author Lukas Reschke <lukas@statuscode.ch>
  11. * @author Maxence Lange <maxence@pontapreta.net>
  12. * @author Morris Jobke <hey@morrisjobke.de>
  13. * @author Piotr Filiciak <piotr@filiciak.pl>
  14. * @author Robin Appelman <robin@icewind.nl>
  15. * @author Roeland Jago Douma <roeland@famdouma.nl>
  16. * @author Sascha Sambale <mastixmc@gmail.com>
  17. * @author Thomas Müller <thomas.mueller@tmit.eu>
  18. * @author Vincent Petry <pvince81@owncloud.com>
  19. *
  20. * @license AGPL-3.0
  21. *
  22. * This code is free software: you can redistribute it and/or modify
  23. * it under the terms of the GNU Affero General Public License, version 3,
  24. * as published by the Free Software Foundation.
  25. *
  26. * This program is distributed in the hope that it will be useful,
  27. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  29. * GNU Affero General Public License for more details.
  30. *
  31. * You should have received a copy of the GNU Affero General Public License, version 3,
  32. * along with this program. If not, see <http://www.gnu.org/licenses/>
  33. *
  34. */
  35. namespace OCA\Files_Sharing\Controller;
  36. use OC\Security\CSP\ContentSecurityPolicy;
  37. use OC_Files;
  38. use OC_Util;
  39. use OCA\FederatedFileSharing\FederatedShareProvider;
  40. use OCP\AppFramework\AuthPublicShareController;
  41. use OCP\AppFramework\Http\Template\SimpleMenuAction;
  42. use OCP\AppFramework\Http\Template\ExternalShareMenuAction;
  43. use OCP\AppFramework\Http\Template\LinkMenuAction;
  44. use OCP\AppFramework\Http\Template\PublicTemplateResponse;
  45. use OCP\Defaults;
  46. use OCP\IL10N;
  47. use OCP\Template;
  48. use OCP\Share;
  49. use OCP\IRequest;
  50. use OCP\AppFramework\Http\TemplateResponse;
  51. use OCP\AppFramework\Http\NotFoundResponse;
  52. use OCP\IURLGenerator;
  53. use OCP\IConfig;
  54. use OCP\ILogger;
  55. use OCP\IUserManager;
  56. use OCP\ISession;
  57. use OCP\IPreview;
  58. use OCA\Files_Sharing\Activity\Providers\Downloads;
  59. use OCP\Files\NotFoundException;
  60. use OCP\Files\IRootFolder;
  61. use OCP\Share\Exceptions\ShareNotFound;
  62. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  63. use Symfony\Component\EventDispatcher\GenericEvent;
  64. use OCP\Share\IManager as ShareManager;
  65. /**
  66. * Class ShareController
  67. *
  68. * @package OCA\Files_Sharing\Controllers
  69. */
  70. class ShareController extends AuthPublicShareController {
  71. /** @var IConfig */
  72. protected $config;
  73. /** @var IUserManager */
  74. protected $userManager;
  75. /** @var ILogger */
  76. protected $logger;
  77. /** @var \OCP\Activity\IManager */
  78. protected $activityManager;
  79. /** @var IPreview */
  80. protected $previewManager;
  81. /** @var IRootFolder */
  82. protected $rootFolder;
  83. /** @var FederatedShareProvider */
  84. protected $federatedShareProvider;
  85. /** @var EventDispatcherInterface */
  86. protected $eventDispatcher;
  87. /** @var IL10N */
  88. protected $l10n;
  89. /** @var Defaults */
  90. protected $defaults;
  91. /** @var ShareManager */
  92. protected $shareManager;
  93. /** @var Share\IShare */
  94. protected $share;
  95. /**
  96. * @param string $appName
  97. * @param IRequest $request
  98. * @param IConfig $config
  99. * @param IURLGenerator $urlGenerator
  100. * @param IUserManager $userManager
  101. * @param ILogger $logger
  102. * @param \OCP\Activity\IManager $activityManager
  103. * @param \OCP\Share\IManager $shareManager
  104. * @param ISession $session
  105. * @param IPreview $previewManager
  106. * @param IRootFolder $rootFolder
  107. * @param FederatedShareProvider $federatedShareProvider
  108. * @param EventDispatcherInterface $eventDispatcher
  109. * @param IL10N $l10n
  110. * @param Defaults $defaults
  111. */
  112. public function __construct(string $appName,
  113. IRequest $request,
  114. IConfig $config,
  115. IURLGenerator $urlGenerator,
  116. IUserManager $userManager,
  117. ILogger $logger,
  118. \OCP\Activity\IManager $activityManager,
  119. ShareManager $shareManager,
  120. ISession $session,
  121. IPreview $previewManager,
  122. IRootFolder $rootFolder,
  123. FederatedShareProvider $federatedShareProvider,
  124. EventDispatcherInterface $eventDispatcher,
  125. IL10N $l10n,
  126. Defaults $defaults) {
  127. parent::__construct($appName, $request, $session, $urlGenerator);
  128. $this->config = $config;
  129. $this->userManager = $userManager;
  130. $this->logger = $logger;
  131. $this->activityManager = $activityManager;
  132. $this->previewManager = $previewManager;
  133. $this->rootFolder = $rootFolder;
  134. $this->federatedShareProvider = $federatedShareProvider;
  135. $this->eventDispatcher = $eventDispatcher;
  136. $this->l10n = $l10n;
  137. $this->defaults = $defaults;
  138. $this->shareManager = $shareManager;
  139. }
  140. /**
  141. * @PublicPage
  142. * @NoCSRFRequired
  143. *
  144. * Show the authentication page
  145. * The form has to submit to the authenticate method route
  146. */
  147. public function showAuthenticate(): TemplateResponse {
  148. $templateParameters = ['share' => $this->share];
  149. $event = new GenericEvent(null, $templateParameters);
  150. $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
  151. $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
  152. if ($this->share->getSendPasswordByTalk()) {
  153. $csp = new ContentSecurityPolicy();
  154. $csp->addAllowedConnectDomain('*');
  155. $csp->addAllowedMediaDomain('blob:');
  156. $csp->allowEvalScript(true);
  157. $response->setContentSecurityPolicy($csp);
  158. }
  159. return $response;
  160. }
  161. /**
  162. * The template to show when authentication failed
  163. */
  164. protected function showAuthFailed(): TemplateResponse {
  165. $templateParameters = ['share' => $this->share, 'wrongpw' => true];
  166. $event = new GenericEvent(null, $templateParameters);
  167. $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
  168. $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
  169. if ($this->share->getSendPasswordByTalk()) {
  170. $csp = new ContentSecurityPolicy();
  171. $csp->addAllowedConnectDomain('*');
  172. $csp->addAllowedMediaDomain('blob:');
  173. $csp->allowEvalScript(true);
  174. $response->setContentSecurityPolicy($csp);
  175. }
  176. return $response;
  177. }
  178. protected function verifyPassword(string $password): bool {
  179. return $this->shareManager->checkPassword($this->share, $password);
  180. }
  181. protected function getPasswordHash(): string {
  182. return $this->share->getPassword();
  183. }
  184. public function isValidToken(): bool {
  185. try {
  186. $this->share = $this->shareManager->getShareByToken($this->getToken());
  187. } catch (ShareNotFound $e) {
  188. return false;
  189. }
  190. return true;
  191. }
  192. protected function isPasswordProtected(): bool {
  193. return $this->share->getPassword() !== null;
  194. }
  195. protected function authSucceeded() {
  196. // For share this was always set so it is still used in other apps
  197. $this->session->set('public_link_authenticated', (string)$this->share->getId());
  198. }
  199. protected function authFailed() {
  200. $this->emitAccessShareHook($this->share, 403, 'Wrong password');
  201. }
  202. /**
  203. * throws hooks when a share is attempted to be accessed
  204. *
  205. * @param \OCP\Share\IShare|string $share the Share instance if available,
  206. * otherwise token
  207. * @param int $errorCode
  208. * @param string $errorMessage
  209. * @throws \OC\HintException
  210. * @throws \OC\ServerNotAvailableException
  211. */
  212. protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
  213. $itemType = $itemSource = $uidOwner = '';
  214. $token = $share;
  215. $exception = null;
  216. if($share instanceof \OCP\Share\IShare) {
  217. try {
  218. $token = $share->getToken();
  219. $uidOwner = $share->getSharedBy();
  220. $itemType = $share->getNodeType();
  221. $itemSource = $share->getNodeId();
  222. } catch (\Exception $e) {
  223. // we log what we know and pass on the exception afterwards
  224. $exception = $e;
  225. }
  226. }
  227. \OC_Hook::emit(Share::class, 'share_link_access', [
  228. 'itemType' => $itemType,
  229. 'itemSource' => $itemSource,
  230. 'uidOwner' => $uidOwner,
  231. 'token' => $token,
  232. 'errorCode' => $errorCode,
  233. 'errorMessage' => $errorMessage,
  234. ]);
  235. if(!is_null($exception)) {
  236. throw $exception;
  237. }
  238. }
  239. /**
  240. * Validate the permissions of the share
  241. *
  242. * @param Share\IShare $share
  243. * @return bool
  244. */
  245. private function validateShare(\OCP\Share\IShare $share) {
  246. return $share->getNode()->isReadable() && $share->getNode()->isShareable();
  247. }
  248. /**
  249. * @PublicPage
  250. * @NoCSRFRequired
  251. *
  252. * @param string $path
  253. * @return TemplateResponse
  254. * @throws NotFoundException
  255. * @throws \Exception
  256. */
  257. public function showShare($path = ''): TemplateResponse {
  258. \OC_User::setIncognitoMode(true);
  259. // Check whether share exists
  260. try {
  261. $share = $this->shareManager->getShareByToken($this->getToken());
  262. } catch (ShareNotFound $e) {
  263. $this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
  264. throw new NotFoundException();
  265. }
  266. if (!$this->validateShare($share)) {
  267. throw new NotFoundException();
  268. }
  269. $shareNode = $share->getNode();
  270. // We can't get the path of a file share
  271. try {
  272. if ($shareNode instanceof \OCP\Files\File && $path !== '') {
  273. $this->emitAccessShareHook($share, 404, 'Share not found');
  274. throw new NotFoundException();
  275. }
  276. } catch (\Exception $e) {
  277. $this->emitAccessShareHook($share, 404, 'Share not found');
  278. throw $e;
  279. }
  280. $shareTmpl = [];
  281. $shareTmpl['displayName'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
  282. $shareTmpl['owner'] = $share->getShareOwner();
  283. $shareTmpl['filename'] = $shareNode->getName();
  284. $shareTmpl['directory_path'] = $share->getTarget();
  285. $shareTmpl['note'] = $share->getNote();
  286. $shareTmpl['mimetype'] = $shareNode->getMimetype();
  287. $shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype());
  288. $shareTmpl['dirToken'] = $this->getToken();
  289. $shareTmpl['sharingToken'] = $this->getToken();
  290. $shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
  291. $shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
  292. $shareTmpl['dir'] = '';
  293. $shareTmpl['nonHumanFileSize'] = $shareNode->getSize();
  294. $shareTmpl['fileSize'] = \OCP\Util::humanFileSize($shareNode->getSize());
  295. $shareTmpl['hideDownload'] = $share->getHideDownload();
  296. $hideFileList = false;
  297. if ($shareNode instanceof \OCP\Files\Folder) {
  298. $shareIsFolder = true;
  299. try {
  300. $folderNode = $shareNode->get($path);
  301. } catch (\OCP\Files\NotFoundException $e) {
  302. $this->emitAccessShareHook($share, 404, 'Share not found');
  303. throw new NotFoundException();
  304. }
  305. $shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath());
  306. /*
  307. * The OC_Util methods require a view. This just uses the node API
  308. */
  309. $freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
  310. if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
  311. $freeSpace = max($freeSpace, 0);
  312. } else {
  313. $freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
  314. }
  315. $hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
  316. $maxUploadFilesize = $freeSpace;
  317. $folder = new Template('files', 'list', '');
  318. $folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath()));
  319. $folder->assign('dirToken', $this->getToken());
  320. $folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
  321. $folder->assign('isPublic', true);
  322. $folder->assign('hideFileList', $hideFileList);
  323. $folder->assign('publicUploadEnabled', 'no');
  324. // default to list view
  325. $folder->assign('showgridview', false);
  326. $folder->assign('uploadMaxFilesize', $maxUploadFilesize);
  327. $folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
  328. $folder->assign('freeSpace', $freeSpace);
  329. $folder->assign('usedSpacePercent', 0);
  330. $folder->assign('trash', false);
  331. $shareTmpl['folder'] = $folder->fetchPage();
  332. } else {
  333. $shareIsFolder = false;
  334. }
  335. // default to list view
  336. $shareTmpl['showgridview'] = false;
  337. $shareTmpl['hideFileList'] = $hideFileList;
  338. $shareTmpl['shareOwner'] = $this->userManager->get($share->getShareOwner())->getDisplayName();
  339. $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $this->getToken()]);
  340. $shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
  341. $shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
  342. $shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
  343. $shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
  344. $shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
  345. $shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
  346. $shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
  347. if ($shareTmpl['previewSupported']) {
  348. $shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
  349. ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
  350. $ogPreview = $shareTmpl['previewImage'];
  351. // We just have direct previews for image files
  352. if ($shareNode->getMimePart() === 'image') {
  353. $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
  354. $ogPreview = $shareTmpl['previewURL'];
  355. //Whatapp is kind of picky about their size requirements
  356. if ($this->request->isUserAgent(['/^WhatsApp/'])) {
  357. $ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
  358. 'token' => $this->getToken(),
  359. 'x' => 256,
  360. 'y' => 256,
  361. 'a' => true,
  362. ]);
  363. }
  364. }
  365. } else {
  366. $shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
  367. $ogPreview = $shareTmpl['previewImage'];
  368. }
  369. // Load files we need
  370. \OCP\Util::addScript('files', 'file-upload');
  371. \OCP\Util::addStyle('files_sharing', 'publicView');
  372. \OCP\Util::addScript('files_sharing', 'public');
  373. \OCP\Util::addScript('files_sharing', 'templates');
  374. \OCP\Util::addScript('files', 'fileactions');
  375. \OCP\Util::addScript('files', 'fileactionsmenu');
  376. \OCP\Util::addScript('files', 'jquery.fileupload');
  377. \OCP\Util::addScript('files_sharing', 'files_drop');
  378. if (isset($shareTmpl['folder'])) {
  379. // JS required for folders
  380. \OCP\Util::addStyle('files', 'merged');
  381. \OCP\Util::addScript('files', 'filesummary');
  382. \OCP\Util::addScript('files', 'templates');
  383. \OCP\Util::addScript('files', 'breadcrumb');
  384. \OCP\Util::addScript('files', 'fileinfomodel');
  385. \OCP\Util::addScript('files', 'newfilemenu');
  386. \OCP\Util::addScript('files', 'files');
  387. \OCP\Util::addScript('files', 'filemultiselectmenu');
  388. \OCP\Util::addScript('files', 'filelist');
  389. \OCP\Util::addScript('files', 'keyboardshortcuts');
  390. }
  391. // OpenGraph Support: http://ogp.me/
  392. \OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
  393. \OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
  394. \OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
  395. \OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
  396. \OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
  397. \OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
  398. $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts');
  399. $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
  400. $csp->addAllowedFrameDomain('\'self\'');
  401. $response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
  402. $response->setHeaderTitle($shareTmpl['filename']);
  403. $response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['displayName']]));
  404. $isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== \OCP\Constants::PERMISSION_CREATE;
  405. if ($isNoneFileDropFolder && !$share->getHideDownload()) {
  406. \OCP\Util::addScript('files_sharing', 'public_note');
  407. if ($shareIsFolder) {
  408. $response->setHeaderActions([
  409. new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download-white', $shareTmpl['downloadURL'], 0),
  410. new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']),
  411. new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']),
  412. new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['displayName'], $shareTmpl['filename']),
  413. ]);
  414. } else {
  415. $response->setHeaderActions([
  416. new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0),
  417. new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']),
  418. new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']),
  419. new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['displayName'], $shareTmpl['filename']),
  420. ]);
  421. }
  422. }
  423. $response->setContentSecurityPolicy($csp);
  424. $this->emitAccessShareHook($share);
  425. return $response;
  426. }
  427. /**
  428. * @PublicPage
  429. * @NoCSRFRequired
  430. *
  431. * @param string $token
  432. * @param string $files
  433. * @param string $path
  434. * @param string $downloadStartSecret
  435. * @return void|\OCP\AppFramework\Http\Response
  436. * @throws NotFoundException
  437. */
  438. public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
  439. \OC_User::setIncognitoMode(true);
  440. $share = $this->shareManager->getShareByToken($token);
  441. if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
  442. return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
  443. }
  444. $files_list = null;
  445. if (!is_null($files)) { // download selected files
  446. $files_list = json_decode($files);
  447. // in case we get only a single file
  448. if ($files_list === null) {
  449. $files_list = [$files];
  450. }
  451. // Just in case $files is a single int like '1234'
  452. if (!is_array($files_list)) {
  453. $files_list = [$files_list];
  454. }
  455. }
  456. if (!$this->validateShare($share)) {
  457. throw new NotFoundException();
  458. }
  459. $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
  460. $originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
  461. // Single file share
  462. if ($share->getNode() instanceof \OCP\Files\File) {
  463. // Single file download
  464. $this->singleFileDownloaded($share, $share->getNode());
  465. }
  466. // Directory share
  467. else {
  468. /** @var \OCP\Files\Folder $node */
  469. $node = $share->getNode();
  470. // Try to get the path
  471. if ($path !== '') {
  472. try {
  473. $node = $node->get($path);
  474. } catch (NotFoundException $e) {
  475. $this->emitAccessShareHook($share, 404, 'Share not found');
  476. return new NotFoundResponse();
  477. }
  478. }
  479. $originalSharePath = $userFolder->getRelativePath($node->getPath());
  480. if ($node instanceof \OCP\Files\File) {
  481. // Single file download
  482. $this->singleFileDownloaded($share, $share->getNode());
  483. } else if (!empty($files_list)) {
  484. $this->fileListDownloaded($share, $files_list, $node);
  485. } else {
  486. // The folder is downloaded
  487. $this->singleFileDownloaded($share, $share->getNode());
  488. }
  489. }
  490. /* FIXME: We should do this all nicely in OCP */
  491. OC_Util::tearDownFS();
  492. OC_Util::setupFS($share->getShareOwner());
  493. /**
  494. * this sets a cookie to be able to recognize the start of the download
  495. * the content must not be longer than 32 characters and must only contain
  496. * alphanumeric characters
  497. */
  498. if (!empty($downloadStartSecret)
  499. && !isset($downloadStartSecret[32])
  500. && preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
  501. // FIXME: set on the response once we use an actual app framework response
  502. setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
  503. }
  504. $this->emitAccessShareHook($share);
  505. $server_params = array( 'head' => $this->request->getMethod() === 'HEAD' );
  506. /**
  507. * Http range requests support
  508. */
  509. if (isset($_SERVER['HTTP_RANGE'])) {
  510. $server_params['range'] = $this->request->getHeader('Range');
  511. }
  512. // download selected files
  513. if (!is_null($files) && $files !== '') {
  514. // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
  515. // after dispatching the request which results in a "Cannot modify header information" notice.
  516. OC_Files::get($originalSharePath, $files_list, $server_params);
  517. exit();
  518. } else {
  519. // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
  520. // after dispatching the request which results in a "Cannot modify header information" notice.
  521. OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
  522. exit();
  523. }
  524. }
  525. /**
  526. * create activity for every downloaded file
  527. *
  528. * @param Share\IShare $share
  529. * @param array $files_list
  530. * @param \OCP\Files\Folder $node
  531. */
  532. protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
  533. foreach ($files_list as $file) {
  534. $subNode = $node->get($file);
  535. $this->singleFileDownloaded($share, $subNode);
  536. }
  537. }
  538. /**
  539. * create activity if a single file was downloaded from a link share
  540. *
  541. * @param Share\IShare $share
  542. */
  543. protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
  544. $fileId = $node->getId();
  545. $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
  546. $userNodeList = $userFolder->getById($fileId);
  547. $userNode = $userNodeList[0];
  548. $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
  549. $userPath = $userFolder->getRelativePath($userNode->getPath());
  550. $ownerPath = $ownerFolder->getRelativePath($node->getPath());
  551. $parameters = [$userPath];
  552. if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
  553. if ($node instanceof \OCP\Files\File) {
  554. $subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
  555. } else {
  556. $subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
  557. }
  558. $parameters[] = $share->getSharedWith();
  559. } else {
  560. if ($node instanceof \OCP\Files\File) {
  561. $subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
  562. } else {
  563. $subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
  564. }
  565. }
  566. $this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
  567. if ($share->getShareOwner() !== $share->getSharedBy()) {
  568. $parameters[0] = $ownerPath;
  569. $this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
  570. }
  571. }
  572. /**
  573. * publish activity
  574. *
  575. * @param string $subject
  576. * @param array $parameters
  577. * @param string $affectedUser
  578. * @param int $fileId
  579. * @param string $filePath
  580. */
  581. protected function publishActivity($subject,
  582. array $parameters,
  583. $affectedUser,
  584. $fileId,
  585. $filePath) {
  586. $event = $this->activityManager->generateEvent();
  587. $event->setApp('files_sharing')
  588. ->setType('public_links')
  589. ->setSubject($subject, $parameters)
  590. ->setAffectedUser($affectedUser)
  591. ->setObject('files', $fileId, $filePath);
  592. $this->activityManager->publish($event);
  593. }
  594. }