ImageExportPlugin.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Georg Ehrke <georg@owncloud.com>
  6. *
  7. * @license AGPL-3.0
  8. *
  9. * This code is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License, version 3,
  11. * as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU Affero General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public License, version 3,
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>
  20. *
  21. */
  22. namespace OCA\DAV\CardDAV;
  23. use OCP\ILogger;
  24. use Sabre\CardDAV\Card;
  25. use Sabre\DAV\Server;
  26. use Sabre\DAV\ServerPlugin;
  27. use Sabre\HTTP\RequestInterface;
  28. use Sabre\HTTP\ResponseInterface;
  29. use Sabre\VObject\Parameter;
  30. use Sabre\VObject\Property\Binary;
  31. use Sabre\VObject\Reader;
  32. class ImageExportPlugin extends ServerPlugin {
  33. /** @var Server */
  34. protected $server;
  35. /** @var ILogger */
  36. private $logger;
  37. public function __construct(ILogger $logger) {
  38. $this->logger = $logger;
  39. }
  40. /**
  41. * Initializes the plugin and registers event handlers
  42. *
  43. * @param Server $server
  44. * @return void
  45. */
  46. function initialize(Server $server) {
  47. $this->server = $server;
  48. $this->server->on('method:GET', [$this, 'httpGet'], 90);
  49. }
  50. /**
  51. * Intercepts GET requests on addressbook urls ending with ?photo.
  52. *
  53. * @param RequestInterface $request
  54. * @param ResponseInterface $response
  55. * @return bool|void
  56. */
  57. function httpGet(RequestInterface $request, ResponseInterface $response) {
  58. $queryParams = $request->getQueryParameters();
  59. // TODO: in addition to photo we should also add logo some point in time
  60. if (!array_key_exists('photo', $queryParams)) {
  61. return true;
  62. }
  63. $path = $request->getPath();
  64. $node = $this->server->tree->getNodeForPath($path);
  65. if (!($node instanceof Card)) {
  66. return true;
  67. }
  68. $this->server->transactionType = 'carddav-image-export';
  69. // Checking ACL, if available.
  70. if ($aclPlugin = $this->server->getPlugin('acl')) {
  71. /** @var \Sabre\DAVACL\Plugin $aclPlugin */
  72. $aclPlugin->checkPrivileges($path, '{DAV:}read');
  73. }
  74. if ($result = $this->getPhoto($node)) {
  75. $response->setHeader('Content-Type', $result['Content-Type']);
  76. $response->setHeader('Content-Disposition', 'attachment');
  77. $response->setStatus(200);
  78. $response->setBody($result['body']);
  79. // Returning false to break the event chain
  80. return false;
  81. }
  82. return true;
  83. }
  84. function getPhoto(Card $node) {
  85. // TODO: this is kind of expensive - load carddav data from database and parse it
  86. // we might want to build up a cache one day
  87. try {
  88. $vObject = $this->readCard($node->get());
  89. if (!$vObject->PHOTO) {
  90. return false;
  91. }
  92. $photo = $vObject->PHOTO;
  93. $type = $this->getType($photo);
  94. $val = $photo->getValue();
  95. if ($photo->getValueType() === 'URI') {
  96. $parsed = \Sabre\URI\parse($val);
  97. //only allow data://
  98. if ($parsed['scheme'] !== 'data') {
  99. return false;
  100. }
  101. if (substr_count($parsed['path'], ';') === 1) {
  102. list($type,) = explode(';', $parsed['path']);
  103. }
  104. $val = file_get_contents($val);
  105. }
  106. $allowedContentTypes = [
  107. 'image/png',
  108. 'image/jpeg',
  109. 'image/gif',
  110. ];
  111. if(!in_array($type, $allowedContentTypes, true)) {
  112. $type = 'application/octet-stream';
  113. }
  114. return [
  115. 'Content-Type' => $type,
  116. 'body' => $val
  117. ];
  118. } catch(\Exception $ex) {
  119. $this->logger->logException($ex);
  120. }
  121. return false;
  122. }
  123. private function readCard($cardData) {
  124. return Reader::read($cardData);
  125. }
  126. /**
  127. * @param Binary $photo
  128. * @return Parameter
  129. */
  130. private function getType($photo) {
  131. $params = $photo->parameters();
  132. if (isset($params['TYPE']) || isset($params['MEDIATYPE'])) {
  133. /** @var Parameter $typeParam */
  134. $typeParam = isset($params['TYPE']) ? $params['TYPE'] : $params['MEDIATYPE'];
  135. $type = $typeParam->getValue();
  136. if (strpos($type, 'image/') === 0) {
  137. return $type;
  138. } else {
  139. return 'image/' . strtolower($type);
  140. }
  141. }
  142. return '';
  143. }
  144. }