ocsclient.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, ownCloud, Inc.
  4. *
  5. * @author Bart Visscher <bartv@thisnet.nl>
  6. * @author Brice Maron <brice@bmaron.net>
  7. * @author Felix Moeller <mail@felixmoeller.de>
  8. * @author Frank Karlitschek <frank@karlitschek.de>
  9. * @author Joas Schilling <coding@schilljs.com>
  10. * @author Jörn Friedrich Dreyer <jfd@butonic.de>
  11. * @author Kamil Domanski <kdomanski@kdemail.net>
  12. * @author Lukas Reschke <lukas@statuscode.ch>
  13. * @author Morris Jobke <hey@morrisjobke.de>
  14. * @author Robin McCorkell <robin@mccorkell.me.uk>
  15. * @author Sam Tuke <mail@samtuke.com>
  16. * @author Thomas Müller <thomas.mueller@tmit.eu>
  17. *
  18. * @license AGPL-3.0
  19. *
  20. * This code is free software: you can redistribute it and/or modify
  21. * it under the terms of the GNU Affero General Public License, version 3,
  22. * as published by the Free Software Foundation.
  23. *
  24. * This program is distributed in the hope that it will be useful,
  25. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. * GNU Affero General Public License for more details.
  28. *
  29. * You should have received a copy of the GNU Affero General Public License, version 3,
  30. * along with this program. If not, see <http://www.gnu.org/licenses/>
  31. *
  32. */
  33. namespace OC;
  34. use OCP\Http\Client\IClientService;
  35. use OCP\IConfig;
  36. use OCP\ILogger;
  37. /**
  38. * Class OCSClient is a class for communication with the ownCloud appstore
  39. *
  40. * @package OC
  41. */
  42. class OCSClient {
  43. /** @var IClientService */
  44. private $httpClientService;
  45. /** @var IConfig */
  46. private $config;
  47. /** @var ILogger */
  48. private $logger;
  49. /**
  50. * @param IClientService $httpClientService
  51. * @param IConfig $config
  52. * @param ILogger $logger
  53. */
  54. public function __construct(IClientService $httpClientService,
  55. IConfig $config,
  56. ILogger $logger) {
  57. $this->httpClientService = $httpClientService;
  58. $this->config = $config;
  59. $this->logger = $logger;
  60. }
  61. /**
  62. * Returns whether the AppStore is enabled (i.e. because the AppStore is disabled for EE)
  63. *
  64. * @return bool
  65. */
  66. public function isAppStoreEnabled() {
  67. // For a regular edition default to true, all others default to false
  68. $default = false;
  69. if (\OC_Util::getEditionString() === '') {
  70. $default = true;
  71. }
  72. return $this->config->getSystemValue('appstoreenabled', $default) === true;
  73. }
  74. /**
  75. * Get the url of the OCS AppStore server.
  76. *
  77. * @return string of the AppStore server
  78. */
  79. private function getAppStoreUrl() {
  80. return $this->config->getSystemValue('appstoreurl', 'https://api.owncloud.com/v1');
  81. }
  82. /**
  83. * @param string $body
  84. * @param string $action
  85. * @return null|\SimpleXMLElement
  86. */
  87. private function loadData($body, $action) {
  88. $loadEntities = libxml_disable_entity_loader(true);
  89. $data = @simplexml_load_string($body);
  90. libxml_disable_entity_loader($loadEntities);
  91. if($data === false) {
  92. libxml_clear_errors();
  93. $this->logger->error(
  94. sprintf('Could not get %s, content was no valid XML', $action),
  95. [
  96. 'app' => 'core',
  97. ]
  98. );
  99. return null;
  100. }
  101. return $data;
  102. }
  103. /**
  104. * Get all the categories from the OCS server
  105. *
  106. * @param array $targetVersion The target ownCloud version
  107. * @return array|null an array of category ids or null
  108. * @note returns NULL if config value appstoreenabled is set to false
  109. * This function returns a list of all the application categories on the OCS server
  110. */
  111. public function getCategories(array $targetVersion) {
  112. if (!$this->isAppStoreEnabled()) {
  113. return null;
  114. }
  115. $client = $this->httpClientService->newClient();
  116. try {
  117. $response = $client->get(
  118. $this->getAppStoreUrl() . '/content/categories',
  119. [
  120. 'timeout' => 5,
  121. 'query' => [
  122. 'version' => implode('x', $targetVersion),
  123. ],
  124. ]
  125. );
  126. } catch(\Exception $e) {
  127. $this->logger->error(
  128. sprintf('Could not get categories: %s', $e->getMessage()),
  129. [
  130. 'app' => 'core',
  131. ]
  132. );
  133. return null;
  134. }
  135. $data = $this->loadData($response->getBody(), 'categories');
  136. if($data === null) {
  137. return null;
  138. }
  139. $tmp = $data->data;
  140. $cats = [];
  141. foreach ($tmp->category as $value) {
  142. $id = (int)$value->id;
  143. $name = (string)$value->name;
  144. $cats[$id] = $name;
  145. }
  146. return $cats;
  147. }
  148. /**
  149. * Get all the applications from the OCS server
  150. * @param array $categories
  151. * @param int $page
  152. * @param string $filter
  153. * @param array $targetVersion The target ownCloud version
  154. * @return array An array of application data
  155. */
  156. public function getApplications(array $categories, $page, $filter, array $targetVersion) {
  157. if (!$this->isAppStoreEnabled()) {
  158. return [];
  159. }
  160. $client = $this->httpClientService->newClient();
  161. try {
  162. $response = $client->get(
  163. $this->getAppStoreUrl() . '/content/data',
  164. [
  165. 'timeout' => 5,
  166. 'query' => [
  167. 'version' => implode('x', $targetVersion),
  168. 'filter' => $filter,
  169. 'categories' => implode('x', $categories),
  170. 'sortmode' => 'new',
  171. 'page' => $page,
  172. 'pagesize' => 100,
  173. 'approved' => $filter
  174. ],
  175. ]
  176. );
  177. } catch(\Exception $e) {
  178. $this->logger->error(
  179. sprintf('Could not get applications: %s', $e->getMessage()),
  180. [
  181. 'app' => 'core',
  182. ]
  183. );
  184. return [];
  185. }
  186. $data = $this->loadData($response->getBody(), 'applications');
  187. if($data === null) {
  188. return [];
  189. }
  190. $tmp = $data->data->content;
  191. $tmpCount = count($tmp);
  192. $apps = [];
  193. for ($i = 0; $i < $tmpCount; $i++) {
  194. $app = [];
  195. $app['id'] = (string)$tmp[$i]->id;
  196. $app['name'] = (string)$tmp[$i]->name;
  197. $app['label'] = (string)$tmp[$i]->label;
  198. $app['version'] = (string)$tmp[$i]->version;
  199. $app['type'] = (string)$tmp[$i]->typeid;
  200. $app['typename'] = (string)$tmp[$i]->typename;
  201. $app['personid'] = (string)$tmp[$i]->personid;
  202. $app['profilepage'] = (string)$tmp[$i]->profilepage;
  203. $app['license'] = (string)$tmp[$i]->license;
  204. $app['detailpage'] = (string)$tmp[$i]->detailpage;
  205. $app['preview'] = (string)$tmp[$i]->smallpreviewpic1;
  206. $app['preview-full'] = (string)$tmp[$i]->previewpic1;
  207. $app['changed'] = strtotime($tmp[$i]->changed);
  208. $app['description'] = (string)$tmp[$i]->description;
  209. $app['score'] = (string)$tmp[$i]->score;
  210. $app['downloads'] = (int)$tmp[$i]->downloads;
  211. $app['level'] = (int)$tmp[$i]->approved;
  212. $apps[] = $app;
  213. }
  214. return $apps;
  215. }
  216. /**
  217. * Get an the applications from the OCS server
  218. *
  219. * @param string $id
  220. * @param array $targetVersion The target ownCloud version
  221. * @return array|null an array of application data or null
  222. *
  223. * This function returns an applications from the OCS server
  224. */
  225. public function getApplication($id, array $targetVersion) {
  226. if (!$this->isAppStoreEnabled()) {
  227. return null;
  228. }
  229. $client = $this->httpClientService->newClient();
  230. try {
  231. $response = $client->get(
  232. $this->getAppStoreUrl() . '/content/data/' . urlencode($id),
  233. [
  234. 'timeout' => 5,
  235. 'query' => [
  236. 'version' => implode('x', $targetVersion),
  237. ],
  238. ]
  239. );
  240. } catch(\Exception $e) {
  241. $this->logger->error(
  242. sprintf('Could not get application: %s', $e->getMessage()),
  243. [
  244. 'app' => 'core',
  245. ]
  246. );
  247. return null;
  248. }
  249. $data = $this->loadData($response->getBody(), 'application');
  250. if($data === null) {
  251. return null;
  252. }
  253. $tmp = $data->data->content;
  254. if (is_null($tmp)) {
  255. \OCP\Util::writeLog('core', 'No update found at the Nextcloud appstore for app ' . $id, \OCP\Util::DEBUG);
  256. return null;
  257. }
  258. $app = [];
  259. $app['id'] = (int)$id;
  260. $app['name'] = (string)$tmp->name;
  261. $app['version'] = (string)$tmp->version;
  262. $app['type'] = (string)$tmp->typeid;
  263. $app['label'] = (string)$tmp->label;
  264. $app['typename'] = (string)$tmp->typename;
  265. $app['personid'] = (string)$tmp->personid;
  266. $app['profilepage'] = (string)$tmp->profilepage;
  267. $app['detailpage'] = (string)$tmp->detailpage;
  268. $app['preview1'] = (string)$tmp->smallpreviewpic1;
  269. $app['preview2'] = (string)$tmp->smallpreviewpic2;
  270. $app['preview3'] = (string)$tmp->smallpreviewpic3;
  271. $app['changed'] = strtotime($tmp->changed);
  272. $app['description'] = (string)$tmp->description;
  273. $app['detailpage'] = (string)$tmp->detailpage;
  274. $app['score'] = (int)$tmp->score;
  275. $app['level'] = (int)$tmp->approved;
  276. return $app;
  277. }
  278. /**
  279. * Get the download url for an application from the OCS server
  280. * @param string $id
  281. * @param array $targetVersion The target ownCloud version
  282. * @return array|null an array of application data or null
  283. */
  284. public function getApplicationDownload($id, array $targetVersion) {
  285. if (!$this->isAppStoreEnabled()) {
  286. return null;
  287. }
  288. $url = $this->getAppStoreUrl() . '/content/download/' . urlencode($id) . '/1';
  289. $client = $this->httpClientService->newClient();
  290. try {
  291. $response = $client->get(
  292. $url,
  293. [
  294. 'timeout' => 5,
  295. 'query' => [
  296. 'version' => implode('x', $targetVersion),
  297. ],
  298. ]
  299. );
  300. } catch(\Exception $e) {
  301. $this->logger->error(
  302. sprintf('Could not get application download URL: %s', $e->getMessage()),
  303. [
  304. 'app' => 'core',
  305. ]
  306. );
  307. return null;
  308. }
  309. $data = $this->loadData($response->getBody(), 'application download URL');
  310. if($data === null) {
  311. return null;
  312. }
  313. $tmp = $data->data->content;
  314. $app = [];
  315. if (isset($tmp->downloadlink)) {
  316. $app['downloadlink'] = (string)$tmp->downloadlink;
  317. } else {
  318. $app['downloadlink'] = '';
  319. }
  320. return $app;
  321. }
  322. }