* @copyright Olivier Tétard , 2017 */ namespace OCA\Ownpad\Controller; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\ContentSecurityPolicy; use OCP\AppFramework\Http\TemplateResponse; use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUserSession; use OCP\IConfig; use EtherpadLite\Client; class DisplayController extends Controller { /** @var IURLGenerator */ private $urlGenerator; /** @var IUserSession */ private $userSession; /** @var IConfig */ private $config; /** @var Client */ private $eplInstance; /** * @param string $AppName * @param IRequest $request * @param IURLGenerator $urlGenerator */ public function __construct( $AppName, IRequest $request, IURLGenerator $urlGenerator, IUserSession $userSession, IConfig $config ) { parent::__construct($AppName, $request); $this->urlGenerator = $urlGenerator; $this->userSession = $userSession; $this->config = $config; if($this->config->getAppValue('ownpad', 'ownpad_etherpad_enable', 'no') !== 'no' AND $this->config->getAppValue('ownpad', 'ownpad_etherpad_useapi', 'no') !== 'no') { $eplHost = $this->config->getAppValue('ownpad', 'ownpad_etherpad_host', ''); $eplApiKey = $this->config->getAppValue('ownpad', 'ownpad_etherpad_apikey', ''); $this->eplInstance = new Client($eplApiKey, $eplHost . "/api"); } } /** * @PublicPage * @NoCSRFRequired * * @return TemplateResponse */ public function showPad($file, $dir) { /* * Filesystem is not setup per default on normal route since * Nextcloud 21. More information: * - https://github.com/nextcloud/server/issues/23210 * - https://github.com/nextcloud/server/pull/23821 */ \OC_Util::setupFS(); /* Retrieve file content to find pad’s URL */ $path = \OC\Files\Filesystem::normalizePath($dir."/".$file); $content = \OC\Files\Filesystem::file_get_contents($path); preg_match('/URL=(.*)$/', $content, $matches); $url = $matches[1]; $title = $file; $eplHost = $this->config->getAppValue('ownpad', 'ownpad_etherpad_host', ''); $eplHost = rtrim($eplHost, '/'); $protectedPadRegex = sprintf('/%s\/p\/(g\.\w{16})\\$(.*)$/', preg_quote($eplHost, '/')); $match = preg_match($protectedPadRegex, $url, $matches); /* * We are facing a “protected” pad. Call for Etherpad API to * create the session and then properly configure the cookie. */ if($match) { $groupID = $matches[1]; $username = $this->userSession->getUser()->getUID(); $displayName = $this->userSession->getUser()->getDisplayName(); $author = $this->eplInstance->createAuthorIfNotExistsFor($username, $displayName); $session = $this->eplInstance->createSession($groupID, $author->authorID, time() + 3600); $cookieDomain = $this->config->getAppValue('ownpad', 'ownpad_etherpad_cookie_domain', ''); setcookie('sessionID', $session->sessionID, 0, '/', $cookieDomain, true, false); } /* * Not totally sure that this is the right way to proceed… * * First we decode the URL (to avoid double encode), then we * replace spaces with underscore (as they are converted as * such by Etherpad), then we encode the URL properly (and we * avoid to urlencode() the protocol scheme). * * Magic urlencode() function was stolen from this answer on * StackOverflow: . */ $url = urldecode($url); $url = str_replace(' ', '_', $url); $url = preg_replace_callback('#://([^/]+)/(=)?([^?]+)#', function ($match) { return '://' . $match[1] . '/' . $match[2] . join('/', array_map('rawurlencode', explode('/', $match[3]))); }, $url); $params = [ 'urlGenerator' => $this->urlGenerator, 'url' => $url, 'title' => $title, ]; // Check for valid URL // Get File-Ending $split = explode(".", $file); $fileending = $split[count($split)-1]; // Get Host-URL if($fileending === "calc") { $host = \OC::$server->getConfig()->getAppValue('ownpad', 'ownpad_ethercalc_host', false); } elseif($fileending === "pad") { $host = \OC::$server->getConfig()->getAppValue('ownpad', 'ownpad_etherpad_host', false); } if(substr($host, -1, 1) !== '/') { $host .= '/'; } // Escape all RegEx-Characters $hostreg = preg_quote($host); // Escape all Slashes in URL to use in RegEx $hostreg = preg_replace("/\//", "\/", $host); // Final Regex-String if($fileending === "calc") { /* * Ethercalc documents with “multisheet” support starts * with a `=`. */ $regex = "/^".$hostreg."=?[^\/]+$/"; } elseif($fileending === "pad") { /* * Etherpad documents can contain special characters, for * “protected pads” for example. */ $regex = "/^".$hostreg."p\/[^\/]+$/"; } // Show the Pad, if URL is valid if (preg_match($regex, $url) === 1) { $response = new TemplateResponse($this->appName, 'viewer', $params, 'blank'); } else { // Show Error-Page $response = new TemplateResponse($this->appName, 'noviewer', $params, 'blank'); } /* * Allow Etherpad and Ethercalc domains to the * Content-Security-frame- list. * * This feature was introduced in ownCloud 8.1. */ $policy = new ContentSecurityPolicy(); if($this->config->getAppValue('ownpad', 'ownpad_etherpad_enable', 'no') !== 'no') { $policy->addAllowedFrameDomain($this->config->getAppValue('ownpad', 'ownpad_etherpad_host', '')); $policy->addAllowedChildSrcDomain($this->config->getAppValue('ownpad', 'ownpad_etherpad_host', '')); } if($this->config->getAppValue('ownpad', 'ownpad_ethercalc_enable', 'no') !== 'no') { $policy->addAllowedFrameDomain($this->config->getAppValue('ownpad', 'ownpad_ethercalc_host', '')); $policy->addAllowedChildSrcDomain($this->config->getAppValue('ownpad', 'ownpad_ethercalc_host', '')); } $response->setContentSecurityPolicy($policy); return $response; } }