php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #77581 SFTP with files with # (hash) in name do not work
Submitted: 2019-02-07 22:03 UTC Modified: 2021-02-10 11:53 UTC
Votes:6
Avg. Score:4.3 ± 0.9
Reproduced:5 of 5 (100.0%)
Same Version:1 (20.0%)
Same OS:1 (20.0%)
From: memso at memso dot net Assigned: ramsey (profile)
Status: Verified Package: ssh2 (PECL)
PHP Version: 7.3.2 OS: CentOS 7
Private report: No CVE-ID: None
 [2019-02-07 22:03 UTC] memso at memso dot net
Description:
------------
Attempting to use the ssh2.sftp stream methods to access files with a hash (#) in the file name always fails.

This seems to occur with all file stream methods. I've tried stat, fopen and is_file, all act like the file does not exist.

Removing the # or changing it to another character fixes the problem, but I do not have control over a remote SFTP I am downloading from and cannot download their files which have a # in the file name.

Test script:
---------------
// Assuming a file named "Some#Test.txt" exists
$ssh2 = ssh2_connect("server", 22);
$sftp = ssh2_sftp($ssh2);
print_r(stat("ssh2.sftp://" . intval($sftp) . "/Some#Test.txt"));

Expected result:
----------------
Results for the "Some#Test.txt" file are expected.

Actual result:
--------------
"false" is output instead

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2019-02-10 00:28 UTC] ramsey@php.net
I've not seen intval() used on a resource like this. Should that work to allow the ssh2.sftp stream wrapper to connect to the server?

Have you tried using ssh2_sftp_stat() instead? For example:

print_r(ssh2_sftp_stat($sftp, 'Some#Test.txt'));

That works for me, though I was able to reproduce your particular issue, using the ssh2.sftp stream wrapper.

I don't think this is a bug. stat() uses URL wrappers, and a hash mark in URLs is the start of a fragment, so I think this behaves as expected.


To Reproduce Results:
---------------------

Start a Docker container for a local SFTP server:

docker run --rm -v "${PWD}/files:/home/foo" -p 2222:22 -d atmoz/sftp:alpine foo:pass:1001

Then create the following files in ${PWD}/files:

touch files/foo.txt
touch files/"Some#Test.txt"

Now, run the following script. It'll try to stat each file twice. Once with ssh2_sftp_stat() and once using stat() with the ssh2.sftp stream wrapper. Note how everything succeeds except the last stat() for Some#Test.txt.

<?php
$ssh2 = ssh2_connect('127.0.0.1', 2222);

if (ssh2_auth_password($ssh2, 'foo', 'pass')) {
    $sftp = ssh2_sftp($ssh2);

    print_r(ssh2_sftp_stat($sftp, 'foo.txt'));
    print_r(ssh2_sftp_stat($sftp, 'Some#Test.txt'));

    print_r(stat('ssh2.sftp://foo:pass@127.0.0.1:2222/foo.txt'));
    print_r(stat('ssh2.sftp://foo:pass@127.0.0.1:2222/Some#Test.txt'));
} else {
    echo 'Could not authenticate.' . PHP_EOL;
}
 [2019-02-10 12:31 UTC] cmb@php.net
-Status: Open +Status: Not a bug -Assigned To: +Assigned To: cmb
 [2019-02-10 12:31 UTC] cmb@php.net
> I've not seen intval() used on a resource like this. Should that
> work to allow the ssh2.sftp stream wrapper to connect to the
> server?

It's document this way in the manual[1], and relies on resource to
int conversion to give the unique resource number[2]. 

> I don't think this is a bug.

ACK.  The hash mark has to be properly escaped:

  print_r(stat("ssh2.sftp://" . intval($sftp) . "/Some%23Test.txt"));

[1] <http://php.net/manual/en/function.ssh2-sftp.php#refsect1-function.ssh2-sftp-examples>
[2] <http://php.net/manual/en/function.ssh2-sftp.php#refsect1-function.ssh2-sftp-examples>
 [2019-02-11 18:35 UTC] memso at memso dot net
@ cmb@php.net

Your solution does not work. If the SFTP code is supposed to convert URL encoded strings back to real strings before accessing files, it is not doing so.

To make it very clear what is happening, I created 2 files on the SFTP.

Some#Test.txt
Correct file

Some%23Test.txt
WRONG FILE

When I run the following code:
$ssh2 = ssh2_connect("server", 22);
$sftp = ssh2_sftp($ssh2);
print_r(file_get_contents("ssh2.sftp://" . intval($sftp) . "/Some%23Test.txt"));

The result is:
WRONG FILE
 [2019-02-11 18:43 UTC] cmb@php.net
-Status: Not a bug +Status: Open -Assigned To: cmb +Assigned To:
 [2019-02-11 18:43 UTC] cmb@php.net
Well, then there is something wrong.
 [2019-02-11 22:16 UTC] ramsey@php.net
-Status: Open +Status: Verified -Assigned To: +Assigned To: ramsey
 [2020-06-19 10:10 UTC] tsmgeek at gmail dot com
I also am seeing this problem but in 7.4.7.
Recent upgrade from 7.2.x to 7.4.x and if we have # in filenames it truncates it by the time it gets to remote server.

Expected: 200618T192940#1141738.xml
Actual: 200618T192940
 [2021-02-10 11:53 UTC] cmb@php.net
Ah, well, there is indeed a problem which has been introduced[1]
when PHP 7.3 compatibility was added.  Prior to PHP 7.3.0, the
code fixed the URL parsing by restoring the path to include
everything after the path (i.e. query string and fragment)[2].
This was done to fix bug #59794.  However, as of PHP 7.4.0, this
restoral of the path does no longer happen.  Fixing this would be
easy, but there is bug #66314 which claims that the mentioned fix
would break other use cases.  Not sure what to do here.

[1] <https://github.com/php/pecl-networking-ssh2/commit/a8835aab2c15e794fce13bd927295719e384ad2d>
[2] <https://github.com/php/pecl-networking-ssh2/blob/93265d71bdeb23350e8320126c7949ed791310df/ssh2_fopen_wrappers.c#L289>
[3] <https://github.com/php/pecl-networking-ssh2/blob/93265d71bdeb23350e8320126c7949ed791310df/ssh2_fopen_wrappers.c#L297>
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sun Nov 24 02:01:28 2024 UTC