smb/src/Wrapped/Parser.php

181 lines
5.7 KiB
PHP

<?php
/**
* Copyright (c) 2014 Robin Appelman <icewind@owncloud.com>
* This file is licensed under the Licensed under the MIT license:
* http://opensource.org/licenses/MIT
*/
namespace Icewind\SMB\Wrapped;
use Icewind\SMB\Exception\AccessDeniedException;
use Icewind\SMB\Exception\AlreadyExistsException;
use Icewind\SMB\Exception\AuthenticationException;
use Icewind\SMB\Exception\Exception;
use Icewind\SMB\Exception\FileInUseException;
use Icewind\SMB\Exception\InvalidHostException;
use Icewind\SMB\Exception\InvalidParameterException;
use Icewind\SMB\Exception\InvalidResourceException;
use Icewind\SMB\Exception\InvalidTypeException;
use Icewind\SMB\Exception\NoLoginServerException;
use Icewind\SMB\Exception\NotEmptyException;
use Icewind\SMB\Exception\NotFoundException;
use Icewind\SMB\TimeZoneProvider;
class Parser {
const MSG_NOT_FOUND = 'Error opening local file ';
/**
* @var \Icewind\SMB\TimeZoneProvider
*/
protected $timeZoneProvider;
// todo replace with static once <5.6 support is dropped
// see error.h
const EXCEPTION_MAP = [
ErrorCodes::LogonFailure => AuthenticationException::class,
ErrorCodes::PathNotFound => NotFoundException::class,
ErrorCodes::ObjectNotFound => NotFoundException::class,
ErrorCodes::NoSuchFile => NotFoundException::class,
ErrorCodes::NameCollision => AlreadyExistsException::class,
ErrorCodes::AccessDenied => AccessDeniedException::class,
ErrorCodes::DirectoryNotEmpty => NotEmptyException::class,
ErrorCodes::FileIsADirectory => InvalidTypeException::class,
ErrorCodes::NotADirectory => InvalidTypeException::class,
ErrorCodes::SharingViolation => FileInUseException::class,
ErrorCodes::InvalidParameter => InvalidParameterException::class
];
/**
* @param TimeZoneProvider $timeZoneProvider
*/
public function __construct(TimeZoneProvider $timeZoneProvider) {
$this->timeZoneProvider = $timeZoneProvider;
}
private function getErrorCode($line) {
$parts = explode(' ', $line);
foreach ($parts as $part) {
if (substr($part, 0, 9) === 'NT_STATUS') {
return $part;
}
}
return false;
}
public function checkForError($output, $path) {
if (strpos($output[0], 'does not exist')) {
throw new NotFoundException($path);
}
$error = $this->getErrorCode($output[0]);
if (substr($output[0], 0, strlen(self::MSG_NOT_FOUND)) === self::MSG_NOT_FOUND) {
$localPath = substr($output[0], strlen(self::MSG_NOT_FOUND));
throw new InvalidResourceException('Failed opening local file "' . $localPath . '" for writing');
}
throw Exception::fromMap(self::EXCEPTION_MAP, $error, $path);
}
/**
* check if the first line holds a connection failure
*
* @param $line
* @throws AuthenticationException
* @throws InvalidHostException
* @throws NoLoginServerException
* @throws AccessDeniedException
*/
public function checkConnectionError($line) {
$line = rtrim($line, ')');
if (substr($line, -23) === ErrorCodes::LogonFailure) {
throw new AuthenticationException('Invalid login');
}
if (substr($line, -26) === ErrorCodes::BadHostName) {
throw new InvalidHostException('Invalid hostname');
}
if (substr($line, -22) === ErrorCodes::Unsuccessful) {
throw new InvalidHostException('Connection unsuccessful');
}
if (substr($line, -28) === ErrorCodes::ConnectionRefused) {
throw new InvalidHostException('Connection refused');
}
if (substr($line, -26) === ErrorCodes::NoLogonServers) {
throw new NoLoginServerException('No login server');
}
if (substr($line, -23) === ErrorCodes::AccessDenied) {
throw new AccessDeniedException('Access denied');
}
}
public function parseMode($mode) {
$result = 0;
$modeStrings = array(
'R' => FileInfo::MODE_READONLY,
'H' => FileInfo::MODE_HIDDEN,
'S' => FileInfo::MODE_SYSTEM,
'D' => FileInfo::MODE_DIRECTORY,
'A' => FileInfo::MODE_ARCHIVE,
'N' => FileInfo::MODE_NORMAL
);
foreach ($modeStrings as $char => $val) {
if (strpos($mode, $char) !== false) {
$result |= $val;
}
}
return $result;
}
public function parseStat($output) {
$data = [];
foreach ($output as $line) {
// A line = explode statement may not fill all array elements
// properly. May happen when accessing non Windows Fileservers
$words = explode(':', $line, 2);
$name = isset($words[0]) ? $words[0] : '';
$value = isset($words[1]) ? $words[1] : '';
$value = trim($value);
$data[$name] = $value;
}
return [
'mtime' => strtotime($data['write_time']),
'mode' => hexdec(substr($data['attributes'], strpos($data['attributes'], '('), -1)),
'size' => isset($data['stream']) ? (int)(explode(' ', $data['stream'])[1]) : 0
];
}
public function parseDir($output, $basePath) {
//last line is used space
array_pop($output);
$regex = '/^\s*(.*?)\s\s\s\s+(?:([NDHARS]*)\s+)?([0-9]+)\s+(.*)$/';
//2 spaces, filename, optional type, size, date
$content = array();
foreach ($output as $line) {
if (preg_match($regex, $line, $matches)) {
list(, $name, $mode, $size, $time) = $matches;
if ($name !== '.' and $name !== '..') {
$mode = $this->parseMode($mode);
$time = strtotime($time . ' ' . $this->timeZoneProvider->get());
$content[] = new FileInfo($basePath . '/' . $name, $name, $size, $time, $mode);
}
}
}
return $content;
}
public function parseListShares($output) {
$shareNames = array();
foreach ($output as $line) {
if (strpos($line, '|')) {
list($type, $name, $description) = explode('|', $line);
if (strtolower($type) === 'disk') {
$shareNames[$name] = $description;
}
} else if (strpos($line, 'Disk')) {
// new output format
list($name, $description) = explode('Disk', $line);
$shareNames[trim($name)] = trim($description);
}
}
return $shareNames;
}
}