Passed
Push — master ( 5024f2...d6ff5d )
by Robin
14:15 queued 12s
created
apps/files_external/lib/Lib/Storage/FTP.php 1 patch
Indentation   +345 added lines, -345 removed lines patch added patch discarded remove patch
@@ -31,349 +31,349 @@
 block discarded – undo
31 31
 use OCP\Files\StorageNotAvailableException;
32 32
 
33 33
 class FTP extends Common {
34
-	use CopyDirectory;
35
-
36
-	private $root;
37
-	private $host;
38
-	private $password;
39
-	private $username;
40
-	private $secure;
41
-	private $port;
42
-	private $utf8Mode;
43
-
44
-	/** @var FtpConnection|null */
45
-	private $connection;
46
-
47
-	public function __construct($params) {
48
-		if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
49
-			$this->host = $params['host'];
50
-			$this->username = $params['user'];
51
-			$this->password = $params['password'];
52
-			if (isset($params['secure'])) {
53
-				if (is_string($params['secure'])) {
54
-					$this->secure = ($params['secure'] === 'true');
55
-				} else {
56
-					$this->secure = (bool)$params['secure'];
57
-				}
58
-			} else {
59
-				$this->secure = false;
60
-			}
61
-			$this->root = isset($params['root']) ? '/' . ltrim($params['root']) : '/';
62
-			$this->port = $params['port'] ?? 21;
63
-			$this->utf8Mode = isset($params['utf8']) && $params['utf8'];
64
-		} else {
65
-			throw new \Exception('Creating ' . self::class . ' storage failed, required parameters not set');
66
-		}
67
-	}
68
-
69
-	public function __destruct() {
70
-		$this->connection = null;
71
-	}
72
-
73
-	protected function getConnection(): FtpConnection {
74
-		if (!$this->connection) {
75
-			try {
76
-				$this->connection = new FtpConnection(
77
-					$this->secure,
78
-					$this->host,
79
-					$this->port,
80
-					$this->username,
81
-					$this->password
82
-				);
83
-			} catch (\Exception $e) {
84
-				throw new StorageNotAvailableException("Failed to create ftp connection", 0, $e);
85
-			}
86
-			if ($this->utf8Mode) {
87
-				if (!$this->connection->setUtf8Mode()) {
88
-					throw new StorageNotAvailableException("Could not set UTF-8 mode");
89
-				}
90
-			}
91
-		}
92
-
93
-		return $this->connection;
94
-	}
95
-
96
-	public function getId() {
97
-		return 'ftp::' . $this->username . '@' . $this->host . '/' . $this->root;
98
-	}
99
-
100
-	protected function buildPath($path) {
101
-		return rtrim($this->root . '/' . $path, '/');
102
-	}
103
-
104
-	public static function checkDependencies() {
105
-		if (function_exists('ftp_login')) {
106
-			return true;
107
-		} else {
108
-			return ['ftp'];
109
-		}
110
-	}
111
-
112
-	public function filemtime($path) {
113
-		$result = $this->getConnection()->mdtm($this->buildPath($path));
114
-
115
-		if ($result === -1) {
116
-			if ($this->is_dir($path)) {
117
-				$list = $this->getConnection()->mlsd($this->buildPath($path));
118
-				if (!$list) {
119
-					\OC::$server->getLogger()->warning("Unable to get last modified date for ftp folder ($path), failed to list folder contents");
120
-					return time();
121
-				}
122
-				$currentDir = current(array_filter($list, function ($item) {
123
-					return $item['type'] === 'cdir';
124
-				}));
125
-				if ($currentDir) {
126
-					[$modify] = explode('.', $currentDir['modify'] ?? '', 2);
127
-					$time = \DateTime::createFromFormat('YmdHis', $modify);
128
-					if ($time === false) {
129
-						throw new \Exception("Invalid date format for directory: $currentDir");
130
-					}
131
-					return $time->getTimestamp();
132
-				} else {
133
-					\OC::$server->getLogger()->warning("Unable to get last modified date for ftp folder ($path), folder contents doesn't include current folder");
134
-					return time();
135
-				}
136
-			} else {
137
-				return false;
138
-			}
139
-		} else {
140
-			return $result;
141
-		}
142
-	}
143
-
144
-	public function filesize($path): false|int|float {
145
-		$result = $this->getConnection()->size($this->buildPath($path));
146
-		if ($result === -1) {
147
-			return false;
148
-		} else {
149
-			return $result;
150
-		}
151
-	}
152
-
153
-	public function rmdir($path) {
154
-		if ($this->is_dir($path)) {
155
-			$result = $this->getConnection()->rmdir($this->buildPath($path));
156
-			// recursive rmdir support depends on the ftp server
157
-			if ($result) {
158
-				return $result;
159
-			} else {
160
-				return $this->recursiveRmDir($path);
161
-			}
162
-		} elseif ($this->is_file($path)) {
163
-			return $this->unlink($path);
164
-		} else {
165
-			return false;
166
-		}
167
-	}
168
-
169
-	/**
170
-	 * @param string $path
171
-	 * @return bool
172
-	 */
173
-	private function recursiveRmDir($path): bool {
174
-		$contents = $this->getDirectoryContent($path);
175
-		$result = true;
176
-		foreach ($contents as $content) {
177
-			if ($content['mimetype'] === FileInfo::MIMETYPE_FOLDER) {
178
-				$result = $result && $this->recursiveRmDir($path . '/' . $content['name']);
179
-			} else {
180
-				$result = $result && $this->getConnection()->delete($this->buildPath($path . '/' . $content['name']));
181
-			}
182
-		}
183
-		$result = $result && $this->getConnection()->rmdir($this->buildPath($path));
184
-
185
-		return $result;
186
-	}
187
-
188
-	public function test() {
189
-		try {
190
-			return $this->getConnection()->systype() !== false;
191
-		} catch (\Exception $e) {
192
-			return false;
193
-		}
194
-	}
195
-
196
-	public function stat($path) {
197
-		if (!$this->file_exists($path)) {
198
-			return false;
199
-		}
200
-		return [
201
-			'mtime' => $this->filemtime($path),
202
-			'size' => $this->filesize($path),
203
-		];
204
-	}
205
-
206
-	public function file_exists($path) {
207
-		if ($path === '' || $path === '.' || $path === '/') {
208
-			return true;
209
-		}
210
-		return $this->filetype($path) !== false;
211
-	}
212
-
213
-	public function unlink($path) {
214
-		switch ($this->filetype($path)) {
215
-			case 'dir':
216
-				return $this->rmdir($path);
217
-			case 'file':
218
-				return $this->getConnection()->delete($this->buildPath($path));
219
-			default:
220
-				return false;
221
-		}
222
-	}
223
-
224
-	public function opendir($path) {
225
-		$files = $this->getConnection()->nlist($this->buildPath($path));
226
-		return IteratorDirectory::wrap($files);
227
-	}
228
-
229
-	public function mkdir($path) {
230
-		if ($this->is_dir($path)) {
231
-			return false;
232
-		}
233
-		return $this->getConnection()->mkdir($this->buildPath($path)) !== false;
234
-	}
235
-
236
-	public function is_dir($path) {
237
-		if ($path === "") {
238
-			return true;
239
-		}
240
-		if ($this->getConnection()->chdir($this->buildPath($path)) === true) {
241
-			$this->getConnection()->chdir('/');
242
-			return true;
243
-		} else {
244
-			return false;
245
-		}
246
-	}
247
-
248
-	public function is_file($path) {
249
-		return $this->filesize($path) !== false;
250
-	}
251
-
252
-	public function filetype($path) {
253
-		if ($this->is_dir($path)) {
254
-			return 'dir';
255
-		} elseif ($this->is_file($path)) {
256
-			return 'file';
257
-		} else {
258
-			return false;
259
-		}
260
-	}
261
-
262
-	public function fopen($path, $mode) {
263
-		$useExisting = true;
264
-		switch ($mode) {
265
-			case 'r':
266
-			case 'rb':
267
-				return $this->readStream($path);
268
-			case 'w':
269
-			case 'w+':
270
-			case 'wb':
271
-			case 'wb+':
272
-				$useExisting = false;
273
-				// no break
274
-			case 'a':
275
-			case 'ab':
276
-			case 'r+':
277
-			case 'a+':
278
-			case 'x':
279
-			case 'x+':
280
-			case 'c':
281
-			case 'c+':
282
-				//emulate these
283
-				if ($useExisting and $this->file_exists($path)) {
284
-					if (!$this->isUpdatable($path)) {
285
-						return false;
286
-					}
287
-					$tmpFile = $this->getCachedFile($path);
288
-				} else {
289
-					if (!$this->isCreatable(dirname($path))) {
290
-						return false;
291
-					}
292
-					$tmpFile = \OC::$server->getTempManager()->getTemporaryFile();
293
-				}
294
-				$source = fopen($tmpFile, $mode);
295
-				return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $path) {
296
-					$this->writeStream($path, fopen($tmpFile, 'r'));
297
-					unlink($tmpFile);
298
-				});
299
-		}
300
-		return false;
301
-	}
302
-
303
-	public function writeStream(string $path, $stream, int $size = null): int {
304
-		if ($size === null) {
305
-			$stream = CountWrapper::wrap($stream, function ($writtenSize) use (&$size) {
306
-				$size = $writtenSize;
307
-			});
308
-		}
309
-
310
-		$this->getConnection()->fput($this->buildPath($path), $stream);
311
-		fclose($stream);
312
-
313
-		return $size;
314
-	}
315
-
316
-	public function readStream(string $path) {
317
-		$stream = fopen('php://temp', 'w+');
318
-		$result = $this->getConnection()->fget($stream, $this->buildPath($path));
319
-		rewind($stream);
320
-
321
-		if (!$result) {
322
-			fclose($stream);
323
-			return false;
324
-		}
325
-		return $stream;
326
-	}
327
-
328
-	public function touch($path, $mtime = null) {
329
-		if ($this->file_exists($path)) {
330
-			return false;
331
-		} else {
332
-			$this->file_put_contents($path, '');
333
-			return true;
334
-		}
335
-	}
336
-
337
-	public function rename($source, $target) {
338
-		$this->unlink($target);
339
-		return $this->getConnection()->rename($this->buildPath($source), $this->buildPath($target));
340
-	}
341
-
342
-	public function getDirectoryContent($directory): \Traversable {
343
-		$files = $this->getConnection()->mlsd($this->buildPath($directory));
344
-		$mimeTypeDetector = \OC::$server->getMimeTypeDetector();
345
-
346
-		foreach ($files as $file) {
347
-			$name = $file['name'];
348
-			if ($file['type'] === 'cdir' || $file['type'] === 'pdir') {
349
-				continue;
350
-			}
351
-			$permissions = Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
352
-			$isDir = $file['type'] === 'dir';
353
-			if ($isDir) {
354
-				$permissions += Constants::PERMISSION_CREATE;
355
-			}
356
-
357
-			$data = [];
358
-			$data['mimetype'] = $isDir ? FileInfo::MIMETYPE_FOLDER : $mimeTypeDetector->detectPath($name);
359
-
360
-			// strip fractional seconds
361
-			[$modify] = explode('.', $file['modify'], 2);
362
-			$mtime = \DateTime::createFromFormat('YmdGis', $modify);
363
-			$data['mtime'] = $mtime === false ? time() : $mtime->getTimestamp();
364
-			if ($isDir) {
365
-				$data['size'] = -1; //unknown
366
-			} elseif (isset($file['size'])) {
367
-				$data['size'] = $file['size'];
368
-			} else {
369
-				$data['size'] = $this->filesize($directory . '/' . $name);
370
-			}
371
-			$data['etag'] = uniqid();
372
-			$data['storage_mtime'] = $data['mtime'];
373
-			$data['permissions'] = $permissions;
374
-			$data['name'] = $name;
375
-
376
-			yield $data;
377
-		}
378
-	}
34
+    use CopyDirectory;
35
+
36
+    private $root;
37
+    private $host;
38
+    private $password;
39
+    private $username;
40
+    private $secure;
41
+    private $port;
42
+    private $utf8Mode;
43
+
44
+    /** @var FtpConnection|null */
45
+    private $connection;
46
+
47
+    public function __construct($params) {
48
+        if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
49
+            $this->host = $params['host'];
50
+            $this->username = $params['user'];
51
+            $this->password = $params['password'];
52
+            if (isset($params['secure'])) {
53
+                if (is_string($params['secure'])) {
54
+                    $this->secure = ($params['secure'] === 'true');
55
+                } else {
56
+                    $this->secure = (bool)$params['secure'];
57
+                }
58
+            } else {
59
+                $this->secure = false;
60
+            }
61
+            $this->root = isset($params['root']) ? '/' . ltrim($params['root']) : '/';
62
+            $this->port = $params['port'] ?? 21;
63
+            $this->utf8Mode = isset($params['utf8']) && $params['utf8'];
64
+        } else {
65
+            throw new \Exception('Creating ' . self::class . ' storage failed, required parameters not set');
66
+        }
67
+    }
68
+
69
+    public function __destruct() {
70
+        $this->connection = null;
71
+    }
72
+
73
+    protected function getConnection(): FtpConnection {
74
+        if (!$this->connection) {
75
+            try {
76
+                $this->connection = new FtpConnection(
77
+                    $this->secure,
78
+                    $this->host,
79
+                    $this->port,
80
+                    $this->username,
81
+                    $this->password
82
+                );
83
+            } catch (\Exception $e) {
84
+                throw new StorageNotAvailableException("Failed to create ftp connection", 0, $e);
85
+            }
86
+            if ($this->utf8Mode) {
87
+                if (!$this->connection->setUtf8Mode()) {
88
+                    throw new StorageNotAvailableException("Could not set UTF-8 mode");
89
+                }
90
+            }
91
+        }
92
+
93
+        return $this->connection;
94
+    }
95
+
96
+    public function getId() {
97
+        return 'ftp::' . $this->username . '@' . $this->host . '/' . $this->root;
98
+    }
99
+
100
+    protected function buildPath($path) {
101
+        return rtrim($this->root . '/' . $path, '/');
102
+    }
103
+
104
+    public static function checkDependencies() {
105
+        if (function_exists('ftp_login')) {
106
+            return true;
107
+        } else {
108
+            return ['ftp'];
109
+        }
110
+    }
111
+
112
+    public function filemtime($path) {
113
+        $result = $this->getConnection()->mdtm($this->buildPath($path));
114
+
115
+        if ($result === -1) {
116
+            if ($this->is_dir($path)) {
117
+                $list = $this->getConnection()->mlsd($this->buildPath($path));
118
+                if (!$list) {
119
+                    \OC::$server->getLogger()->warning("Unable to get last modified date for ftp folder ($path), failed to list folder contents");
120
+                    return time();
121
+                }
122
+                $currentDir = current(array_filter($list, function ($item) {
123
+                    return $item['type'] === 'cdir';
124
+                }));
125
+                if ($currentDir) {
126
+                    [$modify] = explode('.', $currentDir['modify'] ?? '', 2);
127
+                    $time = \DateTime::createFromFormat('YmdHis', $modify);
128
+                    if ($time === false) {
129
+                        throw new \Exception("Invalid date format for directory: $currentDir");
130
+                    }
131
+                    return $time->getTimestamp();
132
+                } else {
133
+                    \OC::$server->getLogger()->warning("Unable to get last modified date for ftp folder ($path), folder contents doesn't include current folder");
134
+                    return time();
135
+                }
136
+            } else {
137
+                return false;
138
+            }
139
+        } else {
140
+            return $result;
141
+        }
142
+    }
143
+
144
+    public function filesize($path): false|int|float {
145
+        $result = $this->getConnection()->size($this->buildPath($path));
146
+        if ($result === -1) {
147
+            return false;
148
+        } else {
149
+            return $result;
150
+        }
151
+    }
152
+
153
+    public function rmdir($path) {
154
+        if ($this->is_dir($path)) {
155
+            $result = $this->getConnection()->rmdir($this->buildPath($path));
156
+            // recursive rmdir support depends on the ftp server
157
+            if ($result) {
158
+                return $result;
159
+            } else {
160
+                return $this->recursiveRmDir($path);
161
+            }
162
+        } elseif ($this->is_file($path)) {
163
+            return $this->unlink($path);
164
+        } else {
165
+            return false;
166
+        }
167
+    }
168
+
169
+    /**
170
+     * @param string $path
171
+     * @return bool
172
+     */
173
+    private function recursiveRmDir($path): bool {
174
+        $contents = $this->getDirectoryContent($path);
175
+        $result = true;
176
+        foreach ($contents as $content) {
177
+            if ($content['mimetype'] === FileInfo::MIMETYPE_FOLDER) {
178
+                $result = $result && $this->recursiveRmDir($path . '/' . $content['name']);
179
+            } else {
180
+                $result = $result && $this->getConnection()->delete($this->buildPath($path . '/' . $content['name']));
181
+            }
182
+        }
183
+        $result = $result && $this->getConnection()->rmdir($this->buildPath($path));
184
+
185
+        return $result;
186
+    }
187
+
188
+    public function test() {
189
+        try {
190
+            return $this->getConnection()->systype() !== false;
191
+        } catch (\Exception $e) {
192
+            return false;
193
+        }
194
+    }
195
+
196
+    public function stat($path) {
197
+        if (!$this->file_exists($path)) {
198
+            return false;
199
+        }
200
+        return [
201
+            'mtime' => $this->filemtime($path),
202
+            'size' => $this->filesize($path),
203
+        ];
204
+    }
205
+
206
+    public function file_exists($path) {
207
+        if ($path === '' || $path === '.' || $path === '/') {
208
+            return true;
209
+        }
210
+        return $this->filetype($path) !== false;
211
+    }
212
+
213
+    public function unlink($path) {
214
+        switch ($this->filetype($path)) {
215
+            case 'dir':
216
+                return $this->rmdir($path);
217
+            case 'file':
218
+                return $this->getConnection()->delete($this->buildPath($path));
219
+            default:
220
+                return false;
221
+        }
222
+    }
223
+
224
+    public function opendir($path) {
225
+        $files = $this->getConnection()->nlist($this->buildPath($path));
226
+        return IteratorDirectory::wrap($files);
227
+    }
228
+
229
+    public function mkdir($path) {
230
+        if ($this->is_dir($path)) {
231
+            return false;
232
+        }
233
+        return $this->getConnection()->mkdir($this->buildPath($path)) !== false;
234
+    }
235
+
236
+    public function is_dir($path) {
237
+        if ($path === "") {
238
+            return true;
239
+        }
240
+        if ($this->getConnection()->chdir($this->buildPath($path)) === true) {
241
+            $this->getConnection()->chdir('/');
242
+            return true;
243
+        } else {
244
+            return false;
245
+        }
246
+    }
247
+
248
+    public function is_file($path) {
249
+        return $this->filesize($path) !== false;
250
+    }
251
+
252
+    public function filetype($path) {
253
+        if ($this->is_dir($path)) {
254
+            return 'dir';
255
+        } elseif ($this->is_file($path)) {
256
+            return 'file';
257
+        } else {
258
+            return false;
259
+        }
260
+    }
261
+
262
+    public function fopen($path, $mode) {
263
+        $useExisting = true;
264
+        switch ($mode) {
265
+            case 'r':
266
+            case 'rb':
267
+                return $this->readStream($path);
268
+            case 'w':
269
+            case 'w+':
270
+            case 'wb':
271
+            case 'wb+':
272
+                $useExisting = false;
273
+                // no break
274
+            case 'a':
275
+            case 'ab':
276
+            case 'r+':
277
+            case 'a+':
278
+            case 'x':
279
+            case 'x+':
280
+            case 'c':
281
+            case 'c+':
282
+                //emulate these
283
+                if ($useExisting and $this->file_exists($path)) {
284
+                    if (!$this->isUpdatable($path)) {
285
+                        return false;
286
+                    }
287
+                    $tmpFile = $this->getCachedFile($path);
288
+                } else {
289
+                    if (!$this->isCreatable(dirname($path))) {
290
+                        return false;
291
+                    }
292
+                    $tmpFile = \OC::$server->getTempManager()->getTemporaryFile();
293
+                }
294
+                $source = fopen($tmpFile, $mode);
295
+                return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $path) {
296
+                    $this->writeStream($path, fopen($tmpFile, 'r'));
297
+                    unlink($tmpFile);
298
+                });
299
+        }
300
+        return false;
301
+    }
302
+
303
+    public function writeStream(string $path, $stream, int $size = null): int {
304
+        if ($size === null) {
305
+            $stream = CountWrapper::wrap($stream, function ($writtenSize) use (&$size) {
306
+                $size = $writtenSize;
307
+            });
308
+        }
309
+
310
+        $this->getConnection()->fput($this->buildPath($path), $stream);
311
+        fclose($stream);
312
+
313
+        return $size;
314
+    }
315
+
316
+    public function readStream(string $path) {
317
+        $stream = fopen('php://temp', 'w+');
318
+        $result = $this->getConnection()->fget($stream, $this->buildPath($path));
319
+        rewind($stream);
320
+
321
+        if (!$result) {
322
+            fclose($stream);
323
+            return false;
324
+        }
325
+        return $stream;
326
+    }
327
+
328
+    public function touch($path, $mtime = null) {
329
+        if ($this->file_exists($path)) {
330
+            return false;
331
+        } else {
332
+            $this->file_put_contents($path, '');
333
+            return true;
334
+        }
335
+    }
336
+
337
+    public function rename($source, $target) {
338
+        $this->unlink($target);
339
+        return $this->getConnection()->rename($this->buildPath($source), $this->buildPath($target));
340
+    }
341
+
342
+    public function getDirectoryContent($directory): \Traversable {
343
+        $files = $this->getConnection()->mlsd($this->buildPath($directory));
344
+        $mimeTypeDetector = \OC::$server->getMimeTypeDetector();
345
+
346
+        foreach ($files as $file) {
347
+            $name = $file['name'];
348
+            if ($file['type'] === 'cdir' || $file['type'] === 'pdir') {
349
+                continue;
350
+            }
351
+            $permissions = Constants::PERMISSION_ALL - Constants::PERMISSION_CREATE;
352
+            $isDir = $file['type'] === 'dir';
353
+            if ($isDir) {
354
+                $permissions += Constants::PERMISSION_CREATE;
355
+            }
356
+
357
+            $data = [];
358
+            $data['mimetype'] = $isDir ? FileInfo::MIMETYPE_FOLDER : $mimeTypeDetector->detectPath($name);
359
+
360
+            // strip fractional seconds
361
+            [$modify] = explode('.', $file['modify'], 2);
362
+            $mtime = \DateTime::createFromFormat('YmdGis', $modify);
363
+            $data['mtime'] = $mtime === false ? time() : $mtime->getTimestamp();
364
+            if ($isDir) {
365
+                $data['size'] = -1; //unknown
366
+            } elseif (isset($file['size'])) {
367
+                $data['size'] = $file['size'];
368
+            } else {
369
+                $data['size'] = $this->filesize($directory . '/' . $name);
370
+            }
371
+            $data['etag'] = uniqid();
372
+            $data['storage_mtime'] = $data['mtime'];
373
+            $data['permissions'] = $permissions;
374
+            $data['name'] = $name;
375
+
376
+            yield $data;
377
+        }
378
+    }
379 379
 }
Please login to merge, or discard this patch.
apps/files_external/lib/Lib/Storage/FtpConnection.php 1 patch
Indentation   +210 added lines, -210 removed lines patch added patch discarded remove patch
@@ -27,214 +27,214 @@
 block discarded – undo
27 27
  * Low level wrapper around the ftp functions that smooths over some difference between servers
28 28
  */
29 29
 class FtpConnection {
30
-	/** @var resource|\FTP\Connection */
31
-	private $connection;
32
-
33
-	public function __construct(bool $secure, string $hostname, int $port, string $username, string $password) {
34
-		if ($secure) {
35
-			$connection = ftp_ssl_connect($hostname, $port);
36
-		} else {
37
-			$connection = ftp_connect($hostname, $port);
38
-		}
39
-
40
-		if ($connection === false) {
41
-			throw new \Exception("Failed to connect to ftp");
42
-		}
43
-
44
-		if (ftp_login($connection, $username, $password) === false) {
45
-			throw new \Exception("Failed to connect to login to ftp");
46
-		}
47
-
48
-		ftp_pasv($connection, true);
49
-		$this->connection = $connection;
50
-	}
51
-
52
-	public function __destruct() {
53
-		if ($this->connection) {
54
-			ftp_close($this->connection);
55
-		}
56
-		$this->connection = null;
57
-	}
58
-
59
-	public function setUtf8Mode(): bool {
60
-		$response = ftp_raw($this->connection, "OPTS UTF8 ON");
61
-		return substr($response[0], 0, 3) === '200';
62
-	}
63
-
64
-	public function fput(string $path, $handle) {
65
-		return @ftp_fput($this->connection, $path, $handle, FTP_BINARY);
66
-	}
67
-
68
-	public function fget($handle, string $path) {
69
-		return @ftp_fget($this->connection, $handle, $path, FTP_BINARY);
70
-	}
71
-
72
-	public function mkdir(string $path) {
73
-		return @ftp_mkdir($this->connection, $path);
74
-	}
75
-
76
-	public function chdir(string $path) {
77
-		return @ftp_chdir($this->connection, $path);
78
-	}
79
-
80
-	public function delete(string $path) {
81
-		return @ftp_delete($this->connection, $path);
82
-	}
83
-
84
-	public function rmdir(string $path) {
85
-		return @ftp_rmdir($this->connection, $path);
86
-	}
87
-
88
-	public function rename(string $source, string $target) {
89
-		return @ftp_rename($this->connection, $source, $target);
90
-	}
91
-
92
-	public function mdtm(string $path): int {
93
-		$result = @ftp_mdtm($this->connection, $path);
94
-
95
-		// filezilla doesn't like empty path with mdtm
96
-		if ($result === -1 && $path === "") {
97
-			$result = @ftp_mdtm($this->connection, "/");
98
-		}
99
-		return $result;
100
-	}
101
-
102
-	public function size(string $path) {
103
-		return @ftp_size($this->connection, $path);
104
-	}
105
-
106
-	public function systype() {
107
-		return @ftp_systype($this->connection);
108
-	}
109
-
110
-	public function nlist(string $path) {
111
-		$files = @ftp_nlist($this->connection, $path);
112
-		return array_map(function ($name) {
113
-			if (strpos($name, '/') !== false) {
114
-				$name = basename($name);
115
-			}
116
-			return $name;
117
-		}, $files);
118
-	}
119
-
120
-	public function mlsd(string $path) {
121
-		$files = @ftp_mlsd($this->connection, $path);
122
-
123
-		if ($files !== false) {
124
-			return array_map(function ($file) {
125
-				if (strpos($file['name'], '/') !== false) {
126
-					$file['name'] = basename($file['name']);
127
-				}
128
-				return $file;
129
-			}, $files);
130
-		} else {
131
-			// not all servers support mlsd, in those cases we parse the raw list ourselves
132
-			$rawList = @ftp_rawlist($this->connection, '-aln ' . $path);
133
-			if ($rawList === false) {
134
-				return false;
135
-			}
136
-			return $this->parseRawList($rawList, $path);
137
-		}
138
-	}
139
-
140
-	// rawlist parsing logic is based on the ftp implementation from https://github.com/thephpleague/flysystem
141
-	private function parseRawList(array $rawList, string $directory): array {
142
-		return array_map(function ($item) use ($directory) {
143
-			return $this->parseRawListItem($item, $directory);
144
-		}, $rawList);
145
-	}
146
-
147
-	private function parseRawListItem(string $item, string $directory): array {
148
-		$isWindows = preg_match('/^[0-9]{2,4}-[0-9]{2}-[0-9]{2}/', $item);
149
-
150
-		return $isWindows ? $this->parseWindowsItem($item, $directory) : $this->parseUnixItem($item, $directory);
151
-	}
152
-
153
-	private function parseUnixItem(string $item, string $directory): array {
154
-		$item = preg_replace('#\s+#', ' ', $item, 7);
155
-
156
-		if (count(explode(' ', $item, 9)) !== 9) {
157
-			throw new \RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
158
-		}
159
-
160
-		[$permissions, /* $number */, /* $owner */, /* $group */, $size, $month, $day, $time, $name] = explode(' ', $item, 9);
161
-		if ($name === '.') {
162
-			$type = 'cdir';
163
-		} elseif ($name === '..') {
164
-			$type = 'pdir';
165
-		} else {
166
-			$type = substr($permissions, 0, 1) === 'd' ? 'dir' : 'file';
167
-		}
168
-
169
-		$parsedDate = (new \DateTime())
170
-			->setTimestamp(strtotime("$month $day $time"));
171
-		$tomorrow = (new \DateTime())->add(new \DateInterval("P1D"));
172
-
173
-		// since the provided date doesn't include the year, we either set it to the correct year
174
-		// or when the date would otherwise be in the future (by more then 1 day to account for timezone errors)
175
-		// we use last year
176
-		if ($parsedDate > $tomorrow) {
177
-			$parsedDate = $parsedDate->sub(new \DateInterval("P1Y"));
178
-		}
179
-
180
-		$formattedDate = $parsedDate
181
-			->format('YmdHis');
182
-
183
-		return [
184
-			'type' => $type,
185
-			'name' => $name,
186
-			'modify' => $formattedDate,
187
-			'perm' => $this->normalizePermissions($permissions),
188
-			'size' => (int)$size,
189
-		];
190
-	}
191
-
192
-	private function normalizePermissions(string $permissions) {
193
-		$isDir = substr($permissions, 0, 1) === 'd';
194
-		// remove the type identifier and only use owner permissions
195
-		$permissions = substr($permissions, 1, 4);
196
-
197
-		// map the string rights to the ftp counterparts
198
-		$filePermissionsMap = ['r' => 'r', 'w' => 'fadfw'];
199
-		$dirPermissionsMap = ['r' => 'e', 'w' => 'flcdmp'];
200
-
201
-		$map = $isDir ? $dirPermissionsMap : $filePermissionsMap;
202
-
203
-		return array_reduce(str_split($permissions), function ($ftpPermissions, $permission) use ($map) {
204
-			if (isset($map[$permission])) {
205
-				$ftpPermissions .= $map[$permission];
206
-			}
207
-			return $ftpPermissions;
208
-		}, '');
209
-	}
210
-
211
-	private function parseWindowsItem(string $item, string $directory): array {
212
-		$item = preg_replace('#\s+#', ' ', trim($item), 3);
213
-
214
-		if (count(explode(' ', $item, 4)) !== 4) {
215
-			throw new \RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
216
-		}
217
-
218
-		[$date, $time, $size, $name] = explode(' ', $item, 4);
219
-
220
-		// Check for the correct date/time format
221
-		$format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i';
222
-		$formattedDate = \DateTime::createFromFormat($format, $date . $time)->format('YmdGis');
223
-
224
-		if ($name === '.') {
225
-			$type = 'cdir';
226
-		} elseif ($name === '..') {
227
-			$type = 'pdir';
228
-		} else {
229
-			$type = ($size === '<DIR>') ? 'dir' : 'file';
230
-		}
231
-
232
-		return [
233
-			'type' => $type,
234
-			'name' => $name,
235
-			'modify' => $formattedDate,
236
-			'perm' => ($type === 'file') ? 'adfrw' : 'flcdmpe',
237
-			'size' => (int)$size,
238
-		];
239
-	}
30
+    /** @var resource|\FTP\Connection */
31
+    private $connection;
32
+
33
+    public function __construct(bool $secure, string $hostname, int $port, string $username, string $password) {
34
+        if ($secure) {
35
+            $connection = ftp_ssl_connect($hostname, $port);
36
+        } else {
37
+            $connection = ftp_connect($hostname, $port);
38
+        }
39
+
40
+        if ($connection === false) {
41
+            throw new \Exception("Failed to connect to ftp");
42
+        }
43
+
44
+        if (ftp_login($connection, $username, $password) === false) {
45
+            throw new \Exception("Failed to connect to login to ftp");
46
+        }
47
+
48
+        ftp_pasv($connection, true);
49
+        $this->connection = $connection;
50
+    }
51
+
52
+    public function __destruct() {
53
+        if ($this->connection) {
54
+            ftp_close($this->connection);
55
+        }
56
+        $this->connection = null;
57
+    }
58
+
59
+    public function setUtf8Mode(): bool {
60
+        $response = ftp_raw($this->connection, "OPTS UTF8 ON");
61
+        return substr($response[0], 0, 3) === '200';
62
+    }
63
+
64
+    public function fput(string $path, $handle) {
65
+        return @ftp_fput($this->connection, $path, $handle, FTP_BINARY);
66
+    }
67
+
68
+    public function fget($handle, string $path) {
69
+        return @ftp_fget($this->connection, $handle, $path, FTP_BINARY);
70
+    }
71
+
72
+    public function mkdir(string $path) {
73
+        return @ftp_mkdir($this->connection, $path);
74
+    }
75
+
76
+    public function chdir(string $path) {
77
+        return @ftp_chdir($this->connection, $path);
78
+    }
79
+
80
+    public function delete(string $path) {
81
+        return @ftp_delete($this->connection, $path);
82
+    }
83
+
84
+    public function rmdir(string $path) {
85
+        return @ftp_rmdir($this->connection, $path);
86
+    }
87
+
88
+    public function rename(string $source, string $target) {
89
+        return @ftp_rename($this->connection, $source, $target);
90
+    }
91
+
92
+    public function mdtm(string $path): int {
93
+        $result = @ftp_mdtm($this->connection, $path);
94
+
95
+        // filezilla doesn't like empty path with mdtm
96
+        if ($result === -1 && $path === "") {
97
+            $result = @ftp_mdtm($this->connection, "/");
98
+        }
99
+        return $result;
100
+    }
101
+
102
+    public function size(string $path) {
103
+        return @ftp_size($this->connection, $path);
104
+    }
105
+
106
+    public function systype() {
107
+        return @ftp_systype($this->connection);
108
+    }
109
+
110
+    public function nlist(string $path) {
111
+        $files = @ftp_nlist($this->connection, $path);
112
+        return array_map(function ($name) {
113
+            if (strpos($name, '/') !== false) {
114
+                $name = basename($name);
115
+            }
116
+            return $name;
117
+        }, $files);
118
+    }
119
+
120
+    public function mlsd(string $path) {
121
+        $files = @ftp_mlsd($this->connection, $path);
122
+
123
+        if ($files !== false) {
124
+            return array_map(function ($file) {
125
+                if (strpos($file['name'], '/') !== false) {
126
+                    $file['name'] = basename($file['name']);
127
+                }
128
+                return $file;
129
+            }, $files);
130
+        } else {
131
+            // not all servers support mlsd, in those cases we parse the raw list ourselves
132
+            $rawList = @ftp_rawlist($this->connection, '-aln ' . $path);
133
+            if ($rawList === false) {
134
+                return false;
135
+            }
136
+            return $this->parseRawList($rawList, $path);
137
+        }
138
+    }
139
+
140
+    // rawlist parsing logic is based on the ftp implementation from https://github.com/thephpleague/flysystem
141
+    private function parseRawList(array $rawList, string $directory): array {
142
+        return array_map(function ($item) use ($directory) {
143
+            return $this->parseRawListItem($item, $directory);
144
+        }, $rawList);
145
+    }
146
+
147
+    private function parseRawListItem(string $item, string $directory): array {
148
+        $isWindows = preg_match('/^[0-9]{2,4}-[0-9]{2}-[0-9]{2}/', $item);
149
+
150
+        return $isWindows ? $this->parseWindowsItem($item, $directory) : $this->parseUnixItem($item, $directory);
151
+    }
152
+
153
+    private function parseUnixItem(string $item, string $directory): array {
154
+        $item = preg_replace('#\s+#', ' ', $item, 7);
155
+
156
+        if (count(explode(' ', $item, 9)) !== 9) {
157
+            throw new \RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
158
+        }
159
+
160
+        [$permissions, /* $number */, /* $owner */, /* $group */, $size, $month, $day, $time, $name] = explode(' ', $item, 9);
161
+        if ($name === '.') {
162
+            $type = 'cdir';
163
+        } elseif ($name === '..') {
164
+            $type = 'pdir';
165
+        } else {
166
+            $type = substr($permissions, 0, 1) === 'd' ? 'dir' : 'file';
167
+        }
168
+
169
+        $parsedDate = (new \DateTime())
170
+            ->setTimestamp(strtotime("$month $day $time"));
171
+        $tomorrow = (new \DateTime())->add(new \DateInterval("P1D"));
172
+
173
+        // since the provided date doesn't include the year, we either set it to the correct year
174
+        // or when the date would otherwise be in the future (by more then 1 day to account for timezone errors)
175
+        // we use last year
176
+        if ($parsedDate > $tomorrow) {
177
+            $parsedDate = $parsedDate->sub(new \DateInterval("P1Y"));
178
+        }
179
+
180
+        $formattedDate = $parsedDate
181
+            ->format('YmdHis');
182
+
183
+        return [
184
+            'type' => $type,
185
+            'name' => $name,
186
+            'modify' => $formattedDate,
187
+            'perm' => $this->normalizePermissions($permissions),
188
+            'size' => (int)$size,
189
+        ];
190
+    }
191
+
192
+    private function normalizePermissions(string $permissions) {
193
+        $isDir = substr($permissions, 0, 1) === 'd';
194
+        // remove the type identifier and only use owner permissions
195
+        $permissions = substr($permissions, 1, 4);
196
+
197
+        // map the string rights to the ftp counterparts
198
+        $filePermissionsMap = ['r' => 'r', 'w' => 'fadfw'];
199
+        $dirPermissionsMap = ['r' => 'e', 'w' => 'flcdmp'];
200
+
201
+        $map = $isDir ? $dirPermissionsMap : $filePermissionsMap;
202
+
203
+        return array_reduce(str_split($permissions), function ($ftpPermissions, $permission) use ($map) {
204
+            if (isset($map[$permission])) {
205
+                $ftpPermissions .= $map[$permission];
206
+            }
207
+            return $ftpPermissions;
208
+        }, '');
209
+    }
210
+
211
+    private function parseWindowsItem(string $item, string $directory): array {
212
+        $item = preg_replace('#\s+#', ' ', trim($item), 3);
213
+
214
+        if (count(explode(' ', $item, 4)) !== 4) {
215
+            throw new \RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
216
+        }
217
+
218
+        [$date, $time, $size, $name] = explode(' ', $item, 4);
219
+
220
+        // Check for the correct date/time format
221
+        $format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i';
222
+        $formattedDate = \DateTime::createFromFormat($format, $date . $time)->format('YmdGis');
223
+
224
+        if ($name === '.') {
225
+            $type = 'cdir';
226
+        } elseif ($name === '..') {
227
+            $type = 'pdir';
228
+        } else {
229
+            $type = ($size === '<DIR>') ? 'dir' : 'file';
230
+        }
231
+
232
+        return [
233
+            'type' => $type,
234
+            'name' => $name,
235
+            'modify' => $formattedDate,
236
+            'perm' => ($type === 'file') ? 'adfrw' : 'flcdmpe',
237
+            'size' => (int)$size,
238
+        ];
239
+    }
240 240
 }
Please login to merge, or discard this patch.