diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php index d58a97e2956..68f33d94995 100644 --- a/apps/files_sharing/appinfo/routes.php +++ b/apps/files_sharing/appinfo/routes.php @@ -1,4 +1,5 @@ create('core_ajax_public_preview', '/publicpreview')->action( function() { diff --git a/apps/files_sharing/application.php b/apps/files_sharing/application.php new file mode 100644 index 00000000000..089ed6afbda --- /dev/null +++ b/apps/files_sharing/application.php @@ -0,0 +1,73 @@ +getContainer(); + + /** + * Controllers + */ + $container->registerService('ShareController', function(SimpleContainer $c) { + return new ShareController( + $c->query('AppName'), + $c->query('Request'), + $c->query('UserSession'), + $c->query('ServerContainer')->getAppConfig(), + $c->query('ServerContainer')->getConfig(), + $c->query('URLGenerator'), + $c->query('ServerContainer')->getUserManager(), + $c->query('ServerContainer')->getLogger() + ); + }); + + /** + * Core class wrappers + */ + $container->registerService('UserSession', function(SimpleContainer $c) { + return $c->query('ServerContainer')->getUserSession(); + }); + $container->registerService('URLGenerator', function(SimpleContainer $c) { + return $c->query('ServerContainer')->getUrlGenerator(); + }); + + /** + * Middleware + */ + $container->registerService('SharingCheckMiddleware', function(SimpleContainer $c){ + return new SharingCheckMiddleware( + $c->query('AppName'), + $c->query('ServerContainer')->getAppConfig(), + $c->getCoreApi() + ); + }); + + // Execute middlewares + $container->registerMiddleware('SharingCheckMiddleware'); + } + +} diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 52679a7158d..0627ed6ab54 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -109,14 +109,12 @@ OCA.Sharing.PublicApp = { filename = JSON.stringify(filename); } var path = dir || FileList.getCurrentDirectory(); + var token = $('#sharingToken').val(); var params = { - service: 'files', - t: $('#sharingToken').val(), path: path, - files: filename, - download: null + files: filename }; - return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params); + return OC.generateUrl('/s/'+token+'/download') + '?' + OC.buildQueryString(params); }; this.fileList.getAjaxUrl = function (action, params) { @@ -126,12 +124,11 @@ OCA.Sharing.PublicApp = { }; this.fileList.linkTo = function (dir) { + var token = $('#sharingToken').val(); var params = { - service: 'files', - t: $('#sharingToken').val(), dir: dir }; - return OC.filePath('', '', 'public.php') + '?' + OC.buildQueryString(params); + return OC.generateUrl('/s/'+token+'') + '?' + OC.buildQueryString(params); }; this.fileList.generatePreviewUrl = function (urlSpec) { @@ -193,8 +190,6 @@ OCA.Sharing.PublicApp = { _onDirectoryChanged: function (e) { OC.Util.History.pushState({ - service: 'files', - t: $('#sharingToken').val(), // arghhhh, why is this not called "dir" !? path: e.dir }); diff --git a/apps/files_sharing/lib/controllers/sharecontroller.php b/apps/files_sharing/lib/controllers/sharecontroller.php new file mode 100644 index 00000000000..a3d5b6d44a0 --- /dev/null +++ b/apps/files_sharing/lib/controllers/sharecontroller.php @@ -0,0 +1,272 @@ + + * @author Lukas Reschke + * @copyright 2014 Clark Tomlinson & Lukas Reschke + * + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCA\Files_Sharing\Controllers; + +use OC; +use OC\Files\Filesystem; +use OC_Files; +use OC_Util; +use OCP; +use OCP\Template; +use OCP\JSON; +use OCP\Share; +use OCP\AppFramework\Controller; +use OCP\IRequest; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\AppFramework\Http\RedirectResponse; +use OCP\AppFramework\IApi; +use OC\URLGenerator; +use OC\AppConfig; +use OCP\ILogger; +use OCA\Files_Sharing\Helper; +use OCP\User; +use OCP\Util; + +/** + * Class ShareController + * + * @package OCA\Files_Sharing\Controllers + */ +class ShareController extends Controller { + + /** @var \OC\User\Session */ + protected $userSession; + /** @var \OC\AppConfig */ + protected $appConfig; + /** @var \OCP\IConfig */ + protected $config; + /** @var \OC\URLGenerator */ + protected $urlGenerator; + /** @var \OC\User\Manager */ + protected $userManager; + /** @var \OCP\ILogger */ + protected $logger; + + /** + * @param string $appName + * @param IRequest $request + * @param OC\User\Session $userSession + * @param AppConfig $appConfig + * @param OCP\IConfig $config + * @param URLGenerator $urlGenerator + * @param OC\User\Manager $userManager + * @param ILogger $logger + */ + public function __construct($appName, + IRequest $request, + OC\User\Session $userSession, + AppConfig $appConfig, + OCP\IConfig $config, + URLGenerator $urlGenerator, + OC\User\Manager $userManager, + ILogger $logger) { + parent::__construct($appName, $request); + + $this->userSession = $userSession; + $this->appConfig = $appConfig; + $this->config = $config; + $this->urlGenerator = $urlGenerator; + $this->userManager = $userManager; + $this->logger = $logger; + } + + /** + * @PublicPage + * @NoCSRFRequired + * + * @param string $token + * + * @return TemplateResponse|RedirectResponse + */ + public function showAuthenticate($token) { + $linkItem = Share::getShareByToken($token, false); + + if(Helper::authenticate($linkItem)) { + return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token))); + } + + return new TemplateResponse($this->appName, 'authenticate', array(), 'guest'); + } + + /** + * @PublicPage + * + * Authenticates against password-protected shares + * @param $token + * @param string $password + * @return RedirectResponse|TemplateResponse + */ + public function authenticate($token, $password = '') { + $linkItem = Share::getShareByToken($token, false); + if($linkItem === false) { + return new TemplateResponse('core', '404', array(), 'guest'); + } + + $authenticate = Helper::authenticate($linkItem, $password); + + if($authenticate === true) { + return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token))); + } + + return new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest'); + } + + /** + * @PublicPage + * @NoCSRFRequired + * + * @param string $token + * @param string $path + * + * @return TemplateResponse + */ + public function showShare($token, $path = '') { + \OC_User::setIncognitoMode(true); + + // Check whether share exists + $linkItem = Share::getShareByToken($token, false); + if($linkItem === false) { + return new TemplateResponse('core', '404', array(), 'guest'); + } + + $linkItem = OCP\Share::getShareByToken($token, false); + $shareOwner = $linkItem['uid_owner']; + $originalSharePath = null; + $rootLinkItem = OCP\Share::resolveReShare($linkItem); + if (isset($rootLinkItem['uid_owner'])) { + OCP\JSON::checkUserExists($rootLinkItem['uid_owner']); + OC_Util::tearDownFS(); + OC_Util::setupFS($rootLinkItem['uid_owner']); + $originalSharePath = Filesystem::getPath($linkItem['file_source']); + } + + // Share is password protected - check whether the user is permitted to access the share + if (isset($linkItem['share_with']) && !Helper::authenticate($linkItem)) { + return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate', + array('token' => $token))); + } + + if (Filesystem::isReadable($originalSharePath . $path)) { + $getPath = Filesystem::normalizePath($path); + $originalSharePath .= $path; + } + + $dir = dirname($originalSharePath); + $file = basename($originalSharePath); + + $shareTmpl = array(); + $shareTmpl['displayName'] = User::getDisplayName($shareOwner); + $shareTmpl['filename'] = $file; + $shareTmpl['directory_path'] = $linkItem['file_target']; + $shareTmpl['mimetype'] = Filesystem::getMimeType($originalSharePath); + $shareTmpl['dirToken'] = $linkItem['token']; + $shareTmpl['sharingToken'] = $token; + $shareTmpl['server2serversharing'] = Helper::isOutgoingServer2serverShareEnabled(); + $shareTmpl['protected'] = isset($linkItem['share_with']) ? 'true' : 'false'; + $shareTmpl['dir'] = $dir; + $shareTmpl['fileSize'] = \OCP\Util::humanFileSize(\OC\Files\Filesystem::filesize($file)); + + // Show file list + if (Filesystem::is_dir($originalSharePath)) { + $shareTmpl['dir'] = $getPath; + $files = array(); + $maxUploadFilesize = Util::maxUploadFilesize($originalSharePath); + $freeSpace = Util::freeSpace($originalSharePath); + $uploadLimit = Util::uploadLimit(); + $folder = new Template('files', 'list', ''); + $folder->assign('dir', $getPath); + $folder->assign('dirToken', $linkItem['token']); + $folder->assign('permissions', OCP\PERMISSION_READ); + $folder->assign('isPublic', true); + $folder->assign('publicUploadEnabled', 'no'); + $folder->assign('files', $files); + $folder->assign('uploadMaxFilesize', $maxUploadFilesize); + $folder->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); + $folder->assign('freeSpace', $freeSpace); + $folder->assign('uploadLimit', $uploadLimit); // PHP upload limit + $folder->assign('usedSpacePercent', 0); + $folder->assign('trash', false); + $shareTmpl['folder'] = $folder->fetchPage(); + } + + $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', array('token' => $token)); + + return new TemplateResponse($this->appName, 'public', $shareTmpl, 'base'); + } + + /** + * @PublicPage + * @NoCSRFRequired + * @param string $token + * @param string $files + * @param string $path + * @return void|RedirectResponse + */ + public function downloadShare($token, $files = null, $path = '') { + \OC_User::setIncognitoMode(true); + + $linkItem = OCP\Share::getShareByToken($token, false); + + // Share is password protected - check whether the user is permitted to access the share + if (isset($linkItem['share_with'])) { + if(!Helper::authenticate($linkItem)) { + return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate', + array('token' => $token))); + } + } + + $originalSharePath = self::getPath($token); + + if (isset($originalSharePath) && Filesystem::isReadable($originalSharePath . $path)) { + $getPath = Filesystem::normalizePath($path); + $originalSharePath .= $getPath; + } + + if (!is_null($files)) { // download selected files + $files_list = json_decode($files); + // in case we get only a single file + if ($files_list === NULL ) { + $files_list = array($files); + } + + // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well + // after dispatching the request which results in a "Cannot modify header information" notice. + OC_Files::get($originalSharePath, $files_list, $_SERVER['REQUEST_METHOD'] == 'HEAD'); + exit(); + } else { + // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well + // after dispatching the request which results in a "Cannot modify header information" notice. + OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $_SERVER['REQUEST_METHOD'] == 'HEAD'); + exit(); + } + } + + /** + * @param $token + * @return null|string + */ + private function getPath($token) { + $linkItem = Share::getShareByToken($token, false); + $path = null; + if (is_array($linkItem) && isset($linkItem['uid_owner'])) { + // seems to be a valid share + $rootLinkItem = Share::resolveReShare($linkItem); + if (isset($rootLinkItem['uid_owner'])) { + JSON::checkUserExists($rootLinkItem['uid_owner']); + OC_Util::tearDownFS(); + OC_Util::setupFS($rootLinkItem['uid_owner']); + $path = Filesystem::getPath($linkItem['file_source']); + } + } + return $path; + } +} diff --git a/apps/files_sharing/lib/helper.php b/apps/files_sharing/lib/helper.php index e7ca4fcccd4..3a2d51cddb7 100644 --- a/apps/files_sharing/lib/helper.php +++ b/apps/files_sharing/lib/helper.php @@ -95,7 +95,7 @@ class Helper { * * @return boolean true if authorized, false otherwise */ - public static function authenticate($linkItem, $password) { + public static function authenticate($linkItem, $password = null) { if ($password !== null) { if ($linkItem['share_type'] == \OCP\Share::SHARE_TYPE_LINK) { // Check Password diff --git a/apps/files_sharing/lib/middleware/sharingcheckmiddleware.php b/apps/files_sharing/lib/middleware/sharingcheckmiddleware.php new file mode 100644 index 00000000000..af79cd9e94a --- /dev/null +++ b/apps/files_sharing/lib/middleware/sharingcheckmiddleware.php @@ -0,0 +1,84 @@ +appName = $appName; + $this->appConfig = $appConfig; + $this->api = $api; + } + + /** + * Check if sharing is enabled before the controllers is executed + */ + public function beforeController($controller, $methodName) { + if(!$this->isSharingEnabled()) { + throw new \Exception('Sharing is disabled.'); + } + } + + /** + * Return 404 page in case of an exception + * @param \OCP\AppFramework\Controller $controller + * @param string $methodName + * @param \Exception $exception + * @return TemplateResponse + */ + public function afterException($controller, $methodName, \Exception $exception){ + return new TemplateResponse('core', '404', array(), 'guest'); + } + + /** + * Check whether sharing is enabled + * @return bool + */ + private function isSharingEnabled() { + // FIXME: This check is done here since the route is globally defined and not inside the files_sharing app + // Check whether the sharing application is enabled + if(!$this->api->isAppEnabled($this->appName)) { + return false; + } + + // Check whether public sharing is enabled + if($this->appConfig->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { + return false; + } + + return true; + } + +} diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index 4320c105103..d9b8f0f4f30 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -1,205 +1,17 @@ getAppConfig(); +$urlGenerator = new \OC\URLGenerator(\OC::$server->getConfig()); +$token = isset($_GET['t']) ? $_GET['t'] : ''; +$route = isset($_GET['download']) ? 'files_sharing.sharecontroller.downloadshare' : 'files_sharing.sharecontroller.showshare'; -if ($appConfig->getValue('core', 'shareapi_allow_links', 'yes') !== 'yes') { - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); -} - -// Legacy sharing links via public.php have the token in $GET['t'] -if (isset($_GET['t'])) { - $token = $_GET['t']; -} - -if (isset($token)) { - $linkItem = OCP\Share::getShareByToken($token, false); - if (is_array($linkItem) && isset($linkItem['uid_owner'])) { - // seems to be a valid share - $type = $linkItem['item_type']; - $fileSource = $linkItem['file_source']; - $shareOwner = $linkItem['uid_owner']; - $path = null; - $rootLinkItem = OCP\Share::resolveReShare($linkItem); - if (isset($rootLinkItem['uid_owner'])) { - OCP\JSON::checkUserExists($rootLinkItem['uid_owner']); - OC_Util::tearDownFS(); - OC_Util::setupFS($rootLinkItem['uid_owner']); - $path = \OC\Files\Filesystem::getPath($linkItem['file_source']); - } - } -} -if (isset($path)) { - if (!isset($linkItem['item_type'])) { - OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR); - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); - } - if (isset($linkItem['share_with'])) { - // Authenticate share_with - $url = OCP\Util::linkToPublic('files') . '&t=' . $token; - if (isset($_GET['file'])) { - $url .= '&file=' . urlencode($_GET['file']); - } else { - if (isset($_GET['dir'])) { - $url .= '&dir=' . urlencode($_GET['dir']); - } - } - if (isset($_POST['password'])) { - $password = $_POST['password']; - if ($linkItem['share_type'] == OCP\Share::SHARE_TYPE_LINK) { - // Check Password - $forcePortable = (CRYPT_BLOWFISH != 1); - $hasher = new PasswordHash(8, $forcePortable); - if (!($hasher->CheckPassword($password.OC_Config::getValue('passwordsalt', ''), - $linkItem['share_with']))) { - OCP\Util::addStyle('files_sharing', 'authenticate'); - $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest'); - $tmpl->assign('URL', $url); - $tmpl->assign('wrongpw', true); - $tmpl->printPage(); - exit(); - } else { - // Save item id in session for future requests - \OC::$server->getSession()->set('public_link_authenticated', $linkItem['id']); - } - } else { - OCP\Util::writeLog('share', 'Unknown share type '.$linkItem['share_type'] - .' for share id '.$linkItem['id'], \OCP\Util::ERROR); - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); - } - - } else { - // Check if item id is set in session - if ( ! \OC::$server->getSession()->exists('public_link_authenticated') - || \OC::$server->getSession()->get('public_link_authenticated') !== $linkItem['id'] - ) { - // Prompt for password - OCP\Util::addStyle('files_sharing', 'authenticate'); - $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest'); - $tmpl->assign('URL', $url); - $tmpl->printPage(); - exit(); - } - } - } - $basePath = $path; - $rootName = \OC_Util::basename($path); - if (isset($_GET['path']) && \OC\Files\Filesystem::isReadable($basePath . $_GET['path'])) { - $getPath = \OC\Files\Filesystem::normalizePath($_GET['path']); - $path .= $getPath; - } else { - $getPath = ''; - } - $dir = dirname($path); - $file = basename($path); - // Download the file - if (isset($_GET['download'])) { - if (!\OCP\App::isEnabled('files_encryption')) { - // encryption app requires the session to store the keys in - \OC::$server->getSession()->close(); - } - if (isset($_GET['files'])) { // download selected files - $files = $_GET['files']; - $files_list = json_decode($files); - // in case we get only a single file - if (!is_array($files_list)) { - $files_list = array($files); - } - OC_Files::get($path, $files_list, $_SERVER['REQUEST_METHOD'] == 'HEAD'); - } else { - OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD'); - } - exit(); - } else { - OCP\Util::addScript('files', 'file-upload'); - OCP\Util::addStyle('files_sharing', 'public'); - OCP\Util::addStyle('files_sharing', 'mobile'); - OCP\Util::addScript('files_sharing', 'public'); - OCP\Util::addScript('files', 'fileactions'); - OCP\Util::addScript('files', 'jquery.iframe-transport'); - OCP\Util::addScript('files', 'jquery.fileupload'); - $maxUploadFilesize=OCP\Util::maxUploadFilesize($path); - $tmpl = new OCP\Template('files_sharing', 'public', 'base'); - $tmpl->assign('displayName', \OCP\User::getDisplayName($shareOwner)); - $tmpl->assign('filename', $file); - $tmpl->assign('directory_path', $linkItem['file_target']); - $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); - $tmpl->assign('dirToken', $linkItem['token']); - $tmpl->assign('sharingToken', $token); - $tmpl->assign('server2serversharing', Helper::isOutgoingServer2serverShareEnabled()); - $tmpl->assign('protected', isset($linkItem['share_with']) ? 'true' : 'false'); - - $urlLinkIdentifiers= (isset($token)?'&t='.$token:'') - .(isset($_GET['dir'])?'&dir='.$_GET['dir']:'') - .(isset($_GET['file'])?'&file='.$_GET['file']:''); - // Show file list - if (\OC\Files\Filesystem::is_dir($path)) { - $tmpl->assign('dir', $getPath); - - OCP\Util::addStyle('files', 'files'); - OCP\Util::addStyle('files', 'upload'); - OCP\Util::addScript('files', 'filesummary'); - OCP\Util::addScript('files', 'breadcrumb'); - OCP\Util::addScript('files', 'files'); - OCP\Util::addScript('files', 'filelist'); - OCP\Util::addscript('files', 'keyboardshortcuts'); - $files = array(); - $rootLength = strlen($basePath) + 1; - $maxUploadFilesize=OCP\Util::maxUploadFilesize($path); - - $freeSpace=OCP\Util::freeSpace($path); - $uploadLimit=OCP\Util::uploadLimit(); - $folder = new OCP\Template('files', 'list', ''); - $folder->assign('dir', $getPath); - $folder->assign('dirToken', $linkItem['token']); - $folder->assign('permissions', OCP\PERMISSION_READ); - $folder->assign('isPublic', true); - $folder->assign('publicUploadEnabled', 'no'); - $folder->assign('files', $files); - $folder->assign('uploadMaxFilesize', $maxUploadFilesize); - $folder->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); - $folder->assign('freeSpace', $freeSpace); - $folder->assign('uploadLimit', $uploadLimit); // PHP upload limit - $folder->assign('usedSpacePercent', 0); - $folder->assign('trash', false); - $tmpl->assign('folder', $folder->fetchPage()); - $tmpl->assign('downloadURL', - OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=' . urlencode($getPath)); - } else { - $tmpl->assign('dir', $dir); - - // Show file preview if viewer is available - if ($type == 'file') { - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download'); - } else { - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') - .$urlLinkIdentifiers.'&download&path='.urlencode($getPath)); - } - } - $tmpl->printPage(); - } - exit(); -} else { - OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); -} - -$errorTemplate = new OCP\Template('files_sharing', 'part.404', ''); -$errorContent = $errorTemplate->fetchPage(); - -header('HTTP/1.0 404 Not Found'); -OCP\Util::addStyle('files_sharing', '404'); -$tmpl = new OCP\Template('', '404', 'guest'); -$tmpl->assign('content', $errorContent); -$tmpl->printPage(); +OC_Response::redirect($urlGenerator->linkToRoute($route, array('token' => $token))); diff --git a/apps/files_sharing/templates/authenticate.php b/apps/files_sharing/templates/authenticate.php index 0c4ac6ca445..e3aa62b9ece 100644 --- a/apps/files_sharing/templates/authenticate.php +++ b/apps/files_sharing/templates/authenticate.php @@ -1,4 +1,9 @@ -
+ +
t('This share is password-protected')); ?>
@@ -8,6 +13,7 @@

+ + @@ -24,7 +44,7 @@ $previewSupported = OC\Preview::isMimeSupported($_['mimetype']) ? 'true' : 'fals

-
+
@@ -67,7 +87,7 @@ $previewSupported = OC\Preview::isMimeSupported($_['mimetype']) ? 'true' : 'fals