APIController.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
  5. *
  6. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  7. * @author Joas Schilling <coding@schilljs.com>
  8. *
  9. * @license GNU AGPL version 3 or any later version
  10. *
  11. * This program is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Affero General Public License as
  13. * published by the Free Software Foundation, either version 3 of the
  14. * License, or (at your option) any later version.
  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
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. *
  24. */
  25. namespace OCA\UpdateNotification\Controller;
  26. use OC\App\AppStore\Fetcher\AppFetcher;
  27. use OCA\UpdateNotification\ResponseDefinitions;
  28. use OCP\App\AppPathNotFoundException;
  29. use OCP\App\IAppManager;
  30. use OCP\AppFramework\Http;
  31. use OCP\AppFramework\Http\DataResponse;
  32. use OCP\AppFramework\OCSController;
  33. use OCP\IConfig;
  34. use OCP\IRequest;
  35. use OCP\IUserSession;
  36. use OCP\L10N\IFactory;
  37. /**
  38. * @psalm-import-type UpdateNotificationApp from ResponseDefinitions
  39. */
  40. class APIController extends OCSController {
  41. /** @var IConfig */
  42. protected $config;
  43. /** @var IAppManager */
  44. protected $appManager;
  45. /** @var AppFetcher */
  46. protected $appFetcher;
  47. /** @var IFactory */
  48. protected $l10nFactory;
  49. /** @var IUserSession */
  50. protected $userSession;
  51. /** @var string */
  52. protected $language;
  53. /**
  54. * List of apps that were in the appstore but are now shipped and don't have
  55. * a compatible update available.
  56. *
  57. * @var array<string, int>
  58. */
  59. protected array $appsShippedInFutureVersion = [
  60. 'bruteforcesettings' => 25,
  61. 'suspicious_login' => 25,
  62. 'twofactor_totp' => 25,
  63. ];
  64. public function __construct(string $appName,
  65. IRequest $request,
  66. IConfig $config,
  67. IAppManager $appManager,
  68. AppFetcher $appFetcher,
  69. IFactory $l10nFactory,
  70. IUserSession $userSession) {
  71. parent::__construct($appName, $request);
  72. $this->config = $config;
  73. $this->appManager = $appManager;
  74. $this->appFetcher = $appFetcher;
  75. $this->l10nFactory = $l10nFactory;
  76. $this->userSession = $userSession;
  77. }
  78. /**
  79. * List available updates for apps
  80. *
  81. * @param string $newVersion Server version to check updates for
  82. *
  83. * @return DataResponse<Http::STATUS_OK, array{missing: UpdateNotificationApp[], available: UpdateNotificationApp[]}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{appstore_disabled: bool, already_on_latest?: bool}, array{}>
  84. *
  85. * 200: Apps returned
  86. * 404: New versions not found
  87. */
  88. public function getAppList(string $newVersion): DataResponse {
  89. if (!$this->config->getSystemValue('appstoreenabled', true)) {
  90. return new DataResponse([
  91. 'appstore_disabled' => true,
  92. ], Http::STATUS_NOT_FOUND);
  93. }
  94. // Get list of installed custom apps
  95. $installedApps = $this->appManager->getInstalledApps();
  96. $installedApps = array_filter($installedApps, function ($app) {
  97. try {
  98. $this->appManager->getAppPath($app);
  99. } catch (AppPathNotFoundException $e) {
  100. return false;
  101. }
  102. return !$this->appManager->isShipped($app) && !isset($this->appsShippedInFutureVersion[$app]);
  103. });
  104. if (empty($installedApps)) {
  105. return new DataResponse([
  106. 'missing' => [],
  107. 'available' => [],
  108. ]);
  109. }
  110. $this->appFetcher->setVersion($newVersion, 'future-apps.json', false);
  111. // Apps available on the app store for that version
  112. $availableApps = array_map(static function (array $app) {
  113. return $app['id'];
  114. }, $this->appFetcher->get());
  115. if (empty($availableApps)) {
  116. return new DataResponse([
  117. 'appstore_disabled' => false,
  118. 'already_on_latest' => false,
  119. ], Http::STATUS_NOT_FOUND);
  120. }
  121. $this->language = $this->l10nFactory->getUserLanguage($this->userSession->getUser());
  122. // Ignore apps that are deployed from git
  123. $installedApps = array_filter($installedApps, function (string $appId) {
  124. try {
  125. return !file_exists($this->appManager->getAppPath($appId) . '/.git');
  126. } catch (AppPathNotFoundException $e) {
  127. return true;
  128. }
  129. });
  130. $missing = array_diff($installedApps, $availableApps);
  131. $missing = array_map([$this, 'getAppDetails'], $missing);
  132. sort($missing);
  133. $available = array_intersect($installedApps, $availableApps);
  134. $available = array_map([$this, 'getAppDetails'], $available);
  135. sort($available);
  136. return new DataResponse([
  137. 'missing' => $missing,
  138. 'available' => $available,
  139. ]);
  140. }
  141. /**
  142. * Get translated app name
  143. *
  144. * @param string $appId
  145. * @return UpdateNotificationApp
  146. */
  147. protected function getAppDetails(string $appId): array {
  148. $app = $this->appManager->getAppInfo($appId, false, $this->language);
  149. /** @var ?string $name */
  150. $name = $app['name'];
  151. return [
  152. 'appId' => $appId,
  153. 'appName' => $name ?? $appId,
  154. ];
  155. }
  156. }