Completed
Push — master ( b43c12...260188 )
by Robin
02:29
created

Parser::parseMode()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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