summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMichael Grosser <development@stp-ip.net>2016-11-02 17:00:24 +0000
committerGitHub <noreply@github.com>2016-11-02 17:00:24 +0000
commite81d04cd8d2ac0de3d06d4586550469384c5d91a (patch)
tree0e243d105db3e00f1d3f899b9ecfbd73f3d98235 /lib
parent23dd62b653e42201468f9bed369a5993dea876b0 (diff)
parent0eeef26a8e2515b802015fe09bef7909aa183491 (diff)
downloadnextcloud-server-e81d04cd8d2ac0de3d06d4586550469384c5d91a.tar.gz
nextcloud-server-e81d04cd8d2ac0de3d06d4586550469384c5d91a.zip
Merge pull request #1940 from nextcloud/new-appstore
Use new appstore API
Diffstat (limited to 'lib')
-rw-r--r--lib/composer/composer/autoload_classmap.php6
-rw-r--r--lib/composer/composer/autoload_static.php6
-rw-r--r--lib/private/App/AppStore/Fetcher/AppFetcher.php56
-rw-r--r--lib/private/App/AppStore/Fetcher/CategoryFetcher.php45
-rw-r--r--lib/private/App/AppStore/Fetcher/Fetcher.php92
-rw-r--r--lib/private/App/AppStore/Version/Version.php52
-rw-r--r--lib/private/App/AppStore/Version/VersionParser.php83
-rw-r--r--lib/private/App/DependencyAnalyzer.php9
-rw-r--r--lib/private/Archive/Archive.php21
-rw-r--r--lib/private/Installer.php526
-rw-r--r--lib/private/OCSClient.php351
-rw-r--r--lib/private/Server.php32
-rw-r--r--lib/private/Updater.php15
-rw-r--r--lib/private/legacy/app.php245
-rw-r--r--lib/private/legacy/util.php1
15 files changed, 649 insertions, 891 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 532a6f39848..ddd531868d4 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -279,6 +279,11 @@ return array(
'OC\\AppFramework\\Utility\\TimeFactory' => $baseDir . '/lib/private/AppFramework/Utility/TimeFactory.php',
'OC\\AppHelper' => $baseDir . '/lib/private/AppHelper.php',
'OC\\App\\AppManager' => $baseDir . '/lib/private/App/AppManager.php',
+ 'OC\\App\\AppStore\\Fetcher\\AppFetcher' => $baseDir . '/lib/private/App/AppStore/Fetcher/AppFetcher.php',
+ 'OC\\App\\AppStore\\Fetcher\\CategoryFetcher' => $baseDir . '/lib/private/App/AppStore/Fetcher/CategoryFetcher.php',
+ 'OC\\App\\AppStore\\Fetcher\\Fetcher' => $baseDir . '/lib/private/App/AppStore/Fetcher/Fetcher.php',
+ 'OC\\App\\AppStore\\Version\\Version' => $baseDir . '/lib/private/App/AppStore/Version/Version.php',
+ 'OC\\App\\AppStore\\Version\\VersionParser' => $baseDir . '/lib/private/App/AppStore/Version/VersionParser.php',
'OC\\App\\CodeChecker\\AbstractCheck' => $baseDir . '/lib/private/App/CodeChecker/AbstractCheck.php',
'OC\\App\\CodeChecker\\CodeChecker' => $baseDir . '/lib/private/App/CodeChecker/CodeChecker.php',
'OC\\App\\CodeChecker\\DeprecationCheck' => $baseDir . '/lib/private/App/CodeChecker/DeprecationCheck.php',
@@ -602,7 +607,6 @@ return array(
'OC\\Notification\\Action' => $baseDir . '/lib/private/Notification/Action.php',
'OC\\Notification\\Manager' => $baseDir . '/lib/private/Notification/Manager.php',
'OC\\Notification\\Notification' => $baseDir . '/lib/private/Notification/Notification.php',
- 'OC\\OCSClient' => $baseDir . '/lib/private/OCSClient.php',
'OC\\OCS\\CoreCapabilities' => $baseDir . '/lib/private/OCS/CoreCapabilities.php',
'OC\\OCS\\Exception' => $baseDir . '/lib/private/OCS/Exception.php',
'OC\\OCS\\Person' => $baseDir . '/lib/private/OCS/Person.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index c0a3e9b50c6..99a3c3d540e 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -309,6 +309,11 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\AppFramework\\Utility\\TimeFactory' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Utility/TimeFactory.php',
'OC\\AppHelper' => __DIR__ . '/../../..' . '/lib/private/AppHelper.php',
'OC\\App\\AppManager' => __DIR__ . '/../../..' . '/lib/private/App/AppManager.php',
+ 'OC\\App\\AppStore\\Fetcher\\AppFetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Fetcher/AppFetcher.php',
+ 'OC\\App\\AppStore\\Fetcher\\CategoryFetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Fetcher/CategoryFetcher.php',
+ 'OC\\App\\AppStore\\Fetcher\\Fetcher' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Fetcher/Fetcher.php',
+ 'OC\\App\\AppStore\\Version\\Version' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Version/Version.php',
+ 'OC\\App\\AppStore\\Version\\VersionParser' => __DIR__ . '/../../..' . '/lib/private/App/AppStore/Version/VersionParser.php',
'OC\\App\\CodeChecker\\AbstractCheck' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/AbstractCheck.php',
'OC\\App\\CodeChecker\\CodeChecker' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/CodeChecker.php',
'OC\\App\\CodeChecker\\DeprecationCheck' => __DIR__ . '/../../..' . '/lib/private/App/CodeChecker/DeprecationCheck.php',
@@ -632,7 +637,6 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OC\\Notification\\Action' => __DIR__ . '/../../..' . '/lib/private/Notification/Action.php',
'OC\\Notification\\Manager' => __DIR__ . '/../../..' . '/lib/private/Notification/Manager.php',
'OC\\Notification\\Notification' => __DIR__ . '/../../..' . '/lib/private/Notification/Notification.php',
- 'OC\\OCSClient' => __DIR__ . '/../../..' . '/lib/private/OCSClient.php',
'OC\\OCS\\CoreCapabilities' => __DIR__ . '/../../..' . '/lib/private/OCS/CoreCapabilities.php',
'OC\\OCS\\Exception' => __DIR__ . '/../../..' . '/lib/private/OCS/Exception.php',
'OC\\OCS\\Person' => __DIR__ . '/../../..' . '/lib/private/OCS/Person.php',
diff --git a/lib/private/App/AppStore/Fetcher/AppFetcher.php b/lib/private/App/AppStore/Fetcher/AppFetcher.php
new file mode 100644
index 00000000000..19e61d416a0
--- /dev/null
+++ b/lib/private/App/AppStore/Fetcher/AppFetcher.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\App\AppStore\Fetcher;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Files\IAppData;
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+
+class AppFetcher extends Fetcher {
+ /**
+ * @param IAppData $appData
+ * @param IClientService $clientService
+ * @param ITimeFactory $timeFactory
+ * @param IConfig $config;
+ */
+ public function __construct(IAppData $appData,
+ IClientService $clientService,
+ ITimeFactory $timeFactory,
+ IConfig $config) {
+ parent::__construct(
+ $appData,
+ $clientService,
+ $timeFactory
+ );
+
+ $this->fileName = 'apps.json';
+
+ $versionArray = \OC_Util::getVersion();
+ $this->endpointUrl = sprintf(
+ 'https://apps.nextcloud.com/api/v1/platform/%d.%d.%d/apps.json',
+ $versionArray[0],
+ $versionArray[1],
+ $versionArray[2]
+ );
+ }
+}
diff --git a/lib/private/App/AppStore/Fetcher/CategoryFetcher.php b/lib/private/App/AppStore/Fetcher/CategoryFetcher.php
new file mode 100644
index 00000000000..74201ec3737
--- /dev/null
+++ b/lib/private/App/AppStore/Fetcher/CategoryFetcher.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\App\AppStore\Fetcher;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Files\IAppData;
+use OCP\Http\Client\IClientService;
+
+class CategoryFetcher extends Fetcher {
+ /**
+ * @param IAppData $appData
+ * @param IClientService $clientService
+ * @param ITimeFactory $timeFactory
+ */
+ public function __construct(IAppData $appData,
+ IClientService $clientService,
+ ITimeFactory $timeFactory) {
+ parent::__construct(
+ $appData,
+ $clientService,
+ $timeFactory
+ );
+ $this->fileName = 'categories.json';
+ $this->endpointUrl = 'https://apps.nextcloud.com/api/v1/categories.json';
+ }
+}
diff --git a/lib/private/App/AppStore/Fetcher/Fetcher.php b/lib/private/App/AppStore/Fetcher/Fetcher.php
new file mode 100644
index 00000000000..cffff9176e2
--- /dev/null
+++ b/lib/private/App/AppStore/Fetcher/Fetcher.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\App\AppStore\Fetcher;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\Files\IAppData;
+use OCP\Files\NotFoundException;
+use OCP\Http\Client\IClientService;
+
+abstract class Fetcher {
+ const INVALIDATE_AFTER_SECONDS = 300;
+
+ /** @var IAppData */
+ private $appData;
+ /** @var IClientService */
+ private $clientService;
+ /** @var ITimeFactory */
+ private $timeFactory;
+ /** @var string */
+ protected $fileName;
+ /** @var string */
+ protected $endpointUrl;
+
+ /**
+ * @param IAppData $appData
+ * @param IClientService $clientService
+ * @param ITimeFactory $timeFactory
+ */
+ public function __construct(IAppData $appData,
+ IClientService $clientService,
+ ITimeFactory $timeFactory) {
+ $this->appData = $appData;
+ $this->clientService = $clientService;
+ $this->timeFactory = $timeFactory;
+ }
+
+ /**
+ * Returns the array with the categories on the appstore server
+ *
+ * @return array
+ */
+ public function get() {
+ $rootFolder = $this->appData->getFolder('/');
+
+ try {
+ // File does already exists
+ $file = $rootFolder->getFile($this->fileName);
+ $jsonBlob = json_decode($file->getContent(), true);
+ if(is_array($jsonBlob)) {
+ // If the timestamp is older than 300 seconds request the files new
+ if((int)$jsonBlob['timestamp'] > ($this->timeFactory->getTime() - self::INVALIDATE_AFTER_SECONDS)) {
+ return $jsonBlob['data'];
+ }
+ }
+ } catch (NotFoundException $e) {
+ // File does not already exists
+ $file = $rootFolder->newFile($this->fileName);
+ }
+
+ // Refresh the file content
+ $client = $this->clientService->newClient();
+ try {
+ $response = $client->get($this->endpointUrl);
+ $responseJson = [];
+ $responseJson['data'] = json_decode($response->getBody(), true);
+ $responseJson['timestamp'] = $this->timeFactory->getTime();
+ $file->putContent(json_encode($responseJson));
+ return json_decode($file->getContent(), true)['data'];
+ } catch (\Exception $e) {
+ return [];
+ }
+ }
+}
diff --git a/lib/private/App/AppStore/Version/Version.php b/lib/private/App/AppStore/Version/Version.php
new file mode 100644
index 00000000000..ca182ae078b
--- /dev/null
+++ b/lib/private/App/AppStore/Version/Version.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\App\AppStore\Version;
+
+class Version {
+ /** @var string */
+ private $minVersion;
+ /** @var string */
+ private $maxVersion;
+
+ /**
+ * @param string $minVersion
+ * @param string $maxVersion
+ */
+ public function __construct($minVersion, $maxVersion) {
+ $this->minVersion = $minVersion;
+ $this->maxVersion = $maxVersion;
+ }
+
+ /**
+ * @return string
+ */
+ public function getMinimumVersion() {
+ return $this->minVersion;
+ }
+
+ /**
+ * @return string
+ */
+ public function getMaximumVersion() {
+ return $this->maxVersion;
+ }
+}
diff --git a/lib/private/App/AppStore/Version/VersionParser.php b/lib/private/App/AppStore/Version/VersionParser.php
new file mode 100644
index 00000000000..b548ef386d9
--- /dev/null
+++ b/lib/private/App/AppStore/Version/VersionParser.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OC\App\AppStore\Version;
+
+/**
+ * Class VersionParser parses the versions as sent by the Nextcloud app store
+ *
+ * @package OC\App\AppStore
+ */
+class VersionParser {
+ /**
+ * @param string $versionString
+ * @return bool
+ */
+ private function isValidVersionString($versionString) {
+ return (bool)preg_match('/^[0-9.]+$/', $versionString);
+ }
+
+ /**
+ * Returns the version for a version string
+ *
+ * @param string $versionSpec
+ * @return Version
+ * @throws \Exception If the version cannot be parsed
+ */
+ public function getVersion($versionSpec) {
+ // * indicates that the version is compatible with all versions
+ if($versionSpec === '*') {
+ return new Version('', '');
+ }
+
+ // Count the amount of =, if it is one then it's either maximum or minimum
+ // version. If it is two then it is maximum and minimum.
+ $versionElements = explode(' ', $versionSpec);
+ $firstVersion = isset($versionElements[0]) ? $versionElements[0] : '';
+ $firstVersionNumber = substr($firstVersion, 2);
+ $secondVersion = isset($versionElements[1]) ? $versionElements[1] : '';
+ $secondVersionNumber = substr($secondVersion, 2);
+
+ switch(count($versionElements)) {
+ case 1:
+ if(!$this->isValidVersionString($firstVersionNumber)) {
+ break;
+ }
+ if(substr($firstVersion, 0, 1) === '>') {
+ return new Version($firstVersionNumber, '');
+ } else {
+ return new Version('', $firstVersionNumber);
+ }
+ case 2:
+ if(!$this->isValidVersionString($firstVersionNumber) || !$this->isValidVersionString($secondVersionNumber)) {
+ break;
+ }
+ return new Version($firstVersionNumber, $secondVersionNumber);
+ }
+
+ throw new \Exception(
+ sprintf(
+ 'Version cannot be parsed: %s',
+ $versionSpec
+ )
+ );
+ }
+}
diff --git a/lib/private/App/DependencyAnalyzer.php b/lib/private/App/DependencyAnalyzer.php
index 67268981e99..c24b25ff14d 100644
--- a/lib/private/App/DependencyAnalyzer.php
+++ b/lib/private/App/DependencyAnalyzer.php
@@ -1,6 +1,7 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch>
*
* @author Bernhard Posselt <dev@bernhard-posselt.com>
* @author Joas Schilling <coding@schilljs.com>
@@ -294,7 +295,9 @@ class DependencyAnalyzer {
private function analyzeOC(array $dependencies, array $appInfo) {
$missing = [];
$minVersion = null;
- if (isset($dependencies['owncloud']['@attributes']['min-version'])) {
+ if (isset($dependencies['nextcloud']['@attributes']['min-version'])) {
+ $minVersion = $dependencies['nextcloud']['@attributes']['min-version'];
+ } elseif (isset($dependencies['owncloud']['@attributes']['min-version'])) {
$minVersion = $dependencies['owncloud']['@attributes']['min-version'];
} elseif (isset($appInfo['requiremin'])) {
$minVersion = $appInfo['requiremin'];
@@ -302,7 +305,9 @@ class DependencyAnalyzer {
$minVersion = $appInfo['require'];
}
$maxVersion = null;
- if (isset($dependencies['owncloud']['@attributes']['max-version'])) {
+ if (isset($dependencies['nextcloud']['@attributes']['max-version'])) {
+ $maxVersion = $dependencies['nextcloud']['@attributes']['max-version'];
+ } elseif (isset($dependencies['owncloud']['@attributes']['max-version'])) {
$maxVersion = $dependencies['owncloud']['@attributes']['max-version'];
} elseif (isset($appInfo['requiremax'])) {
$maxVersion = $appInfo['requiremax'];
diff --git a/lib/private/Archive/Archive.php b/lib/private/Archive/Archive.php
index da2c53f2aa1..fadc12d2a24 100644
--- a/lib/private/Archive/Archive.php
+++ b/lib/private/Archive/Archive.php
@@ -32,26 +32,7 @@
namespace OC\Archive;
-abstract class Archive{
- /**
- * Open any of the supported archive types
- *
- * @param string $path
- * @return Archive|void
- */
- public static function open($path) {
- $mime = \OC::$server->getMimeTypeDetector()->detect($path);
-
- switch($mime) {
- case 'application/zip':
- return new ZIP($path);
- case 'application/x-gzip':
- return new TAR($path);
- case 'application/x-bzip2':
- return new TAR($path);
- }
- }
-
+abstract class Archive {
/**
* @param $source
*/
diff --git a/lib/private/Installer.php b/lib/private/Installer.php
index 009df790585..2366b762654 100644
--- a/lib/private/Installer.php
+++ b/lib/private/Installer.php
@@ -1,6 +1,7 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Bart Visscher <bartv@thisnet.nl>
@@ -40,90 +41,64 @@
namespace OC;
+use OC\App\AppStore\Fetcher\AppFetcher;
use OC\App\CodeChecker\CodeChecker;
use OC\App\CodeChecker\EmptyCheck;
use OC\App\CodeChecker\PrivateCheck;
+use OC\Archive\Archive;
+use OC\Archive\TAR;
use OC_App;
use OC_DB;
use OC_Helper;
+use OCP\Http\Client\IClientService;
+use OCP\ILogger;
+use OCP\ITempManager;
+use phpseclib\File\X509;
/**
- * This class provides the functionality needed to install, update and remove plugins/apps
+ * This class provides the functionality needed to install, update and remove apps
*/
class Installer {
+ /** @var AppFetcher */
+ private $appFetcher;
+ /** @var IClientService */
+ private $clientService;
+ /** @var ITempManager */
+ private $tempManager;
+ /** @var ILogger */
+ private $logger;
/**
+ * @param AppFetcher $appFetcher
+ * @param IClientService $clientService
+ * @param ITempManager $tempManager
+ * @param ILogger $logger
+ */
+ public function __construct(AppFetcher $appFetcher,
+ IClientService $clientService,
+ ITempManager $tempManager,
+ ILogger $logger) {
+ $this->appFetcher = $appFetcher;
+ $this->clientService = $clientService;
+ $this->tempManager = $tempManager;
+ $this->logger = $logger;
+ }
+
+ /**
+ * Installs an app that is located in one of the app folders already
*
- * This function installs an app. All information needed are passed in the
- * associative array $data.
- * The following keys are required:
- * - source: string, can be "path" or "http"
- *
- * One of the following keys is required:
- * - path: path to the file containing the app
- * - href: link to the downloadable file containing the app
- *
- * The following keys are optional:
- * - pretend: boolean, if set true the system won't do anything
- * - noinstall: boolean, if true appinfo/install.php won't be loaded
- * - inactive: boolean, if set true the appconfig/app.sample.php won't be
- * renamed
- *
- * This function works as follows
- * -# fetching the file
- * -# unzipping it
- * -# check the code
- * -# installing the database at appinfo/database.xml
- * -# including appinfo/install.php
- * -# setting the installed version
- *
- * It is the task of oc_app_install to create the tables and do whatever is
- * needed to get the app working.
- *
- * Installs an app
- * @param array $data with all information
+ * @param string $appId App to install
* @throws \Exception
* @return integer
*/
- public static function installApp( $data = array()) {
- $l = \OC::$server->getL10N('lib');
-
- list($extractDir, $path) = self::downloadApp($data);
-
- $info = self::checkAppsIntegrity($data, $extractDir, $path);
- $appId = OC_App::cleanAppId($info['id']);
- $basedir = OC_App::getInstallPath().'/'.$appId;
- //check if the destination directory already exists
- if(is_dir($basedir)) {
- OC_Helper::rmdirr($extractDir);
- if($data['source']=='http') {
- unlink($path);
- }
- throw new \Exception($l->t("App directory already exists"));
- }
-
- if(!empty($data['pretent'])) {
- return false;
+ public function installApp($appId) {
+ $app = \OC_App::findAppInDirectories($appId);
+ if($app === false) {
+ throw new \Exception('App not found in any app directory');
}
- //copy the app to the correct place
- if(@!mkdir($basedir)) {
- OC_Helper::rmdirr($extractDir);
- if($data['source']=='http') {
- unlink($path);
- }
- throw new \Exception($l->t("Can't create app folder. Please fix permissions. %s", array($basedir)));
- }
-
- $extractDir .= '/' . $info['id'];
- if(!file_exists($extractDir)) {
- OC_Helper::rmdirr($basedir);
- throw new \Exception($l->t("Archive does not contain a directory named %s", $info['id']));
- }
- OC_Helper::copyr($extractDir, $basedir);
-
- //remove temporary files
- OC_Helper::rmdirr($extractDir);
+ $basedir = $app['path'].'/'.$appId;
+ $info = OC_App::getAppInfo($basedir.'/appinfo/info.xml', true);
//install the database
if(is_file($basedir.'/appinfo/database.xml')) {
@@ -168,259 +143,189 @@ class Installer {
*
* Checks whether or not an app is installed, i.e. registered in apps table.
*/
- public static function isInstalled( $app ) {
+ public static function isInstalled( $app ) {
return (\OC::$server->getConfig()->getAppValue($app, "installed_version", null) !== null);
}
/**
- * @brief Update an application
- * @param array $info
- * @param bool $isShipped
- * @throws \Exception
- * @return bool
- *
- * This function could work like described below, but currently it disables and then
- * enables the app again. This does result in an updated app.
- *
- *
- * This function installs an app. All information needed are passed in the
- * associative array $info.
- * The following keys are required:
- * - source: string, can be "path" or "http"
- *
- * One of the following keys is required:
- * - path: path to the file containing the app
- * - href: link to the downloadable file containing the app
- *
- * The following keys are optional:
- * - pretend: boolean, if set true the system won't do anything
- * - noupgrade: boolean, if true appinfo/upgrade.php won't be loaded
- *
- * This function works as follows
- * -# fetching the file
- * -# removing the old files
- * -# unzipping new file
- * -# including appinfo/upgrade.php
- * -# setting the installed version
- *
- * upgrade.php can determine the current installed version of the app using
- * "\OC::$server->getAppConfig()->getValue($appid, 'installed_version')"
- */
- public static function updateApp($info=array(), $isShipped=false) {
- list($extractDir, $path) = self::downloadApp($info);
- $info = self::checkAppsIntegrity($info, $extractDir, $path, $isShipped);
-
- $currentDir = OC_App::getAppPath($info['id']);
- $basedir = OC_App::getInstallPath();
- $basedir .= '/';
- $basedir .= $info['id'];
-
- if($currentDir !== false && is_writable($currentDir)) {
- $basedir = $currentDir;
- }
- if(is_dir($basedir)) {
- OC_Helper::rmdirr($basedir);
- }
-
- $appInExtractDir = $extractDir;
- if (substr($extractDir, -1) !== '/') {
- $appInExtractDir .= '/';
- }
-
- $appInExtractDir .= $info['id'];
- OC_Helper::copyr($appInExtractDir, $basedir);
- OC_Helper::rmdirr($extractDir);
-
- return OC_App::updateApp($info['id']);
- }
-
- /**
- * update an app by it's id
+ * Updates the specified app from the appstore
*
- * @param integer $ocsId
+ * @param string $appId
* @return bool
- * @throws \Exception
*/
- public static function updateAppByOCSId($ocsId) {
- $ocsClient = new OCSClient(
- \OC::$server->getHTTPClientService(),
- \OC::$server->getConfig(),
- \OC::$server->getLogger()
- );
- $appData = $ocsClient->getApplication($ocsId, \OCP\Util::getVersion());
- $download = $ocsClient->getApplicationDownload($ocsId, \OCP\Util::getVersion());
-
- if (isset($download['downloadlink']) && trim($download['downloadlink']) !== '') {
- $download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
- $info = array(
- 'source' => 'http',
- 'href' => $download['downloadlink'],
- 'appdata' => $appData
- );
- } else {
- throw new \Exception('Could not fetch app info!');
+ public function updateAppstoreApp($appId) {
+ if(self::isUpdateAvailable($appId, $this->appFetcher)) {
+ try {
+ $this->downloadApp($appId);
+ } catch (\Exception $e) {
+ $this->logger->error($e->getMessage(), ['app' => 'core']);
+ return false;
+ }
+ return OC_App::updateApp($appId);
}
- return self::updateApp($info);
+ return false;
}
/**
- * @param array $data
- * @return array
- * @throws \Exception
+ * Downloads an app and puts it into the app directory
+ *
+ * @param string $appId
+ *
+ * @throws \Exception If the installation was not successful
*/
- public static function downloadApp($data = array()) {
- $l = \OC::$server->getL10N('lib');
-
- if(!isset($data['source'])) {
- throw new \Exception($l->t("No source specified when installing app"));
- }
+ public function downloadApp($appId) {
+ $appId = strtolower($appId);
+
+ $apps = $this->appFetcher->get();
+ foreach($apps as $app) {
+ if($app['id'] === $appId) {
+ // Load the certificate
+ $certificate = new X509();
+ $certificate->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
+ $loadedCertificate = $certificate->loadX509($app['certificate']);
+
+ // Verify if the certificate has been revoked
+ $crl = new X509();
+ $crl->loadCA(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crt'));
+ $crl->loadCRL(file_get_contents(__DIR__ . '/../../resources/codesigning/root.crl'));
+ if($crl->validateSignature() !== true) {
+ throw new \Exception('Could not validate CRL signature');
+ }
+ $csn = $loadedCertificate['tbsCertificate']['serialNumber']->toString();
+ $revoked = $crl->getRevoked($csn);
+ if ($revoked !== false) {
+ throw new \Exception(
+ sprintf(
+ 'Certificate "%s" has been revoked',
+ $csn
+ )
+ );
+ }
- //download the file if necessary
- if($data['source']=='http') {
- $pathInfo = pathinfo($data['href']);
- $extension = isset($pathInfo['extension']) ? '.' . $pathInfo['extension'] : '';
- $path = \OC::$server->getTempManager()->getTemporaryFile($extension);
- if(!isset($data['href'])) {
- throw new \Exception($l->t("No href specified when installing app from http"));
- }
- $client = \OC::$server->getHTTPClientService()->newClient();
- $client->get($data['href'], ['save_to' => $path]);
- } else {
- if(!isset($data['path'])) {
- throw new \Exception($l->t("No path specified when installing app from local file"));
- }
- $path=$data['path'];
- }
+ // Verify if the certificate has been issued by the Nextcloud Code Authority CA
+ if($certificate->validateSignature() !== true) {
+ throw new \Exception(
+ sprintf(
+ 'App with id %s has a certificate not issued by a trusted Code Signing Authority',
+ $appId
+ )
+ );
+ }
- //detect the archive type
- $mime = \OC::$server->getMimeTypeDetector()->detect($path);
- if ($mime !=='application/zip' && $mime !== 'application/x-gzip' && $mime !== 'application/x-bzip2') {
- throw new \Exception($l->t("Archives of type %s are not supported", array($mime)));
- }
+ // Verify if the certificate is issued for the requested app id
+ $certInfo = openssl_x509_parse($app['certificate']);
+ if(!isset($certInfo['subject']['CN'])) {
+ throw new \Exception(
+ sprintf(
+ 'App with id %s has a cert with no CN',
+ $appId
+ )
+ );
+ }
+ if($certInfo['subject']['CN'] !== $appId) {
+ throw new \Exception(
+ sprintf(
+ 'App with id %s has a cert issued to %s',
+ $appId,
+ $certInfo['subject']['CN']
+ )
+ );
+ }
- //extract the archive in a temporary folder
- $extractDir = \OC::$server->getTempManager()->getTemporaryFolder();
- OC_Helper::rmdirr($extractDir);
- mkdir($extractDir);
- if($archive=\OC\Archive\Archive::open($path)) {
- $archive->extract($extractDir);
- } else {
- OC_Helper::rmdirr($extractDir);
- if($data['source']=='http') {
- unlink($path);
- }
- throw new \Exception($l->t("Failed to open archive when installing app"));
- }
+ // Download the release
+ $tempFile = $this->tempManager->getTemporaryFile('.tar.gz');
+ $client = $this->clientService->newClient();
+ $client->get($app['releases'][0]['download'], ['save_to' => $tempFile]);
+
+ // Check if the signature actually matches the downloaded content
+ $certificate = openssl_get_publickey($app['certificate']);
+ $verified = (bool)openssl_verify(file_get_contents($tempFile), base64_decode($app['releases'][0]['signature']), $certificate, OPENSSL_ALGO_SHA512);
+ openssl_free_key($certificate);
+
+ if($verified === true) {
+ // Seems to match, let's proceed
+ $extractDir = $this->tempManager->getTemporaryFolder();
+ $archive = new TAR($tempFile);
+
+ if($archive) {
+ $archive->extract($extractDir);
+ $allFiles = scandir($extractDir);
+ $folders = array_diff($allFiles, ['.', '..']);
+ $folders = array_values($folders);
+
+ if(count($folders) > 1) {
+ throw new \Exception(
+ sprintf(
+ 'Extracted app %s has more than 1 folder',
+ $appId
+ )
+ );
+ }
- return array(
- $extractDir,
- $path
- );
- }
+ // Check if appinfo/info.xml has the same app ID as well
+ $loadEntities = libxml_disable_entity_loader(false);
+ $xml = simplexml_load_file($extractDir . '/' . $folders[0] . '/appinfo/info.xml');
+ libxml_disable_entity_loader($loadEntities);
+ if((string)$xml->id !== $appId) {
+ throw new \Exception(
+ sprintf(
+ 'App for id %s has a wrong app ID in info.xml: %s',
+ $appId,
+ (string)$xml->id
+ )
+ );
+ }
- /**
- * check an app's integrity
- * @param array $data
- * @param string $extractDir
- * @param string $path
- * @param bool $isShipped
- * @return array
- * @throws \Exception
- */
- public static function checkAppsIntegrity($data, $extractDir, $path, $isShipped = false) {
- $l = \OC::$server->getL10N('lib');
- //load the info.xml file of the app
- if(!is_file($extractDir.'/appinfo/info.xml')) {
- //try to find it in a subdir
- $dh=opendir($extractDir);
- if(is_resource($dh)) {
- while (($folder = readdir($dh)) !== false) {
- if($folder[0]!='.' and is_dir($extractDir.'/'.$folder)) {
- if(is_file($extractDir.'/'.$folder.'/appinfo/info.xml')) {
- $extractDir.='/'.$folder;
+ $baseDir = OC_App::getInstallPath() . '/' . $appId;
+ // Remove old app with the ID if existent
+ OC_Helper::rmdirr($baseDir);
+ // Move to app folder
+ if(@mkdir($baseDir)) {
+ $extractDir .= '/' . $folders[0];
+ OC_Helper::copyr($extractDir, $baseDir);
}
+ OC_Helper::copyr($extractDir, $baseDir);
+ OC_Helper::rmdirr($extractDir);
+ return;
+ } else {
+ throw new \Exception(
+ sprintf(
+ 'Could not extract app with ID %s to %s',
+ $appId,
+ $extractDir
+ )
+ );
}
- }
- }
- }
- if(!is_file($extractDir.'/appinfo/info.xml')) {
- OC_Helper::rmdirr($extractDir);
- if($data['source'] === 'http') {
- unlink($path);
- }
- throw new \Exception($l->t("App does not provide an info.xml file"));
- }
-
- $info = OC_App::getAppInfo($extractDir.'/appinfo/info.xml', true);
- if(!is_array($info)) {
- throw new \Exception($l->t('App cannot be installed because appinfo file cannot be read.'));
- }
-
- // We can't trust the parsed info.xml file as it may have been tampered
- // with by an attacker and thus we need to use the local data to check
- // whether the application needs to be signed.
- $appId = OC_App::cleanAppId($data['appdata']['id']);
- $appBelongingToId = OC_App::getInternalAppIdByOcs($appId);
- if(is_string($appBelongingToId)) {
- $previouslySigned = \OC::$server->getConfig()->getAppValue($appBelongingToId, 'signed', 'false');
- } else {
- $appBelongingToId = $info['id'];
- $previouslySigned = 'false';
- }
- if($data['appdata']['level'] === OC_App::officialApp || $previouslySigned === 'true') {
- \OC::$server->getConfig()->setAppValue($appBelongingToId, 'signed', 'true');
- $integrityResult = \OC::$server->getIntegrityCodeChecker()->verifyAppSignature(
- $appBelongingToId,
- $extractDir
- );
- if($integrityResult !== []) {
- $e = new \Exception(
- $l->t(
- 'Signature could not get checked. Please contact the app developer and check your admin screen.'
+ } else {
+ // Signature does not match
+ throw new \Exception(
+ sprintf(
+ 'App with id %s has invalid signature',
+ $appId
)
- );
- throw $e;
+ );
+ }
}
}
- // check the code for not allowed calls
- if(!$isShipped && !Installer::checkCode($extractDir)) {
- OC_Helper::rmdirr($extractDir);
- throw new \Exception($l->t("App can't be installed because of not allowed code in the App"));
- }
-
- // check if the app is compatible with this version of ownCloud
- if(!OC_App::isAppCompatible(\OCP\Util::getVersion(), $info)) {
- OC_Helper::rmdirr($extractDir);
- throw new \Exception($l->t("App can't be installed because it is not compatible with this version of the server"));
- }
-
- // check if shipped tag is set which is only allowed for apps that are shipped with ownCloud
- if(!$isShipped && isset($info['shipped']) && ($info['shipped']=='true')) {
- OC_Helper::rmdirr($extractDir);
- throw new \Exception($l->t("App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps"));
- }
-
- // check if the ocs version is the same as the version in info.xml/version
- $version = trim($info['version']);
-
- if(isset($data['appdata']['version']) && $version<>trim($data['appdata']['version'])) {
- OC_Helper::rmdirr($extractDir);
- throw new \Exception($l->t("App can't be installed because the version in info.xml is not the same as the version reported from the app store"));
- }
-
- return $info;
+ throw new \Exception(
+ sprintf(
+ 'Could not download app %s',
+ $appId
+ )
+ );
}
/**
* Check if an update for the app is available
- * @param string $app
- * @return string|false false or the version number of the update
*
- * The function will check if an update for a version is available
+ * @param string $appId
+ * @param AppFetcher $appFetcher
+ * @return string|false false or the version number of the update
*/
- public static function isUpdateAvailable( $app ) {
+ public static function isUpdateAvailable($appId,
+ AppFetcher $appFetcher) {
static $isInstanceReadyForUpdates = null;
if ($isInstanceReadyForUpdates === null) {
@@ -436,27 +341,20 @@ class Installer {
return false;
}
- $ocsid=\OC::$server->getAppConfig()->getValue( $app, 'ocsid', '');
-
- if($ocsid<>'') {
- $ocsClient = new OCSClient(
- \OC::$server->getHTTPClientService(),
- \OC::$server->getConfig(),
- \OC::$server->getLogger()
- );
- $ocsdata = $ocsClient->getApplication($ocsid, \OCP\Util::getVersion());
- $ocsversion= (string) $ocsdata['version'];
- $currentversion=OC_App::getAppVersion($app);
- if (version_compare($ocsversion, $currentversion, '>')) {
- return($ocsversion);
- }else{
- return false;
+ $apps = $appFetcher->get();
+ foreach($apps as $app) {
+ if($app['id'] === $appId) {
+ $currentVersion = OC_App::getAppVersion($appId);
+ $newestVersion = $app['releases'][0]['version'];
+ if (version_compare($newestVersion, $currentVersion, '>')) {
+ return $newestVersion;
+ } else {
+ return false;
+ }
}
-
- }else{
- return false;
}
+ return false;
}
/**
@@ -466,7 +364,7 @@ class Installer {
*
* The function will check if the app is already downloaded in the apps repository
*/
- public static function isDownloaded( $name ) {
+ public function isDownloaded($name) {
foreach(\OC::$APPSROOTS as $dir) {
$dirToTest = $dir['path'];
$dirToTest .= '/';
@@ -483,7 +381,7 @@ class Installer {
/**
* Removes an app
- * @param string $name name of the application to remove
+ * @param string $appId ID of the application to remove
* @return boolean
*
*
@@ -494,12 +392,10 @@ class Installer {
* The function will not delete preferences, tables and the configuration,
* this has to be done by the function oc_app_uninstall().
*/
- public static function removeApp($appId) {
-
- if(Installer::isDownloaded( $appId )) {
- $appDir=OC_App::getInstallPath() . '/' . $appId;
+ public function removeApp($appId) {
+ if($this->isDownloaded( $appId )) {
+ $appDir = OC_App::getInstallPath() . '/' . $appId;
OC_Helper::rmdirr($appDir);
-
return true;
}else{
\OCP\Util::writeLog('core', 'can\'t remove app '.$appId.'. It is not installed.', \OCP\Util::ERROR);
@@ -620,7 +516,7 @@ class Installer {
}
/**
- * @param $basedir
+ * @param string $script
*/
private static function includeAppScript($script) {
if ( file_exists($script) ){
diff --git a/lib/private/OCSClient.php b/lib/private/OCSClient.php
deleted file mode 100644
index 76c0b136c06..00000000000
--- a/lib/private/OCSClient.php
+++ /dev/null
@@ -1,351 +0,0 @@
-<?php
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Bart Visscher <bartv@thisnet.nl>
- * @author Brice Maron <brice@bmaron.net>
- * @author Felix Moeller <mail@felixmoeller.de>
- * @author Frank Karlitschek <frank@karlitschek.de>
- * @author Jarrett <JetUni@users.noreply.github.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Jörn Friedrich Dreyer <jfd@butonic.de>
- * @author Kamil Domanski <kdomanski@kdemail.net>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin McCorkell <robin@mccorkell.me.uk>
- * @author Sam Tuke <mail@samtuke.com>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-namespace OC;
-
-use OCP\Http\Client\IClientService;
-use OCP\IConfig;
-use OCP\ILogger;
-
-/**
- * Class OCSClient is a class for communication with the ownCloud appstore
- *
- * @package OC
- */
-class OCSClient {
- /** @var IClientService */
- private $httpClientService;
- /** @var IConfig */
- private $config;
- /** @var ILogger */
- private $logger;
-
- /**
- * @param IClientService $httpClientService
- * @param IConfig $config
- * @param ILogger $logger
- */
- public function __construct(IClientService $httpClientService,
- IConfig $config,
- ILogger $logger) {
- $this->httpClientService = $httpClientService;
- $this->config = $config;
- $this->logger = $logger;
- }
-
- /**
- * Returns whether the AppStore is enabled (i.e. because the AppStore is disabled for EE)
- *
- * @return bool
- */
- public function isAppStoreEnabled() {
- return $this->config->getSystemValue('appstoreenabled', true) === true;
- }
-
- /**
- * Get the url of the OCS AppStore server.
- *
- * @return string of the AppStore server
- */
- private function getAppStoreUrl() {
- return $this->config->getSystemValue('appstoreurl', 'https://api.owncloud.com/v1');
- }
-
- /**
- * @param string $body
- * @param string $action
- * @return null|\SimpleXMLElement
- */
- private function loadData($body, $action) {
- $loadEntities = libxml_disable_entity_loader(true);
- $data = @simplexml_load_string($body);
- libxml_disable_entity_loader($loadEntities);
-
- if($data === false) {
- libxml_clear_errors();
- $this->logger->error(
- sprintf('Could not get %s, content was no valid XML', $action),
- [
- 'app' => 'core',
- ]
- );
- return null;
- }
-
- return $data;
- }
-
- /**
- * Get all the categories from the OCS server
- *
- * @param array $targetVersion The target ownCloud version
- * @return array|null an array of category ids or null
- * @note returns NULL if config value appstoreenabled is set to false
- * This function returns a list of all the application categories on the OCS server
- */
- public function getCategories(array $targetVersion) {
- if (!$this->isAppStoreEnabled()) {
- return null;
- }
-
- $client = $this->httpClientService->newClient();
- try {
- $response = $client->get(
- $this->getAppStoreUrl() . '/content/categories',
- [
- 'timeout' => 20,
- 'query' => [
- 'version' => implode('x', $targetVersion),
- ],
- ]
- );
- } catch(\Exception $e) {
- $this->logger->error(
- sprintf('Could not get categories: %s', $e->getMessage()),
- [
- 'app' => 'core',
- ]
- );
- return null;
- }
-
- $data = $this->loadData($response->getBody(), 'categories');
- if($data === null) {
- return null;
- }
-
- $tmp = $data->data;
- $cats = [];
-
- foreach ($tmp->category as $value) {
- $id = (int)$value->id;
- $name = (string)$value->name;
- $cats[$id] = $name;
- }
-
- return $cats;
- }
-
- /**
- * Get all the applications from the OCS server
- * @param array $categories
- * @param int $page
- * @param string $filter
- * @param array $targetVersion The target ownCloud version
- * @return array An array of application data
- */
- public function getApplications(array $categories, $page, $filter, array $targetVersion) {
- if (!$this->isAppStoreEnabled()) {
- return [];
- }
-
- $client = $this->httpClientService->newClient();
- try {
- $response = $client->get(
- $this->getAppStoreUrl() . '/content/data',
- [
- 'timeout' => 20,
- 'query' => [
- 'version' => implode('x', $targetVersion),
- 'filter' => $filter,
- 'categories' => implode('x', $categories),
- 'sortmode' => 'new',
- 'page' => $page,
- 'pagesize' => 100,
- 'approved' => $filter
- ],
- ]
- );
- } catch(\Exception $e) {
- $this->logger->error(
- sprintf('Could not get applications: %s', $e->getMessage()),
- [
- 'app' => 'core',
- ]
- );
- return [];
- }
-
- $data = $this->loadData($response->getBody(), 'applications');
- if($data === null) {
- return [];
- }
-
- $tmp = $data->data->content;
- $tmpCount = count($tmp);
-
- $apps = [];
- for ($i = 0; $i < $tmpCount; $i++) {
- $app = [];
- $app['id'] = (string)$tmp[$i]->id;
- $app['name'] = (string)$tmp[$i]->name;
- $app['label'] = (string)$tmp[$i]->label;
- $app['version'] = (string)$tmp[$i]->version;
- $app['type'] = (string)$tmp[$i]->typeid;
- $app['typename'] = (string)$tmp[$i]->typename;
- $app['personid'] = (string)$tmp[$i]->personid;
- $app['profilepage'] = (string)$tmp[$i]->profilepage;
- $app['license'] = (string)$tmp[$i]->license;
- $app['detailpage'] = (string)$tmp[$i]->detailpage;
- $app['preview'] = (string)$tmp[$i]->smallpreviewpic1;
- $app['preview-full'] = (string)$tmp[$i]->previewpic1;
- $app['changed'] = strtotime($tmp[$i]->changed);
- $app['description'] = (string)$tmp[$i]->description;
- $app['score'] = (string)$tmp[$i]->score;
- $app['downloads'] = (int)$tmp[$i]->downloads;
- $app['level'] = (int)$tmp[$i]->approved;
-
- $apps[] = $app;
- }
-
- return $apps;
- }
-
-
- /**
- * Get an the applications from the OCS server
- *
- * @param string $id
- * @param array $targetVersion The target ownCloud version
- * @return array|null an array of application data or null
- *
- * This function returns an applications from the OCS server
- */
- public function getApplication($id, array $targetVersion) {
- if (!$this->isAppStoreEnabled()) {
- return null;
- }
-
- $client = $this->httpClientService->newClient();
- try {
- $response = $client->get(
- $this->getAppStoreUrl() . '/content/data/' . urlencode($id),
- [
- 'timeout' => 20,
- 'query' => [
- 'version' => implode('x', $targetVersion),
- ],
- ]
- );
- } catch(\Exception $e) {
- $this->logger->error(
- sprintf('Could not get application: %s', $e->getMessage()),
- [
- 'app' => 'core',
- ]
- );
- return null;
- }
-
- $data = $this->loadData($response->getBody(), 'application');
- if($data === null) {
- return null;
- }
-
- $tmp = $data->data->content;
- if (is_null($tmp)) {
- \OCP\Util::writeLog('core', 'No update found at the ownCloud appstore for app ' . $id, \OCP\Util::DEBUG);
- return null;
- }
-
- $app = [];
- $app['id'] = (int)$id;
- $app['name'] = (string)$tmp->name;
- $app['version'] = (string)$tmp->version;
- $app['type'] = (string)$tmp->typeid;
- $app['label'] = (string)$tmp->label;
- $app['typename'] = (string)$tmp->typename;
- $app['personid'] = (string)$tmp->personid;
- $app['profilepage'] = (string)$tmp->profilepage;
- $app['detailpage'] = (string)$tmp->detailpage;
- $app['preview1'] = (string)$tmp->smallpreviewpic1;
- $app['preview2'] = (string)$tmp->smallpreviewpic2;
- $app['preview3'] = (string)$tmp->smallpreviewpic3;
- $app['changed'] = strtotime($tmp->changed);
- $app['description'] = (string)$tmp->description;
- $app['detailpage'] = (string)$tmp->detailpage;
- $app['score'] = (int)$tmp->score;
- $app['level'] = (int)$tmp->approved;
-
- return $app;
- }
-
- /**
- * Get the download url for an application from the OCS server
- * @param string $id
- * @param array $targetVersion The target ownCloud version
- * @return array|null an array of application data or null
- */
- public function getApplicationDownload($id, array $targetVersion) {
- if (!$this->isAppStoreEnabled()) {
- return null;
- }
- $url = $this->getAppStoreUrl() . '/content/download/' . urlencode($id) . '/1';
- $client = $this->httpClientService->newClient();
- try {
- $response = $client->get(
- $url,
- [
- 'timeout' => 20,
- 'query' => [
- 'version' => implode('x', $targetVersion),
- ],
- ]
- );
- } catch(\Exception $e) {
- $this->logger->error(
- sprintf('Could not get application download URL: %s', $e->getMessage()),
- [
- 'app' => 'core',
- ]
- );
- return null;
- }
-
- $data = $this->loadData($response->getBody(), 'application download URL');
- if($data === null) {
- return null;
- }
-
- $tmp = $data->data->content;
- $app = [];
- if (isset($tmp->downloadlink)) {
- $app['downloadlink'] = (string)$tmp->downloadlink;
- } else {
- $app['downloadlink'] = '';
- }
- return $app;
- }
-
-}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index 21ec311401d..3ea358498da 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -1,6 +1,7 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Bart Visscher <bartv@thisnet.nl>
@@ -41,6 +42,8 @@
namespace OC;
use bantu\IniGetWrapper\IniGetWrapper;
+use OC\App\AppStore\Fetcher\AppFetcher;
+use OC\App\AppStore\Fetcher\CategoryFetcher;
use OC\AppFramework\Http\Request;
use OC\AppFramework\Db\Db;
use OC\AppFramework\Utility\TimeFactory;
@@ -320,6 +323,21 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerService('AppHelper', function ($c) {
return new \OC\AppHelper();
});
+ $this->registerService('AppFetcher', function ($c) {
+ return new AppFetcher(
+ $this->getAppDataDir('appstore'),
+ $this->getHTTPClientService(),
+ $this->query(TimeFactory::class),
+ $this->getConfig()
+ );
+ });
+ $this->registerService('CategoryFetcher', function ($c) {
+ return new CategoryFetcher(
+ $this->getAppDataDir('appstore'),
+ $this->getHTTPClientService(),
+ $this->query(TimeFactory::class)
+ );
+ });
$this->registerService('UserCache', function ($c) {
return new Cache\File();
});
@@ -580,13 +598,6 @@ class Server extends ServerContainer implements IServerContainer {
$c->getThemingDefaults()
);
});
- $this->registerService('OcsClient', function (Server $c) {
- return new OCSClient(
- $this->getHTTPClientService(),
- $this->getConfig(),
- $this->getLogger()
- );
- });
$this->registerService('LDAPProvider', function(Server $c) {
$config = $c->getConfig();
$factoryClass = $config->getSystemValue('ldapProviderFactory', null);
@@ -1008,6 +1019,13 @@ class Server extends ServerContainer implements IServerContainer {
}
/**
+ * @return AppFetcher
+ */
+ public function getAppFetcher() {
+ return $this->query('AppFetcher');
+ }
+
+ /**
* Returns an ICache instance. Since 8.1.0 it returns a fake cache. Use
* getMemCacheFactory() instead.
*
diff --git a/lib/private/Updater.php b/lib/private/Updater.php
index 646fc031a83..cd2934f7196 100644
--- a/lib/private/Updater.php
+++ b/lib/private/Updater.php
@@ -1,6 +1,7 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Frank Karlitschek <frank@karlitschek.de>
@@ -426,11 +427,15 @@ class Updater extends BasicEmitter {
private function upgradeAppStoreApps(array $disabledApps) {
foreach($disabledApps as $app) {
try {
- if (Installer::isUpdateAvailable($app)) {
- $ocsId = \OC::$server->getConfig()->getAppValue($app, 'ocsid', '');
-
- $this->emit('\OC\Updater', 'upgradeAppStoreApp', array($app));
- Installer::updateAppByOCSId($ocsId);
+ $installer = new Installer(
+ \OC::$server->getAppFetcher(),
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getTempManager(),
+ $this->log
+ );
+ if (Installer::isUpdateAvailable($app, \OC::$server->getAppFetcher())) {
+ $this->emit('\OC\Updater', 'upgradeAppStoreApp', [$app]);
+ $installer->updateAppstoreApp($app);
}
} catch (\Exception $ex) {
$this->log->logException($ex, ['app' => 'core']);
diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php
index d25534aa822..a89a4650c5d 100644
--- a/lib/private/legacy/app.php
+++ b/lib/private/legacy/app.php
@@ -1,6 +1,7 @@
<?php
/**
* @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Bart Visscher <bartv@thisnet.nl>
@@ -326,24 +327,44 @@ class OC_App {
/**
* enables an app
*
- * @param mixed $app app
+ * @param string $appId
* @param array $groups (optional) when set, only these groups will have access to the app
* @throws \Exception
* @return void
*
* This function set an app as enabled in appconfig.
*/
- public static function enable($app, $groups = null) {
+ public function enable($appId,
+ $groups = null) {
self::$enabledAppsCache = []; // flush
- if (!Installer::isInstalled($app)) {
- $app = self::installApp($app);
+ $l = \OC::$server->getL10N('core');
+ $config = \OC::$server->getConfig();
+
+ // Check if app is already downloaded
+ $installer = new Installer(
+ \OC::$server->getAppFetcher(),
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getTempManager(),
+ \OC::$server->getLogger()
+ );
+ $isDownloaded = $installer->isDownloaded($appId);
+
+ if(!$isDownloaded) {
+ $installer->downloadApp($appId);
+ }
+
+ if (!Installer::isInstalled($appId)) {
+ $appId = self::installApp(
+ $appId,
+ $config,
+ $l
+ );
+ $installer->installApp($appId);
} else {
// check for required dependencies
- $config = \OC::$server->getConfig();
- $l = \OC::$server->getL10N('core');
- $info = self::getAppInfo($app);
-
+ $info = self::getAppInfo($appId);
self::checkAppDependencies($config, $l, $info);
+ $installer->installApp($appId);
}
$appManager = \OC::$server->getAppManager();
@@ -356,42 +377,21 @@ class OC_App {
$groupsList[] = $groupManager->get($group);
}
}
- $appManager->enableAppForGroups($app, $groupsList);
+ $appManager->enableAppForGroups($appId, $groupsList);
} else {
- $appManager->enableApp($app);
+ $appManager->enableApp($appId);
}
- $info = self::getAppInfo($app);
+ $info = self::getAppInfo($appId);
if(isset($info['settings']) && is_array($info['settings'])) {
- $appPath = self::getAppPath($app);
- self::registerAutoloading($app, $appPath);
+ $appPath = self::getAppPath($appId);
+ self::registerAutoloading($appId, $appPath);
\OC::$server->getSettingsManager()->setupSettings($info['settings']);
}
}
/**
* @param string $app
- * @return int
- */
- private static function downloadApp($app) {
- $ocsClient = new OCSClient(
- \OC::$server->getHTTPClientService(),
- \OC::$server->getConfig(),
- \OC::$server->getLogger()
- );
- $appData = $ocsClient->getApplication($app, \OCP\Util::getVersion());
- $download = $ocsClient->getApplicationDownload($app, \OCP\Util::getVersion());
- if(isset($download['downloadlink']) and $download['downloadlink']!='') {
- // Replace spaces in download link without encoding entire URL
- $download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
- $info = array('source' => 'http', 'href' => $download['downloadlink'], 'appdata' => $appData);
- $app = Installer::installApp($info);
- }
- return $app;
- }
-
- /**
- * @param string $app
* @return bool
*/
public static function removeApp($app) {
@@ -399,7 +399,13 @@ class OC_App {
return false;
}
- return Installer::removeApp($app);
+ $installer = new Installer(
+ \OC::$server->getAppFetcher(),
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getTempManager(),
+ \OC::$server->getLogger()
+ );
+ return $installer->removeApp($app);
}
/**
@@ -409,11 +415,6 @@ class OC_App {
* @throws Exception
*/
public static function disable($app) {
- // Convert OCS ID to regular application identifier
- if(self::getInternalAppIdByOcs($app) !== false) {
- $app = self::getInternalAppIdByOcs($app);
- }
-
// flush
self::$enabledAppsCache = array();
@@ -554,7 +555,7 @@ class OC_App {
* @param string $appId
* @return false|string
*/
- protected static function findAppInDirectories($appId) {
+ public static function findAppInDirectories($appId) {
$sanitizedAppId = self::cleanAppId($appId);
if($sanitizedAppId !== $appId) {
return false;
@@ -613,18 +614,6 @@ class OC_App {
return false;
}
-
- /**
- * check if an app's directory is writable
- *
- * @param string $appId
- * @return bool
- */
- public static function isAppDirWritable($appId) {
- $path = self::getAppPath($appId);
- return ($path !== false) ? is_writable($path) : false;
- }
-
/**
* Get the path for the given app on the access
* If the app is defined in multiple directories, the first one is taken. (false if not found)
@@ -837,20 +826,11 @@ class OC_App {
/**
* List all apps, this is used in apps.php
*
- * @param bool $onlyLocal
- * @param bool $includeUpdateInfo Should we check whether there is an update
- * in the app store?
- * @param OCSClient $ocsClient
* @return array
*/
- public static function listAllApps($onlyLocal = false,
- $includeUpdateInfo = true,
- OCSClient $ocsClient) {
+ public function listAllApps() {
$installedApps = OC_App::getAllApps();
- //TODO which apps do we want to blacklist and how do we integrate
- // blacklisting with the multi apps folder feature?
-
//we don't want to show configuration for these
$blacklist = \OC::$server->getAppManager()->getAlwaysEnabledApps();
$appList = array();
@@ -893,8 +873,6 @@ class OC_App {
$info['removable'] = true;
}
- $info['update'] = ($includeUpdateInfo) ? Installer::isUpdateAvailable($app) : null;
-
$appPath = self::getAppPath($app);
if($appPath !== false) {
$appIcon = $appPath . '/img/' . $app . '.svg';
@@ -926,29 +904,8 @@ class OC_App {
$appList[] = $info;
}
}
- if ($onlyLocal) {
- $remoteApps = [];
- } else {
- $remoteApps = OC_App::getAppstoreApps('approved', null, $ocsClient);
- }
- if ($remoteApps) {
- // Remove duplicates
- foreach ($appList as $app) {
- foreach ($remoteApps AS $key => $remote) {
- if ($app['name'] === $remote['name'] ||
- (isset($app['ocsid']) &&
- $app['ocsid'] === $remote['id'])
- ) {
- unset($remoteApps[$key]);
- }
- }
- }
- $combinedApps = array_merge($appList, $remoteApps);
- } else {
- $combinedApps = $appList;
- }
- return $combinedApps;
+ return $appList;
}
/**
@@ -966,70 +923,6 @@ class OC_App {
return false;
}
- /**
- * Get a list of all apps on the appstore
- * @param string $filter
- * @param string|null $category
- * @param OCSClient $ocsClient
- * @return array|bool multi-dimensional array of apps.
- * Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description
- */
- public static function getAppstoreApps($filter = 'approved',
- $category = null,
- OCSClient $ocsClient) {
- $categories = [$category];
-
- if (is_null($category)) {
- $categoryNames = $ocsClient->getCategories(\OCP\Util::getVersion());
- if (is_array($categoryNames)) {
- // Check that categories of apps were retrieved correctly
- if (!$categories = array_keys($categoryNames)) {
- return false;
- }
- } else {
- return false;
- }
- }
-
- $page = 0;
- $remoteApps = $ocsClient->getApplications($categories, $page, $filter, \OCP\Util::getVersion());
- $apps = [];
- $i = 0;
- $l = \OC::$server->getL10N('core');
- foreach ($remoteApps as $app) {
- $potentialCleanId = self::getInternalAppIdByOcs($app['id']);
- // enhance app info (for example the description)
- $apps[$i] = OC_App::parseAppInfo($app);
- $apps[$i]['author'] = $app['personid'];
- $apps[$i]['ocs_id'] = $app['id'];
- $apps[$i]['internal'] = 0;
- $apps[$i]['active'] = ($potentialCleanId !== false) ? self::isEnabled($potentialCleanId) : false;
- $apps[$i]['update'] = false;
- $apps[$i]['groups'] = false;
- $apps[$i]['score'] = $app['score'];
- $apps[$i]['removable'] = false;
- if ($app['label'] == 'recommended') {
- $apps[$i]['internallabel'] = (string)$l->t('Recommended');
- $apps[$i]['internalclass'] = 'recommendedapp';
- }
-
- // Apps from the appstore are always assumed to be compatible with the
- // the current release as the initial filtering is done on the appstore
- $apps[$i]['dependencies']['owncloud']['@attributes']['min-version'] = implode('.', \OCP\Util::getVersion());
- $apps[$i]['dependencies']['owncloud']['@attributes']['max-version'] = implode('.', \OCP\Util::getVersion());
-
- $i++;
- }
-
-
-
- if (empty($apps)) {
- return false;
- } else {
- return $apps;
- }
- }
-
public static function shouldUpgrade($app) {
$versions = self::getAppVersions();
$currentVersion = OC_App::getAppVersion($app);
@@ -1083,7 +976,9 @@ class OC_App {
public static function isAppCompatible($ocVersion, $appInfo) {
$requireMin = '';
$requireMax = '';
- if (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
+ if (isset($appInfo['dependencies']['nextcloud']['@attributes']['min-version'])) {
+ $requireMin = $appInfo['dependencies']['nextcloud']['@attributes']['min-version'];
+ } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['min-version'])) {
$requireMin = $appInfo['dependencies']['owncloud']['@attributes']['min-version'];
} else if (isset($appInfo['requiremin'])) {
$requireMin = $appInfo['requiremin'];
@@ -1091,7 +986,9 @@ class OC_App {
$requireMin = $appInfo['require'];
}
- if (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
+ if (isset($appInfo['dependencies']['nextcloud']['@attributes']['max-version'])) {
+ $requireMax = $appInfo['dependencies']['nextcloud']['@attributes']['max-version'];
+ } elseif (isset($appInfo['dependencies']['owncloud']['@attributes']['max-version'])) {
$requireMax = $appInfo['dependencies']['owncloud']['@attributes']['max-version'];
} else if (isset($appInfo['requiremax'])) {
$requireMax = $appInfo['requiremax'];
@@ -1132,46 +1029,16 @@ class OC_App {
/**
* @param string $app
+ * @param \OCP\IConfig $config
+ * @param \OCP\IL10N $l
* @return bool
+ *
* @throws Exception if app is not compatible with this version of ownCloud
* @throws Exception if no app-name was specified
*/
- public static function installApp($app) {
- $appName = $app; // $app will be overwritten, preserve name for error logging
- $l = \OC::$server->getL10N('core');
- $config = \OC::$server->getConfig();
- $ocsClient = new OCSClient(
- \OC::$server->getHTTPClientService(),
- $config,
- \OC::$server->getLogger()
- );
- $appData = $ocsClient->getApplication($app, \OCP\Util::getVersion());
-
- // check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string
- if (!is_numeric($app)) {
- $shippedVersion = self::getAppVersion($app);
- if ($appData && version_compare($shippedVersion, $appData['version'], '<')) {
- $app = self::downloadApp($app);
- } else {
- $app = Installer::installShippedApp($app);
- }
- } else {
- // Maybe the app is already installed - compare the version in this
- // case and use the local already installed one.
- // FIXME: This is a horrible hack. I feel sad. The god of code cleanness may forgive me.
- $internalAppId = self::getInternalAppIdByOcs($app);
- if($internalAppId !== false) {
- if($appData && version_compare(\OC_App::getAppVersion($internalAppId), $appData['version'], '<')) {
- $app = self::downloadApp($app);
- } else {
- self::enable($internalAppId);
- $app = $internalAppId;
- }
- } else {
- $app = self::downloadApp($app);
- }
- }
-
+ public function installApp($app,
+ \OCP\IConfig $config,
+ \OCP\IL10N $l) {
if ($app !== false) {
// check if the app is compatible with this version of ownCloud
$info = self::getAppInfo($app);
diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php
index e4c2caeafd7..5cd92eaa415 100644
--- a/lib/private/legacy/util.php
+++ b/lib/private/legacy/util.php
@@ -757,6 +757,7 @@ class OC_Util {
'simplexml_load_string' => 'SimpleXML',
'hash' => 'HASH Message Digest Framework',
'curl_init' => 'cURL',
+ 'openssl_verify' => 'OpenSSL',
],
'defined' => array(
'PDO::ATTR_DRIVER_NAME' => 'PDO'