Completed
Push — master ( 72cf29...ecec76 )
by Sebastian
03:13
created

Sftp::createCollector()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 2
1
<?php
2
namespace phpbu\App\Backup\Sync;
3
4
use phpbu\App\Backup\Collector;
5
use phpbu\App\Backup\Path;
6
use phpbu\App\Backup\Target;
7
use phpbu\App\Configuration;
8
use phpbu\App\Result;
9
use phpbu\App\Util;
10
use phpseclib;
11
12
/**
13
 * Sftp sync
14
 *
15
 * @package    phpbu
16
 * @subpackage Backup
17
 * @author     Sebastian Feldmann <[email protected]>
18
 * @copyright  Sebastian Feldmann <[email protected]>
19
 * @license    https://opensource.org/licenses/MIT The MIT License (MIT)
20
 * @link       http://phpbu.de/
21
 * @since      Class available since Release 1.0.0
22
 */
23
class Sftp extends Xtp
24
{
25
    /**
26
     * @var phpseclib\Net\SFTP
27
     */
28
    protected $sftp;
29
30
    /**
31
     * Remote path where to put the backup
32
     *
33
     * @var Path
34
     */
35
    protected $remotePath;
36
37
    /**
38
     * @var int
39
     */
40
    protected $time;
41
42
    /**
43
     * @var string
44
     */
45
    protected $privateKey;
46
47
    /**
48
     * (non-PHPDoc)
49
     *
50
     * @see    \phpbu\App\Backup\Sync::setup()
51
     * @param  array $config
52
     * @throws \phpbu\App\Backup\Sync\Exception
53
     * @throws \phpbu\App\Exception
54
     */
55 9
    public function setup(array $config)
56
    {
57 9
        if (!Util\Arr::isSetAndNotEmptyString($config, 'password') && !Util\Arr::isSetAndNotEmptyString($config, 'private_key')) {
58 4
            throw new Exception('\'password\' or \'private_key\' must be presented');
59
        }
60 5
        parent::setup($config);
61
62 5
        $this->time = time();
63 5
        $privateKey = Util\Arr::getValue($config, 'key', '');
64 5
        if (!empty($privateKey)) {
65
            // get absolute private key path
66 2
            $privateKey = Util\Path::toAbsolutePath($privateKey, Configuration::getWorkingDirectory());
67 2
            if (!file_exists($privateKey)) {
68 1
                throw new Exception("Private key not found at specified path");
69
            }
70
        }
71 4
        $this->privateKey = $privateKey;
72 4
        $this->remotePath = new Path($config['path'], $this->time);
73
74 4
        $this->setUpClearable($config);
75 4
    }
76
77
    /**
78
     * Check for required loaded libraries or extensions.
79
     *
80
     * @throws \phpbu\App\Backup\Sync\Exception
81
     */
82 5
    protected function checkRequirements()
83
    {
84 5
        if (!class_exists('\\phpseclib\\Net\\SFTP')) {
85
            throw new Exception('phpseclib not installed - use composer to install "phpseclib/phpseclib" version 2.x');
86
        }
87 5
    }
88
89
    /**
90
     * Return implemented (*)TP protocol name.
91
     *
92
     * @return string
93
     */
94 1
    protected function getProtocolName()
95
    {
96 1
        return 'SFTP';
97
    }
98
99
    /**
100
     * (non-PHPDoc)
101
     *
102
     * @see    \phpbu\App\Backup\Sync::sync()
103
     * @param  \phpbu\App\Backup\Target $target
104
     * @param  \phpbu\App\Result        $result
105
     * @throws \phpbu\App\Backup\Sync\Exception
106
     */
107 1
    public function sync(Target $target, Result $result)
108
    {
109 1
        $this->sftp     = $this->login();
110 1
        $remoteFilename = $target->getFilename();
111 1
        $localFile      = $target->getPathname();
112
113 1
        foreach ($this->getRemoteDirectoryList() as $dir) {
114 1
            if (!$this->sftp->is_dir($dir)) {
115 1
                $result->debug(sprintf('creating remote dir \'%s\'', $dir));
116 1
                $this->sftp->mkdir($dir);
117
            }
118 1
            $result->debug(sprintf('change to remote dir \'%s\'', $dir));
119 1
            $this->sftp->chdir($dir);
120
        }
121
122 1
        $result->debug(sprintf('store file \'%s\' as \'%s\'', $localFile, $remoteFilename));
123 1
        $result->debug(sprintf('last error \'%s\'', $this->sftp->getLastSFTPError()));
124
125 1
        if (!$this->sftp->put($remoteFilename, $localFile, phpseclib\Net\SFTP::SOURCE_LOCAL_FILE)) {
126
            throw new Exception(sprintf('error uploading file: %s - %s', $localFile, $this->sftp->getLastSFTPError()));
127
        }
128
129
        // run remote cleanup
130 1
        $this->cleanup($target, $result);
131 1
    }
132
133
    /**
134
     * Create a sftp handle.
135
     *
136
     * @return \phpseclib\Net\SFTP
137
     * @throws \phpbu\App\Backup\Sync\Exception
138
     */
139
    protected function login() : phpseclib\Net\SFTP
140
    {
141
        // silence phpseclib
142
        $old  = error_reporting(0);
143
        $sftp = new phpseclib\Net\SFTP($this->host);
144
        $auth = $this->getAuth();
145
146
        if (!$sftp->login($this->user, $auth)) {
147
            error_reporting($old);
148
            throw new Exception(
149
                sprintf(
150
                    'authentication failed for %s@%s%s',
151
                    $this->user,
152
                    $this->host,
153
                    empty($this->password) ? '' : ' with password ****'
154
                )
155
            );
156
        }
157
        // restore old error reporting
158
        error_reporting($old);
159
160
        // if presented relative path, determine absolute path and update
161
        if (!Util\Path::isAbsolutePath($this->remotePath->getPath())) {
162
            $this->remotePath = new Path($sftp->realpath('.') . '/' . $this->remotePath->getPathRaw(), $this->time);
163
        }
164
165
        return $sftp;
166
    }
167
168
    /**
169
     * Return a phpseclib authentication thingy.
170
     *
171
     * @return \phpseclib\Crypt\RSA|string
172
     */
173
    private function getAuth()
174
    {
175
        $auth = $this->password;
176
        // if private key should be used
177
        if ($this->privateKey) {
178
            $auth = new phpseclib\Crypt\RSA();
179
            $auth->loadKey(file_get_contents($this->privateKey));
180
            if ($this->password) {
181
                $auth->setPassword($this->password);
182
            }
183
        }
184
        return $auth;
185
    }
186
187
    /**
188
     * Return list of remote directories to travers.
189
     *
190
     * @return array
191
     */
192 1
    private function getRemoteDirectoryList() : array
193
    {
194 1
        return Util\Path::getDirectoryListFromAbsolutePath($this->remotePath->getPath());
195
    }
196
197
    /**
198
     * Creates collector for SFTP
199
     *
200
     * @param \phpbu\App\Backup\Target $target
201
     * @return \phpbu\App\Backup\Collector
202
     */
203
    protected function createCollector(Target $target): Collector
204
    {
205
        return new Collector\Sftp($target, $this->sftp, $this->remotePath);
206
    }
207
}
208