mirror of
https://codeberg.org/icewind/SMB.git
synced 2026-06-03 17:24:07 +02:00
Merge pull request 'nix-ci' (#138) from nix-ci into master
Reviewed-on: https://codeberg.org/icewind/SMB/pulls/138
This commit is contained in:
commit
d91b5b63b1
81 changed files with 4808 additions and 313 deletions
|
|
@ -6,6 +6,7 @@ end_of_line=lf
|
|||
insert_final_newline=false
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
insert_final_newline=true
|
||||
|
||||
[{composer.lock,.babelrc,.stylelintrc,.eslintrc,jest.config,*.bowerrc,*.jsb3,*.jsb2,*.json}]
|
||||
indent_style=space
|
||||
|
|
@ -15,7 +16,7 @@ indent_size=2
|
|||
indent_style=tab
|
||||
tab_width=4
|
||||
|
||||
[{*.module,*.hphp,*.phtml,*.php5,*.php4,*.php,*.inc}]
|
||||
[{*.module,*.hphp,*.phtml,*.php5,*.php4,*.php,*.stub,*.inc}]
|
||||
indent_style=tab
|
||||
tab_width=4
|
||||
|
||||
|
|
|
|||
|
|
@ -10,33 +10,29 @@ on:
|
|||
name: CI
|
||||
|
||||
jobs:
|
||||
php-cs-fixer:
|
||||
name: PHP-CS-Fixer
|
||||
runs-on: ubuntu-latest
|
||||
checks:
|
||||
name: Nix checks
|
||||
runs-on: nix
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup PHP
|
||||
uses: https://github.com/shivammathur/setup-php@v2
|
||||
- uses: https://codeberg.org/icewind/attic-action@v1
|
||||
with:
|
||||
php-version: '8.2'
|
||||
extensions: apcu
|
||||
- name: Composer
|
||||
run: composer install
|
||||
- name: PHP-CS-Fixer
|
||||
run: |
|
||||
composer run cs:check
|
||||
name: link
|
||||
instance: https://cache.icewind.link
|
||||
authToken: "${{ secrets.ATTIC_TOKEN }}"
|
||||
- run: nix flake check --keep-going
|
||||
|
||||
php-versions:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: nix
|
||||
name: Unit tests - PHP ${{ matrix.php-version }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
- "82"
|
||||
- "83"
|
||||
- "84"
|
||||
|
||||
services:
|
||||
samba:
|
||||
|
|
@ -44,52 +40,44 @@ jobs:
|
|||
env:
|
||||
ACCOUNT_test: test
|
||||
UID_test: 1000
|
||||
SAMBA_VOLUME_CONFIG_test: "[test]; path=/tmp; valid users = test; guest ok = no; read only = no; browseable = yes"
|
||||
SAMBA_VOLUME_CONFIG_test:
|
||||
"[test]; path=/tmp; valid users = test; guest ok = no; read only =
|
||||
no; browseable = yes"
|
||||
|
||||
steps:
|
||||
- name: Install packages
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y smbclient libsmbclient-dev
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup PHP
|
||||
uses: https://github.com/shivammathur/setup-php@v2
|
||||
- uses: https://codeberg.org/icewind/attic-action@v1
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
extensions: apcu, smbclient
|
||||
coverage: pcov
|
||||
name: link
|
||||
instance: https://cache.icewind.link
|
||||
authToken: "${{ secrets.ATTIC_TOKEN }}"
|
||||
- name: Composer
|
||||
run: composer install
|
||||
run: |
|
||||
cp -r $(nix build .#vendor --print-out-paths --no-link)/vendor vendor
|
||||
- name: Config
|
||||
run: |
|
||||
echo '{"host": "samba","user": "test","password": "test","share": "test","root": ""}' > tests/config.json
|
||||
- name: Setup phpunit
|
||||
run: |
|
||||
nix build .#apps.x86_64-linux.phpunit${{ matrix.php-version }}.program
|
||||
- name: PHPUnit Tests - smbclient
|
||||
uses: https://github.com/nick-invision/retry@v2
|
||||
with:
|
||||
timeout_minutes: 2
|
||||
max_attempts: 3
|
||||
retry_on: timeout
|
||||
command: php ./vendor/bin/phpunit tests -c tests/phpunit.xml --coverage-clover=coverage.xml
|
||||
run:
|
||||
nix run .#phpunit${{ matrix.php-version }} -- tests -c
|
||||
tests/phpunit.xml
|
||||
env:
|
||||
BACKEND: smbclient
|
||||
- name: PHPUnit Tests - libsmbclient
|
||||
uses: https://github.com/nick-invision/retry@v2
|
||||
with:
|
||||
timeout_minutes: 2
|
||||
max_attempts: 3
|
||||
retry_on: timeout
|
||||
command: php ./vendor/bin/phpunit tests -c tests/phpunit.xml --coverage-clover=coverage.xml
|
||||
run: |
|
||||
mkdir -p /var/lock
|
||||
nix run .#phpunit${{ matrix.php-version }} -- tests -c tests/phpunit.xml
|
||||
env:
|
||||
BACKEND: libsmbclient
|
||||
- uses: https://github.com/codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage.xml
|
||||
|
||||
smb-versions:
|
||||
runs-on: ubuntu-22.04
|
||||
name: Unit tests - Samba ${{ matrix.server-version }} - smbclient ${{ matrix.client-version }}
|
||||
name:
|
||||
Unit tests - Samba ${{ matrix.server-version }} - smbclient ${{
|
||||
matrix.client-version }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
|
@ -115,7 +103,9 @@ jobs:
|
|||
env:
|
||||
ACCOUNT_test: test
|
||||
UID_test: 1000
|
||||
SAMBA_VOLUME_CONFIG_test: "[test]; path=/tmp; valid users = test; guest ok = no; read only = no; browseable = yes"
|
||||
SAMBA_VOLUME_CONFIG_test:
|
||||
"[test]; path=/tmp; valid users = test; guest ok = no; read only =
|
||||
no; browseable = yes"
|
||||
|
||||
steps:
|
||||
- name: Setup smbclient
|
||||
|
|
@ -134,7 +124,6 @@ jobs:
|
|||
with:
|
||||
php-version: 8.2
|
||||
extensions: apcu, smbclient
|
||||
coverage: pcov
|
||||
- name: Composer
|
||||
run: composer install
|
||||
- name: Config
|
||||
|
|
@ -146,12 +135,9 @@ jobs:
|
|||
timeout_minutes: 2
|
||||
max_attempts: 3
|
||||
retry_on: timeout
|
||||
command: php ./vendor/bin/phpunit tests -c tests/phpunit.xml --coverage-clover=coverage.xml
|
||||
command: php ./vendor/bin/phpunit tests -c tests/phpunit.xml
|
||||
env:
|
||||
BACKEND: smbclient
|
||||
- uses: https://github.com/codecov/codecov-action@v3
|
||||
with:
|
||||
files: ./coverage.xml
|
||||
|
||||
alpine-test:
|
||||
runs-on: alpine-latest
|
||||
|
|
@ -163,7 +149,9 @@ jobs:
|
|||
env:
|
||||
ACCOUNT_test: test
|
||||
UID_test: 1000
|
||||
SAMBA_VOLUME_CONFIG_test: "[test]; path=/tmp; valid users = test; guest ok = no; read only = no; browseable = yes"
|
||||
SAMBA_VOLUME_CONFIG_test:
|
||||
"[test]; path=/tmp; valid users = test; guest ok = no; read only =
|
||||
no; browseable = yes"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
@ -182,64 +170,6 @@ jobs:
|
|||
timeout_minutes: 2
|
||||
max_attempts: 3
|
||||
retry_on: timeout
|
||||
command: php ./vendor/bin/phpunit tests -c tests/phpunit.xml --coverage-clover=coverage.xml
|
||||
command: php ./vendor/bin/phpunit tests -c tests/phpunit.xml
|
||||
env:
|
||||
BACKEND: smbclient
|
||||
|
||||
static-psalm-analysis:
|
||||
runs-on: ubuntu-latest
|
||||
name: Psalm static analysis - PHP ${{ matrix.php-version }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.2"
|
||||
- "8.3"
|
||||
- "8.4"
|
||||
|
||||
steps:
|
||||
- name: krb5-dev
|
||||
env:
|
||||
DEBIAN_FRONTEND: noninteractive
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libkrb5-dev libsmbclient-dev
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up php
|
||||
uses: https://github.com/shivammathur/setup-php@master
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
extensions: apcu, smbclient, krb5
|
||||
env:
|
||||
fail-fast: true
|
||||
- name: Install dependencies
|
||||
run: composer i
|
||||
- name: Run coding standards check
|
||||
run: composer run psalm
|
||||
|
||||
phpstan:
|
||||
name: PHPStan Static Analysis
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: krb5-dev
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libkrb5-dev
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup PHP
|
||||
uses: https://github.com/shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: 8.3
|
||||
extensions: apcu, smbclient, krb5
|
||||
env:
|
||||
fail-fast: true
|
||||
- name: Composer
|
||||
run: composer install
|
||||
- env:
|
||||
BACKEND: smbclient
|
||||
run: php ./vendor/bin/phpstan analyse --level 6 src
|
||||
|
|
@ -10,6 +10,6 @@ jobs:
|
|||
reuse-compliance-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: https://github.com/actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
- name: REUSE Compliance Check
|
||||
uses: https://github.com/fsfe/reuse-action@a46482ca367aef4454a87620aa37c2be4b2f8106 # v3.0.0
|
||||
- uses: https://github.com/actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
|
||||
- name: REUSE Compliance Check
|
||||
uses: https://github.com/fsfe/reuse-action@a46482ca367aef4454a87620aa37c2be4b2f8106 # v3.0.0
|
||||
|
|
|
|||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -2,8 +2,8 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
.idea
|
||||
vendor
|
||||
composer.lock
|
||||
.php_cs.cache
|
||||
listen.php
|
||||
test.php
|
||||
*.cache
|
||||
*.cache
|
||||
result
|
||||
|
|
|
|||
|
|
@ -5,19 +5,17 @@
|
|||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
$finder = PhpCsFixer\Finder::create()
|
||||
->exclude('vendor')
|
||||
->in(__DIR__)
|
||||
;
|
||||
$finder = PhpCsFixer\Finder::create()->in(__DIR__);
|
||||
|
||||
return (new PhpCsFixer\Config())
|
||||
->setRules([
|
||||
'@PSR2' => true,
|
||||
'@PSR2' => true,
|
||||
'curly_braces_position' => [
|
||||
'classes_opening_brace' => 'same_line',
|
||||
'classes_opening_brace' => 'same_line',
|
||||
'functions_opening_brace' => 'same_line',
|
||||
],
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'braces' => ['position_after_functions_and_oop_constructs' => 'same'],
|
||||
'array_syntax' => ['syntax' => 'short'],
|
||||
'braces' => ['position_after_functions_and_oop_constructs' => 'same'],
|
||||
'binary_operator_spaces' => ['operators' => ['=>' => 'align_single_space']],
|
||||
])
|
||||
->setIndent("\t")
|
||||
|
|
|
|||
83
README.md
83
README.md
|
|
@ -2,24 +2,25 @@
|
|||
- SPDX-FileCopyrightText: 2014 Robin Appelman <robin@icewind.nl>
|
||||
- SPDX-License-Identifier: MIT
|
||||
-->
|
||||
SMB
|
||||
===
|
||||
|
||||
# SMB
|
||||
|
||||
[](https://github.com/icewind1991/SMB/actions/workflows/ci.yaml)
|
||||
[](https://codecov.io/gh/icewind1991/SMB)
|
||||
|
||||
PHP wrapper for `smbclient` and [`libsmbclient-php`](https://github.com/eduardok/libsmbclient-php)
|
||||
PHP wrapper for `smbclient` and
|
||||
[`libsmbclient-php`](https://github.com/eduardok/libsmbclient-php)
|
||||
|
||||
- Reuses a single `smbclient` instance for multiple requests
|
||||
- Doesn't leak the password to the process list
|
||||
- Simple 1-on-1 mapping of SMB commands
|
||||
- A stream-based api to remove the need for temporary files
|
||||
- Support for using libsmbclient directly trough [`libsmbclient-php`](https://github.com/eduardok/libsmbclient-php)
|
||||
- Support for using libsmbclient directly trough
|
||||
[`libsmbclient-php`](https://github.com/eduardok/libsmbclient-php)
|
||||
|
||||
Examples
|
||||
----
|
||||
## Examples
|
||||
|
||||
### Connect to a share ###
|
||||
### Connect to a share
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
|
@ -35,10 +36,10 @@ $server = $serverFactory->createServer('localhost', $auth);
|
|||
$share = $server->getShare('test');
|
||||
```
|
||||
|
||||
The server factory will automatically pick between the `smbclient` and `libsmbclient-php`
|
||||
based backend depending on what is available.
|
||||
The server factory will automatically pick between the `smbclient` and
|
||||
`libsmbclient-php` based backend depending on what is available.
|
||||
|
||||
### Using anonymous authentication ###
|
||||
### Using anonymous authentication
|
||||
|
||||
```php
|
||||
$serverFactory = new ServerFactory();
|
||||
|
|
@ -46,7 +47,7 @@ $auth = new AnonymousAuth();
|
|||
$server = $serverFactory->createServer('localhost', $auth);
|
||||
```
|
||||
|
||||
### Using kerberos authentication ###
|
||||
### Using kerberos authentication
|
||||
|
||||
There are two ways of using kerberos to authenticate against the smb server:
|
||||
|
||||
|
|
@ -55,7 +56,8 @@ There are two ways of using kerberos to authenticate against the smb server:
|
|||
|
||||
### Using a server ticket
|
||||
|
||||
Using a server ticket allows the web server to authenticate against the smb server using an existing machine account.
|
||||
Using a server ticket allows the web server to authenticate against the smb
|
||||
server using an existing machine account.
|
||||
|
||||
The ticket needs to be available in the environment of the php process.
|
||||
|
||||
|
|
@ -67,15 +69,18 @@ $server = $serverFactory->createServer('localhost', $auth);
|
|||
|
||||
### Re-using a client ticket
|
||||
|
||||
By re-using a client ticket you can create a single sign-on setup where the user authenticates against
|
||||
the web service using kerberos. And the web server can forward that ticket to the smb server, allowing it
|
||||
to act on the behalf of the user without requiring the user to enter his password.
|
||||
By re-using a client ticket you can create a single sign-on setup where the user
|
||||
authenticates against the web service using kerberos. And the web server can
|
||||
forward that ticket to the smb server, allowing it to act on the behalf of the
|
||||
user without requiring the user to enter his password.
|
||||
|
||||
The setup for such a system is fairly involved and requires roughly the following this
|
||||
The setup for such a system is fairly involved and requires roughly the
|
||||
following this
|
||||
|
||||
- The web server is authenticated against kerberos with a machine account
|
||||
- Delegation is enabled for the web server's machine account
|
||||
- The web server is setup to perform kerberos authentication and save the ticket in it's environment
|
||||
- The web server is setup to perform kerberos authentication and save the ticket
|
||||
in it's environment
|
||||
- Php has the krb5 extension installed
|
||||
- The client authenticates using a ticket with forwarding enabled
|
||||
|
||||
|
|
@ -86,36 +91,36 @@ $auth->setTicket(KerberosTicket::fromEnv());
|
|||
$server = $serverFactory->createServer('localhost', $auth);
|
||||
```
|
||||
|
||||
### Upload a file ###
|
||||
### Upload a file
|
||||
|
||||
```php
|
||||
$share->put($fileToUpload, 'example.txt');
|
||||
```
|
||||
|
||||
### Download a file ###
|
||||
### Download a file
|
||||
|
||||
```php
|
||||
$share->get('example.txt', $target);
|
||||
```
|
||||
|
||||
### List shares on the remote server ###
|
||||
### List shares on the remote server
|
||||
|
||||
```php
|
||||
$shares = $server->listShares();
|
||||
|
||||
foreach ($shares as $share) {
|
||||
echo $share->getName() . "\n";
|
||||
echo $share->getName() . "\n";
|
||||
}
|
||||
```
|
||||
|
||||
### List the content of a folder ###
|
||||
### List the content of a folder
|
||||
|
||||
```php
|
||||
$content = $share->dir('test');
|
||||
|
||||
foreach ($content as $info) {
|
||||
echo $info->getName() . "\n";
|
||||
echo "\tsize :" . $info->getSize() . "\n";
|
||||
echo $info->getName() . "\n";
|
||||
echo "\tsize :" . $info->getSize() . "\n";
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -135,20 +140,22 @@ fwrite($fh, 'bar');
|
|||
fclose($fh);
|
||||
```
|
||||
|
||||
**Note**: write() will truncate your file to 0bytes. You may open a writeable stream with append() which will point
|
||||
the cursor to the end of the file or create it if it does not exist yet. (append() is only compatible with libsmbclient-php)
|
||||
**Note**: write() will truncate your file to 0bytes. You may open a writeable
|
||||
stream with append() which will point the cursor to the end of the file or
|
||||
create it if it does not exist yet. (append() is only compatible with
|
||||
libsmbclient-php)
|
||||
|
||||
```php
|
||||
$fh = $share->append('test.txt');
|
||||
fwrite($fh, 'bar');
|
||||
fclose($fh);
|
||||
```
|
||||
|
||||
|
||||
### Using notify
|
||||
|
||||
```php
|
||||
$share->notify('')->listen(function (\Icewind\SMB\Change $change) {
|
||||
echo $change->getCode() . ': ' . $change->getPath() . "\n";
|
||||
echo $change->getCode() . ': ' . $change->getPath() . "\n";
|
||||
});
|
||||
```
|
||||
|
||||
|
|
@ -169,24 +176,30 @@ $options->setMaxProtocol(IOptions::PROTOCOL_SMB3);
|
|||
$serverFactory = new ServerFactory($options);
|
||||
```
|
||||
|
||||
Note, setting the protocol version is not supported with php-smbclient version 1.0.1 or lower.
|
||||
Note, setting the protocol version is not supported with php-smbclient version
|
||||
1.0.1 or lower.
|
||||
|
||||
### Customizing system integration
|
||||
|
||||
The `smbclient` backend needs to get various information about the system it's running on to function
|
||||
such as the paths of various binaries or the system timezone.
|
||||
While the default logic for getting this information should work on most systems, it is possible to customize this behaviour.
|
||||
The `smbclient` backend needs to get various information about the system it's
|
||||
running on to function such as the paths of various binaries or the system
|
||||
timezone. While the default logic for getting this information should work on
|
||||
most systems, it is possible to customize this behaviour.
|
||||
|
||||
In order to customize the integration you provide a custom implementation of `ITimezoneProvider` and/or `ISystem` and pass them as arguments to the `ServerFactory`.
|
||||
In order to customize the integration you provide a custom implementation of
|
||||
`ITimezoneProvider` and/or `ISystem` and pass them as arguments to the
|
||||
`ServerFactory`.
|
||||
|
||||
## Testing SMB
|
||||
|
||||
Use the following steps to check if the library can connect to your SMB share.
|
||||
|
||||
1. Clone this repository or download the source as [zip](https://github.com/icewind1991/SMB/archive/master.zip)
|
||||
1. Clone this repository or download the source as
|
||||
[zip](https://github.com/icewind1991/SMB/archive/master.zip)
|
||||
2. Make sure [composer](https://getcomposer.org/) is installed
|
||||
3. Run `composer install` in the root of the repository
|
||||
4. Edit `example.php` with the relevant settings for your share.
|
||||
5. Run `php example.php`
|
||||
|
||||
If everything works correctly then the contents of the share should be outputted.
|
||||
If everything works correctly then the contents of the share should be
|
||||
outputted.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# SPDX-FileCopyrightText: 2021 Robin Appelman <robin@icewind.nl>
|
||||
# SPDX-License-Identifier: MIT
|
||||
comment: false
|
||||
comment: false
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
"icewind/streams": ">=0.7.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5|^9.3.8",
|
||||
"phpunit/phpunit": "10.5.58",
|
||||
"friendsofphp/php-cs-fixer": "v3.89.0",
|
||||
"phpstan/phpstan": "^0.12.57",
|
||||
"psalm/phar": "6.*"
|
||||
|
|
|
|||
4402
composer.lock
generated
Normal file
4402
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
2
composer.lock.license
Normal file
2
composer.lock.license
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: 2025 Robin Appelman <robin@icewind.nl>
|
||||
SPDX-License-Identifier: MIT
|
||||
103
flake.lock
generated
103
flake.lock
generated
|
|
@ -1,17 +1,34 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1747046372,
|
||||
"narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flakelight": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"flakelight-php",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1751892651,
|
||||
"narHash": "sha256-oLNt26YpwTVj+t7BunpmL+iOUHVt5VLvYB1vnJ7Kw28=",
|
||||
"lastModified": 1756730985,
|
||||
"narHash": "sha256-Uv5lLUZfFxQv6RHi1TqLTKso0j0eUVMQQwud29LTV/s=",
|
||||
"owner": "nix-community",
|
||||
"repo": "flakelight",
|
||||
"rev": "f4604c27e117ad54391160ae207b519694b9f845",
|
||||
"rev": "950121d809b75c32e73684b32ccba8d4e8a67703",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -20,6 +37,28 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flakelight-php": {
|
||||
"inputs": {
|
||||
"flakelight": "flakelight",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"phps": "phps"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1761438617,
|
||||
"narHash": "sha256-9j8gFMuUtd4LTOQkHBltxouXp7lvPxgE0r4uCW96Syk=",
|
||||
"ref": "refs/heads/main",
|
||||
"rev": "a7d73a95377469d26c3cde813b32f4e8666dbfbc",
|
||||
"revCount": 11,
|
||||
"type": "git",
|
||||
"url": "https://codeberg.org/icewind/flakelight-php.git"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://codeberg.org/icewind/flakelight-php.git"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1751741127,
|
||||
|
|
@ -35,11 +74,67 @@
|
|||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"phps": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nixpkgs": [
|
||||
"flakelight-php",
|
||||
"nixpkgs"
|
||||
],
|
||||
"utils": "utils"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1760894074,
|
||||
"narHash": "sha256-z2EYR3CA5PWTkUr5WkDni2CZQ326msHLPghJLx11pZ0=",
|
||||
"owner": "fossar",
|
||||
"repo": "nix-phps",
|
||||
"rev": "d2807871f18ab2150b1e46b7ee126fe0bf200d4c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "fossar",
|
||||
"repo": "nix-phps",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flakelight": "flakelight",
|
||||
"flakelight-php": "flakelight-php",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
|
|
|
|||
33
flake.nix
33
flake.nix
|
|
@ -1,34 +1,15 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "nixpkgs/nixos-25.05";
|
||||
flakelight = {
|
||||
url = "github:nix-community/flakelight";
|
||||
flakelight-php = {
|
||||
url = "git+https://codeberg.org/icewind/flakelight-php.git";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
outputs = {flakelight, ...}:
|
||||
flakelight ./. {
|
||||
formatters = pkgs: {
|
||||
"*.php" = pkgs.lib.getExe pkgs.php84.packages.php-cs-fixer;
|
||||
};
|
||||
devShell.packages = pkgs: let
|
||||
php_version = "81";
|
||||
php = pkgs.pkgs."php${php_version}".buildEnv {
|
||||
extensions = {
|
||||
enabled,
|
||||
all,
|
||||
}:
|
||||
enabled
|
||||
++ (with all; [
|
||||
dom
|
||||
simplexml
|
||||
tokenizer
|
||||
filter
|
||||
]);
|
||||
};
|
||||
in [
|
||||
php.packages.composer
|
||||
php
|
||||
];
|
||||
outputs = {flakelight-php, ...}:
|
||||
flakelight-php ./. {
|
||||
vendorHash = "sha256-Pj19ClD0+4ONc0i3CJQ3GroaSc/039CixNuTjhXIQy4=";
|
||||
phpExtensions = all: with all; [smbclient krb5];
|
||||
testDependencies = pkgs: with pkgs; [samba];
|
||||
};
|
||||
}
|
||||
|
|
|
|||
7
phpstan.neon
Normal file
7
phpstan.neon
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
parameters:
|
||||
level: 7
|
||||
paths:
|
||||
- src
|
||||
stubFiles:
|
||||
- stubs/krb.stub
|
||||
- stubs/smbclient.stub
|
||||
2
phpstan.neon.license
Normal file
2
phpstan.neon.license
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: 2021 Robin Appelman <robin@icewind.nl>
|
||||
SPDX-License-Identifier: MIT
|
||||
|
|
@ -6,10 +6,11 @@
|
|||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
findUnusedCode="false"
|
||||
ensureOverrideAttribute="false"
|
||||
>
|
||||
<stubs>
|
||||
<file name="tests/krb.phpstub" preloadClasses="true"/>
|
||||
<file name="tests/smbclient.phpstub" preloadClasses="true"/>
|
||||
<file name="stubs/krb.stub" preloadClasses="true"/>
|
||||
<file name="stubs/smbclient.stub" preloadClasses="true"/>
|
||||
</stubs>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
SPDX-FileCopyrightText: 2021 Robin Appelman <robin@icewind.nl>
|
||||
SPDX-FileCopyrightText: 2025 Robin Appelman <robin@icewind.nl>
|
||||
SPDX-License-Identifier: MIT
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
namespace Icewind\SMB;
|
||||
|
||||
class ACL {
|
||||
final class ACL {
|
||||
const TYPE_ALLOW = 0;
|
||||
const TYPE_DENY = 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ namespace Icewind\SMB;
|
|||
|
||||
use Icewind\SMB\Exception\Exception;
|
||||
|
||||
class AnonymousAuth implements IAuth {
|
||||
final class AnonymousAuth implements IAuth {
|
||||
public function getUsername(): ?string {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getWorkgroup(): ?string {
|
||||
public function getWorkgroup(): string {
|
||||
return 'dummy';
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
namespace Icewind\SMB;
|
||||
|
||||
class BasicAuth implements IAuth {
|
||||
final class BasicAuth implements IAuth {
|
||||
/** @var string */
|
||||
private $username;
|
||||
/** @var string|null */
|
||||
|
|
@ -20,7 +20,7 @@ class BasicAuth implements IAuth {
|
|||
$this->password = $password;
|
||||
}
|
||||
|
||||
public function getUsername(): ?string {
|
||||
public function getUsername(): string {
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +28,7 @@ class BasicAuth implements IAuth {
|
|||
return $this->workgroup;
|
||||
}
|
||||
|
||||
public function getPassword(): ?string {
|
||||
public function getPassword(): string {
|
||||
return $this->password;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
namespace Icewind\SMB;
|
||||
|
||||
class Change {
|
||||
final class Change {
|
||||
/** @var int */
|
||||
private $code;
|
||||
/** @var string */
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class AccessDeniedException extends ConnectException {
|
||||
final class AccessDeniedException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class AlreadyExistsException extends InvalidRequestException {
|
||||
final class AlreadyExistsException extends InvalidRequestException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class AuthenticationException extends ConnectException {
|
||||
final class AuthenticationException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class ConnectionAbortedException extends ConnectException {
|
||||
final class ConnectionAbortedException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class ConnectionException extends ConnectException {
|
||||
final class ConnectionException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class ConnectionRefusedException extends ConnectException {
|
||||
final class ConnectionRefusedException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class ConnectionResetException extends ConnectException {
|
||||
final class ConnectionResetException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class DependencyException extends Exception {
|
||||
final class DependencyException extends Exception {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class Exception extends \Exception {
|
|||
if (isset($exceptionMap[$error])) {
|
||||
$exceptionClass = $exceptionMap[$error];
|
||||
if (is_numeric($error)) {
|
||||
return new $exceptionClass($path, $error);
|
||||
return new $exceptionClass($path, (int)$error);
|
||||
} else {
|
||||
return new $exceptionClass($path);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class FileInUseException extends InvalidRequestException {
|
||||
final class FileInUseException extends InvalidRequestException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class ForbiddenException extends InvalidRequestException {
|
||||
final class ForbiddenException extends InvalidRequestException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class HostDownException extends ConnectException {
|
||||
final class HostDownException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class InvalidArgumentException extends InvalidRequestException {
|
||||
final class InvalidArgumentException extends InvalidRequestException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class InvalidHostException extends ConnectException {
|
||||
final class InvalidHostException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class InvalidParameterException extends InvalidRequestException {
|
||||
final class InvalidParameterException extends InvalidRequestException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class InvalidPathException extends InvalidRequestException {
|
||||
final class InvalidPathException extends InvalidRequestException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class InvalidResourceException extends Exception {
|
||||
final class InvalidResourceException extends Exception {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,5 +8,5 @@ declare(strict_types=1);
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class InvalidTicket extends Exception {
|
||||
final class InvalidTicket extends Exception {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class InvalidTypeException extends InvalidRequestException {
|
||||
final class InvalidTypeException extends InvalidRequestException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class NoLoginServerException extends ConnectException {
|
||||
final class NoLoginServerException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class NoRouteToHostException extends ConnectException {
|
||||
final class NoRouteToHostException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class NotEmptyException extends InvalidRequestException {
|
||||
final class NotEmptyException extends InvalidRequestException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class NotFoundException extends InvalidRequestException {
|
||||
final class NotFoundException extends InvalidRequestException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class OutOfSpaceException extends InvalidRequestException {
|
||||
final class OutOfSpaceException extends InvalidRequestException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Icewind\SMB\Exception;
|
|||
|
||||
use Throwable;
|
||||
|
||||
class RevisionMismatchException extends Exception {
|
||||
final class RevisionMismatchException extends Exception {
|
||||
public function __construct(string $message = 'Protocol version mismatch', int $code = 0, ?Throwable $previous = null) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@
|
|||
|
||||
namespace Icewind\SMB\Exception;
|
||||
|
||||
class TimedOutException extends ConnectException {
|
||||
final class TimedOutException extends ConnectException {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use Icewind\SMB\Exception\InvalidTicket;
|
|||
*
|
||||
* @deprecated Use `KerberosAuth` with `$auth->setTicket(KerberosTicket::fromEnv())` instead
|
||||
*/
|
||||
class KerberosApacheAuth extends KerberosAuth implements IAuth {
|
||||
final class KerberosApacheAuth extends KerberosAuth implements IAuth {
|
||||
public function getTicket(): KerberosTicket {
|
||||
if ($this->ticket === null) {
|
||||
$ticket = KerberosTicket::fromEnv();
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Icewind\SMB;
|
|||
use Icewind\SMB\Exception\InvalidTicket;
|
||||
use KRB5CCache;
|
||||
|
||||
class KerberosTicket {
|
||||
final class KerberosTicket {
|
||||
/** @var KRB5CCache */
|
||||
private $krb5;
|
||||
/** @var string */
|
||||
|
|
@ -55,8 +55,16 @@ class KerberosTicket {
|
|||
return new KerberosTicket($krb5, $ticketName);
|
||||
}
|
||||
|
||||
public static function load(string $ticket): KerberosTicket {
|
||||
private static function tmpNam(): string {
|
||||
$tmpFilename = tempnam(sys_get_temp_dir(), "krb5cc_php_");
|
||||
if ($tmpFilename === false) {
|
||||
throw new \Exception("Failed to create temporary file for ticket");
|
||||
}
|
||||
return $tmpFilename;
|
||||
}
|
||||
|
||||
public static function load(string $ticket): KerberosTicket {
|
||||
$tmpFilename = self::tmpNam();
|
||||
file_put_contents($tmpFilename, $ticket);
|
||||
register_shutdown_function(function () use ($tmpFilename) {
|
||||
if (file_exists($tmpFilename)) {
|
||||
|
|
@ -74,12 +82,15 @@ class KerberosTicket {
|
|||
if (substr($this->cacheName, 0, 5) === 'FILE:') {
|
||||
$ticket = file_get_contents(substr($this->cacheName, 5));
|
||||
} else {
|
||||
$tmpFilename = tempnam(sys_get_temp_dir(), "krb5cc_php_");
|
||||
$tmpFilename = self::tmpNam();
|
||||
$tmpCacheFile = "FILE:" . $tmpFilename;
|
||||
$this->krb5->save($tmpCacheFile);
|
||||
$ticket = file_get_contents($tmpFilename);
|
||||
unlink($tmpFilename);
|
||||
}
|
||||
if ($ticket === false) {
|
||||
throw new \Exception("Failed to read saved ticket");
|
||||
}
|
||||
return $ticket;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use Icewind\SMB\Exception\Exception;
|
|||
use Icewind\SMB\Exception\NotFoundException;
|
||||
use Icewind\SMB\IFileInfo;
|
||||
|
||||
class NativeFileInfo implements IFileInfo {
|
||||
final class NativeFileInfo implements IFileInfo {
|
||||
/** @var string */
|
||||
protected $path;
|
||||
/** @var string */
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use Icewind\SMB\StringBuffer;
|
|||
/**
|
||||
* Stream optimized for read only usage
|
||||
*/
|
||||
class NativeReadStream extends NativeStream {
|
||||
final class NativeReadStream extends NativeStream {
|
||||
const CHUNK_SIZE = 1048576; // 1MB chunks
|
||||
|
||||
/** @var StringBuffer */
|
||||
|
|
@ -78,7 +78,7 @@ class NativeReadStream extends NativeStream {
|
|||
return $this->readBuffer->remaining() <= 0 && parent::stream_eof();
|
||||
}
|
||||
|
||||
public function stream_tell() {
|
||||
public function stream_tell(): int {
|
||||
return $this->pos;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use Icewind\SMB\IShare;
|
|||
use Icewind\SMB\ISystem;
|
||||
use Icewind\SMB\ITimeZoneProvider;
|
||||
|
||||
class NativeServer extends AbstractServer {
|
||||
final class NativeServer extends AbstractServer {
|
||||
/**
|
||||
* @var NativeState
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use Icewind\SMB\IServer;
|
|||
use Icewind\SMB\Wrapped\Server;
|
||||
use Icewind\SMB\Wrapped\Share;
|
||||
|
||||
class NativeShare extends AbstractShare {
|
||||
final class NativeShare extends AbstractShare {
|
||||
/**
|
||||
* @var IServer $server
|
||||
*/
|
||||
|
|
@ -204,6 +204,9 @@ class NativeShare extends AbstractShare {
|
|||
*/
|
||||
public function put(string $source, string $target): bool {
|
||||
$sourceHandle = fopen($source, 'rb');
|
||||
if (!$sourceHandle) {
|
||||
return false;
|
||||
}
|
||||
$targetUrl = $this->buildUrl($target);
|
||||
|
||||
$targetHandle = $this->getState()->create($targetUrl);
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use Icewind\SMB\IOptions;
|
|||
/**
|
||||
* Low level wrapper for libsmbclient-php with error handling
|
||||
*/
|
||||
class NativeState {
|
||||
final class NativeState {
|
||||
/** @var resource|null */
|
||||
protected $state = null;
|
||||
|
||||
|
|
@ -182,6 +182,10 @@ class NativeState {
|
|||
if (!$this->state) {
|
||||
throw new ConnectionException("Not connected");
|
||||
}
|
||||
/**
|
||||
* false positive from wrong reflection info
|
||||
* @phpstan-ignore arguments.count
|
||||
*/
|
||||
$result = @smbclient_rename($this->state, $old, $this->state, $new);
|
||||
|
||||
$this->testResult($result, $new);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ abstract class NativeStream implements File {
|
|||
if (stream_wrapper_unregister('nativesmb') === false) {
|
||||
throw new Exception("Failed to unregister stream wrapper");
|
||||
}
|
||||
if ($fh === false) {
|
||||
throw new \Exception("Failed to start stream wrapper");
|
||||
}
|
||||
return $fh;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use Icewind\SMB\StringBuffer;
|
|||
/**
|
||||
* Stream optimized for write only usage
|
||||
*/
|
||||
class NativeWriteStream extends NativeStream {
|
||||
final class NativeWriteStream extends NativeStream {
|
||||
const CHUNK_SIZE = 1048576; // 1MB chunks
|
||||
|
||||
/** @var StringBuffer */
|
||||
|
|
@ -58,7 +58,7 @@ class NativeWriteStream extends NativeStream {
|
|||
parent::stream_write($this->writeBuffer->flush());
|
||||
}
|
||||
|
||||
public function stream_write($data) {
|
||||
public function stream_write($data): int {
|
||||
$written = $this->writeBuffer->push($data);
|
||||
$this->pos += $written;
|
||||
|
||||
|
|
@ -79,7 +79,7 @@ class NativeWriteStream extends NativeStream {
|
|||
return parent::stream_close() && $flushResult;
|
||||
}
|
||||
|
||||
public function stream_tell() {
|
||||
public function stream_tell(): int {
|
||||
return $this->pos;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
namespace Icewind\SMB;
|
||||
|
||||
class Options implements IOptions {
|
||||
final class Options implements IOptions {
|
||||
/** @var int */
|
||||
private $timeout = 20;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use Icewind\SMB\Exception\DependencyException;
|
|||
use Icewind\SMB\Native\NativeServer;
|
||||
use Icewind\SMB\Wrapped\Server;
|
||||
|
||||
class ServerFactory {
|
||||
final class ServerFactory {
|
||||
const BACKENDS = [
|
||||
NativeServer::class,
|
||||
Server::class
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Icewind\SMB;
|
||||
|
||||
class StringBuffer {
|
||||
final class StringBuffer {
|
||||
/** @var string */
|
||||
private $buffer = "";
|
||||
/** @var int */
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Icewind\SMB;
|
|||
|
||||
use Icewind\SMB\Exception\Exception;
|
||||
|
||||
class System implements ISystem {
|
||||
final class System implements ISystem {
|
||||
/** @var (string|null)[] */
|
||||
private $paths = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
namespace Icewind\SMB;
|
||||
|
||||
class TimeZoneProvider implements ITimeZoneProvider {
|
||||
final class TimeZoneProvider implements ITimeZoneProvider {
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use Icewind\SMB\Exception\ConnectionRefusedException;
|
|||
use Icewind\SMB\Exception\InvalidHostException;
|
||||
use Icewind\SMB\Exception\NoLoginServerException;
|
||||
|
||||
class Connection extends RawConnection {
|
||||
final class Connection extends RawConnection {
|
||||
const DELIMITER = 'smb:';
|
||||
const DELIMITER_LENGTH = 4;
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ class Connection extends RawConnection {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string|bool $promptLine (optional) prompt line that might contain some info about the error
|
||||
* @param string|false $promptLine (optional) prompt line that might contain some info about the error
|
||||
* @throws ConnectException
|
||||
* @return no-return
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
namespace Icewind\SMB\Wrapped;
|
||||
|
||||
class ErrorCodes {
|
||||
final class ErrorCodes {
|
||||
/**
|
||||
* connection errors
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ namespace Icewind\SMB\Wrapped;
|
|||
use Icewind\SMB\ACL;
|
||||
use Icewind\SMB\IFileInfo;
|
||||
|
||||
class FileInfo implements IFileInfo {
|
||||
final class FileInfo implements IFileInfo {
|
||||
/** @var string */
|
||||
protected $path;
|
||||
/** @var string */
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use Icewind\SMB\Exception\Exception;
|
|||
use Icewind\SMB\Exception\RevisionMismatchException;
|
||||
use Icewind\SMB\INotifyHandler;
|
||||
|
||||
class NotifyHandler implements INotifyHandler {
|
||||
final class NotifyHandler implements INotifyHandler {
|
||||
/** @var Connection */
|
||||
private $connection;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use Icewind\SMB\Exception\NoLoginServerException;
|
|||
use Icewind\SMB\Exception\NotEmptyException;
|
||||
use Icewind\SMB\Exception\NotFoundException;
|
||||
|
||||
class Parser {
|
||||
final class Parser {
|
||||
const MSG_NOT_FOUND = 'Error opening local file ';
|
||||
|
||||
/**
|
||||
|
|
@ -146,7 +146,7 @@ class Parser {
|
|||
// 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] : '';
|
||||
$name = $words[0];
|
||||
$value = isset($words[1]) ? $words[1] : '';
|
||||
$value = trim($value);
|
||||
|
||||
|
|
@ -159,8 +159,8 @@ class Parser {
|
|||
throw new Exception("Malformed state response from server");
|
||||
}
|
||||
return [
|
||||
'mtime' => strtotime($data['write_time']),
|
||||
'mode' => hexdec(substr($data['attributes'], $attributeStart + 1, -1)),
|
||||
'mtime' => (int)strtotime($data['write_time']),
|
||||
'mode' => (int)hexdec(substr($data['attributes'], $attributeStart + 1, -1)),
|
||||
'size' => isset($data['stream']) ? (int)(explode(' ', $data['stream'])[1]) : 0
|
||||
];
|
||||
}
|
||||
|
|
@ -182,7 +182,7 @@ class Parser {
|
|||
list(, $name, $mode, $size, $time) = $matches;
|
||||
if ($name !== '.' and $name !== '..') {
|
||||
$mode = $this->parseMode(strtoupper($mode));
|
||||
$time = strtotime($time . ' ' . $this->timeZone);
|
||||
$time = (int)strtotime($time . ' ' . $this->timeZone);
|
||||
$path = $basePath . '/' . $name;
|
||||
$content[] = new FileInfo($path, $name, (int)$size, $time, $mode, function () use ($aclCallback, $path): array {
|
||||
return $aclCallback($path);
|
||||
|
|
@ -255,7 +255,7 @@ class Parser {
|
|||
}
|
||||
|
||||
if (substr($mask, 0, 2) === '0x') {
|
||||
$maskInt = hexdec($mask);
|
||||
$maskInt = (int)hexdec($mask);
|
||||
} else {
|
||||
$maskInt = 0;
|
||||
foreach (explode('|', $mask) as $maskString) {
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class RawConnection {
|
|||
'COLUMNS' => 8192, // prevent smbclient from line-wrapping it's output
|
||||
'TZ' => 'UTC',
|
||||
]);
|
||||
$this->process = proc_open($this->command, $descriptorSpec, $this->pipes, '/', $env);
|
||||
$this->process = proc_open($this->command, $descriptorSpec, $this->pipes, '/', $env) ?: null;
|
||||
if (!$this->isValid()) {
|
||||
throw new ConnectionException();
|
||||
}
|
||||
|
|
@ -211,7 +211,9 @@ class RawConnection {
|
|||
? "username=$user"
|
||||
: "username=$user\npassword=$password\n";
|
||||
|
||||
$this->authStream = fopen('php://temp', 'w+');
|
||||
/** @var resource $stream */
|
||||
$stream = fopen('php://temp', 'w+');
|
||||
$this->authStream = $stream;
|
||||
fwrite($this->authStream, $auth);
|
||||
rewind($this->authStream);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use Icewind\SMB\Exception\InvalidHostException;
|
|||
use Icewind\SMB\IShare;
|
||||
use Icewind\SMB\ISystem;
|
||||
|
||||
class Server extends AbstractServer {
|
||||
final class Server extends AbstractServer {
|
||||
/**
|
||||
* Check if the smbclient php extension is available
|
||||
*
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use Icewind\Streams\CallbackWrapper;
|
|||
use Icewind\SMB\Native\NativeShare;
|
||||
use Icewind\SMB\Native\NativeServer;
|
||||
|
||||
class Share extends AbstractShare {
|
||||
final class Share extends AbstractShare {
|
||||
/**
|
||||
* @var IServer $server
|
||||
*/
|
||||
|
|
|
|||
37
stubs/krb.stub
Normal file
37
stubs/krb.stub
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Robin Appelman <robin@icewind.nl>
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
class KRB5CCache {
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEntries(): array {
|
||||
return [];
|
||||
}
|
||||
public function getName(): string {
|
||||
return "";
|
||||
}
|
||||
/**
|
||||
* @param string[] $flags
|
||||
*/
|
||||
public function initKeytab(string $principal, string $keytab, array $flags = []): void {
|
||||
}
|
||||
/**
|
||||
* @param string[] $flags
|
||||
*/
|
||||
public function initPassword(string $principal, string $password, array $flags = []): void {
|
||||
}
|
||||
public function isValid(): bool {
|
||||
return false;
|
||||
}
|
||||
public function open(string $source): void {
|
||||
}
|
||||
public function save(string $destination): void {
|
||||
}
|
||||
public function setConfig(string $destination): void {
|
||||
}
|
||||
}
|
||||
|
|
@ -59,8 +59,7 @@ function smbclient_option_set($state, int $option, $value) {
|
|||
#if HAVE_SMBC_SETOPTIONPROTOCOLS
|
||||
/**
|
||||
* @param resource $state
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
* @return bool
|
||||
*/
|
||||
function smbclient_client_protocols($state, string $minproto = null, string $maxproto = null): bool {
|
||||
}
|
||||
|
|
@ -77,6 +76,7 @@ function smbclient_opendir($state, string $path) {
|
|||
/**
|
||||
* @param resource $state
|
||||
* @param resource $dir
|
||||
* @return false|string[]
|
||||
*/
|
||||
function smbclient_readdir($state, $dir): false|array {
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ function smbclient_closedir($state, $dir): bool {
|
|||
|
||||
/**
|
||||
* @param resource $state
|
||||
* @return false|resource
|
||||
* @return false|array<string|int,int>
|
||||
*/
|
||||
function smbclient_stat($state, string $path): false|array {
|
||||
}
|
||||
|
|
@ -98,6 +98,7 @@ function smbclient_stat($state, string $path): false|array {
|
|||
/**
|
||||
* @param resource $state
|
||||
* @param resource $file
|
||||
* @return false|array<string|int,int>
|
||||
*/
|
||||
function smbclient_fstat($state, $file): false|array {
|
||||
}
|
||||
|
|
@ -190,6 +191,7 @@ function smbclient_utimes($state, string $path, int $mtime = -1, int $atime = -1
|
|||
|
||||
/**
|
||||
* @param resource $state
|
||||
* @return false|string[]
|
||||
*/
|
||||
function smbclient_listxattr($state, string $path): false|array {
|
||||
}
|
||||
|
|
@ -214,7 +216,7 @@ function smbclient_removexattr($state, string $path, string $name): bool {
|
|||
|
||||
/**
|
||||
* @param resource $state
|
||||
* @return false|resource
|
||||
* @return false|array<string|int,int>
|
||||
*/
|
||||
function smbclient_statvfs($state, string $path): false|array {
|
||||
}
|
||||
|
|
@ -222,6 +224,7 @@ function smbclient_statvfs($state, string $path): false|array {
|
|||
/**
|
||||
* @param resource $state
|
||||
* @param resource $file
|
||||
* @return false|array<string|int,int>
|
||||
*/
|
||||
function smbclient_fstatvfs($state, $file): false|array {
|
||||
}
|
||||
|
|
@ -240,4 +243,4 @@ const SMBCLIENT_OPT_NETBIOS_NAME = 11;
|
|||
const SMBCLIENT_OPT_WORKGROUP = 12;
|
||||
const SMBCLIENT_OPT_USER = 13;
|
||||
const SMBCLIENT_OPT_PORT = 14;
|
||||
const SMBCLIENT_OPT_TIMEOUT = 15;
|
||||
const SMBCLIENT_OPT_TIMEOUT = 15;
|
||||
|
|
@ -27,7 +27,7 @@ use Icewind\SMB\Options;
|
|||
use Icewind\SMB\System;
|
||||
use Icewind\SMB\TimeZoneProvider;
|
||||
|
||||
abstract class AbstractShareTest extends TestCase {
|
||||
abstract class AbstractShareTestBase extends TestCase {
|
||||
/**
|
||||
* @var \Icewind\SMB\IServer $server
|
||||
*/
|
||||
|
|
@ -15,7 +15,7 @@ use Icewind\SMB\Options;
|
|||
use Icewind\SMB\System;
|
||||
use Icewind\SMB\TimeZoneProvider;
|
||||
|
||||
class NativeShareTest extends AbstractShareTest {
|
||||
class NativeShareTest extends AbstractShareTestBase {
|
||||
public function getServerClass(): string {
|
||||
$this->requireBackendEnv('libsmbclient');
|
||||
if (!function_exists('smbclient_state_new')) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
namespace Icewind\SMB\Test;
|
||||
|
||||
use Icewind\SMB\BasicAuth;
|
||||
use Icewind\SMB\IOptions;
|
||||
use Icewind\SMB\Native\NativeServer;
|
||||
use Icewind\SMB\Options;
|
||||
use Icewind\SMB\System;
|
||||
|
|
@ -36,6 +37,9 @@ class NativeStreamTest extends TestCase {
|
|||
$this->markTestSkipped('libsmbclient php extension not installed');
|
||||
}
|
||||
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
|
||||
$options = new Options();
|
||||
$options->setMinProtocol(IOptions::PROTOCOL_SMB2);
|
||||
$options->setMaxProtocol(IOptions::PROTOCOL_SMB3);
|
||||
$this->server = new NativeServer(
|
||||
$this->config->host,
|
||||
new BasicAuth(
|
||||
|
|
@ -45,7 +49,7 @@ class NativeStreamTest extends TestCase {
|
|||
),
|
||||
new System(),
|
||||
new TimeZoneProvider(new System()),
|
||||
new Options()
|
||||
$options
|
||||
);
|
||||
$this->share = $this->server->getShare($this->config->share);
|
||||
if ($this->config->root) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use Icewind\SMB\Exception\AlreadyExistsException;
|
|||
use Icewind\SMB\Exception\Exception;
|
||||
use Icewind\SMB\Exception\RevisionMismatchException;
|
||||
use Icewind\SMB\INotifyHandler;
|
||||
use Icewind\SMB\IShare;
|
||||
use Icewind\SMB\ISystem;
|
||||
use Icewind\SMB\Options;
|
||||
use Icewind\SMB\System;
|
||||
use Icewind\SMB\TimeZoneProvider;
|
||||
|
|
@ -199,8 +199,7 @@ class NotifyHandlerTest extends TestCase {
|
|||
public function testNoStdBuf(): void {
|
||||
$this->requireBackendEnv('smbclient');
|
||||
$this->config = json_decode(file_get_contents(__DIR__ . '/config.json'));
|
||||
$system = $this->getMockBuilder(System::class)
|
||||
->onlyMethods(['getStdBufPath'])
|
||||
$system = $this->getMockBuilder(ISystem::class)
|
||||
->getMock();
|
||||
$system->method('getStdBufPath')
|
||||
->willReturn(null);
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ namespace Icewind\SMB\Test;
|
|||
use Icewind\SMB\AnonymousAuth;
|
||||
use Icewind\SMB\Exception\DependencyException;
|
||||
use Icewind\SMB\IAuth;
|
||||
use Icewind\SMB\ISystem;
|
||||
use Icewind\SMB\Native\NativeServer;
|
||||
use Icewind\SMB\ServerFactory;
|
||||
use Icewind\SMB\System;
|
||||
use Icewind\SMB\Wrapped\Server;
|
||||
|
||||
class ServerFactoryTest extends TestCase {
|
||||
|
|
@ -25,13 +25,14 @@ class ServerFactoryTest extends TestCase {
|
|||
}
|
||||
|
||||
public function testSmbClient() {
|
||||
$this->requireBackendEnv('smbclient');
|
||||
$system = $this->getMockBuilder(System::class)
|
||||
->onlyMethods(['libSmbclientAvailable'])
|
||||
$system = $this->getMockBuilder(ISystem::class)
|
||||
->getMock();
|
||||
$system->expects($this->any())
|
||||
->method('libSmbclientAvailable')
|
||||
->willReturn(false);
|
||||
$system->expects($this->any())
|
||||
->method('getSmbclientPath')
|
||||
->willReturn("/usr/bin/smbclient");
|
||||
$factory = new ServerFactory(null, $system);
|
||||
$this->assertInstanceOf(Server::class, $factory->createServer('localhost', $this->credentials));
|
||||
}
|
||||
|
|
@ -41,15 +42,19 @@ class ServerFactoryTest extends TestCase {
|
|||
if (!function_exists('smbclient_state_new')) {
|
||||
$this->markTestSkipped('libsmbclient php extension not installed');
|
||||
}
|
||||
$factory = new ServerFactory();
|
||||
$system = $this->getMockBuilder(ISystem::class)
|
||||
->getMock();
|
||||
$system->expects($this->any())
|
||||
->method('libSmbclientAvailable')
|
||||
->willReturn(true);
|
||||
$factory = new ServerFactory(null, $system);
|
||||
$this->assertInstanceOf(NativeServer::class, $factory->createServer('localhost', $this->credentials));
|
||||
}
|
||||
|
||||
public function testNoBackend() {
|
||||
$this->expectException(DependencyException::class);
|
||||
$this->requireBackendEnv('smbclient');
|
||||
$system = $this->getMockBuilder(System::class)
|
||||
->setMethods(['libSmbclientAvailable', 'getSmbclientPath'])
|
||||
$system = $this->getMockBuilder(ISystem::class)
|
||||
->getMock();
|
||||
$system->expects($this->any())
|
||||
->method('libSmbclientAvailable')
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use Icewind\SMB\System;
|
|||
use Icewind\SMB\TimeZoneProvider;
|
||||
use Icewind\SMB\Wrapped\Server as NormalServer;
|
||||
|
||||
class ShareTest extends AbstractShareTest {
|
||||
class ShareTest extends AbstractShareTestBase {
|
||||
public function getServerClass(): string {
|
||||
$this->requireBackendEnv('smbclient');
|
||||
return NormalServer::class;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"host": "skybox.icewind.link",
|
||||
"user": "test",
|
||||
"password": "test",
|
||||
"share": "test",
|
||||
"root": "test"
|
||||
"host": "172.17.0.2",
|
||||
"user": "test",
|
||||
"password": "test",
|
||||
"share": "test",
|
||||
"root": ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022 Robin Appelman <robin@icewind.nl>
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
class KRB5CCache {
|
||||
public function getEntries(): array { return [];}
|
||||
public function getName(): string {return "";}
|
||||
public function initKeytab(string $principal, string $keytab, array $flags = []): void {}
|
||||
public function initPassword(string $principal, string $password, array $flags = []): void {}
|
||||
public function isValid(): bool {return false;}
|
||||
public function open(string $source): void {}
|
||||
public function save(string $destination): void {}
|
||||
public function setConfig(string $destination): void {}
|
||||
}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
- SPDX-FileCopyrightText: 2012 Robin Appelman <robin@icewind.nl>
|
||||
- SPDX-License-Identifier: MIT
|
||||
-->
|
||||
<phpunit bootstrap="bootstrap.php">
|
||||
<testsuite name='SMB'>
|
||||
<directory suffix='.php'>./</directory>
|
||||
</testsuite>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">../src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="bootstrap.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory suffix=".php">../src</directory>
|
||||
</include>
|
||||
</coverage>
|
||||
<testsuite name="SMB">
|
||||
<directory suffix=".php">./</directory>
|
||||
</testsuite>
|
||||
</phpunit>
|
||||
|
|
|
|||
|
|
@ -34,4 +34,4 @@ docker run -d --name apache${FORGEJO_RUN_NUMBER} -v $PWD:/var/www/html -v /tmp/s
|
|||
APACHE_IP=$(docker inspect apache${FORGEJO_RUN_NUMBER} --format '{{.NetworkSettings.IPAddress}}')
|
||||
|
||||
# add the dns record for apache
|
||||
docker exec dc${FORGEJO_RUN_NUMBER} samba-tool dns add krb.domain.test domain.test httpd A $APACHE_IP -U administrator --password=passwOrd1
|
||||
docker exec dc${FORGEJO_RUN_NUMBER} samba-tool dns add krb.domain.test domain.test httpd A $APACHE_IP -U administrator --password=passwOrd1
|
||||
|
|
|
|||
9
tests/start-server.sh
Executable file
9
tests/start-server.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# SPDX-FileCopyrightText: 2025 Robin Appelman <robin@icewind.nl>
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
|
||||
docker rm -f smb-test
|
||||
docker run -d --name smb-test -e ACCOUNT_test=test -e UID_test=1000 -e SAMBA_VOLUME_CONFIG_test="[test]; path=/tmp; valid users = test; guest ok = no; read only = no; browseable = yes" servercontainers/samba
|
||||
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' smb-test
|
||||
Loading…
Add table
Add a link
Reference in a new issue