Passed
Push — master ( 80a4ed...26ec76 )
by Robin
73:34 queued 71:57
created

Connection   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 111
Duplicated Lines 0 %

Test Coverage

Coverage 70.83%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 48
dl 0
loc 111
ccs 34
cts 48
cp 0.7083
rs 10
c 2
b 0
f 0
wmc 19

7 Methods

Rating   Name   Duplication   Size   Complexity  
A unknownError() 0 9 3
A close() 0 6 2
A isPrompt() 0 2 2
A __construct() 0 3 1
A write() 0 2 1
B read() 0 29 7
A clearTillPrompt() 0 10 3
1
<?php
2
/**
3
 * Copyright (c) 2014 Robin Appelman <[email protected]>
4
 * This file is licensed under the Licensed under the MIT license:
5
 * http://opensource.org/licenses/MIT
6
 */
7
8
namespace Icewind\SMB\Wrapped;
9
10
use Icewind\SMB\Exception\AuthenticationException;
11
use Icewind\SMB\Exception\ConnectException;
12
use Icewind\SMB\Exception\ConnectionException;
13
use Icewind\SMB\Exception\ConnectionRefusedException;
14
use Icewind\SMB\Exception\InvalidHostException;
15
use Icewind\SMB\Exception\NoLoginServerException;
16
17
class Connection extends RawConnection {
18
	const DELIMITER = 'smb:';
19
	const DELIMITER_LENGTH = 4;
20
21
	/** @var Parser */
22
	private $parser;
23
24 486
	public function __construct($command, Parser $parser, $env = []) {
25 486
		parent::__construct($command, $env);
26 486
		$this->parser = $parser;
27 486
	}
28
29
	/**
30
	 * send input to smbclient
31
	 *
32
	 * @param string $input
33
	 */
34 486
	public function write($input) {
35 486
		return parent::write($input . PHP_EOL);
36
	}
37
38
	/**
39
	 * @throws ConnectException
40
	 */
41 486
	public function clearTillPrompt() {
42 486
		$this->write('');
43
		do {
44 486
			$promptLine = $this->readLine();
45 486
			$this->parser->checkConnectionError($promptLine);
46 486
		} while (!$this->isPrompt($promptLine));
47 486
		if ($this->write('') === false) {
48
			throw new ConnectionRefusedException();
49
		}
50 486
		$this->readLine();
51 486
	}
52
53
	/**
54
	 * get all unprocessed output from smbclient until the next prompt
55
	 *
56
	 * @param callable $callback (optional) callback to call for every line read
57
	 * @return string[]
58
	 * @throws AuthenticationException
59
	 * @throws ConnectException
60
	 * @throws ConnectionException
61
	 * @throws InvalidHostException
62
	 * @throws NoLoginServerException
63
	 */
64 484
	public function read(callable $callback = null) {
65 484
		if (!$this->isValid()) {
66
			throw new ConnectionException('Connection not valid');
67
		}
68 484
		$promptLine = $this->readLine(); //first line is prompt
69 484
		$this->parser->checkConnectionError($promptLine);
70
71 484
		$output = [];
72 484
		if (!$this->isPrompt($promptLine)) {
73 2
			$line = $promptLine;
74
		} else {
75 484
			$line = $this->readLine();
76
		}
77 484
		if ($line === false) {
78
			$this->unknownError($promptLine);
79
		}
80 484
		while (!$this->isPrompt($line)) { //next prompt functions as delimiter
81 484
			if (is_callable($callback)) {
82
				$result = $callback($line);
83
				if ($result === false) { // allow the callback to close the connection for infinite running commands
84
					$this->close(true);
85
					break;
86
				}
87
			} else {
88 484
				$output[] = $line;
89
			}
90 484
			$line = $this->readLine();
91
		}
92 484
		return $output;
93
	}
94
95
	/**
96
	 * Check
97
	 *
98
	 * @param $line
99
	 * @return bool
100
	 */
101 486
	private function isPrompt($line) {
102 486
		return mb_substr($line, 0, self::DELIMITER_LENGTH) === self::DELIMITER || $line === false;
103
	}
104
105
	/**
106
	 * @param string $promptLine (optional) prompt line that might contain some info about the error
107
	 * @throws ConnectException
108
	 */
109
	private function unknownError($promptLine = '') {
110
		if ($promptLine) { //maybe we have some error we missed on the previous line
111
			throw new ConnectException('Unknown error (' . $promptLine . ')');
112
		} else {
113
			$error = $this->readError(); // maybe something on stderr
114
			if ($error) {
115
				throw new ConnectException('Unknown error (' . $error . ')');
116
			} else {
117
				throw new ConnectException('Unknown error');
118
			}
119
		}
120
	}
121
122 486
	public function close($terminate = true) {
123 486
		if (get_resource_type($this->getInputStream()) === 'stream') {
124
			// ignore any errors while trying to send the close command, the process might already be dead
125 486
			@$this->write('close' . PHP_EOL);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for write(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

125
			/** @scrutinizer ignore-unhandled */ @$this->write('close' . PHP_EOL);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
126
		}
127 486
		parent::close($terminate);
128 486
	}
129
}
130