Completed
Push — master ( 94c2f1...705483 )
by Morris
31s
created

Fetcher::fetch()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 21
nc 9
nop 2
dl 0
loc 34
rs 8.439
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Lukas Reschke <[email protected]>
4
 *
5
 * @license GNU AGPL version 3 or any later version
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as
9
 * published by the Free Software Foundation, either version 3 of the
10
 * License, or (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 *
20
 */
21
22
namespace OC\App\AppStore\Fetcher;
23
24
use OCP\AppFramework\Http;
25
use OCP\AppFramework\Utility\ITimeFactory;
26
use OCP\Files\IAppData;
27
use OCP\Files\NotFoundException;
28
use OCP\Http\Client\IClientService;
29
use OCP\IConfig;
30
31
abstract class Fetcher {
32
	const INVALIDATE_AFTER_SECONDS = 300;
33
34
	/** @var IAppData */
35
	protected $appData;
36
	/** @var IClientService */
37
	protected $clientService;
38
	/** @var ITimeFactory */
39
	protected $timeFactory;
40
	/** @var IConfig */
41
	protected $config;
42
	/** @var string */
43
	protected $fileName;
44
	/** @var string */
45
	protected $endpointUrl;
46
	/** @var string */
47
	protected $version;
48
49
	/**
50
	 * @param IAppData $appData
51
	 * @param IClientService $clientService
52
	 * @param ITimeFactory $timeFactory
53
	 * @param IConfig $config
54
	 */
55
	public function __construct(IAppData $appData,
56
								IClientService $clientService,
57
								ITimeFactory $timeFactory,
58
								IConfig $config) {
59
		$this->appData = $appData;
60
		$this->clientService = $clientService;
61
		$this->timeFactory = $timeFactory;
62
		$this->config = $config;
63
	}
64
65
	/**
66
	 * Fetches the response from the server
67
	 *
68
	 * @param string $ETag
69
	 * @param string $content
70
	 *
71
	 * @return array
72
	 */
73
	protected function fetch($ETag, $content) {
74
		$appstoreenabled = $this->config->getSystemValue('appstoreenabled', true);
75
76
		if (!$appstoreenabled) {
77
			return [];
78
		}
79
80
		$options = [];
81
82
		if ($ETag !== '') {
83
			$options['headers'] = [
84
				'If-None-Match' => $ETag,
85
			];
86
		}
87
88
		$client = $this->clientService->newClient();
89
		$response = $client->get($this->endpointUrl, $options);
90
91
		$responseJson = [];
92
		if ($response->getStatusCode() === Http::STATUS_NOT_MODIFIED) {
93
			$responseJson['data'] = json_decode($content, true);
94
		} else {
95
			$responseJson['data'] = json_decode($response->getBody(), true);
96
			$ETag = $response->getHeader('ETag');
97
		}
98
99
		$responseJson['timestamp'] = $this->timeFactory->getTime();
100
		$responseJson['ncversion'] = $this->getVersion();
101
		if ($ETag !== '') {
102
			$responseJson['ETag'] = $ETag;
103
		}
104
105
		return $responseJson;
106
	}
107
108
	/**
109
	 * Returns the array with the categories on the appstore server
110
	 *
111
	 * @return array
112
	 */
113
	public function get() {
114
		$appstoreenabled = $this->config->getSystemValue('appstoreenabled', true);
115
116
		if (!$appstoreenabled) {
117
			return [];
118
		}
119
120
		$rootFolder = $this->appData->getFolder('/');
121
122
		$ETag = '';
123
		$content = '';
124
125
		try {
126
			// File does already exists
127
			$file = $rootFolder->getFile($this->fileName);
128
			$jsonBlob = json_decode($file->getContent(), true);
129
			if (is_array($jsonBlob)) {
130
131
				// No caching when the version has been updated
132
				if (isset($jsonBlob['ncversion']) && $jsonBlob['ncversion'] === $this->getVersion()) {
133
134
					// If the timestamp is older than 300 seconds request the files new
135
					if ((int)$jsonBlob['timestamp'] > ($this->timeFactory->getTime() - self::INVALIDATE_AFTER_SECONDS)) {
136
						return $jsonBlob['data'];
137
					}
138
139
					if (isset($jsonBlob['ETag'])) {
140
						$ETag = $jsonBlob['ETag'];
141
						$content = json_encode($jsonBlob['data']);
142
					}
143
				}
144
			}
145
		} catch (NotFoundException $e) {
146
			// File does not already exists
147
			$file = $rootFolder->newFile($this->fileName);
148
		}
149
150
		// Refresh the file content
151
		try {
152
			$responseJson = $this->fetch($ETag, $content);
153
			$file->putContent(json_encode($responseJson));
154
			return json_decode($file->getContent(), true)['data'];
155
		} catch (\Exception $e) {
156
			return [];
157
		}
158
	}
159
160
	/**
161
	 * Get the currently Nextcloud version
162
	 * @return string
163
	 */
164
	protected function getVersion() {
165
		if ($this->version === null) {
166
			$this->version = $this->config->getSystemValue('version', '0.0.0');
167
		}
168
		return $this->version;
169
	}
170
171
	/**
172
	 * Set the current Nextcloud version
173
	 * @param string $version
174
	 */
175
	public function setVersion($version) {
176
		$this->version = $version;
177
	}
178
}
179