* * @license AGPL-3.0 * * This code is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License, version 3, * along with this program. If not, see * */ namespace OCA\DAV\CardDAV; use OCP\Files\NotFoundException; use OCP\ILogger; use Sabre\CardDAV\Card; use Sabre\DAV\Server; use Sabre\DAV\ServerPlugin; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; use Sabre\VObject\Parameter; use Sabre\VObject\Property\Binary; use Sabre\VObject\Reader; class ImageExportPlugin extends ServerPlugin { /** @var Server */ protected $server; /** @var ILogger */ private $logger; /** @var PhotoCache */ private $cache; /** * ImageExportPlugin constructor. * * @param ILogger $logger * @param PhotoCache $cache */ public function __construct(ILogger $logger, PhotoCache $cache) { $this->logger = $logger; $this->cache = $cache; } /** * Initializes the plugin and registers event handlers * * @param Server $server * @return void */ public function initialize(Server $server) { $this->server = $server; $this->server->on('method:GET', [$this, 'httpGet'], 90); } /** * Intercepts GET requests on addressbook urls ending with ?photo. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool */ public function httpGet(RequestInterface $request, ResponseInterface $response) { $queryParams = $request->getQueryParameters(); // TODO: in addition to photo we should also add logo some point in time if (!array_key_exists('photo', $queryParams)) { return true; } $size = -1; if (isset($queryParams['size'])) { $size = (int)$queryParams['size']; } $path = $request->getPath(); $node = $this->server->tree->getNodeForPath($path); if (!($node instanceof Card)) { return true; } $this->server->transactionType = 'carddav-image-export'; // Checking ACL, if available. if ($aclPlugin = $this->server->getPlugin('acl')) { /** @var \Sabre\DAVACL\Plugin $aclPlugin */ $aclPlugin->checkPrivileges($path, '{DAV:}read'); } // Fetch addressbook $addressbookpath = explode('/', $path); array_pop($addressbookpath); $addressbookpath = implode('/', $addressbookpath); /** @var AddressBook $addressbook */ $addressbook = $this->server->tree->getNodeForPath($addressbookpath); $hash = md5($addressbook->getResourceId() . $node->getName()); $response->setHeader('Cache-Control', 'private, max-age=3600, must-revalidate'); $response->setHeader('Etag', $node->getETag() ); $response->setHeader('Pragma', 'public'); try { $file = $this->cache->get($hash, $size, $node); $response->setHeader('Content-Type', $file->getMimeType()); $response->setHeader('Content-Disposition', 'inline'); $response->setStatus(200); $response->setBody($file->getContent()); } catch (NotFoundException $e) { $response->setStatus(404); } return false; } function getPhoto(Card $node) { // TODO: this is kind of expensive - load carddav data from database and parse it // we might want to build up a cache one day try { $vObject = $this->readCard($node->get()); if (!$vObject->PHOTO) { return false; } $photo = $vObject->PHOTO; $type = $this->getType($photo); $val = $photo->getValue(); if ($photo->getValueType() === 'URI') { $parsed = \Sabre\URI\parse($val); //only allow data:// if ($parsed['scheme'] !== 'data') { return false; } if (substr_count($parsed['path'], ';') === 1) { list($type,) = explode(';', $parsed['path']); } $val = file_get_contents($val); } $allowedContentTypes = [ 'image/png', 'image/jpeg', 'image/gif', ]; if(!in_array($type, $allowedContentTypes, true)) { $type = 'application/octet-stream'; } return [ 'Content-Type' => $type, 'body' => $val ]; } catch(\Exception $ex) { $this->logger->logException($ex); } return false; } private function readCard($cardData) { return Reader::read($cardData); } /** * @param Binary $photo * @return Parameter */ private function getType($photo) { $params = $photo->parameters(); if (isset($params['TYPE']) || isset($params['MEDIATYPE'])) { /** @var Parameter $typeParam */ $typeParam = isset($params['TYPE']) ? $params['TYPE'] : $params['MEDIATYPE']; $type = $typeParam->getValue(); if (strpos($type, 'image/') === 0) { return $type; } else { return 'image/' . strtolower($type); } } return ''; } }