AddressBook.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Georg Ehrke <oc.list@georgehrke.com>
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Roeland Jago Douma <roeland@famdouma.nl>
  8. * @author Thomas Müller <thomas.mueller@tmit.eu>
  9. *
  10. * @license AGPL-3.0
  11. *
  12. * This code is free software: you can redistribute it and/or modify
  13. * it under the terms of the GNU Affero General Public License, version 3,
  14. * as published by the Free Software Foundation.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License, version 3,
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>
  23. *
  24. */
  25. namespace OCA\DAV\CardDAV;
  26. use OCA\DAV\DAV\Sharing\IShareable;
  27. use OCA\DAV\Exception\UnsupportedLimitOnInitialSyncException;
  28. use OCP\IL10N;
  29. use Sabre\CardDAV\Backend\BackendInterface;
  30. use Sabre\CardDAV\Card;
  31. use Sabre\DAV\Exception\Forbidden;
  32. use Sabre\DAV\Exception\NotFound;
  33. use Sabre\DAV\PropPatch;
  34. /**
  35. * Class AddressBook
  36. *
  37. * @package OCA\DAV\CardDAV
  38. * @property BackendInterface|CardDavBackend $carddavBackend
  39. */
  40. class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable {
  41. /**
  42. * AddressBook constructor.
  43. *
  44. * @param BackendInterface $carddavBackend
  45. * @param array $addressBookInfo
  46. * @param IL10N $l10n
  47. */
  48. public function __construct(BackendInterface $carddavBackend, array $addressBookInfo, IL10N $l10n) {
  49. parent::__construct($carddavBackend, $addressBookInfo);
  50. if ($this->addressBookInfo['{DAV:}displayname'] === CardDavBackend::PERSONAL_ADDRESSBOOK_NAME &&
  51. $this->getName() === CardDavBackend::PERSONAL_ADDRESSBOOK_URI) {
  52. $this->addressBookInfo['{DAV:}displayname'] = $l10n->t('Contacts');
  53. }
  54. }
  55. /**
  56. * Updates the list of shares.
  57. *
  58. * The first array is a list of people that are to be added to the
  59. * addressbook.
  60. *
  61. * Every element in the add array has the following properties:
  62. * * href - A url. Usually a mailto: address
  63. * * commonName - Usually a first and last name, or false
  64. * * summary - A description of the share, can also be false
  65. * * readOnly - A boolean value
  66. *
  67. * Every element in the remove array is just the address string.
  68. *
  69. * @param array $add
  70. * @param array $remove
  71. * @return void
  72. * @throws Forbidden
  73. */
  74. public function updateShares(array $add, array $remove) {
  75. if ($this->isShared()) {
  76. throw new Forbidden();
  77. }
  78. $this->carddavBackend->updateShares($this, $add, $remove);
  79. }
  80. /**
  81. * Returns the list of people whom this addressbook is shared with.
  82. *
  83. * Every element in this array should have the following properties:
  84. * * href - Often a mailto: address
  85. * * commonName - Optional, for example a first + last name
  86. * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants.
  87. * * readOnly - boolean
  88. * * summary - Optional, a description for the share
  89. *
  90. * @return array
  91. */
  92. public function getShares() {
  93. if ($this->isShared()) {
  94. return [];
  95. }
  96. return $this->carddavBackend->getShares($this->getResourceId());
  97. }
  98. public function getACL() {
  99. $acl = [
  100. [
  101. 'privilege' => '{DAV:}read',
  102. 'principal' => $this->getOwner(),
  103. 'protected' => true,
  104. ],[
  105. 'privilege' => '{DAV:}write',
  106. 'principal' => $this->getOwner(),
  107. 'protected' => true,
  108. ]
  109. ];
  110. if ($this->getOwner() === 'principals/system/system') {
  111. $acl[] = [
  112. 'privilege' => '{DAV:}read',
  113. 'principal' => '{DAV:}authenticated',
  114. 'protected' => true,
  115. ];
  116. }
  117. if (!$this->isShared()) {
  118. return $acl;
  119. }
  120. if ($this->getOwner() !== parent::getOwner()) {
  121. $acl[] = [
  122. 'privilege' => '{DAV:}read',
  123. 'principal' => parent::getOwner(),
  124. 'protected' => true,
  125. ];
  126. if ($this->canWrite()) {
  127. $acl[] = [
  128. 'privilege' => '{DAV:}write',
  129. 'principal' => parent::getOwner(),
  130. 'protected' => true,
  131. ];
  132. }
  133. }
  134. $acl = $this->carddavBackend->applyShareAcl($this->getResourceId(), $acl);
  135. $allowedPrincipals = [$this->getOwner(), parent::getOwner(), 'principals/system/system'];
  136. return array_filter($acl, function($rule) use ($allowedPrincipals) {
  137. return \in_array($rule['principal'], $allowedPrincipals, true);
  138. });
  139. }
  140. public function getChildACL() {
  141. return $this->getACL();
  142. }
  143. public function getChild($name) {
  144. $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name);
  145. if (!$obj) {
  146. throw new NotFound('Card not found');
  147. }
  148. $obj['acl'] = $this->getChildACL();
  149. return new Card($this->carddavBackend, $this->addressBookInfo, $obj);
  150. }
  151. /**
  152. * @return int
  153. */
  154. public function getResourceId() {
  155. return $this->addressBookInfo['id'];
  156. }
  157. public function getOwner() {
  158. if (isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) {
  159. return $this->addressBookInfo['{http://owncloud.org/ns}owner-principal'];
  160. }
  161. return parent::getOwner();
  162. }
  163. public function delete() {
  164. if (isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) {
  165. $principal = 'principal:' . parent::getOwner();
  166. $shares = $this->carddavBackend->getShares($this->getResourceId());
  167. $shares = array_filter($shares, function($share) use ($principal){
  168. return $share['href'] === $principal;
  169. });
  170. if (empty($shares)) {
  171. throw new Forbidden();
  172. }
  173. $this->carddavBackend->updateShares($this, [], [
  174. $principal
  175. ]);
  176. return;
  177. }
  178. parent::delete();
  179. }
  180. public function propPatch(PropPatch $propPatch) {
  181. if (isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) {
  182. throw new Forbidden();
  183. }
  184. parent::propPatch($propPatch);
  185. }
  186. public function getContactsGroups() {
  187. return $this->carddavBackend->collectCardProperties($this->getResourceId(), 'CATEGORIES');
  188. }
  189. private function isShared() {
  190. if (!isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) {
  191. return false;
  192. }
  193. return $this->addressBookInfo['{http://owncloud.org/ns}owner-principal'] !== $this->addressBookInfo['principaluri'];
  194. }
  195. private function canWrite() {
  196. if (isset($this->addressBookInfo['{http://owncloud.org/ns}read-only'])) {
  197. return !$this->addressBookInfo['{http://owncloud.org/ns}read-only'];
  198. }
  199. return true;
  200. }
  201. public function getChanges($syncToken, $syncLevel, $limit = null) {
  202. if (!$syncToken && $limit) {
  203. throw new UnsupportedLimitOnInitialSyncException();
  204. }
  205. return parent::getChanges($syncToken, $syncLevel, $limit);
  206. }
  207. }