Passed
Push — master ( bbba7b...22edb6 )
by Blizzz
09:41 queued 12s
created
lib/public/ITagManager.php 1 patch
Indentation   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -46,17 +46,17 @@
 block discarded – undo
46 46
  */
47 47
 interface ITagManager {
48 48
 
49
-	/**
50
-	 * Create a new \OCP\ITags instance and load tags from db for the current user.
51
-	 *
52
-	 * @see \OCP\ITags
53
-	 * @param string $type The type identifier e.g. 'contact' or 'event'.
54
-	 * @param array $defaultTags An array of default tags to be used if none are stored.
55
-	 * @param boolean $includeShared Whether to include tags for items shared with this user by others.
56
-	 * @param string $userId user for which to retrieve the tags, defaults to the currently
57
-	 * logged in user
58
-	 * @return \OCP\ITags
59
-	 * @since 6.0.0 - parameter $includeShared and $userId were added in 8.0.0
60
-	 */
61
-	public function load($type, $defaultTags = [], $includeShared = false, $userId = null);
49
+    /**
50
+     * Create a new \OCP\ITags instance and load tags from db for the current user.
51
+     *
52
+     * @see \OCP\ITags
53
+     * @param string $type The type identifier e.g. 'contact' or 'event'.
54
+     * @param array $defaultTags An array of default tags to be used if none are stored.
55
+     * @param boolean $includeShared Whether to include tags for items shared with this user by others.
56
+     * @param string $userId user for which to retrieve the tags, defaults to the currently
57
+     * logged in user
58
+     * @return \OCP\ITags
59
+     * @since 6.0.0 - parameter $includeShared and $userId were added in 8.0.0
60
+     */
61
+    public function load($type, $defaultTags = [], $includeShared = false, $userId = null);
62 62
 }
Please login to merge, or discard this patch.
lib/private/DB/PgSqlTools.php 2 patches
Indentation   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -32,41 +32,41 @@
 block discarded – undo
32 32
  */
33 33
 class PgSqlTools {
34 34
 
35
-	/** @var \OCP\IConfig */
36
-	private $config;
35
+    /** @var \OCP\IConfig */
36
+    private $config;
37 37
 
38
-	/**
39
-	 * @param \OCP\IConfig $config
40
-	 */
41
-	public function __construct(IConfig $config) {
42
-		$this->config = $config;
43
-	}
38
+    /**
39
+     * @param \OCP\IConfig $config
40
+     */
41
+    public function __construct(IConfig $config) {
42
+        $this->config = $config;
43
+    }
44 44
 
45
-	/**
46
-	 * @brief Resynchronizes all sequences of a database after using INSERTs
47
-	 *        without leaving out the auto-incremented column.
48
-	 * @param \OC\DB\Connection $conn
49
-	 * @return null
50
-	 */
51
-	public function resynchronizeDatabaseSequences(Connection $conn) {
52
-		$filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
53
-		$databaseName = $conn->getDatabase();
54
-		$conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression);
45
+    /**
46
+     * @brief Resynchronizes all sequences of a database after using INSERTs
47
+     *        without leaving out the auto-incremented column.
48
+     * @param \OC\DB\Connection $conn
49
+     * @return null
50
+     */
51
+    public function resynchronizeDatabaseSequences(Connection $conn) {
52
+        $filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
53
+        $databaseName = $conn->getDatabase();
54
+        $conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression);
55 55
 
56
-		foreach ($conn->getSchemaManager()->listSequences() as $sequence) {
57
-			$sequenceName = $sequence->getName();
58
-			$sqlInfo = 'SELECT table_schema, table_name, column_name
56
+        foreach ($conn->getSchemaManager()->listSequences() as $sequence) {
57
+            $sequenceName = $sequence->getName();
58
+            $sqlInfo = 'SELECT table_schema, table_name, column_name
59 59
 				FROM information_schema.columns
60 60
 				WHERE column_default = ? AND table_catalog = ?';
61
-			$sequenceInfo = $conn->fetchAssoc($sqlInfo, [
62
-				"nextval('$sequenceName'::regclass)",
63
-				$databaseName
64
-			]);
65
-			$tableName = $sequenceInfo['table_name'];
66
-			$columnName = $sequenceInfo['column_name'];
67
-			$sqlMaxId = "SELECT MAX($columnName) FROM $tableName";
68
-			$sqlSetval = "SELECT setval('$sequenceName', ($sqlMaxId))";
69
-			$conn->executeQuery($sqlSetval);
70
-		}
71
-	}
61
+            $sequenceInfo = $conn->fetchAssoc($sqlInfo, [
62
+                "nextval('$sequenceName'::regclass)",
63
+                $databaseName
64
+            ]);
65
+            $tableName = $sequenceInfo['table_name'];
66
+            $columnName = $sequenceInfo['column_name'];
67
+            $sqlMaxId = "SELECT MAX($columnName) FROM $tableName";
68
+            $sqlSetval = "SELECT setval('$sequenceName', ($sqlMaxId))";
69
+            $conn->executeQuery($sqlSetval);
70
+        }
71
+    }
72 72
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -49,7 +49,7 @@
 block discarded – undo
49 49
 	 * @return null
50 50
 	 */
51 51
 	public function resynchronizeDatabaseSequences(Connection $conn) {
52
-		$filterExpression = '/^' . preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')) . '/';
52
+		$filterExpression = '/^'.preg_quote($this->config->getSystemValue('dbtableprefix', 'oc_')).'/';
53 53
 		$databaseName = $conn->getDatabase();
54 54
 		$conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression);
55 55
 
Please login to merge, or discard this patch.
apps/user_ldap/lib/Mapping/GroupMapping.php 1 patch
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -29,11 +29,11 @@
 block discarded – undo
29 29
  */
30 30
 class GroupMapping extends AbstractMapping {
31 31
 
32
-	/**
33
-	 * returns the DB table name which holds the mappings
34
-	 * @return string
35
-	 */
36
-	protected function getTableName() {
37
-		return '*PREFIX*ldap_group_mapping';
38
-	}
32
+    /**
33
+     * returns the DB table name which holds the mappings
34
+     * @return string
35
+     */
36
+    protected function getTableName() {
37
+        return '*PREFIX*ldap_group_mapping';
38
+    }
39 39
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Mapping/UserMapping.php 1 patch
Indentation   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -29,11 +29,11 @@
 block discarded – undo
29 29
  */
30 30
 class UserMapping extends AbstractMapping {
31 31
 
32
-	/**
33
-	 * returns the DB table name which holds the mappings
34
-	 * @return string
35
-	 */
36
-	protected function getTableName() {
37
-		return '*PREFIX*ldap_user_mapping';
38
-	}
32
+    /**
33
+     * returns the DB table name which holds the mappings
34
+     * @return string
35
+     */
36
+    protected function getTableName() {
37
+        return '*PREFIX*ldap_user_mapping';
38
+    }
39 39
 }
Please login to merge, or discard this patch.
apps/files_versions/lib/Expiration.php 2 patches
Indentation   +169 added lines, -169 removed lines patch added patch discarded remove patch
@@ -29,173 +29,173 @@
 block discarded – undo
29 29
 
30 30
 class Expiration {
31 31
 
32
-	// how long do we keep files a version if no other value is defined in the config file (unit: days)
33
-	const NO_OBLIGATION = -1;
34
-
35
-	/** @var ITimeFactory */
36
-	private $timeFactory;
37
-
38
-	/** @var string */
39
-	private $retentionObligation;
40
-
41
-	/** @var int */
42
-	private $minAge;
43
-
44
-	/** @var int */
45
-	private $maxAge;
46
-
47
-	/** @var bool */
48
-	private $canPurgeToSaveSpace;
49
-
50
-	public function __construct(IConfig $config,ITimeFactory $timeFactory){
51
-		$this->timeFactory = $timeFactory;
52
-		$this->retentionObligation = $config->getSystemValue('versions_retention_obligation', 'auto');
53
-
54
-		if ($this->retentionObligation !== 'disabled') {
55
-			$this->parseRetentionObligation();
56
-		}
57
-	}
58
-
59
-	/**
60
-	 * Is versions expiration enabled
61
-	 * @return bool
62
-	 */
63
-	public function isEnabled(){
64
-		return $this->retentionObligation !== 'disabled';
65
-	}
66
-
67
-	/**
68
-	 * Is default expiration active
69
-	 */
70
-	public function shouldAutoExpire(){
71
-		return $this->minAge === self::NO_OBLIGATION
72
-				|| $this->maxAge === self::NO_OBLIGATION;
73
-	}
74
-
75
-	/**
76
-	 * Check if given timestamp in expiration range
77
-	 * @param int $timestamp
78
-	 * @param bool $quotaExceeded
79
-	 * @return bool
80
-	 */
81
-	public function isExpired($timestamp, $quotaExceeded = false){
82
-		// No expiration if disabled
83
-		if (!$this->isEnabled()) {
84
-			return false;
85
-		}
86
-
87
-		// Purge to save space (if allowed)
88
-		if ($quotaExceeded && $this->canPurgeToSaveSpace) {
89
-			return true;
90
-		}
91
-
92
-		$time = $this->timeFactory->getTime();
93
-		// Never expire dates in future e.g. misconfiguration or negative time
94
-		// adjustment
95
-		if ($time<$timestamp) {
96
-			return false;
97
-		}
98
-
99
-		// Purge as too old
100
-		if ($this->maxAge !== self::NO_OBLIGATION) {
101
-			$maxTimestamp = $time - ($this->maxAge * 86400);
102
-			$isOlderThanMax = $timestamp < $maxTimestamp;
103
-		} else {
104
-			$isOlderThanMax = false;
105
-		}
106
-
107
-		if ($this->minAge !== self::NO_OBLIGATION) {
108
-			// older than Min obligation and we are running out of quota?
109
-			$minTimestamp = $time - ($this->minAge * 86400);
110
-			$isMinReached = ($timestamp < $minTimestamp) && $quotaExceeded;
111
-		} else {
112
-			$isMinReached = false;
113
-		}
114
-
115
-		return $isOlderThanMax || $isMinReached;
116
-	}
117
-
118
-	/**
119
-	 * Get maximal retention obligation as a timestamp
120
-	 * @return int
121
-	 */
122
-	public function getMaxAgeAsTimestamp(){
123
-		$maxAge = false;
124
-		if ($this->isEnabled() && $this->maxAge !== self::NO_OBLIGATION) {
125
-			$time = $this->timeFactory->getTime();
126
-			$maxAge = $time - ($this->maxAge * 86400);
127
-		}
128
-		return $maxAge;
129
-	}
130
-
131
-	/**
132
-	 * Read versions_retention_obligation, validate it 
133
-	 * and set private members accordingly
134
-	 */
135
-	private function parseRetentionObligation(){
136
-		$splitValues = explode(',', $this->retentionObligation);
137
-		if (!isset($splitValues[0])) {
138
-			$minValue = 'auto';
139
-		} else {
140
-			$minValue = trim($splitValues[0]);
141
-		}
142
-
143
-		if (!isset($splitValues[1])) {
144
-			$maxValue = 'auto';
145
-		} else {
146
-			$maxValue = trim($splitValues[1]);
147
-		}
148
-
149
-		$isValid = true;
150
-		// Validate
151
-		if (!ctype_digit($minValue) && $minValue !== 'auto') {
152
-			$isValid = false;
153
-			\OC::$server->getLogger()->warning(
154
-					$minValue . ' is not a valid value for minimal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
155
-					['app'=>'files_versions']
156
-			);
157
-		}
158
-
159
-		if (!ctype_digit($maxValue) && $maxValue !== 'auto') {
160
-			$isValid = false;
161
-			\OC::$server->getLogger()->warning(
162
-					$maxValue . ' is not a valid value for maximal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
163
-					['app'=>'files_versions']
164
-			);
165
-		}
166
-
167
-		if (!$isValid){
168
-			$minValue = 'auto';
169
-			$maxValue = 'auto';
170
-		}
171
-
172
-
173
-		if ($minValue === 'auto' && $maxValue === 'auto') {
174
-			// Default: Delete anytime if space needed
175
-			$this->minAge = self::NO_OBLIGATION;
176
-			$this->maxAge = self::NO_OBLIGATION;
177
-			$this->canPurgeToSaveSpace = true;
178
-		} elseif ($minValue !== 'auto' && $maxValue === 'auto') {
179
-			// Keep for X days but delete anytime if space needed
180
-			$this->minAge = (int)$minValue;
181
-			$this->maxAge = self::NO_OBLIGATION;
182
-			$this->canPurgeToSaveSpace = true;
183
-		} elseif ($minValue === 'auto' && $maxValue !== 'auto') {
184
-			// Delete anytime if space needed, Delete all older than max automatically
185
-			$this->minAge = self::NO_OBLIGATION;
186
-			$this->maxAge = (int)$maxValue;
187
-			$this->canPurgeToSaveSpace = true;
188
-		} elseif ($minValue !== 'auto' && $maxValue !== 'auto') {
189
-			// Delete all older than max OR older than min if space needed
190
-
191
-			// Max < Min as per https://github.com/owncloud/core/issues/16301
192
-			if ($maxValue < $minValue) {
193
-				$maxValue = $minValue;
194
-			}
195
-
196
-			$this->minAge = (int)$minValue;
197
-			$this->maxAge = (int)$maxValue;
198
-			$this->canPurgeToSaveSpace = false;
199
-		}
200
-	}
32
+    // how long do we keep files a version if no other value is defined in the config file (unit: days)
33
+    const NO_OBLIGATION = -1;
34
+
35
+    /** @var ITimeFactory */
36
+    private $timeFactory;
37
+
38
+    /** @var string */
39
+    private $retentionObligation;
40
+
41
+    /** @var int */
42
+    private $minAge;
43
+
44
+    /** @var int */
45
+    private $maxAge;
46
+
47
+    /** @var bool */
48
+    private $canPurgeToSaveSpace;
49
+
50
+    public function __construct(IConfig $config,ITimeFactory $timeFactory){
51
+        $this->timeFactory = $timeFactory;
52
+        $this->retentionObligation = $config->getSystemValue('versions_retention_obligation', 'auto');
53
+
54
+        if ($this->retentionObligation !== 'disabled') {
55
+            $this->parseRetentionObligation();
56
+        }
57
+    }
58
+
59
+    /**
60
+     * Is versions expiration enabled
61
+     * @return bool
62
+     */
63
+    public function isEnabled(){
64
+        return $this->retentionObligation !== 'disabled';
65
+    }
66
+
67
+    /**
68
+     * Is default expiration active
69
+     */
70
+    public function shouldAutoExpire(){
71
+        return $this->minAge === self::NO_OBLIGATION
72
+                || $this->maxAge === self::NO_OBLIGATION;
73
+    }
74
+
75
+    /**
76
+     * Check if given timestamp in expiration range
77
+     * @param int $timestamp
78
+     * @param bool $quotaExceeded
79
+     * @return bool
80
+     */
81
+    public function isExpired($timestamp, $quotaExceeded = false){
82
+        // No expiration if disabled
83
+        if (!$this->isEnabled()) {
84
+            return false;
85
+        }
86
+
87
+        // Purge to save space (if allowed)
88
+        if ($quotaExceeded && $this->canPurgeToSaveSpace) {
89
+            return true;
90
+        }
91
+
92
+        $time = $this->timeFactory->getTime();
93
+        // Never expire dates in future e.g. misconfiguration or negative time
94
+        // adjustment
95
+        if ($time<$timestamp) {
96
+            return false;
97
+        }
98
+
99
+        // Purge as too old
100
+        if ($this->maxAge !== self::NO_OBLIGATION) {
101
+            $maxTimestamp = $time - ($this->maxAge * 86400);
102
+            $isOlderThanMax = $timestamp < $maxTimestamp;
103
+        } else {
104
+            $isOlderThanMax = false;
105
+        }
106
+
107
+        if ($this->minAge !== self::NO_OBLIGATION) {
108
+            // older than Min obligation and we are running out of quota?
109
+            $minTimestamp = $time - ($this->minAge * 86400);
110
+            $isMinReached = ($timestamp < $minTimestamp) && $quotaExceeded;
111
+        } else {
112
+            $isMinReached = false;
113
+        }
114
+
115
+        return $isOlderThanMax || $isMinReached;
116
+    }
117
+
118
+    /**
119
+     * Get maximal retention obligation as a timestamp
120
+     * @return int
121
+     */
122
+    public function getMaxAgeAsTimestamp(){
123
+        $maxAge = false;
124
+        if ($this->isEnabled() && $this->maxAge !== self::NO_OBLIGATION) {
125
+            $time = $this->timeFactory->getTime();
126
+            $maxAge = $time - ($this->maxAge * 86400);
127
+        }
128
+        return $maxAge;
129
+    }
130
+
131
+    /**
132
+     * Read versions_retention_obligation, validate it 
133
+     * and set private members accordingly
134
+     */
135
+    private function parseRetentionObligation(){
136
+        $splitValues = explode(',', $this->retentionObligation);
137
+        if (!isset($splitValues[0])) {
138
+            $minValue = 'auto';
139
+        } else {
140
+            $minValue = trim($splitValues[0]);
141
+        }
142
+
143
+        if (!isset($splitValues[1])) {
144
+            $maxValue = 'auto';
145
+        } else {
146
+            $maxValue = trim($splitValues[1]);
147
+        }
148
+
149
+        $isValid = true;
150
+        // Validate
151
+        if (!ctype_digit($minValue) && $minValue !== 'auto') {
152
+            $isValid = false;
153
+            \OC::$server->getLogger()->warning(
154
+                    $minValue . ' is not a valid value for minimal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
155
+                    ['app'=>'files_versions']
156
+            );
157
+        }
158
+
159
+        if (!ctype_digit($maxValue) && $maxValue !== 'auto') {
160
+            $isValid = false;
161
+            \OC::$server->getLogger()->warning(
162
+                    $maxValue . ' is not a valid value for maximal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
163
+                    ['app'=>'files_versions']
164
+            );
165
+        }
166
+
167
+        if (!$isValid){
168
+            $minValue = 'auto';
169
+            $maxValue = 'auto';
170
+        }
171
+
172
+
173
+        if ($minValue === 'auto' && $maxValue === 'auto') {
174
+            // Default: Delete anytime if space needed
175
+            $this->minAge = self::NO_OBLIGATION;
176
+            $this->maxAge = self::NO_OBLIGATION;
177
+            $this->canPurgeToSaveSpace = true;
178
+        } elseif ($minValue !== 'auto' && $maxValue === 'auto') {
179
+            // Keep for X days but delete anytime if space needed
180
+            $this->minAge = (int)$minValue;
181
+            $this->maxAge = self::NO_OBLIGATION;
182
+            $this->canPurgeToSaveSpace = true;
183
+        } elseif ($minValue === 'auto' && $maxValue !== 'auto') {
184
+            // Delete anytime if space needed, Delete all older than max automatically
185
+            $this->minAge = self::NO_OBLIGATION;
186
+            $this->maxAge = (int)$maxValue;
187
+            $this->canPurgeToSaveSpace = true;
188
+        } elseif ($minValue !== 'auto' && $maxValue !== 'auto') {
189
+            // Delete all older than max OR older than min if space needed
190
+
191
+            // Max < Min as per https://github.com/owncloud/core/issues/16301
192
+            if ($maxValue < $minValue) {
193
+                $maxValue = $minValue;
194
+            }
195
+
196
+            $this->minAge = (int)$minValue;
197
+            $this->maxAge = (int)$maxValue;
198
+            $this->canPurgeToSaveSpace = false;
199
+        }
200
+    }
201 201
 }
Please login to merge, or discard this patch.
Spacing   +14 added lines, -14 removed lines patch added patch discarded remove patch
@@ -47,7 +47,7 @@  discard block
 block discarded – undo
47 47
 	/** @var bool */
48 48
 	private $canPurgeToSaveSpace;
49 49
 
50
-	public function __construct(IConfig $config,ITimeFactory $timeFactory){
50
+	public function __construct(IConfig $config, ITimeFactory $timeFactory) {
51 51
 		$this->timeFactory = $timeFactory;
52 52
 		$this->retentionObligation = $config->getSystemValue('versions_retention_obligation', 'auto');
53 53
 
@@ -60,14 +60,14 @@  discard block
 block discarded – undo
60 60
 	 * Is versions expiration enabled
61 61
 	 * @return bool
62 62
 	 */
63
-	public function isEnabled(){
63
+	public function isEnabled() {
64 64
 		return $this->retentionObligation !== 'disabled';
65 65
 	}
66 66
 
67 67
 	/**
68 68
 	 * Is default expiration active
69 69
 	 */
70
-	public function shouldAutoExpire(){
70
+	public function shouldAutoExpire() {
71 71
 		return $this->minAge === self::NO_OBLIGATION
72 72
 				|| $this->maxAge === self::NO_OBLIGATION;
73 73
 	}
@@ -78,7 +78,7 @@  discard block
 block discarded – undo
78 78
 	 * @param bool $quotaExceeded
79 79
 	 * @return bool
80 80
 	 */
81
-	public function isExpired($timestamp, $quotaExceeded = false){
81
+	public function isExpired($timestamp, $quotaExceeded = false) {
82 82
 		// No expiration if disabled
83 83
 		if (!$this->isEnabled()) {
84 84
 			return false;
@@ -92,7 +92,7 @@  discard block
 block discarded – undo
92 92
 		$time = $this->timeFactory->getTime();
93 93
 		// Never expire dates in future e.g. misconfiguration or negative time
94 94
 		// adjustment
95
-		if ($time<$timestamp) {
95
+		if ($time < $timestamp) {
96 96
 			return false;
97 97
 		}
98 98
 
@@ -119,7 +119,7 @@  discard block
 block discarded – undo
119 119
 	 * Get maximal retention obligation as a timestamp
120 120
 	 * @return int
121 121
 	 */
122
-	public function getMaxAgeAsTimestamp(){
122
+	public function getMaxAgeAsTimestamp() {
123 123
 		$maxAge = false;
124 124
 		if ($this->isEnabled() && $this->maxAge !== self::NO_OBLIGATION) {
125 125
 			$time = $this->timeFactory->getTime();
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
 	 * Read versions_retention_obligation, validate it 
133 133
 	 * and set private members accordingly
134 134
 	 */
135
-	private function parseRetentionObligation(){
135
+	private function parseRetentionObligation() {
136 136
 		$splitValues = explode(',', $this->retentionObligation);
137 137
 		if (!isset($splitValues[0])) {
138 138
 			$minValue = 'auto';
@@ -151,7 +151,7 @@  discard block
 block discarded – undo
151 151
 		if (!ctype_digit($minValue) && $minValue !== 'auto') {
152 152
 			$isValid = false;
153 153
 			\OC::$server->getLogger()->warning(
154
-					$minValue . ' is not a valid value for minimal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
154
+					$minValue.' is not a valid value for minimal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
155 155
 					['app'=>'files_versions']
156 156
 			);
157 157
 		}
@@ -159,12 +159,12 @@  discard block
 block discarded – undo
159 159
 		if (!ctype_digit($maxValue) && $maxValue !== 'auto') {
160 160
 			$isValid = false;
161 161
 			\OC::$server->getLogger()->warning(
162
-					$maxValue . ' is not a valid value for maximal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
162
+					$maxValue.' is not a valid value for maximal versions retention obligation. Check versions_retention_obligation in your config.php. Falling back to auto.',
163 163
 					['app'=>'files_versions']
164 164
 			);
165 165
 		}
166 166
 
167
-		if (!$isValid){
167
+		if (!$isValid) {
168 168
 			$minValue = 'auto';
169 169
 			$maxValue = 'auto';
170 170
 		}
@@ -177,13 +177,13 @@  discard block
 block discarded – undo
177 177
 			$this->canPurgeToSaveSpace = true;
178 178
 		} elseif ($minValue !== 'auto' && $maxValue === 'auto') {
179 179
 			// Keep for X days but delete anytime if space needed
180
-			$this->minAge = (int)$minValue;
180
+			$this->minAge = (int) $minValue;
181 181
 			$this->maxAge = self::NO_OBLIGATION;
182 182
 			$this->canPurgeToSaveSpace = true;
183 183
 		} elseif ($minValue === 'auto' && $maxValue !== 'auto') {
184 184
 			// Delete anytime if space needed, Delete all older than max automatically
185 185
 			$this->minAge = self::NO_OBLIGATION;
186
-			$this->maxAge = (int)$maxValue;
186
+			$this->maxAge = (int) $maxValue;
187 187
 			$this->canPurgeToSaveSpace = true;
188 188
 		} elseif ($minValue !== 'auto' && $maxValue !== 'auto') {
189 189
 			// Delete all older than max OR older than min if space needed
@@ -193,8 +193,8 @@  discard block
 block discarded – undo
193 193
 				$maxValue = $minValue;
194 194
 			}
195 195
 
196
-			$this->minAge = (int)$minValue;
197
-			$this->maxAge = (int)$maxValue;
196
+			$this->minAge = (int) $minValue;
197
+			$this->maxAge = (int) $maxValue;
198 198
 			$this->canPurgeToSaveSpace = false;
199 199
 		}
200 200
 	}
Please login to merge, or discard this patch.
apps/files_external/lib/Command/Notify.php 1 patch
Indentation   +223 added lines, -223 removed lines patch added patch discarded remove patch
@@ -45,252 +45,252 @@
 block discarded – undo
45 45
 use Symfony\Component\Console\Output\OutputInterface;
46 46
 
47 47
 class Notify extends Base {
48
-	/** @var GlobalStoragesService */
49
-	private $globalService;
50
-	/** @var IDBConnection */
51
-	private $connection;
52
-	/** @var ILogger */
53
-	private $logger;
48
+    /** @var GlobalStoragesService */
49
+    private $globalService;
50
+    /** @var IDBConnection */
51
+    private $connection;
52
+    /** @var ILogger */
53
+    private $logger;
54 54
 
55
-	function __construct(GlobalStoragesService $globalService, IDBConnection $connection, ILogger $logger) {
56
-		parent::__construct();
57
-		$this->globalService = $globalService;
58
-		$this->connection = $connection;
59
-		$this->logger = $logger;
60
-	}
55
+    function __construct(GlobalStoragesService $globalService, IDBConnection $connection, ILogger $logger) {
56
+        parent::__construct();
57
+        $this->globalService = $globalService;
58
+        $this->connection = $connection;
59
+        $this->logger = $logger;
60
+    }
61 61
 
62
-	protected function configure() {
63
-		$this
64
-			->setName('files_external:notify')
65
-			->setDescription('Listen for active update notifications for a configured external mount')
66
-			->addArgument(
67
-				'mount_id',
68
-				InputArgument::REQUIRED,
69
-				'the mount id of the mount to listen to'
70
-			)->addOption(
71
-				'user',
72
-				'u',
73
-				InputOption::VALUE_REQUIRED,
74
-				'The username for the remote mount (required only for some mount configuration that don\'t store credentials)'
75
-			)->addOption(
76
-				'password',
77
-				'p',
78
-				InputOption::VALUE_REQUIRED,
79
-				'The password for the remote mount (required only for some mount configuration that don\'t store credentials)'
80
-			)->addOption(
81
-				'path',
82
-				'',
83
-				InputOption::VALUE_REQUIRED,
84
-				'The directory in the storage to listen for updates in',
85
-				'/'
86
-			);
87
-		parent::configure();
88
-	}
62
+    protected function configure() {
63
+        $this
64
+            ->setName('files_external:notify')
65
+            ->setDescription('Listen for active update notifications for a configured external mount')
66
+            ->addArgument(
67
+                'mount_id',
68
+                InputArgument::REQUIRED,
69
+                'the mount id of the mount to listen to'
70
+            )->addOption(
71
+                'user',
72
+                'u',
73
+                InputOption::VALUE_REQUIRED,
74
+                'The username for the remote mount (required only for some mount configuration that don\'t store credentials)'
75
+            )->addOption(
76
+                'password',
77
+                'p',
78
+                InputOption::VALUE_REQUIRED,
79
+                'The password for the remote mount (required only for some mount configuration that don\'t store credentials)'
80
+            )->addOption(
81
+                'path',
82
+                '',
83
+                InputOption::VALUE_REQUIRED,
84
+                'The directory in the storage to listen for updates in',
85
+                '/'
86
+            );
87
+        parent::configure();
88
+    }
89 89
 
90
-	protected function execute(InputInterface $input, OutputInterface $output) {
91
-		$mount = $this->globalService->getStorage($input->getArgument('mount_id'));
92
-		if (is_null($mount)) {
93
-			$output->writeln('<error>Mount not found</error>');
94
-			return 1;
95
-		}
96
-		$noAuth = false;
97
-		try {
98
-			$authBackend = $mount->getAuthMechanism();
99
-			$authBackend->manipulateStorageConfig($mount);
100
-		} catch (InsufficientDataForMeaningfulAnswerException $e) {
101
-			$noAuth = true;
102
-		} catch (StorageNotAvailableException $e) {
103
-			$noAuth = true;
104
-		}
90
+    protected function execute(InputInterface $input, OutputInterface $output) {
91
+        $mount = $this->globalService->getStorage($input->getArgument('mount_id'));
92
+        if (is_null($mount)) {
93
+            $output->writeln('<error>Mount not found</error>');
94
+            return 1;
95
+        }
96
+        $noAuth = false;
97
+        try {
98
+            $authBackend = $mount->getAuthMechanism();
99
+            $authBackend->manipulateStorageConfig($mount);
100
+        } catch (InsufficientDataForMeaningfulAnswerException $e) {
101
+            $noAuth = true;
102
+        } catch (StorageNotAvailableException $e) {
103
+            $noAuth = true;
104
+        }
105 105
 
106
-		if ($input->getOption('user')) {
107
-			$mount->setBackendOption('user', $input->getOption('user'));
108
-		} else if (isset($_ENV['NOTIFY_USER'])) {
109
-			$mount->setBackendOption('user', $_ENV['NOTIFY_USER']);
110
-		} else if (isset($_SERVER['NOTIFY_USER'])) {
111
-			$mount->setBackendOption('user', $_SERVER['NOTIFY_USER']);
112
-		}
113
-		if ($input->getOption('password')) {
114
-			$mount->setBackendOption('password', $input->getOption('password'));
115
-		} else if (isset($_ENV['NOTIFY_PASSWORD'])) {
116
-			$mount->setBackendOption('password', $_ENV['NOTIFY_PASSWORD']);
117
-		} else if (isset($_SERVER['NOTIFY_PASSWORD'])) {
118
-			$mount->setBackendOption('password', $_SERVER['NOTIFY_PASSWORD']);
119
-		}
106
+        if ($input->getOption('user')) {
107
+            $mount->setBackendOption('user', $input->getOption('user'));
108
+        } else if (isset($_ENV['NOTIFY_USER'])) {
109
+            $mount->setBackendOption('user', $_ENV['NOTIFY_USER']);
110
+        } else if (isset($_SERVER['NOTIFY_USER'])) {
111
+            $mount->setBackendOption('user', $_SERVER['NOTIFY_USER']);
112
+        }
113
+        if ($input->getOption('password')) {
114
+            $mount->setBackendOption('password', $input->getOption('password'));
115
+        } else if (isset($_ENV['NOTIFY_PASSWORD'])) {
116
+            $mount->setBackendOption('password', $_ENV['NOTIFY_PASSWORD']);
117
+        } else if (isset($_SERVER['NOTIFY_PASSWORD'])) {
118
+            $mount->setBackendOption('password', $_SERVER['NOTIFY_PASSWORD']);
119
+        }
120 120
 
121
-		try {
122
-			$storage = $this->createStorage($mount);
123
-		} catch (\Exception $e) {
124
-			$output->writeln('<error>Error while trying to create storage</error>');
125
-			if ($noAuth) {
126
-				$output->writeln('<error>Username and/or password required</error>');
127
-			}
128
-			return 1;
129
-		}
130
-		if (!$storage instanceof INotifyStorage) {
131
-			$output->writeln('<error>Mount of type "' . $mount->getBackend()->getText() . '" does not support active update notifications</error>');
132
-			return 1;
133
-		}
121
+        try {
122
+            $storage = $this->createStorage($mount);
123
+        } catch (\Exception $e) {
124
+            $output->writeln('<error>Error while trying to create storage</error>');
125
+            if ($noAuth) {
126
+                $output->writeln('<error>Username and/or password required</error>');
127
+            }
128
+            return 1;
129
+        }
130
+        if (!$storage instanceof INotifyStorage) {
131
+            $output->writeln('<error>Mount of type "' . $mount->getBackend()->getText() . '" does not support active update notifications</error>');
132
+            return 1;
133
+        }
134 134
 
135
-		$verbose = $input->getOption('verbose');
135
+        $verbose = $input->getOption('verbose');
136 136
 
137
-		$path = trim($input->getOption('path'), '/');
138
-		$notifyHandler = $storage->notify($path);
139
-		$this->selfTest($storage, $notifyHandler, $verbose, $output);
140
-		$notifyHandler->listen(function (IChange $change) use ($mount, $verbose, $output) {
141
-			if ($verbose) {
142
-				$this->logUpdate($change, $output);
143
-			}
144
-			if ($change instanceof IRenameChange) {
145
-				$this->markParentAsOutdated($mount->getId(), $change->getTargetPath(), $output);
146
-			}
147
-			$this->markParentAsOutdated($mount->getId(), $change->getPath(), $output);
148
-		});
149
-	}
137
+        $path = trim($input->getOption('path'), '/');
138
+        $notifyHandler = $storage->notify($path);
139
+        $this->selfTest($storage, $notifyHandler, $verbose, $output);
140
+        $notifyHandler->listen(function (IChange $change) use ($mount, $verbose, $output) {
141
+            if ($verbose) {
142
+                $this->logUpdate($change, $output);
143
+            }
144
+            if ($change instanceof IRenameChange) {
145
+                $this->markParentAsOutdated($mount->getId(), $change->getTargetPath(), $output);
146
+            }
147
+            $this->markParentAsOutdated($mount->getId(), $change->getPath(), $output);
148
+        });
149
+    }
150 150
 
151
-	private function createStorage(StorageConfig $mount) {
152
-		$class = $mount->getBackend()->getStorageClass();
153
-		return new $class($mount->getBackendOptions());
154
-	}
151
+    private function createStorage(StorageConfig $mount) {
152
+        $class = $mount->getBackend()->getStorageClass();
153
+        return new $class($mount->getBackendOptions());
154
+    }
155 155
 
156
-	private function markParentAsOutdated($mountId, $path, OutputInterface $output) {
157
-		$parent = ltrim(dirname($path), '/');
158
-		if ($parent === '.') {
159
-			$parent = '';
160
-		}
156
+    private function markParentAsOutdated($mountId, $path, OutputInterface $output) {
157
+        $parent = ltrim(dirname($path), '/');
158
+        if ($parent === '.') {
159
+            $parent = '';
160
+        }
161 161
 
162
-		try {
163
-			$storageIds = $this->getStorageIds($mountId);
164
-		} catch (DriverException $ex) {
165
-			$this->logger->logException($ex, ['message' => 'Error while trying to find correct storage ids.', 'level' => ILogger::WARN]);
166
-			$this->connection = $this->reconnectToDatabase($this->connection, $output);
167
-			$output->writeln('<info>Needed to reconnect to the database</info>');
168
-			$storageIds = $this->getStorageIds($mountId);
169
-		}
170
-		if (count($storageIds) === 0) {
171
-			throw new StorageNotAvailableException('No storages found by mount ID ' . $mountId);
172
-		}
173
-		$storageIds = array_map('intval', $storageIds);
162
+        try {
163
+            $storageIds = $this->getStorageIds($mountId);
164
+        } catch (DriverException $ex) {
165
+            $this->logger->logException($ex, ['message' => 'Error while trying to find correct storage ids.', 'level' => ILogger::WARN]);
166
+            $this->connection = $this->reconnectToDatabase($this->connection, $output);
167
+            $output->writeln('<info>Needed to reconnect to the database</info>');
168
+            $storageIds = $this->getStorageIds($mountId);
169
+        }
170
+        if (count($storageIds) === 0) {
171
+            throw new StorageNotAvailableException('No storages found by mount ID ' . $mountId);
172
+        }
173
+        $storageIds = array_map('intval', $storageIds);
174 174
 
175
-		$result = $this->updateParent($storageIds, $parent);
176
-		if ($result === 0) {
177
-			//TODO: Find existing parent further up the tree in the database and register that folder instead.
178
-			$this->logger->info('Failed updating parent for "' . $path . '" while trying to register change. It may not exist in the filecache.');
179
-		}
180
-	}
175
+        $result = $this->updateParent($storageIds, $parent);
176
+        if ($result === 0) {
177
+            //TODO: Find existing parent further up the tree in the database and register that folder instead.
178
+            $this->logger->info('Failed updating parent for "' . $path . '" while trying to register change. It may not exist in the filecache.');
179
+        }
180
+    }
181 181
 
182
-	private function logUpdate(IChange $change, OutputInterface $output) {
183
-		switch ($change->getType()) {
184
-			case INotifyStorage::NOTIFY_ADDED:
185
-				$text = 'added';
186
-				break;
187
-			case INotifyStorage::NOTIFY_MODIFIED:
188
-				$text = 'modified';
189
-				break;
190
-			case INotifyStorage::NOTIFY_REMOVED:
191
-				$text = 'removed';
192
-				break;
193
-			case INotifyStorage::NOTIFY_RENAMED:
194
-				$text = 'renamed';
195
-				break;
196
-			default:
197
-				return;
198
-		}
182
+    private function logUpdate(IChange $change, OutputInterface $output) {
183
+        switch ($change->getType()) {
184
+            case INotifyStorage::NOTIFY_ADDED:
185
+                $text = 'added';
186
+                break;
187
+            case INotifyStorage::NOTIFY_MODIFIED:
188
+                $text = 'modified';
189
+                break;
190
+            case INotifyStorage::NOTIFY_REMOVED:
191
+                $text = 'removed';
192
+                break;
193
+            case INotifyStorage::NOTIFY_RENAMED:
194
+                $text = 'renamed';
195
+                break;
196
+            default:
197
+                return;
198
+        }
199 199
 
200
-		$text .= ' ' . $change->getPath();
201
-		if ($change instanceof IRenameChange) {
202
-			$text .= ' to ' . $change->getTargetPath();
203
-		}
200
+        $text .= ' ' . $change->getPath();
201
+        if ($change instanceof IRenameChange) {
202
+            $text .= ' to ' . $change->getTargetPath();
203
+        }
204 204
 
205
-		$output->writeln($text);
206
-	}
205
+        $output->writeln($text);
206
+    }
207 207
 
208
-	/**
209
-	 * @param int $mountId
210
-	 * @return array
211
-	 */
212
-	private function getStorageIds($mountId) {
213
-		$qb = $this->connection->getQueryBuilder();
214
-		return $qb
215
-			->select('storage_id')
216
-			->from('mounts')
217
-			->where($qb->expr()->eq('mount_id', $qb->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
218
-			->execute()
219
-			->fetchAll(\PDO::FETCH_COLUMN);
220
-	}
208
+    /**
209
+     * @param int $mountId
210
+     * @return array
211
+     */
212
+    private function getStorageIds($mountId) {
213
+        $qb = $this->connection->getQueryBuilder();
214
+        return $qb
215
+            ->select('storage_id')
216
+            ->from('mounts')
217
+            ->where($qb->expr()->eq('mount_id', $qb->createNamedParameter($mountId, IQueryBuilder::PARAM_INT)))
218
+            ->execute()
219
+            ->fetchAll(\PDO::FETCH_COLUMN);
220
+    }
221 221
 
222
-	/**
223
-	 * @param array $storageIds
224
-	 * @param string $parent
225
-	 * @return int
226
-	 */
227
-	private function updateParent($storageIds, $parent) {
228
-		$pathHash = md5(trim(\OC_Util::normalizeUnicode($parent), '/'));
229
-		$qb = $this->connection->getQueryBuilder();
230
-		return $qb
231
-			->update('filecache')
232
-			->set('size', $qb->createNamedParameter(-1, IQueryBuilder::PARAM_INT))
233
-			->where($qb->expr()->in('storage', $qb->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY, ':storage_ids')))
234
-			->andWhere($qb->expr()->eq('path_hash', $qb->createNamedParameter($pathHash, IQueryBuilder::PARAM_STR)))
235
-			->execute();
236
-	}
222
+    /**
223
+     * @param array $storageIds
224
+     * @param string $parent
225
+     * @return int
226
+     */
227
+    private function updateParent($storageIds, $parent) {
228
+        $pathHash = md5(trim(\OC_Util::normalizeUnicode($parent), '/'));
229
+        $qb = $this->connection->getQueryBuilder();
230
+        return $qb
231
+            ->update('filecache')
232
+            ->set('size', $qb->createNamedParameter(-1, IQueryBuilder::PARAM_INT))
233
+            ->where($qb->expr()->in('storage', $qb->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY, ':storage_ids')))
234
+            ->andWhere($qb->expr()->eq('path_hash', $qb->createNamedParameter($pathHash, IQueryBuilder::PARAM_STR)))
235
+            ->execute();
236
+    }
237 237
 
238
-	/**
239
-	 * @return \OCP\IDBConnection
240
-	 */
241
-	private function reconnectToDatabase(IDBConnection $connection, OutputInterface $output) {
242
-		try {
243
-			$connection->close();
244
-		} catch (\Exception $ex) {
245
-			$this->logger->logException($ex, ['app' => 'files_external', 'message' => 'Error while disconnecting from DB', 'level' => ILogger::WARN]);
246
-			$output->writeln("<info>Error while disconnecting from database: {$ex->getMessage()}</info>");
247
-		}
248
-		while (!$connection->isConnected()) {
249
-			try {
250
-				$connection->connect();
251
-			} catch (\Exception $ex) {
252
-				$this->logger->logException($ex, ['app' => 'files_external', 'message' => 'Error while re-connecting to database', 'level' => ILogger::WARN]);
253
-				$output->writeln("<info>Error while re-connecting to database: {$ex->getMessage()}</info>");
254
-				sleep(60);
255
-			}
256
-		}
257
-		return $connection;
258
-	}
238
+    /**
239
+     * @return \OCP\IDBConnection
240
+     */
241
+    private function reconnectToDatabase(IDBConnection $connection, OutputInterface $output) {
242
+        try {
243
+            $connection->close();
244
+        } catch (\Exception $ex) {
245
+            $this->logger->logException($ex, ['app' => 'files_external', 'message' => 'Error while disconnecting from DB', 'level' => ILogger::WARN]);
246
+            $output->writeln("<info>Error while disconnecting from database: {$ex->getMessage()}</info>");
247
+        }
248
+        while (!$connection->isConnected()) {
249
+            try {
250
+                $connection->connect();
251
+            } catch (\Exception $ex) {
252
+                $this->logger->logException($ex, ['app' => 'files_external', 'message' => 'Error while re-connecting to database', 'level' => ILogger::WARN]);
253
+                $output->writeln("<info>Error while re-connecting to database: {$ex->getMessage()}</info>");
254
+                sleep(60);
255
+            }
256
+        }
257
+        return $connection;
258
+    }
259 259
 
260 260
 
261
-	private function selfTest(IStorage $storage, INotifyHandler $notifyHandler, $verbose, OutputInterface $output) {
262
-		usleep(100 * 1000); //give time for the notify to start
263
-		$storage->file_put_contents('/.nc_test_file.txt', 'test content');
264
-		$storage->mkdir('/.nc_test_folder');
265
-		$storage->file_put_contents('/.nc_test_folder/subfile.txt', 'test content');
261
+    private function selfTest(IStorage $storage, INotifyHandler $notifyHandler, $verbose, OutputInterface $output) {
262
+        usleep(100 * 1000); //give time for the notify to start
263
+        $storage->file_put_contents('/.nc_test_file.txt', 'test content');
264
+        $storage->mkdir('/.nc_test_folder');
265
+        $storage->file_put_contents('/.nc_test_folder/subfile.txt', 'test content');
266 266
 
267
-		usleep(100 * 1000); //time for all changes to be processed
268
-		$changes = $notifyHandler->getChanges();
267
+        usleep(100 * 1000); //time for all changes to be processed
268
+        $changes = $notifyHandler->getChanges();
269 269
 
270
-		$storage->unlink('/.nc_test_file.txt');
271
-		$storage->unlink('/.nc_test_folder/subfile.txt');
272
-		$storage->rmdir('/.nc_test_folder');
270
+        $storage->unlink('/.nc_test_file.txt');
271
+        $storage->unlink('/.nc_test_folder/subfile.txt');
272
+        $storage->rmdir('/.nc_test_folder');
273 273
 
274
-		usleep(100 * 1000); //time for all changes to be processed
275
-		$notifyHandler->getChanges(); // flush
274
+        usleep(100 * 1000); //time for all changes to be processed
275
+        $notifyHandler->getChanges(); // flush
276 276
 
277
-		$foundRootChange = false;
278
-		$foundSubfolderChange = false;
277
+        $foundRootChange = false;
278
+        $foundSubfolderChange = false;
279 279
 
280
-		foreach ($changes as $change) {
281
-			if ($change->getPath() === '/.nc_test_file.txt' || $change->getPath() === '.nc_test_file.txt') {
282
-				$foundRootChange = true;
283
-			} else if ($change->getPath() === '/.nc_test_folder/subfile.txt' || $change->getPath() === '.nc_test_folder/subfile.txt') {
284
-				$foundSubfolderChange = true;
285
-			}
286
-		}
280
+        foreach ($changes as $change) {
281
+            if ($change->getPath() === '/.nc_test_file.txt' || $change->getPath() === '.nc_test_file.txt') {
282
+                $foundRootChange = true;
283
+            } else if ($change->getPath() === '/.nc_test_folder/subfile.txt' || $change->getPath() === '.nc_test_folder/subfile.txt') {
284
+                $foundSubfolderChange = true;
285
+            }
286
+        }
287 287
 
288
-		if ($foundRootChange && $foundSubfolderChange && $verbose) {
289
-			$output->writeln('<info>Self-test successful</info>');
290
-		} else if ($foundRootChange && !$foundSubfolderChange) {
291
-			$output->writeln('<error>Error while running self-test, change is subfolder not detected</error>');
292
-		} else if (!$foundRootChange) {
293
-			$output->writeln('<error>Error while running self-test, no changes detected</error>');
294
-		}
295
-	}
288
+        if ($foundRootChange && $foundSubfolderChange && $verbose) {
289
+            $output->writeln('<info>Self-test successful</info>');
290
+        } else if ($foundRootChange && !$foundSubfolderChange) {
291
+            $output->writeln('<error>Error while running self-test, change is subfolder not detected</error>');
292
+        } else if (!$foundRootChange) {
293
+            $output->writeln('<error>Error while running self-test, no changes detected</error>');
294
+        }
295
+    }
296 296
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/SFTP.php 1 patch
Indentation   +437 added lines, -437 removed lines patch added patch discarded remove patch
@@ -45,441 +45,441 @@
 block discarded – undo
45 45
  * provide access to SFTP servers.
46 46
  */
47 47
 class SFTP extends \OC\Files\Storage\Common {
48
-	private $host;
49
-	private $user;
50
-	private $root;
51
-	private $port = 22;
52
-
53
-	private $auth = [];
54
-
55
-	/**
56
-	 * @var \phpseclib\Net\SFTP
57
-	 */
58
-	protected $client;
59
-
60
-	/**
61
-	 * @param string $host protocol://server:port
62
-	 * @return array [$server, $port]
63
-	 */
64
-	private function splitHost($host) {
65
-		$input = $host;
66
-		if (strpos($host, '://') === false) {
67
-			// add a protocol to fix parse_url behavior with ipv6
68
-			$host = 'http://' . $host;
69
-		}
70
-
71
-		$parsed = parse_url($host);
72
-		if(is_array($parsed) && isset($parsed['port'])) {
73
-			return [$parsed['host'], $parsed['port']];
74
-		} else if (is_array($parsed)) {
75
-			return [$parsed['host'], 22];
76
-		} else {
77
-			return [$input, 22];
78
-		}
79
-	}
80
-
81
-	/**
82
-	 * {@inheritdoc}
83
-	 */
84
-	public function __construct($params) {
85
-		// Register sftp://
86
-		Stream::register();
87
-
88
-		$parsedHost =  $this->splitHost($params['host']);
89
-
90
-		$this->host = $parsedHost[0];
91
-		$this->port = $parsedHost[1];
92
-
93
-		if (!isset($params['user'])) {
94
-			throw new \UnexpectedValueException('no authentication parameters specified');
95
-		}
96
-		$this->user = $params['user'];
97
-
98
-		if (isset($params['public_key_auth'])) {
99
-			$this->auth[] = $params['public_key_auth'];
100
-		}
101
-		if (isset($params['password']) && $params['password'] !== '') {
102
-			$this->auth[] = $params['password'];
103
-		}
104
-
105
-		if ($this->auth === []) {
106
-			throw new \UnexpectedValueException('no authentication parameters specified');
107
-		}
108
-
109
-		$this->root
110
-			= isset($params['root']) ? $this->cleanPath($params['root']) : '/';
111
-
112
-		$this->root = '/' . ltrim($this->root, '/');
113
-		$this->root = rtrim($this->root, '/') . '/';
114
-	}
115
-
116
-	/**
117
-	 * Returns the connection.
118
-	 *
119
-	 * @return \phpseclib\Net\SFTP connected client instance
120
-	 * @throws \Exception when the connection failed
121
-	 */
122
-	public function getConnection() {
123
-		if (!is_null($this->client)) {
124
-			return $this->client;
125
-		}
126
-
127
-		$hostKeys = $this->readHostKeys();
128
-		$this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
129
-
130
-		// The SSH Host Key MUST be verified before login().
131
-		$currentHostKey = $this->client->getServerPublicHostKey();
132
-		if (array_key_exists($this->host, $hostKeys)) {
133
-			if ($hostKeys[$this->host] !== $currentHostKey) {
134
-				throw new \Exception('Host public key does not match known key');
135
-			}
136
-		} else {
137
-			$hostKeys[$this->host] = $currentHostKey;
138
-			$this->writeHostKeys($hostKeys);
139
-		}
140
-
141
-		$login = false;
142
-		foreach ($this->auth as $auth) {
143
-			$login = $this->client->login($this->user, $auth);
144
-			if ($login === true) {
145
-				break;
146
-			}
147
-		}
148
-
149
-		if ($login === false) {
150
-			throw new \Exception('Login failed');
151
-		}
152
-		return $this->client;
153
-	}
154
-
155
-	/**
156
-	 * {@inheritdoc}
157
-	 */
158
-	public function test() {
159
-		if (
160
-			!isset($this->host)
161
-			|| !isset($this->user)
162
-		) {
163
-			return false;
164
-		}
165
-		return $this->getConnection()->nlist() !== false;
166
-	}
167
-
168
-	/**
169
-	 * {@inheritdoc}
170
-	 */
171
-	public function getId(){
172
-		$id = 'sftp::' . $this->user . '@' . $this->host;
173
-		if ($this->port !== 22) {
174
-			$id .= ':' . $this->port;
175
-		}
176
-		// note: this will double the root slash,
177
-		// we should not change it to keep compatible with
178
-		// old storage ids
179
-		$id .= '/' . $this->root;
180
-		return $id;
181
-	}
182
-
183
-	/**
184
-	 * @return string
185
-	 */
186
-	public function getHost() {
187
-		return $this->host;
188
-	}
189
-
190
-	/**
191
-	 * @return string
192
-	 */
193
-	public function getRoot() {
194
-		return $this->root;
195
-	}
196
-
197
-	/**
198
-	 * @return mixed
199
-	 */
200
-	public function getUser() {
201
-		return $this->user;
202
-	}
203
-
204
-	/**
205
-	 * @param string $path
206
-	 * @return string
207
-	 */
208
-	private function absPath($path) {
209
-		return $this->root . $this->cleanPath($path);
210
-	}
211
-
212
-	/**
213
-	 * @return string|false
214
-	 */
215
-	private function hostKeysPath() {
216
-		try {
217
-			$storage_view = \OCP\Files::getStorage('files_external');
218
-			if ($storage_view) {
219
-				return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
220
-					$storage_view->getAbsolutePath('') .
221
-					'ssh_hostKeys';
222
-			}
223
-		} catch (\Exception $e) {
224
-		}
225
-		return false;
226
-	}
227
-
228
-	/**
229
-	 * @param $keys
230
-	 * @return bool
231
-	 */
232
-	protected function writeHostKeys($keys) {
233
-		try {
234
-			$keyPath = $this->hostKeysPath();
235
-			if ($keyPath && file_exists($keyPath)) {
236
-				$fp = fopen($keyPath, 'w');
237
-				foreach ($keys as $host => $key) {
238
-					fwrite($fp, $host . '::' . $key . "\n");
239
-				}
240
-				fclose($fp);
241
-				return true;
242
-			}
243
-		} catch (\Exception $e) {
244
-		}
245
-		return false;
246
-	}
247
-
248
-	/**
249
-	 * @return array
250
-	 */
251
-	protected function readHostKeys() {
252
-		try {
253
-			$keyPath = $this->hostKeysPath();
254
-			if (file_exists($keyPath)) {
255
-				$hosts = [];
256
-				$keys = [];
257
-				$lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
258
-				if ($lines) {
259
-					foreach ($lines as $line) {
260
-						$hostKeyArray = explode("::", $line, 2);
261
-						if (count($hostKeyArray) === 2) {
262
-							$hosts[] = $hostKeyArray[0];
263
-							$keys[] = $hostKeyArray[1];
264
-						}
265
-					}
266
-					return array_combine($hosts, $keys);
267
-				}
268
-			}
269
-		} catch (\Exception $e) {
270
-		}
271
-		return [];
272
-	}
273
-
274
-	/**
275
-	 * {@inheritdoc}
276
-	 */
277
-	public function mkdir($path) {
278
-		try {
279
-			return $this->getConnection()->mkdir($this->absPath($path));
280
-		} catch (\Exception $e) {
281
-			return false;
282
-		}
283
-	}
284
-
285
-	/**
286
-	 * {@inheritdoc}
287
-	 */
288
-	public function rmdir($path) {
289
-		try {
290
-			$result = $this->getConnection()->delete($this->absPath($path), true);
291
-			// workaround: stray stat cache entry when deleting empty folders
292
-			// see https://github.com/phpseclib/phpseclib/issues/706
293
-			$this->getConnection()->clearStatCache();
294
-			return $result;
295
-		} catch (\Exception $e) {
296
-			return false;
297
-		}
298
-	}
299
-
300
-	/**
301
-	 * {@inheritdoc}
302
-	 */
303
-	public function opendir($path) {
304
-		try {
305
-			$list = $this->getConnection()->nlist($this->absPath($path));
306
-			if ($list === false) {
307
-				return false;
308
-			}
309
-
310
-			$id = md5('sftp:' . $path);
311
-			$dirStream = [];
312
-			foreach($list as $file) {
313
-				if ($file !== '.' && $file !== '..') {
314
-					$dirStream[] = $file;
315
-				}
316
-			}
317
-			return IteratorDirectory::wrap($dirStream);
318
-		} catch(\Exception $e) {
319
-			return false;
320
-		}
321
-	}
322
-
323
-	/**
324
-	 * {@inheritdoc}
325
-	 */
326
-	public function filetype($path) {
327
-		try {
328
-			$stat = $this->getConnection()->stat($this->absPath($path));
329
-			if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
330
-				return 'file';
331
-			}
332
-
333
-			if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
334
-				return 'dir';
335
-			}
336
-		} catch (\Exception $e) {
337
-
338
-		}
339
-		return false;
340
-	}
341
-
342
-	/**
343
-	 * {@inheritdoc}
344
-	 */
345
-	public function file_exists($path) {
346
-		try {
347
-			return $this->getConnection()->stat($this->absPath($path)) !== false;
348
-		} catch (\Exception $e) {
349
-			return false;
350
-		}
351
-	}
352
-
353
-	/**
354
-	 * {@inheritdoc}
355
-	 */
356
-	public function unlink($path) {
357
-		try {
358
-			return $this->getConnection()->delete($this->absPath($path), true);
359
-		} catch (\Exception $e) {
360
-			return false;
361
-		}
362
-	}
363
-
364
-	/**
365
-	 * {@inheritdoc}
366
-	 */
367
-	public function fopen($path, $mode) {
368
-		try {
369
-			$absPath = $this->absPath($path);
370
-			switch($mode) {
371
-				case 'r':
372
-				case 'rb':
373
-					if ( !$this->file_exists($path)) {
374
-						return false;
375
-					}
376
-					SFTPReadStream::register();
377
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
378
-					$handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
379
-					return RetryWrapper::wrap($handle);
380
-				case 'w':
381
-				case 'wb':
382
-					SFTPWriteStream::register();
383
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
384
-					return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
385
-				case 'a':
386
-				case 'ab':
387
-				case 'r+':
388
-				case 'w+':
389
-				case 'wb+':
390
-				case 'a+':
391
-				case 'x':
392
-				case 'x+':
393
-				case 'c':
394
-				case 'c+':
395
-					$context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
396
-					$handle = fopen($this->constructUrl($path), $mode, false, $context);
397
-					return RetryWrapper::wrap($handle);
398
-			}
399
-		} catch (\Exception $e) {
400
-		}
401
-		return false;
402
-	}
403
-
404
-	/**
405
-	 * {@inheritdoc}
406
-	 */
407
-	public function touch($path, $mtime=null) {
408
-		try {
409
-			if (!is_null($mtime)) {
410
-				return false;
411
-			}
412
-			if (!$this->file_exists($path)) {
413
-				$this->getConnection()->put($this->absPath($path), '');
414
-			} else {
415
-				return false;
416
-			}
417
-		} catch (\Exception $e) {
418
-			return false;
419
-		}
420
-		return true;
421
-	}
422
-
423
-	/**
424
-	 * @param string $path
425
-	 * @param string $target
426
-	 * @throws \Exception
427
-	 */
428
-	public function getFile($path, $target) {
429
-		$this->getConnection()->get($path, $target);
430
-	}
431
-
432
-	/**
433
-	 * @param string $path
434
-	 * @param string $target
435
-	 * @throws \Exception
436
-	 */
437
-	public function uploadFile($path, $target) {
438
-		$this->getConnection()->put($target, $path, NET_SFTP_LOCAL_FILE);
439
-	}
440
-
441
-	/**
442
-	 * {@inheritdoc}
443
-	 */
444
-	public function rename($source, $target) {
445
-		try {
446
-			if ($this->file_exists($target)) {
447
-				$this->unlink($target);
448
-			}
449
-			return $this->getConnection()->rename(
450
-				$this->absPath($source),
451
-				$this->absPath($target)
452
-			);
453
-		} catch (\Exception $e) {
454
-			return false;
455
-		}
456
-	}
457
-
458
-	/**
459
-	 * {@inheritdoc}
460
-	 */
461
-	public function stat($path) {
462
-		try {
463
-			$stat = $this->getConnection()->stat($this->absPath($path));
464
-
465
-			$mtime = $stat ? $stat['mtime'] : -1;
466
-			$size = $stat ? $stat['size'] : 0;
467
-
468
-			return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1];
469
-		} catch (\Exception $e) {
470
-			return false;
471
-		}
472
-	}
473
-
474
-	/**
475
-	 * @param string $path
476
-	 * @return string
477
-	 */
478
-	public function constructUrl($path) {
479
-		// Do not pass the password here. We want to use the Net_SFTP object
480
-		// supplied via stream context or fail. We only supply username and
481
-		// hostname because this might show up in logs (they are not used).
482
-		$url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
483
-		return $url;
484
-	}
48
+    private $host;
49
+    private $user;
50
+    private $root;
51
+    private $port = 22;
52
+
53
+    private $auth = [];
54
+
55
+    /**
56
+     * @var \phpseclib\Net\SFTP
57
+     */
58
+    protected $client;
59
+
60
+    /**
61
+     * @param string $host protocol://server:port
62
+     * @return array [$server, $port]
63
+     */
64
+    private function splitHost($host) {
65
+        $input = $host;
66
+        if (strpos($host, '://') === false) {
67
+            // add a protocol to fix parse_url behavior with ipv6
68
+            $host = 'http://' . $host;
69
+        }
70
+
71
+        $parsed = parse_url($host);
72
+        if(is_array($parsed) && isset($parsed['port'])) {
73
+            return [$parsed['host'], $parsed['port']];
74
+        } else if (is_array($parsed)) {
75
+            return [$parsed['host'], 22];
76
+        } else {
77
+            return [$input, 22];
78
+        }
79
+    }
80
+
81
+    /**
82
+     * {@inheritdoc}
83
+     */
84
+    public function __construct($params) {
85
+        // Register sftp://
86
+        Stream::register();
87
+
88
+        $parsedHost =  $this->splitHost($params['host']);
89
+
90
+        $this->host = $parsedHost[0];
91
+        $this->port = $parsedHost[1];
92
+
93
+        if (!isset($params['user'])) {
94
+            throw new \UnexpectedValueException('no authentication parameters specified');
95
+        }
96
+        $this->user = $params['user'];
97
+
98
+        if (isset($params['public_key_auth'])) {
99
+            $this->auth[] = $params['public_key_auth'];
100
+        }
101
+        if (isset($params['password']) && $params['password'] !== '') {
102
+            $this->auth[] = $params['password'];
103
+        }
104
+
105
+        if ($this->auth === []) {
106
+            throw new \UnexpectedValueException('no authentication parameters specified');
107
+        }
108
+
109
+        $this->root
110
+            = isset($params['root']) ? $this->cleanPath($params['root']) : '/';
111
+
112
+        $this->root = '/' . ltrim($this->root, '/');
113
+        $this->root = rtrim($this->root, '/') . '/';
114
+    }
115
+
116
+    /**
117
+     * Returns the connection.
118
+     *
119
+     * @return \phpseclib\Net\SFTP connected client instance
120
+     * @throws \Exception when the connection failed
121
+     */
122
+    public function getConnection() {
123
+        if (!is_null($this->client)) {
124
+            return $this->client;
125
+        }
126
+
127
+        $hostKeys = $this->readHostKeys();
128
+        $this->client = new \phpseclib\Net\SFTP($this->host, $this->port);
129
+
130
+        // The SSH Host Key MUST be verified before login().
131
+        $currentHostKey = $this->client->getServerPublicHostKey();
132
+        if (array_key_exists($this->host, $hostKeys)) {
133
+            if ($hostKeys[$this->host] !== $currentHostKey) {
134
+                throw new \Exception('Host public key does not match known key');
135
+            }
136
+        } else {
137
+            $hostKeys[$this->host] = $currentHostKey;
138
+            $this->writeHostKeys($hostKeys);
139
+        }
140
+
141
+        $login = false;
142
+        foreach ($this->auth as $auth) {
143
+            $login = $this->client->login($this->user, $auth);
144
+            if ($login === true) {
145
+                break;
146
+            }
147
+        }
148
+
149
+        if ($login === false) {
150
+            throw new \Exception('Login failed');
151
+        }
152
+        return $this->client;
153
+    }
154
+
155
+    /**
156
+     * {@inheritdoc}
157
+     */
158
+    public function test() {
159
+        if (
160
+            !isset($this->host)
161
+            || !isset($this->user)
162
+        ) {
163
+            return false;
164
+        }
165
+        return $this->getConnection()->nlist() !== false;
166
+    }
167
+
168
+    /**
169
+     * {@inheritdoc}
170
+     */
171
+    public function getId(){
172
+        $id = 'sftp::' . $this->user . '@' . $this->host;
173
+        if ($this->port !== 22) {
174
+            $id .= ':' . $this->port;
175
+        }
176
+        // note: this will double the root slash,
177
+        // we should not change it to keep compatible with
178
+        // old storage ids
179
+        $id .= '/' . $this->root;
180
+        return $id;
181
+    }
182
+
183
+    /**
184
+     * @return string
185
+     */
186
+    public function getHost() {
187
+        return $this->host;
188
+    }
189
+
190
+    /**
191
+     * @return string
192
+     */
193
+    public function getRoot() {
194
+        return $this->root;
195
+    }
196
+
197
+    /**
198
+     * @return mixed
199
+     */
200
+    public function getUser() {
201
+        return $this->user;
202
+    }
203
+
204
+    /**
205
+     * @param string $path
206
+     * @return string
207
+     */
208
+    private function absPath($path) {
209
+        return $this->root . $this->cleanPath($path);
210
+    }
211
+
212
+    /**
213
+     * @return string|false
214
+     */
215
+    private function hostKeysPath() {
216
+        try {
217
+            $storage_view = \OCP\Files::getStorage('files_external');
218
+            if ($storage_view) {
219
+                return \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') .
220
+                    $storage_view->getAbsolutePath('') .
221
+                    'ssh_hostKeys';
222
+            }
223
+        } catch (\Exception $e) {
224
+        }
225
+        return false;
226
+    }
227
+
228
+    /**
229
+     * @param $keys
230
+     * @return bool
231
+     */
232
+    protected function writeHostKeys($keys) {
233
+        try {
234
+            $keyPath = $this->hostKeysPath();
235
+            if ($keyPath && file_exists($keyPath)) {
236
+                $fp = fopen($keyPath, 'w');
237
+                foreach ($keys as $host => $key) {
238
+                    fwrite($fp, $host . '::' . $key . "\n");
239
+                }
240
+                fclose($fp);
241
+                return true;
242
+            }
243
+        } catch (\Exception $e) {
244
+        }
245
+        return false;
246
+    }
247
+
248
+    /**
249
+     * @return array
250
+     */
251
+    protected function readHostKeys() {
252
+        try {
253
+            $keyPath = $this->hostKeysPath();
254
+            if (file_exists($keyPath)) {
255
+                $hosts = [];
256
+                $keys = [];
257
+                $lines = file($keyPath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
258
+                if ($lines) {
259
+                    foreach ($lines as $line) {
260
+                        $hostKeyArray = explode("::", $line, 2);
261
+                        if (count($hostKeyArray) === 2) {
262
+                            $hosts[] = $hostKeyArray[0];
263
+                            $keys[] = $hostKeyArray[1];
264
+                        }
265
+                    }
266
+                    return array_combine($hosts, $keys);
267
+                }
268
+            }
269
+        } catch (\Exception $e) {
270
+        }
271
+        return [];
272
+    }
273
+
274
+    /**
275
+     * {@inheritdoc}
276
+     */
277
+    public function mkdir($path) {
278
+        try {
279
+            return $this->getConnection()->mkdir($this->absPath($path));
280
+        } catch (\Exception $e) {
281
+            return false;
282
+        }
283
+    }
284
+
285
+    /**
286
+     * {@inheritdoc}
287
+     */
288
+    public function rmdir($path) {
289
+        try {
290
+            $result = $this->getConnection()->delete($this->absPath($path), true);
291
+            // workaround: stray stat cache entry when deleting empty folders
292
+            // see https://github.com/phpseclib/phpseclib/issues/706
293
+            $this->getConnection()->clearStatCache();
294
+            return $result;
295
+        } catch (\Exception $e) {
296
+            return false;
297
+        }
298
+    }
299
+
300
+    /**
301
+     * {@inheritdoc}
302
+     */
303
+    public function opendir($path) {
304
+        try {
305
+            $list = $this->getConnection()->nlist($this->absPath($path));
306
+            if ($list === false) {
307
+                return false;
308
+            }
309
+
310
+            $id = md5('sftp:' . $path);
311
+            $dirStream = [];
312
+            foreach($list as $file) {
313
+                if ($file !== '.' && $file !== '..') {
314
+                    $dirStream[] = $file;
315
+                }
316
+            }
317
+            return IteratorDirectory::wrap($dirStream);
318
+        } catch(\Exception $e) {
319
+            return false;
320
+        }
321
+    }
322
+
323
+    /**
324
+     * {@inheritdoc}
325
+     */
326
+    public function filetype($path) {
327
+        try {
328
+            $stat = $this->getConnection()->stat($this->absPath($path));
329
+            if ((int) $stat['type'] === NET_SFTP_TYPE_REGULAR) {
330
+                return 'file';
331
+            }
332
+
333
+            if ((int) $stat['type'] === NET_SFTP_TYPE_DIRECTORY) {
334
+                return 'dir';
335
+            }
336
+        } catch (\Exception $e) {
337
+
338
+        }
339
+        return false;
340
+    }
341
+
342
+    /**
343
+     * {@inheritdoc}
344
+     */
345
+    public function file_exists($path) {
346
+        try {
347
+            return $this->getConnection()->stat($this->absPath($path)) !== false;
348
+        } catch (\Exception $e) {
349
+            return false;
350
+        }
351
+    }
352
+
353
+    /**
354
+     * {@inheritdoc}
355
+     */
356
+    public function unlink($path) {
357
+        try {
358
+            return $this->getConnection()->delete($this->absPath($path), true);
359
+        } catch (\Exception $e) {
360
+            return false;
361
+        }
362
+    }
363
+
364
+    /**
365
+     * {@inheritdoc}
366
+     */
367
+    public function fopen($path, $mode) {
368
+        try {
369
+            $absPath = $this->absPath($path);
370
+            switch($mode) {
371
+                case 'r':
372
+                case 'rb':
373
+                    if ( !$this->file_exists($path)) {
374
+                        return false;
375
+                    }
376
+                    SFTPReadStream::register();
377
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
378
+                    $handle = fopen('sftpread://' . trim($absPath, '/'), 'r', false, $context);
379
+                    return RetryWrapper::wrap($handle);
380
+                case 'w':
381
+                case 'wb':
382
+                    SFTPWriteStream::register();
383
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
384
+                    return fopen('sftpwrite://' . trim($absPath, '/'), 'w', false, $context);
385
+                case 'a':
386
+                case 'ab':
387
+                case 'r+':
388
+                case 'w+':
389
+                case 'wb+':
390
+                case 'a+':
391
+                case 'x':
392
+                case 'x+':
393
+                case 'c':
394
+                case 'c+':
395
+                    $context = stream_context_create(['sftp' => ['session' => $this->getConnection()]]);
396
+                    $handle = fopen($this->constructUrl($path), $mode, false, $context);
397
+                    return RetryWrapper::wrap($handle);
398
+            }
399
+        } catch (\Exception $e) {
400
+        }
401
+        return false;
402
+    }
403
+
404
+    /**
405
+     * {@inheritdoc}
406
+     */
407
+    public function touch($path, $mtime=null) {
408
+        try {
409
+            if (!is_null($mtime)) {
410
+                return false;
411
+            }
412
+            if (!$this->file_exists($path)) {
413
+                $this->getConnection()->put($this->absPath($path), '');
414
+            } else {
415
+                return false;
416
+            }
417
+        } catch (\Exception $e) {
418
+            return false;
419
+        }
420
+        return true;
421
+    }
422
+
423
+    /**
424
+     * @param string $path
425
+     * @param string $target
426
+     * @throws \Exception
427
+     */
428
+    public function getFile($path, $target) {
429
+        $this->getConnection()->get($path, $target);
430
+    }
431
+
432
+    /**
433
+     * @param string $path
434
+     * @param string $target
435
+     * @throws \Exception
436
+     */
437
+    public function uploadFile($path, $target) {
438
+        $this->getConnection()->put($target, $path, NET_SFTP_LOCAL_FILE);
439
+    }
440
+
441
+    /**
442
+     * {@inheritdoc}
443
+     */
444
+    public function rename($source, $target) {
445
+        try {
446
+            if ($this->file_exists($target)) {
447
+                $this->unlink($target);
448
+            }
449
+            return $this->getConnection()->rename(
450
+                $this->absPath($source),
451
+                $this->absPath($target)
452
+            );
453
+        } catch (\Exception $e) {
454
+            return false;
455
+        }
456
+    }
457
+
458
+    /**
459
+     * {@inheritdoc}
460
+     */
461
+    public function stat($path) {
462
+        try {
463
+            $stat = $this->getConnection()->stat($this->absPath($path));
464
+
465
+            $mtime = $stat ? $stat['mtime'] : -1;
466
+            $size = $stat ? $stat['size'] : 0;
467
+
468
+            return ['mtime' => $mtime, 'size' => $size, 'ctime' => -1];
469
+        } catch (\Exception $e) {
470
+            return false;
471
+        }
472
+    }
473
+
474
+    /**
475
+     * @param string $path
476
+     * @return string
477
+     */
478
+    public function constructUrl($path) {
479
+        // Do not pass the password here. We want to use the Net_SFTP object
480
+        // supplied via stream context or fail. We only supply username and
481
+        // hostname because this might show up in logs (they are not used).
482
+        $url = 'sftp://' . urlencode($this->user) . '@' . $this->host . ':' . $this->port . $this->root . $path;
483
+        return $url;
484
+    }
485 485
 }
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/FilesPlugin.php 1 patch
Indentation   +466 added lines, -466 removed lines patch added patch discarded remove patch
@@ -53,470 +53,470 @@
 block discarded – undo
53 53
 
54 54
 class FilesPlugin extends ServerPlugin {
55 55
 
56
-	// namespace
57
-	const NS_OWNCLOUD = 'http://owncloud.org/ns';
58
-	const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
59
-	const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id';
60
-	const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid';
61
-	const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
62
-	const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
63
-	const OCM_SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-cloud-mesh.org/ns}share-permissions';
64
-	const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
65
-	const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
66
-	const GETETAG_PROPERTYNAME = '{DAV:}getetag';
67
-	const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
68
-	const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id';
69
-	const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name';
70
-	const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums';
71
-	const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint';
72
-	const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview';
73
-	const MOUNT_TYPE_PROPERTYNAME = '{http://nextcloud.org/ns}mount-type';
74
-	const IS_ENCRYPTED_PROPERTYNAME = '{http://nextcloud.org/ns}is-encrypted';
75
-	const METADATA_ETAG_PROPERTYNAME = '{http://nextcloud.org/ns}metadata_etag';
76
-	const UPLOAD_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}upload_time';
77
-	const CREATION_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}creation_time';
78
-	const SHARE_NOTE = '{http://nextcloud.org/ns}note';
79
-
80
-	/**
81
-	 * Reference to main server object
82
-	 *
83
-	 * @var \Sabre\DAV\Server
84
-	 */
85
-	private $server;
86
-
87
-	/**
88
-	 * @var Tree
89
-	 */
90
-	private $tree;
91
-
92
-	/**
93
-	 * Whether this is public webdav.
94
-	 * If true, some returned information will be stripped off.
95
-	 *
96
-	 * @var bool
97
-	 */
98
-	private $isPublic;
99
-
100
-	/**
101
-	 * @var bool
102
-	 */
103
-	private $downloadAttachment;
104
-
105
-	/**
106
-	 * @var IConfig
107
-	 */
108
-	private $config;
109
-
110
-	/**
111
-	 * @var IRequest
112
-	 */
113
-	private $request;
114
-
115
-	/**
116
-	 * @var IPreview
117
-	 */
118
-	private $previewManager;
119
-
120
-	/**
121
-	 * @param Tree $tree
122
-	 * @param IConfig $config
123
-	 * @param IRequest $request
124
-	 * @param IPreview $previewManager
125
-	 * @param bool $isPublic
126
-	 * @param bool $downloadAttachment
127
-	 */
128
-	public function __construct(Tree $tree,
129
-								IConfig $config,
130
-								IRequest $request,
131
-								IPreview $previewManager,
132
-								$isPublic = false,
133
-								$downloadAttachment = true) {
134
-		$this->tree = $tree;
135
-		$this->config = $config;
136
-		$this->request = $request;
137
-		$this->isPublic = $isPublic;
138
-		$this->downloadAttachment = $downloadAttachment;
139
-		$this->previewManager = $previewManager;
140
-	}
141
-
142
-	/**
143
-	 * This initializes the plugin.
144
-	 *
145
-	 * This function is called by \Sabre\DAV\Server, after
146
-	 * addPlugin is called.
147
-	 *
148
-	 * This method should set up the required event subscriptions.
149
-	 *
150
-	 * @param \Sabre\DAV\Server $server
151
-	 * @return void
152
-	 */
153
-	public function initialize(\Sabre\DAV\Server $server) {
154
-		$server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
155
-		$server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc';
156
-		$server->protectedProperties[] = self::FILEID_PROPERTYNAME;
157
-		$server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME;
158
-		$server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
159
-		$server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
160
-		$server->protectedProperties[] = self::OCM_SHARE_PERMISSIONS_PROPERTYNAME;
161
-		$server->protectedProperties[] = self::SIZE_PROPERTYNAME;
162
-		$server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
163
-		$server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
164
-		$server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME;
165
-		$server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME;
166
-		$server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME;
167
-		$server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME;
168
-		$server->protectedProperties[] = self::MOUNT_TYPE_PROPERTYNAME;
169
-		$server->protectedProperties[] = self::IS_ENCRYPTED_PROPERTYNAME;
170
-		$server->protectedProperties[] = self::SHARE_NOTE;
171
-
172
-		// normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH
173
-		$allowedProperties = ['{DAV:}getetag'];
174
-		$server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties);
175
-
176
-		$this->server = $server;
177
-		$this->server->on('propFind', [$this, 'handleGetProperties']);
178
-		$this->server->on('propPatch', [$this, 'handleUpdateProperties']);
179
-		$this->server->on('afterBind', [$this, 'sendFileIdHeader']);
180
-		$this->server->on('afterWriteContent', [$this, 'sendFileIdHeader']);
181
-		$this->server->on('afterMethod:GET', [$this,'httpGet']);
182
-		$this->server->on('afterMethod:GET', [$this, 'handleDownloadToken']);
183
-		$this->server->on('afterResponse', function($request, ResponseInterface $response) {
184
-			$body = $response->getBody();
185
-			if (is_resource($body)) {
186
-				fclose($body);
187
-			}
188
-		});
189
-		$this->server->on('beforeMove', [$this, 'checkMove']);
190
-	}
191
-
192
-	/**
193
-	 * Plugin that checks if a move can actually be performed.
194
-	 *
195
-	 * @param string $source source path
196
-	 * @param string $destination destination path
197
-	 * @throws Forbidden
198
-	 * @throws NotFound
199
-	 */
200
-	function checkMove($source, $destination) {
201
-		$sourceNode = $this->tree->getNodeForPath($source);
202
-		if (!$sourceNode instanceof Node) {
203
-			return;
204
-		}
205
-		list($sourceDir,) = \Sabre\Uri\split($source);
206
-		list($destinationDir,) = \Sabre\Uri\split($destination);
207
-
208
-		if ($sourceDir !== $destinationDir) {
209
-			$sourceNodeFileInfo = $sourceNode->getFileInfo();
210
-			if ($sourceNodeFileInfo === null) {
211
-				throw new NotFound($source . ' does not exist');
212
- 			}
213
-
214
-			if (!$sourceNodeFileInfo->isDeletable()) {
215
-				throw new Forbidden($source . " cannot be deleted");
216
-			}
217
-		}
218
-	}
219
-
220
-	/**
221
-	 * This sets a cookie to be able to recognize the start of the download
222
-	 * the content must not be longer than 32 characters and must only contain
223
-	 * alphanumeric characters
224
-	 *
225
-	 * @param RequestInterface $request
226
-	 * @param ResponseInterface $response
227
-	 */
228
-	function handleDownloadToken(RequestInterface $request, ResponseInterface $response) {
229
-		$queryParams = $request->getQueryParameters();
230
-
231
-		/**
232
-		 * this sets a cookie to be able to recognize the start of the download
233
-		 * the content must not be longer than 32 characters and must only contain
234
-		 * alphanumeric characters
235
-		 */
236
-		if (isset($queryParams['downloadStartSecret'])) {
237
-			$token = $queryParams['downloadStartSecret'];
238
-			if (!isset($token[32])
239
-				&& preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) {
240
-				// FIXME: use $response->setHeader() instead
241
-				setcookie('ocDownloadStarted', $token, time() + 20, '/');
242
-			}
243
-		}
244
-	}
245
-
246
-	/**
247
-	 * Add headers to file download
248
-	 *
249
-	 * @param RequestInterface $request
250
-	 * @param ResponseInterface $response
251
-	 */
252
-	function httpGet(RequestInterface $request, ResponseInterface $response) {
253
-		// Only handle valid files
254
-		$node = $this->tree->getNodeForPath($request->getPath());
255
-		if (!($node instanceof IFile)) return;
256
-
257
-		// adds a 'Content-Disposition: attachment' header in case no disposition
258
-		// header has been set before
259
-		if ($this->downloadAttachment &&
260
-			$response->getHeader('Content-Disposition') === null) {
261
-			$filename = $node->getName();
262
-			if ($this->request->isUserAgent(
263
-				[
264
-					Request::USER_AGENT_IE,
265
-					Request::USER_AGENT_ANDROID_MOBILE_CHROME,
266
-					Request::USER_AGENT_FREEBOX,
267
-				])) {
268
-				$response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
269
-			} else {
270
-				$response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
271
-													 . '; filename="' . rawurlencode($filename) . '"');
272
-			}
273
-		}
274
-
275
-		if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
276
-			//Add OC-Checksum header
277
-			/** @var $node File */
278
-			$checksum = $node->getChecksum();
279
-			if ($checksum !== null && $checksum !== '') {
280
-				$response->addHeader('OC-Checksum', $checksum);
281
-			}
282
-		}
283
-	}
284
-
285
-	/**
286
-	 * Adds all ownCloud-specific properties
287
-	 *
288
-	 * @param PropFind $propFind
289
-	 * @param \Sabre\DAV\INode $node
290
-	 * @return void
291
-	 */
292
-	public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) {
293
-
294
-		$httpRequest = $this->server->httpRequest;
295
-
296
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
297
-			/**
298
-			 * This was disabled, because it made dir listing throw an exception,
299
-			 * so users were unable to navigate into folders where one subitem
300
-			 * is blocked by the files_accesscontrol app, see:
301
-			 * https://github.com/nextcloud/files_accesscontrol/issues/65
302
-			 * if (!$node->getFileInfo()->isReadable()) {
303
-			 *     // avoid detecting files through this means
304
-			 *     throw new NotFound();
305
-			 * }
306
-			 */
307
-
308
-			$propFind->handle(self::FILEID_PROPERTYNAME, function() use ($node) {
309
-				return $node->getFileId();
310
-			});
311
-
312
-			$propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function() use ($node) {
313
-				return $node->getInternalFileId();
314
-			});
315
-
316
-			$propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) {
317
-				$perms = $node->getDavPermissions();
318
-				if ($this->isPublic) {
319
-					// remove mount information
320
-					$perms = str_replace(['S', 'M'], '', $perms);
321
-				}
322
-				return $perms;
323
-			});
324
-
325
-			$propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
326
-				return $node->getSharePermissions(
327
-					$httpRequest->getRawServerValue('PHP_AUTH_USER')
328
-				);
329
-			});
330
-
331
-			$propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
332
-				$ncPermissions = $node->getSharePermissions(
333
-					$httpRequest->getRawServerValue('PHP_AUTH_USER')
334
-				);
335
-				$ocmPermissions = $this->ncPermissions2ocmPermissions($ncPermissions);
336
-				return json_encode($ocmPermissions);
337
-			});
338
-
339
-			$propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
340
-				return $node->getETag();
341
-			});
342
-
343
-			$propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node) {
344
-				$owner = $node->getOwner();
345
-				if (!$owner) {
346
-					return null;
347
-				} else {
348
-					return $owner->getUID();
349
-				}
350
-			});
351
-			$propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node) {
352
-				$owner = $node->getOwner();
353
-				if (!$owner) {
354
-					return null;
355
-				} else {
356
-					return $owner->getDisplayName();
357
-				}
358
-			});
359
-
360
-			$propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
361
-				return json_encode($this->previewManager->isAvailable($node->getFileInfo()));
362
-			});
363
-			$propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
364
-				return $node->getSize();
365
-			});
366
-			$propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function () use ($node) {
367
-				return $node->getFileInfo()->getMountPoint()->getMountType();
368
-			});
369
-
370
-			$propFind->handle(self::SHARE_NOTE, function() use ($node, $httpRequest) {
371
-				return $node->getNoteFromShare(
372
-					$httpRequest->getRawServerValue('PHP_AUTH_USER')
373
-				);
374
-			});
375
-		}
376
-
377
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
378
-			$propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) {
379
-				return $this->config->getSystemValue('data-fingerprint', '');
380
-			});
381
-		}
382
-
383
-		if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
384
-			$propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) {
385
-				/** @var $node \OCA\DAV\Connector\Sabre\File */
386
-				try {
387
-					$directDownloadUrl = $node->getDirectDownload();
388
-					if (isset($directDownloadUrl['url'])) {
389
-						return $directDownloadUrl['url'];
390
-					}
391
-				} catch (StorageNotAvailableException $e) {
392
-					return false;
393
-				} catch (ForbiddenException $e) {
394
-					return false;
395
-				}
396
-				return false;
397
-			});
398
-
399
-			$propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) {
400
-				$checksum = $node->getChecksum();
401
-				if ($checksum === null || $checksum === '') {
402
-					return null;
403
-				}
404
-
405
-				return new ChecksumList($checksum);
406
-			});
407
-
408
-			$propFind->handle(self::CREATION_TIME_PROPERTYNAME, function() use ($node) {
409
-				return $node->getFileInfo()->getCreationTime();
410
-			});
411
-
412
-			$propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function() use ($node) {
413
-				return $node->getFileInfo()->getUploadTime();
414
-			});
415
-
416
-		}
417
-
418
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) {
419
-			$propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
420
-				return $node->getSize();
421
-			});
422
-
423
-			$propFind->handle(self::IS_ENCRYPTED_PROPERTYNAME, function() use ($node) {
424
-				return $node->getFileInfo()->isEncrypted() ? '1' : '0';
425
-			});
426
-		}
427
-	}
428
-
429
-	/**
430
-	 * translate Nextcloud permissions to OCM Permissions
431
-	 *
432
-	 * @param $ncPermissions
433
-	 * @return array
434
-	 */
435
-	protected function ncPermissions2ocmPermissions($ncPermissions) {
436
-
437
-		$ocmPermissions = [];
438
-
439
-		if ($ncPermissions & Constants::PERMISSION_SHARE) {
440
-			$ocmPermissions[] = 'share';
441
-		}
442
-
443
-		if ($ncPermissions & Constants::PERMISSION_READ) {
444
-			$ocmPermissions[] = 'read';
445
-		}
446
-
447
-		if (($ncPermissions & Constants::PERMISSION_CREATE) ||
448
-			($ncPermissions & Constants::PERMISSION_UPDATE)) {
449
-			$ocmPermissions[] = 'write';
450
-		}
451
-
452
-		return $ocmPermissions;
453
-
454
-	}
455
-
456
-	/**
457
-	 * Update ownCloud-specific properties
458
-	 *
459
-	 * @param string $path
460
-	 * @param PropPatch $propPatch
461
-	 *
462
-	 * @return void
463
-	 */
464
-	public function handleUpdateProperties($path, PropPatch $propPatch) {
465
-		$node = $this->tree->getNodeForPath($path);
466
-		if (!($node instanceof \OCA\DAV\Connector\Sabre\Node)) {
467
-			return;
468
-		}
469
-
470
-		$propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($node) {
471
-			if (empty($time)) {
472
-				return false;
473
-			}
474
-			$node->touch($time);
475
-			return true;
476
-		});
477
-		$propPatch->handle(self::GETETAG_PROPERTYNAME, function($etag) use ($node) {
478
-			if (empty($etag)) {
479
-				return false;
480
-			}
481
-			if ($node->setEtag($etag) !== -1) {
482
-				return true;
483
-			}
484
-			return false;
485
-		});
486
-		$propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function($time) use ($node) {
487
-			if (empty($time)) {
488
-				return false;
489
-			}
490
-			$node->setCreationTime((int) $time);
491
-			return true;
492
-		});
493
-	}
494
-
495
-	/**
496
-	 * @param string $filePath
497
-	 * @param \Sabre\DAV\INode $node
498
-	 * @throws \Sabre\DAV\Exception\BadRequest
499
-	 */
500
-	public function sendFileIdHeader($filePath, \Sabre\DAV\INode $node = null) {
501
-		// chunked upload handling
502
-		if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
503
-			list($path, $name) = \Sabre\Uri\split($filePath);
504
-			$info = \OC_FileChunking::decodeName($name);
505
-			if (!empty($info)) {
506
-				$filePath = $path . '/' . $info['name'];
507
-			}
508
-		}
509
-
510
-		// we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder
511
-		if (!$this->server->tree->nodeExists($filePath)) {
512
-			return;
513
-		}
514
-		$node = $this->server->tree->getNodeForPath($filePath);
515
-		if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
516
-			$fileId = $node->getFileId();
517
-			if (!is_null($fileId)) {
518
-				$this->server->httpResponse->setHeader('OC-FileId', $fileId);
519
-			}
520
-		}
521
-	}
56
+    // namespace
57
+    const NS_OWNCLOUD = 'http://owncloud.org/ns';
58
+    const NS_NEXTCLOUD = 'http://nextcloud.org/ns';
59
+    const FILEID_PROPERTYNAME = '{http://owncloud.org/ns}id';
60
+    const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid';
61
+    const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
62
+    const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
63
+    const OCM_SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-cloud-mesh.org/ns}share-permissions';
64
+    const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
65
+    const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
66
+    const GETETAG_PROPERTYNAME = '{DAV:}getetag';
67
+    const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
68
+    const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id';
69
+    const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name';
70
+    const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums';
71
+    const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint';
72
+    const HAS_PREVIEW_PROPERTYNAME = '{http://nextcloud.org/ns}has-preview';
73
+    const MOUNT_TYPE_PROPERTYNAME = '{http://nextcloud.org/ns}mount-type';
74
+    const IS_ENCRYPTED_PROPERTYNAME = '{http://nextcloud.org/ns}is-encrypted';
75
+    const METADATA_ETAG_PROPERTYNAME = '{http://nextcloud.org/ns}metadata_etag';
76
+    const UPLOAD_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}upload_time';
77
+    const CREATION_TIME_PROPERTYNAME = '{http://nextcloud.org/ns}creation_time';
78
+    const SHARE_NOTE = '{http://nextcloud.org/ns}note';
79
+
80
+    /**
81
+     * Reference to main server object
82
+     *
83
+     * @var \Sabre\DAV\Server
84
+     */
85
+    private $server;
86
+
87
+    /**
88
+     * @var Tree
89
+     */
90
+    private $tree;
91
+
92
+    /**
93
+     * Whether this is public webdav.
94
+     * If true, some returned information will be stripped off.
95
+     *
96
+     * @var bool
97
+     */
98
+    private $isPublic;
99
+
100
+    /**
101
+     * @var bool
102
+     */
103
+    private $downloadAttachment;
104
+
105
+    /**
106
+     * @var IConfig
107
+     */
108
+    private $config;
109
+
110
+    /**
111
+     * @var IRequest
112
+     */
113
+    private $request;
114
+
115
+    /**
116
+     * @var IPreview
117
+     */
118
+    private $previewManager;
119
+
120
+    /**
121
+     * @param Tree $tree
122
+     * @param IConfig $config
123
+     * @param IRequest $request
124
+     * @param IPreview $previewManager
125
+     * @param bool $isPublic
126
+     * @param bool $downloadAttachment
127
+     */
128
+    public function __construct(Tree $tree,
129
+                                IConfig $config,
130
+                                IRequest $request,
131
+                                IPreview $previewManager,
132
+                                $isPublic = false,
133
+                                $downloadAttachment = true) {
134
+        $this->tree = $tree;
135
+        $this->config = $config;
136
+        $this->request = $request;
137
+        $this->isPublic = $isPublic;
138
+        $this->downloadAttachment = $downloadAttachment;
139
+        $this->previewManager = $previewManager;
140
+    }
141
+
142
+    /**
143
+     * This initializes the plugin.
144
+     *
145
+     * This function is called by \Sabre\DAV\Server, after
146
+     * addPlugin is called.
147
+     *
148
+     * This method should set up the required event subscriptions.
149
+     *
150
+     * @param \Sabre\DAV\Server $server
151
+     * @return void
152
+     */
153
+    public function initialize(\Sabre\DAV\Server $server) {
154
+        $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
155
+        $server->xml->namespaceMap[self::NS_NEXTCLOUD] = 'nc';
156
+        $server->protectedProperties[] = self::FILEID_PROPERTYNAME;
157
+        $server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME;
158
+        $server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
159
+        $server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
160
+        $server->protectedProperties[] = self::OCM_SHARE_PERMISSIONS_PROPERTYNAME;
161
+        $server->protectedProperties[] = self::SIZE_PROPERTYNAME;
162
+        $server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
163
+        $server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
164
+        $server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME;
165
+        $server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME;
166
+        $server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME;
167
+        $server->protectedProperties[] = self::HAS_PREVIEW_PROPERTYNAME;
168
+        $server->protectedProperties[] = self::MOUNT_TYPE_PROPERTYNAME;
169
+        $server->protectedProperties[] = self::IS_ENCRYPTED_PROPERTYNAME;
170
+        $server->protectedProperties[] = self::SHARE_NOTE;
171
+
172
+        // normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH
173
+        $allowedProperties = ['{DAV:}getetag'];
174
+        $server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties);
175
+
176
+        $this->server = $server;
177
+        $this->server->on('propFind', [$this, 'handleGetProperties']);
178
+        $this->server->on('propPatch', [$this, 'handleUpdateProperties']);
179
+        $this->server->on('afterBind', [$this, 'sendFileIdHeader']);
180
+        $this->server->on('afterWriteContent', [$this, 'sendFileIdHeader']);
181
+        $this->server->on('afterMethod:GET', [$this,'httpGet']);
182
+        $this->server->on('afterMethod:GET', [$this, 'handleDownloadToken']);
183
+        $this->server->on('afterResponse', function($request, ResponseInterface $response) {
184
+            $body = $response->getBody();
185
+            if (is_resource($body)) {
186
+                fclose($body);
187
+            }
188
+        });
189
+        $this->server->on('beforeMove', [$this, 'checkMove']);
190
+    }
191
+
192
+    /**
193
+     * Plugin that checks if a move can actually be performed.
194
+     *
195
+     * @param string $source source path
196
+     * @param string $destination destination path
197
+     * @throws Forbidden
198
+     * @throws NotFound
199
+     */
200
+    function checkMove($source, $destination) {
201
+        $sourceNode = $this->tree->getNodeForPath($source);
202
+        if (!$sourceNode instanceof Node) {
203
+            return;
204
+        }
205
+        list($sourceDir,) = \Sabre\Uri\split($source);
206
+        list($destinationDir,) = \Sabre\Uri\split($destination);
207
+
208
+        if ($sourceDir !== $destinationDir) {
209
+            $sourceNodeFileInfo = $sourceNode->getFileInfo();
210
+            if ($sourceNodeFileInfo === null) {
211
+                throw new NotFound($source . ' does not exist');
212
+                }
213
+
214
+            if (!$sourceNodeFileInfo->isDeletable()) {
215
+                throw new Forbidden($source . " cannot be deleted");
216
+            }
217
+        }
218
+    }
219
+
220
+    /**
221
+     * This sets a cookie to be able to recognize the start of the download
222
+     * the content must not be longer than 32 characters and must only contain
223
+     * alphanumeric characters
224
+     *
225
+     * @param RequestInterface $request
226
+     * @param ResponseInterface $response
227
+     */
228
+    function handleDownloadToken(RequestInterface $request, ResponseInterface $response) {
229
+        $queryParams = $request->getQueryParameters();
230
+
231
+        /**
232
+         * this sets a cookie to be able to recognize the start of the download
233
+         * the content must not be longer than 32 characters and must only contain
234
+         * alphanumeric characters
235
+         */
236
+        if (isset($queryParams['downloadStartSecret'])) {
237
+            $token = $queryParams['downloadStartSecret'];
238
+            if (!isset($token[32])
239
+                && preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) {
240
+                // FIXME: use $response->setHeader() instead
241
+                setcookie('ocDownloadStarted', $token, time() + 20, '/');
242
+            }
243
+        }
244
+    }
245
+
246
+    /**
247
+     * Add headers to file download
248
+     *
249
+     * @param RequestInterface $request
250
+     * @param ResponseInterface $response
251
+     */
252
+    function httpGet(RequestInterface $request, ResponseInterface $response) {
253
+        // Only handle valid files
254
+        $node = $this->tree->getNodeForPath($request->getPath());
255
+        if (!($node instanceof IFile)) return;
256
+
257
+        // adds a 'Content-Disposition: attachment' header in case no disposition
258
+        // header has been set before
259
+        if ($this->downloadAttachment &&
260
+            $response->getHeader('Content-Disposition') === null) {
261
+            $filename = $node->getName();
262
+            if ($this->request->isUserAgent(
263
+                [
264
+                    Request::USER_AGENT_IE,
265
+                    Request::USER_AGENT_ANDROID_MOBILE_CHROME,
266
+                    Request::USER_AGENT_FREEBOX,
267
+                ])) {
268
+                $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"');
269
+            } else {
270
+                $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename)
271
+                                                        . '; filename="' . rawurlencode($filename) . '"');
272
+            }
273
+        }
274
+
275
+        if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
276
+            //Add OC-Checksum header
277
+            /** @var $node File */
278
+            $checksum = $node->getChecksum();
279
+            if ($checksum !== null && $checksum !== '') {
280
+                $response->addHeader('OC-Checksum', $checksum);
281
+            }
282
+        }
283
+    }
284
+
285
+    /**
286
+     * Adds all ownCloud-specific properties
287
+     *
288
+     * @param PropFind $propFind
289
+     * @param \Sabre\DAV\INode $node
290
+     * @return void
291
+     */
292
+    public function handleGetProperties(PropFind $propFind, \Sabre\DAV\INode $node) {
293
+
294
+        $httpRequest = $this->server->httpRequest;
295
+
296
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
297
+            /**
298
+             * This was disabled, because it made dir listing throw an exception,
299
+             * so users were unable to navigate into folders where one subitem
300
+             * is blocked by the files_accesscontrol app, see:
301
+             * https://github.com/nextcloud/files_accesscontrol/issues/65
302
+             * if (!$node->getFileInfo()->isReadable()) {
303
+             *     // avoid detecting files through this means
304
+             *     throw new NotFound();
305
+             * }
306
+             */
307
+
308
+            $propFind->handle(self::FILEID_PROPERTYNAME, function() use ($node) {
309
+                return $node->getFileId();
310
+            });
311
+
312
+            $propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function() use ($node) {
313
+                return $node->getInternalFileId();
314
+            });
315
+
316
+            $propFind->handle(self::PERMISSIONS_PROPERTYNAME, function() use ($node) {
317
+                $perms = $node->getDavPermissions();
318
+                if ($this->isPublic) {
319
+                    // remove mount information
320
+                    $perms = str_replace(['S', 'M'], '', $perms);
321
+                }
322
+                return $perms;
323
+            });
324
+
325
+            $propFind->handle(self::SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
326
+                return $node->getSharePermissions(
327
+                    $httpRequest->getRawServerValue('PHP_AUTH_USER')
328
+                );
329
+            });
330
+
331
+            $propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
332
+                $ncPermissions = $node->getSharePermissions(
333
+                    $httpRequest->getRawServerValue('PHP_AUTH_USER')
334
+                );
335
+                $ocmPermissions = $this->ncPermissions2ocmPermissions($ncPermissions);
336
+                return json_encode($ocmPermissions);
337
+            });
338
+
339
+            $propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
340
+                return $node->getETag();
341
+            });
342
+
343
+            $propFind->handle(self::OWNER_ID_PROPERTYNAME, function() use ($node) {
344
+                $owner = $node->getOwner();
345
+                if (!$owner) {
346
+                    return null;
347
+                } else {
348
+                    return $owner->getUID();
349
+                }
350
+            });
351
+            $propFind->handle(self::OWNER_DISPLAY_NAME_PROPERTYNAME, function() use ($node) {
352
+                $owner = $node->getOwner();
353
+                if (!$owner) {
354
+                    return null;
355
+                } else {
356
+                    return $owner->getDisplayName();
357
+                }
358
+            });
359
+
360
+            $propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) {
361
+                return json_encode($this->previewManager->isAvailable($node->getFileInfo()));
362
+            });
363
+            $propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
364
+                return $node->getSize();
365
+            });
366
+            $propFind->handle(self::MOUNT_TYPE_PROPERTYNAME, function () use ($node) {
367
+                return $node->getFileInfo()->getMountPoint()->getMountType();
368
+            });
369
+
370
+            $propFind->handle(self::SHARE_NOTE, function() use ($node, $httpRequest) {
371
+                return $node->getNoteFromShare(
372
+                    $httpRequest->getRawServerValue('PHP_AUTH_USER')
373
+                );
374
+            });
375
+        }
376
+
377
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
378
+            $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) {
379
+                return $this->config->getSystemValue('data-fingerprint', '');
380
+            });
381
+        }
382
+
383
+        if ($node instanceof \OCA\DAV\Connector\Sabre\File) {
384
+            $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) {
385
+                /** @var $node \OCA\DAV\Connector\Sabre\File */
386
+                try {
387
+                    $directDownloadUrl = $node->getDirectDownload();
388
+                    if (isset($directDownloadUrl['url'])) {
389
+                        return $directDownloadUrl['url'];
390
+                    }
391
+                } catch (StorageNotAvailableException $e) {
392
+                    return false;
393
+                } catch (ForbiddenException $e) {
394
+                    return false;
395
+                }
396
+                return false;
397
+            });
398
+
399
+            $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) {
400
+                $checksum = $node->getChecksum();
401
+                if ($checksum === null || $checksum === '') {
402
+                    return null;
403
+                }
404
+
405
+                return new ChecksumList($checksum);
406
+            });
407
+
408
+            $propFind->handle(self::CREATION_TIME_PROPERTYNAME, function() use ($node) {
409
+                return $node->getFileInfo()->getCreationTime();
410
+            });
411
+
412
+            $propFind->handle(self::UPLOAD_TIME_PROPERTYNAME, function() use ($node) {
413
+                return $node->getFileInfo()->getUploadTime();
414
+            });
415
+
416
+        }
417
+
418
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Directory) {
419
+            $propFind->handle(self::SIZE_PROPERTYNAME, function() use ($node) {
420
+                return $node->getSize();
421
+            });
422
+
423
+            $propFind->handle(self::IS_ENCRYPTED_PROPERTYNAME, function() use ($node) {
424
+                return $node->getFileInfo()->isEncrypted() ? '1' : '0';
425
+            });
426
+        }
427
+    }
428
+
429
+    /**
430
+     * translate Nextcloud permissions to OCM Permissions
431
+     *
432
+     * @param $ncPermissions
433
+     * @return array
434
+     */
435
+    protected function ncPermissions2ocmPermissions($ncPermissions) {
436
+
437
+        $ocmPermissions = [];
438
+
439
+        if ($ncPermissions & Constants::PERMISSION_SHARE) {
440
+            $ocmPermissions[] = 'share';
441
+        }
442
+
443
+        if ($ncPermissions & Constants::PERMISSION_READ) {
444
+            $ocmPermissions[] = 'read';
445
+        }
446
+
447
+        if (($ncPermissions & Constants::PERMISSION_CREATE) ||
448
+            ($ncPermissions & Constants::PERMISSION_UPDATE)) {
449
+            $ocmPermissions[] = 'write';
450
+        }
451
+
452
+        return $ocmPermissions;
453
+
454
+    }
455
+
456
+    /**
457
+     * Update ownCloud-specific properties
458
+     *
459
+     * @param string $path
460
+     * @param PropPatch $propPatch
461
+     *
462
+     * @return void
463
+     */
464
+    public function handleUpdateProperties($path, PropPatch $propPatch) {
465
+        $node = $this->tree->getNodeForPath($path);
466
+        if (!($node instanceof \OCA\DAV\Connector\Sabre\Node)) {
467
+            return;
468
+        }
469
+
470
+        $propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($node) {
471
+            if (empty($time)) {
472
+                return false;
473
+            }
474
+            $node->touch($time);
475
+            return true;
476
+        });
477
+        $propPatch->handle(self::GETETAG_PROPERTYNAME, function($etag) use ($node) {
478
+            if (empty($etag)) {
479
+                return false;
480
+            }
481
+            if ($node->setEtag($etag) !== -1) {
482
+                return true;
483
+            }
484
+            return false;
485
+        });
486
+        $propPatch->handle(self::CREATION_TIME_PROPERTYNAME, function($time) use ($node) {
487
+            if (empty($time)) {
488
+                return false;
489
+            }
490
+            $node->setCreationTime((int) $time);
491
+            return true;
492
+        });
493
+    }
494
+
495
+    /**
496
+     * @param string $filePath
497
+     * @param \Sabre\DAV\INode $node
498
+     * @throws \Sabre\DAV\Exception\BadRequest
499
+     */
500
+    public function sendFileIdHeader($filePath, \Sabre\DAV\INode $node = null) {
501
+        // chunked upload handling
502
+        if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
503
+            list($path, $name) = \Sabre\Uri\split($filePath);
504
+            $info = \OC_FileChunking::decodeName($name);
505
+            if (!empty($info)) {
506
+                $filePath = $path . '/' . $info['name'];
507
+            }
508
+        }
509
+
510
+        // we get the node for the given $filePath here because in case of afterCreateFile $node is the parent folder
511
+        if (!$this->server->tree->nodeExists($filePath)) {
512
+            return;
513
+        }
514
+        $node = $this->server->tree->getNodeForPath($filePath);
515
+        if ($node instanceof \OCA\DAV\Connector\Sabre\Node) {
516
+            $fileId = $node->getFileId();
517
+            if (!is_null($fileId)) {
518
+                $this->server->httpResponse->setHeader('OC-FileId', $fileId);
519
+            }
520
+        }
521
+    }
522 522
 }
Please login to merge, or discard this patch.
apps/files_sharing/lib/Controller/ShareController.php 1 patch
Indentation   +662 added lines, -662 removed lines patch added patch discarded remove patch
@@ -83,670 +83,670 @@
 block discarded – undo
83 83
  */
84 84
 class ShareController extends AuthPublicShareController {
85 85
 
86
-	/** @var IConfig */
87
-	protected $config;
88
-	/** @var IUserManager */
89
-	protected $userManager;
90
-	/** @var ILogger */
91
-	protected $logger;
92
-	/** @var \OCP\Activity\IManager */
93
-	protected $activityManager;
94
-	/** @var IPreview */
95
-	protected $previewManager;
96
-	/** @var IRootFolder */
97
-	protected $rootFolder;
98
-	/** @var FederatedShareProvider */
99
-	protected $federatedShareProvider;
100
-	/** @var IAccountManager */
101
-	protected $accountManager;
102
-	/** @var EventDispatcherInterface */
103
-	protected $eventDispatcher;
104
-	/** @var IL10N */
105
-	protected $l10n;
106
-	/** @var Defaults */
107
-	protected $defaults;
108
-	/** @var ShareManager */
109
-	protected $shareManager;
110
-
111
-	/** @var Share\IShare */
112
-	protected $share;
113
-
114
-	/**
115
-	 * @param string $appName
116
-	 * @param IRequest $request
117
-	 * @param IConfig $config
118
-	 * @param IURLGenerator $urlGenerator
119
-	 * @param IUserManager $userManager
120
-	 * @param ILogger $logger
121
-	 * @param \OCP\Activity\IManager $activityManager
122
-	 * @param \OCP\Share\IManager $shareManager
123
-	 * @param ISession $session
124
-	 * @param IPreview $previewManager
125
-	 * @param IRootFolder $rootFolder
126
-	 * @param FederatedShareProvider $federatedShareProvider
127
-	 * @param IAccountManager $accountManager
128
-	 * @param EventDispatcherInterface $eventDispatcher
129
-	 * @param IL10N $l10n
130
-	 * @param Defaults $defaults
131
-	 */
132
-	public function __construct(string $appName,
133
-								IRequest $request,
134
-								IConfig $config,
135
-								IURLGenerator $urlGenerator,
136
-								IUserManager $userManager,
137
-								ILogger $logger,
138
-								\OCP\Activity\IManager $activityManager,
139
-								ShareManager $shareManager,
140
-								ISession $session,
141
-								IPreview $previewManager,
142
-								IRootFolder $rootFolder,
143
-								FederatedShareProvider $federatedShareProvider,
144
-								IAccountManager $accountManager,
145
-								EventDispatcherInterface $eventDispatcher,
146
-								IL10N $l10n,
147
-								Defaults $defaults) {
148
-		parent::__construct($appName, $request, $session, $urlGenerator);
149
-
150
-		$this->config = $config;
151
-		$this->userManager = $userManager;
152
-		$this->logger = $logger;
153
-		$this->activityManager = $activityManager;
154
-		$this->previewManager = $previewManager;
155
-		$this->rootFolder = $rootFolder;
156
-		$this->federatedShareProvider = $federatedShareProvider;
157
-		$this->accountManager = $accountManager;
158
-		$this->eventDispatcher = $eventDispatcher;
159
-		$this->l10n = $l10n;
160
-		$this->defaults = $defaults;
161
-		$this->shareManager = $shareManager;
162
-	}
163
-
164
-	/**
165
-	 * @PublicPage
166
-	 * @NoCSRFRequired
167
-	 *
168
-	 * Show the authentication page
169
-	 * The form has to submit to the authenticate method route
170
-	 */
171
-	public function showAuthenticate(): TemplateResponse {
172
-		$templateParameters = ['share' => $this->share];
173
-
174
-		$event = new GenericEvent(null, $templateParameters);
175
-		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
176
-
177
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
178
-		if ($this->share->getSendPasswordByTalk()) {
179
-			$csp = new ContentSecurityPolicy();
180
-			$csp->addAllowedConnectDomain('*');
181
-			$csp->addAllowedMediaDomain('blob:');
182
-			$response->setContentSecurityPolicy($csp);
183
-		}
184
-
185
-		return $response;
186
-	}
187
-
188
-	/**
189
-	 * The template to show when authentication failed
190
-	 */
191
-	protected function showAuthFailed(): TemplateResponse {
192
-		$templateParameters = ['share' => $this->share, 'wrongpw' => true];
193
-
194
-		$event = new GenericEvent(null, $templateParameters);
195
-		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
196
-
197
-		$response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
198
-		if ($this->share->getSendPasswordByTalk()) {
199
-			$csp = new ContentSecurityPolicy();
200
-			$csp->addAllowedConnectDomain('*');
201
-			$csp->addAllowedMediaDomain('blob:');
202
-			$response->setContentSecurityPolicy($csp);
203
-		}
204
-
205
-		return $response;
206
-	}
207
-
208
-	protected function verifyPassword(string $password): bool {
209
-		return $this->shareManager->checkPassword($this->share, $password);
210
-	}
211
-
212
-	protected function getPasswordHash(): string {
213
-		return $this->share->getPassword();
214
-	}
215
-
216
-	public function isValidToken(): bool {
217
-		try {
218
-			$this->share = $this->shareManager->getShareByToken($this->getToken());
219
-		} catch (ShareNotFound $e) {
220
-			return false;
221
-		}
222
-
223
-		return true;
224
-	}
225
-
226
-	protected function isPasswordProtected(): bool {
227
-		return $this->share->getPassword() !== null;
228
-	}
229
-
230
-	protected function authSucceeded() {
231
-		// For share this was always set so it is still used in other apps
232
-		$this->session->set('public_link_authenticated', (string)$this->share->getId());
233
-	}
234
-
235
-	protected function authFailed() {
236
-		$this->emitAccessShareHook($this->share, 403, 'Wrong password');
237
-	}
238
-
239
-	/**
240
-	 * throws hooks when a share is attempted to be accessed
241
-	 *
242
-	 * @param \OCP\Share\IShare|string $share the Share instance if available,
243
-	 * otherwise token
244
-	 * @param int $errorCode
245
-	 * @param string $errorMessage
246
-	 * @throws \OC\HintException
247
-	 * @throws \OC\ServerNotAvailableException
248
-	 */
249
-	protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
250
-		$itemType = $itemSource = $uidOwner = '';
251
-		$token = $share;
252
-		$exception = null;
253
-		if($share instanceof \OCP\Share\IShare) {
254
-			try {
255
-				$token = $share->getToken();
256
-				$uidOwner = $share->getSharedBy();
257
-				$itemType = $share->getNodeType();
258
-				$itemSource = $share->getNodeId();
259
-			} catch (\Exception $e) {
260
-				// we log what we know and pass on the exception afterwards
261
-				$exception = $e;
262
-			}
263
-		}
264
-		\OC_Hook::emit(Share::class, 'share_link_access', [
265
-			'itemType' => $itemType,
266
-			'itemSource' => $itemSource,
267
-			'uidOwner' => $uidOwner,
268
-			'token' => $token,
269
-			'errorCode' => $errorCode,
270
-			'errorMessage' => $errorMessage,
271
-		]);
272
-		if(!is_null($exception)) {
273
-			throw $exception;
274
-		}
275
-	}
276
-
277
-	/**
278
-	 * Validate the permissions of the share
279
-	 *
280
-	 * @param Share\IShare $share
281
-	 * @return bool
282
-	 */
283
-	private function validateShare(\OCP\Share\IShare $share) {
284
-		// If the owner is disabled no access to the linke is granted
285
-		$owner = $this->userManager->get($share->getShareOwner());
286
-		if ($owner === null || !$owner->isEnabled()) {
287
-			return false;
288
-		}
289
-
290
-		// If the initiator of the share is disabled no access is granted
291
-		$initiator = $this->userManager->get($share->getSharedBy());
292
-		if ($initiator === null || !$initiator->isEnabled()) {
293
-			return false;
294
-		}
295
-
296
-		return $share->getNode()->isReadable() && $share->getNode()->isShareable();
297
-	}
298
-
299
-	/**
300
-	 * @PublicPage
301
-	 * @NoCSRFRequired
302
-	 *
303
-	 *
304
-	 * @param string $path
305
-	 * @return TemplateResponse
306
-	 * @throws NotFoundException
307
-	 * @throws \Exception
308
-	 */
309
-	public function showShare($path = ''): TemplateResponse {
310
-		\OC_User::setIncognitoMode(true);
311
-
312
-		// Check whether share exists
313
-		try {
314
-			$share = $this->shareManager->getShareByToken($this->getToken());
315
-		} catch (ShareNotFound $e) {
316
-			$this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
317
-			throw new NotFoundException();
318
-		}
319
-
320
-		if (!$this->validateShare($share)) {
321
-			throw new NotFoundException();
322
-		}
323
-
324
-		$shareNode = $share->getNode();
325
-
326
-		// We can't get the path of a file share
327
-		try {
328
-			if ($shareNode instanceof \OCP\Files\File && $path !== '') {
329
-				$this->emitAccessShareHook($share, 404, 'Share not found');
330
-				throw new NotFoundException();
331
-			}
332
-		} catch (\Exception $e) {
333
-			$this->emitAccessShareHook($share, 404, 'Share not found');
334
-			throw $e;
335
-		}
336
-
337
-		$shareTmpl = [];
338
-		$shareTmpl['owner'] = '';
339
-		$shareTmpl['shareOwner'] = '';
340
-
341
-		$owner = $this->userManager->get($share->getShareOwner());
342
-		if ($owner instanceof IUser) {
343
-			$ownerAccount = $this->accountManager->getAccount($owner);
344
-
345
-			$ownerName = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
346
-			if ($ownerName->getScope() === IAccountManager::VISIBILITY_PUBLIC) {
347
-				$shareTmpl['owner'] = $owner->getUID();
348
-				$shareTmpl['shareOwner'] = $owner->getDisplayName();
349
-			}
350
-		}
351
-
352
-		$shareTmpl['filename'] = $shareNode->getName();
353
-		$shareTmpl['directory_path'] = $share->getTarget();
354
-		$shareTmpl['note'] = $share->getNote();
355
-		$shareTmpl['mimetype'] = $shareNode->getMimetype();
356
-		$shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype());
357
-		$shareTmpl['dirToken'] = $this->getToken();
358
-		$shareTmpl['sharingToken'] = $this->getToken();
359
-		$shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
360
-		$shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
361
-		$shareTmpl['dir'] = '';
362
-		$shareTmpl['nonHumanFileSize'] = $shareNode->getSize();
363
-		$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($shareNode->getSize());
364
-		$shareTmpl['hideDownload'] = $share->getHideDownload();
365
-
366
-		$hideFileList = false;
367
-
368
-		if ($shareNode instanceof \OCP\Files\Folder) {
369
-
370
-			$shareIsFolder = true;
371
-
372
-			try {
373
-				$folderNode = $shareNode->get($path);
374
-			} catch (\OCP\Files\NotFoundException $e) {
375
-				$this->emitAccessShareHook($share, 404, 'Share not found');
376
-				throw new NotFoundException();
377
-			}
378
-
379
-			$shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath());
380
-
381
-			/*
86
+    /** @var IConfig */
87
+    protected $config;
88
+    /** @var IUserManager */
89
+    protected $userManager;
90
+    /** @var ILogger */
91
+    protected $logger;
92
+    /** @var \OCP\Activity\IManager */
93
+    protected $activityManager;
94
+    /** @var IPreview */
95
+    protected $previewManager;
96
+    /** @var IRootFolder */
97
+    protected $rootFolder;
98
+    /** @var FederatedShareProvider */
99
+    protected $federatedShareProvider;
100
+    /** @var IAccountManager */
101
+    protected $accountManager;
102
+    /** @var EventDispatcherInterface */
103
+    protected $eventDispatcher;
104
+    /** @var IL10N */
105
+    protected $l10n;
106
+    /** @var Defaults */
107
+    protected $defaults;
108
+    /** @var ShareManager */
109
+    protected $shareManager;
110
+
111
+    /** @var Share\IShare */
112
+    protected $share;
113
+
114
+    /**
115
+     * @param string $appName
116
+     * @param IRequest $request
117
+     * @param IConfig $config
118
+     * @param IURLGenerator $urlGenerator
119
+     * @param IUserManager $userManager
120
+     * @param ILogger $logger
121
+     * @param \OCP\Activity\IManager $activityManager
122
+     * @param \OCP\Share\IManager $shareManager
123
+     * @param ISession $session
124
+     * @param IPreview $previewManager
125
+     * @param IRootFolder $rootFolder
126
+     * @param FederatedShareProvider $federatedShareProvider
127
+     * @param IAccountManager $accountManager
128
+     * @param EventDispatcherInterface $eventDispatcher
129
+     * @param IL10N $l10n
130
+     * @param Defaults $defaults
131
+     */
132
+    public function __construct(string $appName,
133
+                                IRequest $request,
134
+                                IConfig $config,
135
+                                IURLGenerator $urlGenerator,
136
+                                IUserManager $userManager,
137
+                                ILogger $logger,
138
+                                \OCP\Activity\IManager $activityManager,
139
+                                ShareManager $shareManager,
140
+                                ISession $session,
141
+                                IPreview $previewManager,
142
+                                IRootFolder $rootFolder,
143
+                                FederatedShareProvider $federatedShareProvider,
144
+                                IAccountManager $accountManager,
145
+                                EventDispatcherInterface $eventDispatcher,
146
+                                IL10N $l10n,
147
+                                Defaults $defaults) {
148
+        parent::__construct($appName, $request, $session, $urlGenerator);
149
+
150
+        $this->config = $config;
151
+        $this->userManager = $userManager;
152
+        $this->logger = $logger;
153
+        $this->activityManager = $activityManager;
154
+        $this->previewManager = $previewManager;
155
+        $this->rootFolder = $rootFolder;
156
+        $this->federatedShareProvider = $federatedShareProvider;
157
+        $this->accountManager = $accountManager;
158
+        $this->eventDispatcher = $eventDispatcher;
159
+        $this->l10n = $l10n;
160
+        $this->defaults = $defaults;
161
+        $this->shareManager = $shareManager;
162
+    }
163
+
164
+    /**
165
+     * @PublicPage
166
+     * @NoCSRFRequired
167
+     *
168
+     * Show the authentication page
169
+     * The form has to submit to the authenticate method route
170
+     */
171
+    public function showAuthenticate(): TemplateResponse {
172
+        $templateParameters = ['share' => $this->share];
173
+
174
+        $event = new GenericEvent(null, $templateParameters);
175
+        $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
176
+
177
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
178
+        if ($this->share->getSendPasswordByTalk()) {
179
+            $csp = new ContentSecurityPolicy();
180
+            $csp->addAllowedConnectDomain('*');
181
+            $csp->addAllowedMediaDomain('blob:');
182
+            $response->setContentSecurityPolicy($csp);
183
+        }
184
+
185
+        return $response;
186
+    }
187
+
188
+    /**
189
+     * The template to show when authentication failed
190
+     */
191
+    protected function showAuthFailed(): TemplateResponse {
192
+        $templateParameters = ['share' => $this->share, 'wrongpw' => true];
193
+
194
+        $event = new GenericEvent(null, $templateParameters);
195
+        $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts::publicShareAuth', $event);
196
+
197
+        $response = new TemplateResponse('core', 'publicshareauth', $templateParameters, 'guest');
198
+        if ($this->share->getSendPasswordByTalk()) {
199
+            $csp = new ContentSecurityPolicy();
200
+            $csp->addAllowedConnectDomain('*');
201
+            $csp->addAllowedMediaDomain('blob:');
202
+            $response->setContentSecurityPolicy($csp);
203
+        }
204
+
205
+        return $response;
206
+    }
207
+
208
+    protected function verifyPassword(string $password): bool {
209
+        return $this->shareManager->checkPassword($this->share, $password);
210
+    }
211
+
212
+    protected function getPasswordHash(): string {
213
+        return $this->share->getPassword();
214
+    }
215
+
216
+    public function isValidToken(): bool {
217
+        try {
218
+            $this->share = $this->shareManager->getShareByToken($this->getToken());
219
+        } catch (ShareNotFound $e) {
220
+            return false;
221
+        }
222
+
223
+        return true;
224
+    }
225
+
226
+    protected function isPasswordProtected(): bool {
227
+        return $this->share->getPassword() !== null;
228
+    }
229
+
230
+    protected function authSucceeded() {
231
+        // For share this was always set so it is still used in other apps
232
+        $this->session->set('public_link_authenticated', (string)$this->share->getId());
233
+    }
234
+
235
+    protected function authFailed() {
236
+        $this->emitAccessShareHook($this->share, 403, 'Wrong password');
237
+    }
238
+
239
+    /**
240
+     * throws hooks when a share is attempted to be accessed
241
+     *
242
+     * @param \OCP\Share\IShare|string $share the Share instance if available,
243
+     * otherwise token
244
+     * @param int $errorCode
245
+     * @param string $errorMessage
246
+     * @throws \OC\HintException
247
+     * @throws \OC\ServerNotAvailableException
248
+     */
249
+    protected function emitAccessShareHook($share, $errorCode = 200, $errorMessage = '') {
250
+        $itemType = $itemSource = $uidOwner = '';
251
+        $token = $share;
252
+        $exception = null;
253
+        if($share instanceof \OCP\Share\IShare) {
254
+            try {
255
+                $token = $share->getToken();
256
+                $uidOwner = $share->getSharedBy();
257
+                $itemType = $share->getNodeType();
258
+                $itemSource = $share->getNodeId();
259
+            } catch (\Exception $e) {
260
+                // we log what we know and pass on the exception afterwards
261
+                $exception = $e;
262
+            }
263
+        }
264
+        \OC_Hook::emit(Share::class, 'share_link_access', [
265
+            'itemType' => $itemType,
266
+            'itemSource' => $itemSource,
267
+            'uidOwner' => $uidOwner,
268
+            'token' => $token,
269
+            'errorCode' => $errorCode,
270
+            'errorMessage' => $errorMessage,
271
+        ]);
272
+        if(!is_null($exception)) {
273
+            throw $exception;
274
+        }
275
+    }
276
+
277
+    /**
278
+     * Validate the permissions of the share
279
+     *
280
+     * @param Share\IShare $share
281
+     * @return bool
282
+     */
283
+    private function validateShare(\OCP\Share\IShare $share) {
284
+        // If the owner is disabled no access to the linke is granted
285
+        $owner = $this->userManager->get($share->getShareOwner());
286
+        if ($owner === null || !$owner->isEnabled()) {
287
+            return false;
288
+        }
289
+
290
+        // If the initiator of the share is disabled no access is granted
291
+        $initiator = $this->userManager->get($share->getSharedBy());
292
+        if ($initiator === null || !$initiator->isEnabled()) {
293
+            return false;
294
+        }
295
+
296
+        return $share->getNode()->isReadable() && $share->getNode()->isShareable();
297
+    }
298
+
299
+    /**
300
+     * @PublicPage
301
+     * @NoCSRFRequired
302
+     *
303
+     *
304
+     * @param string $path
305
+     * @return TemplateResponse
306
+     * @throws NotFoundException
307
+     * @throws \Exception
308
+     */
309
+    public function showShare($path = ''): TemplateResponse {
310
+        \OC_User::setIncognitoMode(true);
311
+
312
+        // Check whether share exists
313
+        try {
314
+            $share = $this->shareManager->getShareByToken($this->getToken());
315
+        } catch (ShareNotFound $e) {
316
+            $this->emitAccessShareHook($this->getToken(), 404, 'Share not found');
317
+            throw new NotFoundException();
318
+        }
319
+
320
+        if (!$this->validateShare($share)) {
321
+            throw new NotFoundException();
322
+        }
323
+
324
+        $shareNode = $share->getNode();
325
+
326
+        // We can't get the path of a file share
327
+        try {
328
+            if ($shareNode instanceof \OCP\Files\File && $path !== '') {
329
+                $this->emitAccessShareHook($share, 404, 'Share not found');
330
+                throw new NotFoundException();
331
+            }
332
+        } catch (\Exception $e) {
333
+            $this->emitAccessShareHook($share, 404, 'Share not found');
334
+            throw $e;
335
+        }
336
+
337
+        $shareTmpl = [];
338
+        $shareTmpl['owner'] = '';
339
+        $shareTmpl['shareOwner'] = '';
340
+
341
+        $owner = $this->userManager->get($share->getShareOwner());
342
+        if ($owner instanceof IUser) {
343
+            $ownerAccount = $this->accountManager->getAccount($owner);
344
+
345
+            $ownerName = $ownerAccount->getProperty(IAccountManager::PROPERTY_DISPLAYNAME);
346
+            if ($ownerName->getScope() === IAccountManager::VISIBILITY_PUBLIC) {
347
+                $shareTmpl['owner'] = $owner->getUID();
348
+                $shareTmpl['shareOwner'] = $owner->getDisplayName();
349
+            }
350
+        }
351
+
352
+        $shareTmpl['filename'] = $shareNode->getName();
353
+        $shareTmpl['directory_path'] = $share->getTarget();
354
+        $shareTmpl['note'] = $share->getNote();
355
+        $shareTmpl['mimetype'] = $shareNode->getMimetype();
356
+        $shareTmpl['previewSupported'] = $this->previewManager->isMimeSupported($shareNode->getMimetype());
357
+        $shareTmpl['dirToken'] = $this->getToken();
358
+        $shareTmpl['sharingToken'] = $this->getToken();
359
+        $shareTmpl['server2serversharing'] = $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
360
+        $shareTmpl['protected'] = $share->getPassword() !== null ? 'true' : 'false';
361
+        $shareTmpl['dir'] = '';
362
+        $shareTmpl['nonHumanFileSize'] = $shareNode->getSize();
363
+        $shareTmpl['fileSize'] = \OCP\Util::humanFileSize($shareNode->getSize());
364
+        $shareTmpl['hideDownload'] = $share->getHideDownload();
365
+
366
+        $hideFileList = false;
367
+
368
+        if ($shareNode instanceof \OCP\Files\Folder) {
369
+
370
+            $shareIsFolder = true;
371
+
372
+            try {
373
+                $folderNode = $shareNode->get($path);
374
+            } catch (\OCP\Files\NotFoundException $e) {
375
+                $this->emitAccessShareHook($share, 404, 'Share not found');
376
+                throw new NotFoundException();
377
+            }
378
+
379
+            $shareTmpl['dir'] = $shareNode->getRelativePath($folderNode->getPath());
380
+
381
+            /*
382 382
 			 * The OC_Util methods require a view. This just uses the node API
383 383
 			 */
384
-			$freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
385
-			if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
386
-				$freeSpace = max($freeSpace, 0);
387
-			} else {
388
-				$freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
389
-			}
390
-
391
-			$hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
392
-			$maxUploadFilesize = $freeSpace;
393
-
394
-			$folder = new Template('files', 'list', '');
395
-
396
-			$folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath()));
397
-			$folder->assign('dirToken', $this->getToken());
398
-			$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
399
-			$folder->assign('isPublic', true);
400
-			$folder->assign('hideFileList', $hideFileList);
401
-			$folder->assign('publicUploadEnabled', 'no');
402
-			// default to list view
403
-			$folder->assign('showgridview', false);
404
-			$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
405
-			$folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
406
-			$folder->assign('freeSpace', $freeSpace);
407
-			$folder->assign('usedSpacePercent', 0);
408
-			$folder->assign('trash', false);
409
-			$shareTmpl['folder'] = $folder->fetchPage();
410
-		} else {
411
-			$shareIsFolder = false;
412
-		}
413
-
414
-		// default to list view
415
-		$shareTmpl['showgridview'] = false;
416
-
417
-		$shareTmpl['hideFileList'] = $hideFileList;
418
-		$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $this->getToken()]);
419
-		$shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
420
-		$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
421
-		$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
422
-		$shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
423
-		$shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
424
-		$shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
425
-		$shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
426
-
427
-		if ($shareTmpl['previewSupported']) {
428
-			$shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
429
-				['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
430
-			$ogPreview = $shareTmpl['previewImage'];
431
-
432
-			// We just have direct previews for image files
433
-			if ($shareNode->getMimePart() === 'image') {
434
-				$shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
435
-
436
-				$ogPreview = $shareTmpl['previewURL'];
437
-
438
-				//Whatapp is kind of picky about their size requirements
439
-				if ($this->request->isUserAgent(['/^WhatsApp/'])) {
440
-					$ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
441
-						'token' => $this->getToken(),
442
-						'x' => 256,
443
-						'y' => 256,
444
-						'a' => true,
445
-					]);
446
-				}
447
-			}
448
-		} else {
449
-			$shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
450
-			$ogPreview = $shareTmpl['previewImage'];
451
-		}
452
-
453
-		// Load files we need
454
-		\OCP\Util::addScript('files', 'semaphore');
455
-		\OCP\Util::addScript('files', 'file-upload');
456
-		\OCP\Util::addStyle('files_sharing', 'publicView');
457
-		\OCP\Util::addScript('files_sharing', 'public');
458
-		\OCP\Util::addScript('files_sharing', 'templates');
459
-		\OCP\Util::addScript('files', 'fileactions');
460
-		\OCP\Util::addScript('files', 'fileactionsmenu');
461
-		\OCP\Util::addScript('files', 'jquery.fileupload');
462
-		\OCP\Util::addScript('files_sharing', 'files_drop');
463
-
464
-		if (isset($shareTmpl['folder'])) {
465
-			// JS required for folders
466
-			\OCP\Util::addStyle('files', 'merged');
467
-			\OCP\Util::addScript('files', 'filesummary');
468
-			\OCP\Util::addScript('files', 'templates');
469
-			\OCP\Util::addScript('files', 'breadcrumb');
470
-			\OCP\Util::addScript('files', 'fileinfomodel');
471
-			\OCP\Util::addScript('files', 'newfilemenu');
472
-			\OCP\Util::addScript('files', 'files');
473
-			\OCP\Util::addScript('files', 'filemultiselectmenu');
474
-			\OCP\Util::addScript('files', 'filelist');
475
-			\OCP\Util::addScript('files', 'keyboardshortcuts');
476
-			\OCP\Util::addScript('files', 'operationprogressbar');
477
-
478
-			// Load Viewer scripts
479
-			if (class_exists(LoadViewer::class)) {
480
-				$this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer());
481
-			}
482
-		}
483
-
484
-		// OpenGraph Support: http://ogp.me/
485
-		\OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
486
-		\OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
487
-		\OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
488
-		\OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
489
-		\OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
490
-		\OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
491
-
492
-		$event = new GenericEvent(null, ['share' => $share]);
493
-		$this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts', $event);
494
-
495
-		$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
496
-		$csp->addAllowedFrameDomain('\'self\'');
497
-
498
-		$response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
499
-		$response->setHeaderTitle($shareTmpl['filename']);
500
-		if ($shareTmpl['shareOwner'] !== '') {
501
-			$response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['shareOwner']]));
502
-		}
503
-
504
-		$isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== \OCP\Constants::PERMISSION_CREATE;
505
-
506
-		if ($isNoneFileDropFolder && !$share->getHideDownload()) {
507
-			\OCP\Util::addScript('files_sharing', 'public_note');
508
-
509
-			$downloadWhite = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
510
-			$downloadAllWhite = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
511
-			$download = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
512
-			$downloadAll = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
513
-			$directLink = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']);
514
-			$externalShare = new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['shareOwner'], $shareTmpl['filename']);
515
-
516
-			$responseComposer = [];
517
-
518
-			if ($shareIsFolder) {
519
-				$responseComposer[] = $downloadAllWhite;
520
-				$responseComposer[] = $downloadAll;
521
-			} else {
522
-				$responseComposer[] = $downloadWhite;
523
-				$responseComposer[] = $download;
524
-			}
525
-			$responseComposer[] = $directLink;
526
-			if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
527
-				$responseComposer[] = $externalShare;
528
-			}
529
-
530
-			$response->setHeaderActions($responseComposer);
531
-		}
532
-
533
-		$response->setContentSecurityPolicy($csp);
534
-
535
-		$this->emitAccessShareHook($share);
536
-
537
-		return $response;
538
-	}
539
-
540
-	/**
541
-	 * @PublicPage
542
-	 * @NoCSRFRequired
543
-	 *
544
-	 * @param string $token
545
-	 * @param string $files
546
-	 * @param string $path
547
-	 * @param string $downloadStartSecret
548
-	 * @return void|\OCP\AppFramework\Http\Response
549
-	 * @throws NotFoundException
550
-	 */
551
-	public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
552
-		\OC_User::setIncognitoMode(true);
553
-
554
-		$share = $this->shareManager->getShareByToken($token);
555
-
556
-		if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
557
-			return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
558
-		}
559
-
560
-		$files_list = null;
561
-		if (!is_null($files)) { // download selected files
562
-			$files_list = json_decode($files);
563
-			// in case we get only a single file
564
-			if ($files_list === null) {
565
-				$files_list = [$files];
566
-			}
567
-			// Just in case $files is a single int like '1234'
568
-			if (!is_array($files_list)) {
569
-				$files_list = [$files_list];
570
-			}
571
-		}
572
-
573
-		if (!$this->validateShare($share)) {
574
-			throw new NotFoundException();
575
-		}
576
-
577
-		$userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
578
-		$originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
579
-
580
-
581
-		// Single file share
582
-		if ($share->getNode() instanceof \OCP\Files\File) {
583
-			// Single file download
584
-			$this->singleFileDownloaded($share, $share->getNode());
585
-		}
586
-		// Directory share
587
-		else {
588
-			/** @var \OCP\Files\Folder $node */
589
-			$node = $share->getNode();
590
-
591
-			// Try to get the path
592
-			if ($path !== '') {
593
-				try {
594
-					$node = $node->get($path);
595
-				} catch (NotFoundException $e) {
596
-					$this->emitAccessShareHook($share, 404, 'Share not found');
597
-					return new NotFoundResponse();
598
-				}
599
-			}
600
-
601
-			$originalSharePath = $userFolder->getRelativePath($node->getPath());
602
-
603
-			if ($node instanceof \OCP\Files\File) {
604
-				// Single file download
605
-				$this->singleFileDownloaded($share, $share->getNode());
606
-			} else {
607
-				try {
608
-					if (!empty($files_list)) {
609
-						$this->fileListDownloaded($share, $files_list, $node);
610
-					} else {
611
-						// The folder is downloaded
612
-						$this->singleFileDownloaded($share, $share->getNode());
613
-					}
614
-				} catch (NotFoundException $e) {
615
-					return new NotFoundResponse();
616
-				}
617
-			}
618
-		}
619
-
620
-		/* FIXME: We should do this all nicely in OCP */
621
-		OC_Util::tearDownFS();
622
-		OC_Util::setupFS($share->getShareOwner());
623
-
624
-		/**
625
-		 * this sets a cookie to be able to recognize the start of the download
626
-		 * the content must not be longer than 32 characters and must only contain
627
-		 * alphanumeric characters
628
-		 */
629
-		if (!empty($downloadStartSecret)
630
-			&& !isset($downloadStartSecret[32])
631
-			&& preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
632
-
633
-			// FIXME: set on the response once we use an actual app framework response
634
-			setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
635
-		}
636
-
637
-		$this->emitAccessShareHook($share);
638
-
639
-		$server_params = [ 'head' => $this->request->getMethod() === 'HEAD' ];
640
-
641
-		/**
642
-		 * Http range requests support
643
-		 */
644
-		if (isset($_SERVER['HTTP_RANGE'])) {
645
-			$server_params['range'] = $this->request->getHeader('Range');
646
-		}
647
-
648
-		// download selected files
649
-		if (!is_null($files) && $files !== '') {
650
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
651
-			// after dispatching the request which results in a "Cannot modify header information" notice.
652
-			OC_Files::get($originalSharePath, $files_list, $server_params);
653
-			exit();
654
-		} else {
655
-			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
656
-			// after dispatching the request which results in a "Cannot modify header information" notice.
657
-			OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
658
-			exit();
659
-		}
660
-	}
661
-
662
-	/**
663
-	 * create activity for every downloaded file
664
-	 *
665
-	 * @param Share\IShare $share
666
-	 * @param array $files_list
667
-	 * @param \OCP\Files\Folder $node
668
-	 * @throws NotFoundException when trying to download a folder or multiple files of a "hide download" share
669
-	 */
670
-	protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
671
-		if ($share->getHideDownload() && count($files_list) > 1) {
672
-			throw new NotFoundException('Downloading more than 1 file');
673
-		}
674
-
675
-		foreach ($files_list as $file) {
676
-			$subNode = $node->get($file);
677
-			$this->singleFileDownloaded($share, $subNode);
678
-		}
679
-
680
-	}
681
-
682
-	/**
683
-	 * create activity if a single file was downloaded from a link share
684
-	 *
685
-	 * @param Share\IShare $share
686
-	 * @throws NotFoundException when trying to download a folder of a "hide download" share
687
-	 */
688
-	protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
689
-		if ($share->getHideDownload() && $node instanceof Folder) {
690
-			throw new NotFoundException('Downloading a folder');
691
-		}
692
-
693
-		$fileId = $node->getId();
694
-
695
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
696
-		$userNodeList = $userFolder->getById($fileId);
697
-		$userNode = $userNodeList[0];
698
-		$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
699
-		$userPath = $userFolder->getRelativePath($userNode->getPath());
700
-		$ownerPath = $ownerFolder->getRelativePath($node->getPath());
701
-
702
-		$parameters = [$userPath];
703
-
704
-		if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
705
-			if ($node instanceof \OCP\Files\File) {
706
-				$subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
707
-			} else {
708
-				$subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
709
-			}
710
-			$parameters[] = $share->getSharedWith();
711
-		} else {
712
-			if ($node instanceof \OCP\Files\File) {
713
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
714
-			} else {
715
-				$subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
716
-			}
717
-		}
718
-
719
-		$this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
720
-
721
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
722
-			$parameters[0] = $ownerPath;
723
-			$this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
724
-		}
725
-	}
726
-
727
-	/**
728
-	 * publish activity
729
-	 *
730
-	 * @param string $subject
731
-	 * @param array $parameters
732
-	 * @param string $affectedUser
733
-	 * @param int $fileId
734
-	 * @param string $filePath
735
-	 */
736
-	protected function publishActivity($subject,
737
-										array $parameters,
738
-										$affectedUser,
739
-										$fileId,
740
-										$filePath) {
741
-
742
-		$event = $this->activityManager->generateEvent();
743
-		$event->setApp('files_sharing')
744
-			->setType('public_links')
745
-			->setSubject($subject, $parameters)
746
-			->setAffectedUser($affectedUser)
747
-			->setObject('files', $fileId, $filePath);
748
-		$this->activityManager->publish($event);
749
-	}
384
+            $freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath());
385
+            if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) {
386
+                $freeSpace = max($freeSpace, 0);
387
+            } else {
388
+                $freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188
389
+            }
390
+
391
+            $hideFileList = !($share->getPermissions() & \OCP\Constants::PERMISSION_READ);
392
+            $maxUploadFilesize = $freeSpace;
393
+
394
+            $folder = new Template('files', 'list', '');
395
+
396
+            $folder->assign('dir', $shareNode->getRelativePath($folderNode->getPath()));
397
+            $folder->assign('dirToken', $this->getToken());
398
+            $folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
399
+            $folder->assign('isPublic', true);
400
+            $folder->assign('hideFileList', $hideFileList);
401
+            $folder->assign('publicUploadEnabled', 'no');
402
+            // default to list view
403
+            $folder->assign('showgridview', false);
404
+            $folder->assign('uploadMaxFilesize', $maxUploadFilesize);
405
+            $folder->assign('uploadMaxHumanFilesize', \OCP\Util::humanFileSize($maxUploadFilesize));
406
+            $folder->assign('freeSpace', $freeSpace);
407
+            $folder->assign('usedSpacePercent', 0);
408
+            $folder->assign('trash', false);
409
+            $shareTmpl['folder'] = $folder->fetchPage();
410
+        } else {
411
+            $shareIsFolder = false;
412
+        }
413
+
414
+        // default to list view
415
+        $shareTmpl['showgridview'] = false;
416
+
417
+        $shareTmpl['hideFileList'] = $hideFileList;
418
+        $shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', ['token' => $this->getToken()]);
419
+        $shareTmpl['shareUrl'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $this->getToken()]);
420
+        $shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
421
+        $shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
422
+        $shareTmpl['previewMaxX'] = $this->config->getSystemValue('preview_max_x', 1024);
423
+        $shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024);
424
+        $shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null);
425
+        $shareTmpl['previewURL'] = $shareTmpl['downloadURL'];
426
+
427
+        if ($shareTmpl['previewSupported']) {
428
+            $shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview',
429
+                ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 'token' => $shareTmpl['dirToken']]);
430
+            $ogPreview = $shareTmpl['previewImage'];
431
+
432
+            // We just have direct previews for image files
433
+            if ($shareNode->getMimePart() === 'image') {
434
+                $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $this->getToken()]);
435
+
436
+                $ogPreview = $shareTmpl['previewURL'];
437
+
438
+                //Whatapp is kind of picky about their size requirements
439
+                if ($this->request->isUserAgent(['/^WhatsApp/'])) {
440
+                    $ogPreview = $this->urlGenerator->linkToRouteAbsolute('files_sharing.PublicPreview.getPreview', [
441
+                        'token' => $this->getToken(),
442
+                        'x' => 256,
443
+                        'y' => 256,
444
+                        'a' => true,
445
+                    ]);
446
+                }
447
+            }
448
+        } else {
449
+            $shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png'));
450
+            $ogPreview = $shareTmpl['previewImage'];
451
+        }
452
+
453
+        // Load files we need
454
+        \OCP\Util::addScript('files', 'semaphore');
455
+        \OCP\Util::addScript('files', 'file-upload');
456
+        \OCP\Util::addStyle('files_sharing', 'publicView');
457
+        \OCP\Util::addScript('files_sharing', 'public');
458
+        \OCP\Util::addScript('files_sharing', 'templates');
459
+        \OCP\Util::addScript('files', 'fileactions');
460
+        \OCP\Util::addScript('files', 'fileactionsmenu');
461
+        \OCP\Util::addScript('files', 'jquery.fileupload');
462
+        \OCP\Util::addScript('files_sharing', 'files_drop');
463
+
464
+        if (isset($shareTmpl['folder'])) {
465
+            // JS required for folders
466
+            \OCP\Util::addStyle('files', 'merged');
467
+            \OCP\Util::addScript('files', 'filesummary');
468
+            \OCP\Util::addScript('files', 'templates');
469
+            \OCP\Util::addScript('files', 'breadcrumb');
470
+            \OCP\Util::addScript('files', 'fileinfomodel');
471
+            \OCP\Util::addScript('files', 'newfilemenu');
472
+            \OCP\Util::addScript('files', 'files');
473
+            \OCP\Util::addScript('files', 'filemultiselectmenu');
474
+            \OCP\Util::addScript('files', 'filelist');
475
+            \OCP\Util::addScript('files', 'keyboardshortcuts');
476
+            \OCP\Util::addScript('files', 'operationprogressbar');
477
+
478
+            // Load Viewer scripts
479
+            if (class_exists(LoadViewer::class)) {
480
+                $this->eventDispatcher->dispatch(LoadViewer::class, new LoadViewer());
481
+            }
482
+        }
483
+
484
+        // OpenGraph Support: http://ogp.me/
485
+        \OCP\Util::addHeader('meta', ['property' => "og:title", 'content' => $shareTmpl['filename']]);
486
+        \OCP\Util::addHeader('meta', ['property' => "og:description", 'content' => $this->defaults->getName() . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : '')]);
487
+        \OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]);
488
+        \OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]);
489
+        \OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]);
490
+        \OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]);
491
+
492
+        $event = new GenericEvent(null, ['share' => $share]);
493
+        $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts', $event);
494
+
495
+        $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
496
+        $csp->addAllowedFrameDomain('\'self\'');
497
+
498
+        $response = new PublicTemplateResponse($this->appName, 'public', $shareTmpl);
499
+        $response->setHeaderTitle($shareTmpl['filename']);
500
+        if ($shareTmpl['shareOwner'] !== '') {
501
+            $response->setHeaderDetails($this->l10n->t('shared by %s', [$shareTmpl['shareOwner']]));
502
+        }
503
+
504
+        $isNoneFileDropFolder = $shareIsFolder === false || $share->getPermissions() !== \OCP\Constants::PERMISSION_CREATE;
505
+
506
+        if ($isNoneFileDropFolder && !$share->getHideDownload()) {
507
+            \OCP\Util::addScript('files_sharing', 'public_note');
508
+
509
+            $downloadWhite = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
510
+            $downloadAllWhite = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download-white', $shareTmpl['downloadURL'], 0);
511
+            $download = new SimpleMenuAction('download', $this->l10n->t('Download'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
512
+            $downloadAll = new SimpleMenuAction('download', $this->l10n->t('Download all files'), 'icon-download', $shareTmpl['downloadURL'], 10, $shareTmpl['fileSize']);
513
+            $directLink = new LinkMenuAction($this->l10n->t('Direct link'), 'icon-public', $shareTmpl['previewURL']);
514
+            $externalShare = new ExternalShareMenuAction($this->l10n->t('Add to your Nextcloud'), 'icon-external', $shareTmpl['owner'], $shareTmpl['shareOwner'], $shareTmpl['filename']);
515
+
516
+            $responseComposer = [];
517
+
518
+            if ($shareIsFolder) {
519
+                $responseComposer[] = $downloadAllWhite;
520
+                $responseComposer[] = $downloadAll;
521
+            } else {
522
+                $responseComposer[] = $downloadWhite;
523
+                $responseComposer[] = $download;
524
+            }
525
+            $responseComposer[] = $directLink;
526
+            if ($this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
527
+                $responseComposer[] = $externalShare;
528
+            }
529
+
530
+            $response->setHeaderActions($responseComposer);
531
+        }
532
+
533
+        $response->setContentSecurityPolicy($csp);
534
+
535
+        $this->emitAccessShareHook($share);
536
+
537
+        return $response;
538
+    }
539
+
540
+    /**
541
+     * @PublicPage
542
+     * @NoCSRFRequired
543
+     *
544
+     * @param string $token
545
+     * @param string $files
546
+     * @param string $path
547
+     * @param string $downloadStartSecret
548
+     * @return void|\OCP\AppFramework\Http\Response
549
+     * @throws NotFoundException
550
+     */
551
+    public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
552
+        \OC_User::setIncognitoMode(true);
553
+
554
+        $share = $this->shareManager->getShareByToken($token);
555
+
556
+        if(!($share->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
557
+            return new \OCP\AppFramework\Http\DataResponse('Share is read-only');
558
+        }
559
+
560
+        $files_list = null;
561
+        if (!is_null($files)) { // download selected files
562
+            $files_list = json_decode($files);
563
+            // in case we get only a single file
564
+            if ($files_list === null) {
565
+                $files_list = [$files];
566
+            }
567
+            // Just in case $files is a single int like '1234'
568
+            if (!is_array($files_list)) {
569
+                $files_list = [$files_list];
570
+            }
571
+        }
572
+
573
+        if (!$this->validateShare($share)) {
574
+            throw new NotFoundException();
575
+        }
576
+
577
+        $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
578
+        $originalSharePath = $userFolder->getRelativePath($share->getNode()->getPath());
579
+
580
+
581
+        // Single file share
582
+        if ($share->getNode() instanceof \OCP\Files\File) {
583
+            // Single file download
584
+            $this->singleFileDownloaded($share, $share->getNode());
585
+        }
586
+        // Directory share
587
+        else {
588
+            /** @var \OCP\Files\Folder $node */
589
+            $node = $share->getNode();
590
+
591
+            // Try to get the path
592
+            if ($path !== '') {
593
+                try {
594
+                    $node = $node->get($path);
595
+                } catch (NotFoundException $e) {
596
+                    $this->emitAccessShareHook($share, 404, 'Share not found');
597
+                    return new NotFoundResponse();
598
+                }
599
+            }
600
+
601
+            $originalSharePath = $userFolder->getRelativePath($node->getPath());
602
+
603
+            if ($node instanceof \OCP\Files\File) {
604
+                // Single file download
605
+                $this->singleFileDownloaded($share, $share->getNode());
606
+            } else {
607
+                try {
608
+                    if (!empty($files_list)) {
609
+                        $this->fileListDownloaded($share, $files_list, $node);
610
+                    } else {
611
+                        // The folder is downloaded
612
+                        $this->singleFileDownloaded($share, $share->getNode());
613
+                    }
614
+                } catch (NotFoundException $e) {
615
+                    return new NotFoundResponse();
616
+                }
617
+            }
618
+        }
619
+
620
+        /* FIXME: We should do this all nicely in OCP */
621
+        OC_Util::tearDownFS();
622
+        OC_Util::setupFS($share->getShareOwner());
623
+
624
+        /**
625
+         * this sets a cookie to be able to recognize the start of the download
626
+         * the content must not be longer than 32 characters and must only contain
627
+         * alphanumeric characters
628
+         */
629
+        if (!empty($downloadStartSecret)
630
+            && !isset($downloadStartSecret[32])
631
+            && preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
632
+
633
+            // FIXME: set on the response once we use an actual app framework response
634
+            setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
635
+        }
636
+
637
+        $this->emitAccessShareHook($share);
638
+
639
+        $server_params = [ 'head' => $this->request->getMethod() === 'HEAD' ];
640
+
641
+        /**
642
+         * Http range requests support
643
+         */
644
+        if (isset($_SERVER['HTTP_RANGE'])) {
645
+            $server_params['range'] = $this->request->getHeader('Range');
646
+        }
647
+
648
+        // download selected files
649
+        if (!is_null($files) && $files !== '') {
650
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
651
+            // after dispatching the request which results in a "Cannot modify header information" notice.
652
+            OC_Files::get($originalSharePath, $files_list, $server_params);
653
+            exit();
654
+        } else {
655
+            // FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
656
+            // after dispatching the request which results in a "Cannot modify header information" notice.
657
+            OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $server_params);
658
+            exit();
659
+        }
660
+    }
661
+
662
+    /**
663
+     * create activity for every downloaded file
664
+     *
665
+     * @param Share\IShare $share
666
+     * @param array $files_list
667
+     * @param \OCP\Files\Folder $node
668
+     * @throws NotFoundException when trying to download a folder or multiple files of a "hide download" share
669
+     */
670
+    protected function fileListDownloaded(Share\IShare $share, array $files_list, \OCP\Files\Folder $node) {
671
+        if ($share->getHideDownload() && count($files_list) > 1) {
672
+            throw new NotFoundException('Downloading more than 1 file');
673
+        }
674
+
675
+        foreach ($files_list as $file) {
676
+            $subNode = $node->get($file);
677
+            $this->singleFileDownloaded($share, $subNode);
678
+        }
679
+
680
+    }
681
+
682
+    /**
683
+     * create activity if a single file was downloaded from a link share
684
+     *
685
+     * @param Share\IShare $share
686
+     * @throws NotFoundException when trying to download a folder of a "hide download" share
687
+     */
688
+    protected function singleFileDownloaded(Share\IShare $share, \OCP\Files\Node $node) {
689
+        if ($share->getHideDownload() && $node instanceof Folder) {
690
+            throw new NotFoundException('Downloading a folder');
691
+        }
692
+
693
+        $fileId = $node->getId();
694
+
695
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
696
+        $userNodeList = $userFolder->getById($fileId);
697
+        $userNode = $userNodeList[0];
698
+        $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
699
+        $userPath = $userFolder->getRelativePath($userNode->getPath());
700
+        $ownerPath = $ownerFolder->getRelativePath($node->getPath());
701
+
702
+        $parameters = [$userPath];
703
+
704
+        if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) {
705
+            if ($node instanceof \OCP\Files\File) {
706
+                $subject = Downloads::SUBJECT_SHARED_FILE_BY_EMAIL_DOWNLOADED;
707
+            } else {
708
+                $subject = Downloads::SUBJECT_SHARED_FOLDER_BY_EMAIL_DOWNLOADED;
709
+            }
710
+            $parameters[] = $share->getSharedWith();
711
+        } else {
712
+            if ($node instanceof \OCP\Files\File) {
713
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
714
+            } else {
715
+                $subject = Downloads::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
716
+            }
717
+        }
718
+
719
+        $this->publishActivity($subject, $parameters, $share->getSharedBy(), $fileId, $userPath);
720
+
721
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
722
+            $parameters[0] = $ownerPath;
723
+            $this->publishActivity($subject, $parameters, $share->getShareOwner(), $fileId, $ownerPath);
724
+        }
725
+    }
726
+
727
+    /**
728
+     * publish activity
729
+     *
730
+     * @param string $subject
731
+     * @param array $parameters
732
+     * @param string $affectedUser
733
+     * @param int $fileId
734
+     * @param string $filePath
735
+     */
736
+    protected function publishActivity($subject,
737
+                                        array $parameters,
738
+                                        $affectedUser,
739
+                                        $fileId,
740
+                                        $filePath) {
741
+
742
+        $event = $this->activityManager->generateEvent();
743
+        $event->setApp('files_sharing')
744
+            ->setType('public_links')
745
+            ->setSubject($subject, $parameters)
746
+            ->setAffectedUser($affectedUser)
747
+            ->setObject('files', $fileId, $filePath);
748
+        $this->activityManager->publish($event);
749
+    }
750 750
 
751 751
 
752 752
 }
Please login to merge, or discard this patch.