Passed
Push — master ( 5330ed...57b234 )
by Robin
02:47
created

Connection::read()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 7.049

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 21
nc 17
nop 1
dl 0
loc 29
ccs 18
cts 20
cp 0.9
crap 7.049
rs 8.6506
c 1
b 0
f 0
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\InvalidHostException;
14
use Icewind\SMB\Exception\NoLoginServerException;
15
16
class Connection extends RawConnection {
17
	const DELIMITER = 'smb:';
18
	const DELIMITER_LENGTH = 4;
19
20
	/** @var Parser */
21
	private $parser;
22
23 508
	public function __construct($command, Parser $parser, $env = []) {
24 508
		parent::__construct($command, $env);
25 508
		$this->parser = $parser;
26 508
	}
27
28
	/**
29
	 * send input to smbclient
30
	 *
31
	 * @param string $input
32
	 */
33 508
	public function write($input) {
34 508
		parent::write($input . PHP_EOL);
35 508
	}
36
37
	/**
38
	 * @throws ConnectException
39
	 */
40 508
	public function clearTillPrompt() {
41 508
		$this->write('');
42
		do {
43 508
			$promptLine = $this->readLine();
44 508
			$this->parser->checkConnectionError($promptLine);
45 508
		} while (!$this->isPrompt($promptLine));
46 508
		$this->write('');
47 508
		$this->readLine();
48 508
	}
49
50
	/**
51
	 * get all unprocessed output from smbclient until the next prompt
52
	 *
53
	 * @param callable $callback (optional) callback to call for every line read
54
	 * @return string[]
55
	 * @throws AuthenticationException
56
	 * @throws ConnectException
57
	 * @throws ConnectionException
58
	 * @throws InvalidHostException
59
	 * @throws NoLoginServerException
60
	 */
61 506
	public function read(callable $callback = null) {
62 506
		if (!$this->isValid()) {
63
			throw new ConnectionException('Connection not valid');
64
		}
65 506
		$promptLine = $this->readLine(); //first line is prompt
66 506
		$this->parser->checkConnectionError($promptLine);
67
68 506
		$output = [];
69 506
		if (!$this->isPrompt($promptLine)) {
70 2
			$line = $promptLine;
71
		} else {
72 506
			$line = $this->readLine();
73
		}
74 506
		if ($line === false) {
75
			$this->unknownError($promptLine);
76
		}
77 506
		while (!$this->isPrompt($line)) { //next prompt functions as delimiter
78 502
			if (is_callable($callback)) {
79 4
				$result = $callback($line);
80 4
				if ($result === false) { // allow the callback to close the connection for infinite running commands
81 4
					$this->close(true);
82 4
					break;
83
				}
84
			} else {
85 498
				$output[] .= $line;
86
			}
87 498
			$line = $this->readLine();
88
		}
89 506
		return $output;
90
	}
91
92
	/**
93
	 * Check
94
	 *
95
	 * @param $line
96
	 * @return bool
97
	 */
98 508
	private function isPrompt($line) {
99 508
		return mb_substr($line, 0, self::DELIMITER_LENGTH) === self::DELIMITER || $line === false;
100
	}
101
102
	/**
103
	 * @param string $promptLine (optional) prompt line that might contain some info about the error
104
	 * @throws ConnectException
105
	 */
106
	private function unknownError($promptLine = '') {
107
		if ($promptLine) { //maybe we have some error we missed on the previous line
108
			throw new ConnectException('Unknown error (' . $promptLine . ')');
109
		} else {
110
			$error = $this->readError(); // maybe something on stderr
111
			if ($error) {
112
				throw new ConnectException('Unknown error (' . $error . ')');
113
			} else {
114
				throw new ConnectException('Unknown error');
115
			}
116
		}
117
	}
118
119 508
	public function close($terminate = true) {
120 508
		if (get_resource_type($this->getInputStream()) === 'stream') {
121
			// ignore any errors while trying to send the close command, the process might already be dead
122 508
			@$this->write('close' . PHP_EOL);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->write('close' . I...nd\SMB\Wrapped\PHP_EOL) targeting Icewind\SMB\Wrapped\Connection::write() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
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

122
			/** @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...
123
		}
124 508
		parent::close($terminate);
125 508
	}
126
}
127