Completed
Push — master ( a95984...72cf29 )
by Sebastian
15s queued 10s
created

Sftp::getProtocolName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
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
     * @var string
49
     */
50
    protected $privateKeyPassword;
51
52
    /**
53
     * (non-PHPDoc)
54
     *
55
     * @see    \phpbu\App\Backup\Sync::setup()
56
     * @param  array $config
57
     * @throws \phpbu\App\Backup\Sync\Exception
58
     * @throws \phpbu\App\Exception
59
     */
60 9
    public function setup(array $config)
61
    {
62 9
        if (!Util\Arr::isSetAndNotEmptyString($config, 'password') && !Util\Arr::isSetAndNotEmptyString($config, 'private_key')) {
63 4
            throw new Exception('\'password\' or \'private_key\' must be presented');
64
        }
65 5
        parent::setup($config);
66
67 5
        $this->time = time();
68 5
        $privateKey = Util\Arr::getValue($config, 'private_key', '');
69 5
        if (!empty($privateKey)) {
70
            // get absolute private key path
71 2
            $privateKey = realpath(Util\Path::toAbsolutePath($privateKey, Configuration::getWorkingDirectory()));
72 2
            if ($privateKey === false) {
73 1
                throw new \phpbu\App\Backup\Sync\Exception("Private key not found at specified path");
74
            }
75
        }
76 4
        $this->privateKey         = $privateKey;
77 4
        $this->privateKeyPassword = Util\Arr::getValue($config, 'private_key_password', '');
78 4
        $this->remotePath         = new Path($config['path'], $this->time, Util\Path::hasLeadingSlash($config['path']));
79
80 4
        $this->setUpClearable($config);
81 4
    }
82
83
    /**
84
     * Check for required loaded libraries or extensions.
85
     *
86
     * @throws \phpbu\App\Backup\Sync\Exception
87
     */
88 5
    protected function checkRequirements()
89
    {
90 5
        if (!class_exists('\\phpseclib\\Net\\SFTP')) {
91
            throw new Exception('phpseclib not installed - use composer to install "phpseclib/phpseclib" version 2.x');
92
        }
93 5
    }
94
95
    /**
96
     * Return implemented (*)TP protocol name.
97
     *
98
     * @return string
99
     */
100 1
    protected function getProtocolName()
101
    {
102 1
        return 'SFTP';
103
    }
104
105
    /**
106
     * (non-PHPDoc)
107
     *
108
     * @see    \phpbu\App\Backup\Sync::sync()
109
     * @param  \phpbu\App\Backup\Target $target
110
     * @param  \phpbu\App\Result        $result
111
     * @throws \phpbu\App\Backup\Sync\Exception
112
     */
113 1
    public function sync(Target $target, Result $result)
114
    {
115 1
        $this->sftp     = $this->login();
116 1
        $remoteFilename = $target->getFilename();
117 1
        $localFile      = $target->getPathname();
118
119 1
        foreach ($this->getRemoteDirectoryList() as $dir) {
120 1
            if (!$this->sftp->is_dir($dir)) {
121 1
                $result->debug(sprintf('creating remote dir \'%s\'', $dir));
122 1
                $this->sftp->mkdir($dir);
123
            }
124 1
            $result->debug(sprintf('change to remote dir \'%s\'', $dir));
125 1
            $this->sftp->chdir($dir);
126
        }
127
128 1
        $result->debug(sprintf('store file \'%s\' as \'%s\'', $localFile, $remoteFilename));
129 1
        $result->debug(sprintf('last error \'%s\'', $this->sftp->getLastSFTPError()));
130
131 1
        if (!$this->sftp->put($remoteFilename, $localFile, phpseclib\Net\SFTP::SOURCE_LOCAL_FILE)) {
132
            throw new Exception(sprintf('error uploading file: %s - %s', $localFile, $this->sftp->getLastSFTPError()));
133
        }
134
135
        // run remote cleanup
136 1
        $this->cleanup($target, $result);
137 1
    }
138
139
    /**
140
     * Create a sftp handle.
141
     *
142
     * @return \phpseclib\Net\SFTP
143
     * @throws \phpbu\App\Backup\Sync\Exception
144
     */
145
    protected function login() : phpseclib\Net\SFTP
146
    {
147
        // silence phpseclib
148
        $old  = error_reporting(0);
149
        $sftp = new phpseclib\Net\SFTP($this->host);
150
        if ($this->privateKey) {
151
            $auth = new phpseclib\Crypt\RSA();
152
            $auth->loadKey(file_get_contents($this->privateKey));
153
            if ($this->privateKeyPassword) {
154
                $auth->setPassword($this->privateKeyPassword);
155
            }
156
        } else {
157
            $auth = $this->password;
158
        }
159
        if (!$sftp->login($this->user, $auth)) {
160
            error_reporting($old);
161
            throw new Exception(
162
                sprintf(
163
                    'authentication failed for %s@%s%s',
164
                    $this->user,
165
                    $this->host,
166
                    empty($this->password) ? '' : ' with password ****'
167
                )
168
            );
169
        }
170
        // restore old error reporting
171
        error_reporting($old);
172
173
        // if presented relative path, determine absolute path and update
174
        if (!Util\Path::isAbsolutePath($this->remotePath->getPath())) {
175
            $this->remotePath = new Path($sftp->realpath('.') . '/' . $this->remotePath->getPathRaw(), $this->time, true);
176
        }
177
178
        return $sftp;
179
    }
180
181
    /**
182
     * Return list of remote directories to travers.
183
     *
184
     * @return array
185
     */
186 1
    private function getRemoteDirectoryList() : array
187
    {
188 1
        return Util\Path::getDirectoryListFromAbsolutePath($this->remotePath->getPath());
189
    }
190
191
    /**
192
     * Creates collector for SFTP
193
     *
194
     * @param \phpbu\App\Backup\Target $target
195
     * @return \phpbu\App\Backup\Collector
196
     */
197
    protected function createCollector(Target $target): Collector
198
    {
199
        return new Collector\Sftp($target, $this->sftp, $this->remotePath);
200
    }
201
}
202