CardDavContext.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. require __DIR__ . '/../../vendor/autoload.php';
  8. use GuzzleHttp\Client;
  9. use GuzzleHttp\Exception\GuzzleException;
  10. use GuzzleHttp\Message\ResponseInterface;
  11. class CardDavContext implements \Behat\Behat\Context\Context {
  12. /** @var string */
  13. private $baseUrl;
  14. /** @var Client */
  15. private $client;
  16. /** @var ResponseInterface */
  17. private $response;
  18. /** @var string */
  19. private $responseXml = '';
  20. /**
  21. * @param string $baseUrl
  22. */
  23. public function __construct($baseUrl) {
  24. $this->baseUrl = $baseUrl;
  25. // in case of ci deployment we take the server url from the environment
  26. $testServerUrl = getenv('TEST_SERVER_URL');
  27. if ($testServerUrl !== false) {
  28. $this->baseUrl = substr($testServerUrl, 0, -5);
  29. }
  30. }
  31. /** @BeforeScenario */
  32. public function setUpScenario() {
  33. $this->client = new Client();
  34. $this->responseXml = '';
  35. }
  36. /** @AfterScenario */
  37. public function afterScenario() {
  38. $davUrl = $this->baseUrl . '/remote.php/dav/addressbooks/users/admin/MyAddressbook';
  39. try {
  40. $this->client->delete(
  41. $davUrl,
  42. [
  43. 'auth' => [
  44. 'admin',
  45. 'admin',
  46. ],
  47. ]
  48. );
  49. } catch (\GuzzleHttp\Exception\ClientException $e) {
  50. }
  51. }
  52. /**
  53. * @When :user requests addressbook :addressBook with statuscode :statusCode on the endpoint :endpoint
  54. * @param string $user
  55. * @param string $addressBook
  56. * @param int $statusCode
  57. * @param string $endpoint
  58. * @throws \Exception
  59. */
  60. public function requestsAddressbookWithStatuscodeOnTheEndpoint($user, $addressBook, $statusCode, $endpoint) {
  61. $davUrl = $this->baseUrl . $endpoint . $addressBook;
  62. $password = ($user === 'admin') ? 'admin' : '123456';
  63. try {
  64. $this->response = $this->client->request(
  65. 'PROPFIND',
  66. $davUrl,
  67. [
  68. 'auth' => [
  69. $user,
  70. $password,
  71. ],
  72. ]
  73. );
  74. } catch (\GuzzleHttp\Exception\ClientException $e) {
  75. $this->response = $e->getResponse();
  76. }
  77. if ((int)$statusCode !== $this->response->getStatusCode()) {
  78. throw new \Exception(
  79. sprintf(
  80. 'Expected %s got %s',
  81. (int)$statusCode,
  82. $this->response->getStatusCode()
  83. )
  84. );
  85. }
  86. $body = $this->response->getBody()->getContents();
  87. if (substr($body, 0, 1) === '<') {
  88. $reader = new Sabre\Xml\Reader();
  89. $reader->xml($body);
  90. $this->responseXml = $reader->parse();
  91. }
  92. }
  93. /**
  94. * @Given :user creates an addressbook named :addressBook with statuscode :statusCode
  95. * @param string $user
  96. * @param string $addressBook
  97. * @param int $statusCode
  98. * @throws \Exception
  99. */
  100. public function createsAnAddressbookNamedWithStatuscode($user, $addressBook, $statusCode) {
  101. $davUrl = $this->baseUrl . '/remote.php/dav/addressbooks/users/' . $user . '/' . $addressBook;
  102. $password = ($user === 'admin') ? 'admin' : '123456';
  103. $this->response = $this->client->request(
  104. 'MKCOL',
  105. $davUrl,
  106. [
  107. 'body' => '<d:mkcol xmlns:card="urn:ietf:params:xml:ns:carddav"
  108. xmlns:d="DAV:">
  109. <d:set>
  110. <d:prop>
  111. <d:resourcetype>
  112. <d:collection />,<card:addressbook />
  113. </d:resourcetype>,<d:displayname>' . $addressBook . '</d:displayname>
  114. </d:prop>
  115. </d:set>
  116. </d:mkcol>',
  117. 'auth' => [
  118. $user,
  119. $password,
  120. ],
  121. 'headers' => [
  122. 'Content-Type' => 'application/xml;charset=UTF-8',
  123. ],
  124. ]
  125. );
  126. if ($this->response->getStatusCode() !== (int)$statusCode) {
  127. throw new \Exception(
  128. sprintf(
  129. 'Expected %s got %s',
  130. (int)$statusCode,
  131. $this->response->getStatusCode()
  132. )
  133. );
  134. }
  135. }
  136. /**
  137. * @When The CardDAV exception is :message
  138. * @param string $message
  139. * @throws \Exception
  140. */
  141. public function theCarddavExceptionIs($message) {
  142. $result = $this->responseXml['value'][0]['value'];
  143. if ($message !== $result) {
  144. throw new \Exception(
  145. sprintf(
  146. 'Expected %s got %s',
  147. $message,
  148. $result
  149. )
  150. );
  151. }
  152. }
  153. /**
  154. * @When The CardDAV error message is :arg1
  155. * @param string $message
  156. * @throws \Exception
  157. */
  158. public function theCarddavErrorMessageIs($message) {
  159. $result = $this->responseXml['value'][1]['value'];
  160. if ($message !== $result) {
  161. throw new \Exception(
  162. sprintf(
  163. 'Expected %s got %s',
  164. $message,
  165. $result
  166. )
  167. );
  168. }
  169. }
  170. /**
  171. * @Given :user uploads the contact :fileName to the addressbook :addressbook
  172. */
  173. public function uploadsTheContactToTheAddressbook($user, $fileName, $addressBook) {
  174. $davUrl = $this->baseUrl . '/remote.php/dav/addressbooks/users/' . $user . '/' . $addressBook . '/' . $fileName;
  175. $password = ($user === 'admin') ? 'admin' : '123456';
  176. $this->response = $this->client->request(
  177. 'PUT',
  178. $davUrl,
  179. [
  180. 'body' => file_get_contents(__DIR__ . '/../../data/' . $fileName),
  181. 'auth' => [
  182. $user,
  183. $password,
  184. ],
  185. 'headers' => [
  186. 'Content-Type' => 'application/xml;charset=UTF-8',
  187. ],
  188. ]
  189. );
  190. if ($this->response->getStatusCode() !== 201) {
  191. throw new \Exception(
  192. sprintf(
  193. 'Expected %s got %s',
  194. 201,
  195. $this->response->getStatusCode()
  196. )
  197. );
  198. }
  199. }
  200. /**
  201. * @When Exporting the picture of contact :fileName from addressbook :addressBook as user :user
  202. */
  203. public function whenExportingThePictureOfContactFromAddressbookAsUser($fileName, $addressBook, $user) {
  204. $davUrl = $this->baseUrl . '/remote.php/dav/addressbooks/users/' . $user . '/' . $addressBook . '/' . $fileName . '?photo=true';
  205. $password = ($user === 'admin') ? 'admin' : '123456';
  206. try {
  207. $this->response = $this->client->request(
  208. 'GET',
  209. $davUrl,
  210. [
  211. 'auth' => [
  212. $user,
  213. $password,
  214. ],
  215. 'headers' => [
  216. 'Content-Type' => 'application/xml;charset=UTF-8',
  217. ],
  218. ]
  219. );
  220. } catch (\GuzzleHttp\Exception\ClientException $e) {
  221. $this->response = $e->getResponse();
  222. }
  223. }
  224. /**
  225. * @When Downloading the contact :fileName from addressbook :addressBook as user :user
  226. */
  227. public function whenDownloadingTheContactFromAddressbookAsUser($fileName, $addressBook, $user) {
  228. $davUrl = $this->baseUrl . '/remote.php/dav/addressbooks/users/' . $user . '/' . $addressBook . '/' . $fileName;
  229. $password = ($user === 'admin') ? 'admin' : '123456';
  230. try {
  231. $this->response = $this->client->request(
  232. 'GET',
  233. $davUrl,
  234. [
  235. 'auth' => [
  236. $user,
  237. $password,
  238. ],
  239. 'headers' => [
  240. 'Content-Type' => 'application/xml;charset=UTF-8',
  241. ],
  242. ]
  243. );
  244. } catch (\GuzzleHttp\Exception\ClientException $e) {
  245. $this->response = $e->getResponse();
  246. }
  247. }
  248. /**
  249. * @Then The following HTTP headers should be set
  250. * @param \Behat\Gherkin\Node\TableNode $table
  251. * @throws \Exception
  252. */
  253. public function theFollowingHttpHeadersShouldBeSet(\Behat\Gherkin\Node\TableNode $table) {
  254. foreach ($table->getTable() as $header) {
  255. $headerName = $header[0];
  256. $expectedHeaderValue = $header[1];
  257. $returnedHeader = $this->response->getHeader($headerName)[0];
  258. if ($returnedHeader !== $expectedHeaderValue) {
  259. throw new \Exception(
  260. sprintf(
  261. "Expected value '%s' for header '%s', got '%s'",
  262. $expectedHeaderValue,
  263. $headerName,
  264. $returnedHeader
  265. )
  266. );
  267. }
  268. }
  269. }
  270. /**
  271. * @When :user sends a create addressbook request to :addressbook on the endpoint :endpoint
  272. */
  273. public function sendsCreateAddressbookRequest(string $user, string $addressbook, string $endpoint) {
  274. $davUrl = $this->baseUrl . $endpoint . $addressbook;
  275. $password = ($user === 'admin') ? 'admin' : '123456';
  276. try {
  277. $this->response = $this->client->request(
  278. 'MKCOL',
  279. $davUrl,
  280. [
  281. 'body' => '<d:mkcol xmlns:card="urn:ietf:params:xml:ns:carddav"
  282. xmlns:d="DAV:">
  283. <d:set>
  284. <d:prop>
  285. <d:resourcetype>
  286. <d:collection />,<card:addressbook />
  287. </d:resourcetype>,<d:displayname>' . $addressbook . '</d:displayname>
  288. </d:prop>
  289. </d:set>
  290. </d:mkcol>',
  291. 'auth' => [
  292. $user,
  293. $password,
  294. ],
  295. 'headers' => [
  296. 'Content-Type' => 'application/xml;charset=UTF-8',
  297. ],
  298. ]
  299. );
  300. } catch (GuzzleException $e) {
  301. $this->response = $e->getResponse();
  302. }
  303. }
  304. /**
  305. * @Then The CardDAV HTTP status code should be :code
  306. * @param int $code
  307. * @throws \Exception
  308. */
  309. public function theCarddavHttpStatusCodeShouldBe($code) {
  310. if ((int)$code !== $this->response->getStatusCode()) {
  311. throw new \Exception(
  312. sprintf(
  313. 'Expected %s got %s',
  314. (int)$code,
  315. $this->response->getStatusCode()
  316. )
  317. );
  318. }
  319. $body = $this->response->getBody()->getContents();
  320. if ($body && substr($body, 0, 1) === '<') {
  321. $reader = new Sabre\Xml\Reader();
  322. $reader->xml($body);
  323. $this->responseXml = $reader->parse();
  324. }
  325. }
  326. }