Completed
Push — master ( 50c9d0...db51c6 )
by Robin
06:58
created

Parser::parseListShares()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 11
cts 11
cp 1
rs 9.2
cc 4
eloc 8
nc 4
nop 1
crap 4
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;
9
10
use Icewind\SMB\Exception\AccessDeniedException;
11
use Icewind\SMB\Exception\AlreadyExistsException;
12
use Icewind\SMB\Exception\AuthenticationException;
13
use Icewind\SMB\Exception\Exception;
14
use Icewind\SMB\Exception\FileInUseException;
15
use Icewind\SMB\Exception\InvalidHostException;
16
use Icewind\SMB\Exception\InvalidResourceException;
17
use Icewind\SMB\Exception\InvalidTypeException;
18
use Icewind\SMB\Exception\NoLoginServerException;
19
use Icewind\SMB\Exception\NotEmptyException;
20
use Icewind\SMB\Exception\NotFoundException;
21
22
class Parser {
23
	const MSG_NOT_FOUND = 'Error opening local file ';
24
25
	/**
26
	 * @var \Icewind\SMB\TimeZoneProvider
27
	 */
28
	protected $timeZoneProvider;
29
30
	/**
31
	 * @param \Icewind\SMB\TimeZoneProvider $timeZoneProvider
32
	 */
33 552
	public function __construct(TimeZoneProvider $timeZoneProvider) {
34 552
		$this->timeZoneProvider = $timeZoneProvider;
35 552
	}
36
37 36
	private function getErrorCode($line) {
38 36
		$parts = explode(' ', $line);
39 36
		foreach ($parts as $part) {
40 36
			if (substr($part, 0, 9) === 'NT_STATUS') {
41 36
				return $part;
42
			}
43 6
		}
44 4
		return false;
45
	}
46
47 36
	public function checkForError($output, $path) {
48 36
		if (strpos($output[0], 'does not exist')) {
49
			throw new NotFoundException($path);
50
		}
51 36
		$error = $this->getErrorCode($output[0]);
52
53 36
		if (substr($output[0], 0, strlen(self::MSG_NOT_FOUND)) === self::MSG_NOT_FOUND) {
54 2
			$localPath = substr($output[0], strlen(self::MSG_NOT_FOUND));
55 2
			throw new InvalidResourceException('Failed opening local file "' . $localPath . '" for writing');
56
		}
57
58
		switch ($error) {
59 36
			case ErrorCodes::PathNotFound:
60 36
			case ErrorCodes::ObjectNotFound:
61 36
			case ErrorCodes::NoSuchFile:
62 24
				throw new NotFoundException($path);
63 14
			case ErrorCodes::NameCollision:
64 2
				throw new AlreadyExistsException($path);
65 12
			case ErrorCodes::AccessDenied:
66
				throw new AccessDeniedException($path);
67 12
			case ErrorCodes::DirectoryNotEmpty:
68 2
				throw new NotEmptyException($path);
69 10
			case ErrorCodes::FileIsADirectory:
70 10
			case ErrorCodes::NotADirectory:
71 6
				throw new InvalidTypeException($path);
72 4
			case ErrorCodes::SharingViolation:
73 2
				throw new FileInUseException($path);
74 2
			default:
75 2
				throw Exception::unknown($path, $error);
76 2
		}
77
	}
78
79
	/**
80
	 * check if the first line holds a connection failure
81
	 *
82
	 * @param $line
83
	 * @throws AuthenticationException
84
	 * @throws InvalidHostException
85
	 * @throws NoLoginServerException
86
	 */
87 504
	public function checkConnectionError($line) {
88 504
		$line = rtrim($line, ')');
89 504
		if (substr($line, -23) === ErrorCodes::LogonFailure) {
90 2
			throw new AuthenticationException('Invalid login');
91
		}
92 502
		if (substr($line, -26) === ErrorCodes::BadHostName) {
93 6
			throw new InvalidHostException('Invalid hostname');
94
		}
95 498
		if (substr($line, -22) === ErrorCodes::Unsuccessful) {
96
			throw new InvalidHostException('Connection unsuccessful');
97
		}
98 498
		if (substr($line, -28) === ErrorCodes::ConnectionRefused) {
99
			throw new InvalidHostException('Connection refused');
100
		}
101 498
		if (substr($line, -26) === ErrorCodes::NoLogonServers) {
102
			throw new NoLoginServerException('No login server');
103
		}
104 498
	}
105
106 226
	public function parseMode($mode) {
107 226
		$result = 0;
108
		$modeStrings = array(
109 226
			'R' => FileInfo::MODE_READONLY,
110 226
			'H' => FileInfo::MODE_HIDDEN,
111 226
			'S' => FileInfo::MODE_SYSTEM,
112 226
			'D' => FileInfo::MODE_DIRECTORY,
113 226
			'A' => FileInfo::MODE_ARCHIVE,
114
			'N' => FileInfo::MODE_NORMAL
115 226
		);
116 226
		foreach ($modeStrings as $char => $val) {
117 226
			if (strpos($mode, $char) !== false) {
118 210
				$result |= $val;
119 210
			}
120 226
		}
121 226
		return $result;
122
	}
123
124 58
	public function parseStat($output) {
125 58
		$mtime = 0;
126 58
		$mode = 0;
127 58
		$size = 0;
128 58
		foreach ($output as $line) {
129
			// A line = explode statement may not fill all array elements
130
			// properly. May happen when accessing non Windows Fileservers
131 58
			$words = explode(':', $line, 2);
132 58
			$name = isset($words[0]) ? $words[0] : '';
133 58
			$value = isset($words[1]) ? $words[1] : '';
134 58
			$value = trim($value);
135 58
			if ($name === 'write_time') {
136 58
				$mtime = strtotime($value);
137 58
			} else if ($name === 'attributes') {
138 58
				$mode = hexdec(substr($value, strpos($value, '('), -1));
139 58
			} else if ($name === 'stream') {
140 56
				list(, $size,) = explode(' ', $value);
141 56
				$size = intval($size);
142 56
			}
143 58
		}
144
		return array(
145 58
			'mtime' => $mtime,
146 58
			'mode' => $mode,
147
			'size' => $size
148 58
		);
149
	}
150
151 500
	public function parseDir($output, $basePath) {
152
		//last line is used space
153 500
		array_pop($output);
154 500
		$regex = '/^\s*(.*?)\s\s\s\s+(?:([NDHARS]*)\s+)?([0-9]+)\s+(.*)$/';
155
		//2 spaces, filename, optional type, size, date
156 500
		$content = array();
157 500
		foreach ($output as $line) {
158 500
			if (preg_match($regex, $line, $matches)) {
159 500
				list(, $name, $mode, $size, $time) = $matches;
160 500
				if ($name !== '.' and $name !== '..') {
161 194
					$mode = $this->parseMode($mode);
162 194
					$time = strtotime($time . ' ' . $this->timeZoneProvider->get());
163 194
					$content[] = new FileInfo($basePath . '/' . $name, $name, $size, $time, $mode);
164 194
				}
165 500
			}
166 500
		}
167 500
		return $content;
168
	}
169
170 4
	public function parseListShares($output) {
171 4
		$shareNames = array();
172 4
		foreach ($output as $line) {
173 4
			if (strpos($line, '|')) {
174 4
				list($type, $name, $description) = explode('|', $line);
175 4
				if (strtolower($type) === 'disk') {
176 4
					$shareNames[$name] = $description;
177 4
				}
178 4
			}
179 4
		}
180 4
		return $shareNames;
181
	}
182
}
183