Can't get SFTP to work in PHP - php

I am writing a simple SFTP client in PHP because we have the need to programatically retrieve files via n remote servers. I am using the PECL SSH2 extension.
I have run up against a road block, though. The documentation on php.net suggests that you can do this:
$stream = fopen("ssh2.sftp://$sftp/path/to/file", 'r');
However, I have an ls method that attempts to something similar
public function ls($dir)
{
$rd = "ssh2.sftp://{$this->sftp}/$dir";
$handle = opendir($rd);
if (!is_resource($handle)) {
throw new SFTPException("Could not open directory.");
}
while (false !== ($file = readdir($handle))) {
if (substr($file, 0, 1) != '.'){
print $file . "\n";
}
}
closedir($handle);
}
I get the following error:
PHP Warning: opendir(): Unable to open ssh2.sftp://Resource id #5/outgoing on remote host
This makes perfect sense because that's what happens when you cast a resource to string. Is the documentation wrong? I tried replacing the resource with host, username, and host and that didn't work either. I know the path is correct because I can run SFTP from the command line and it works fine.
Has anyone else tried to use the SSH2 extenstion with SFTP? Am I missing something obvious here?
UPDATE:
I setup sftp on another machine in-house and it works just fine. So, there must be something about the server I am trying to connect to that isn't working.

When connecting to a SFTP server and you need to connect to the root folder (for instance for reading the content of the folder) you would still get the error when using just "/" as the path.
The solution that I found was to use the path "/./", that's a valid path that references to the root folder. This is useful when the user you are logging with has access only to its own root folder and no full path is available.
So the request to the server when trying to read the contents of the root folder should be something like this:
$rd = "ssh2.sftp://{$this->sftp}/./";

For php versions > 5.6.27 use intval()
$sftpConnection = ssh2_connect($host);
$sftp = ssh2_sftp($sftpConnection);
$fp = fopen("ssh2.sftp://" . intval($sftp) . $remoteFile, "r");
https://bugs.php.net/bug.php?id=73597

I'm having a similar issue. I assume you are doing something similar to this:
$dir = "ssh2.sftp://{$sftp}{$remote_dir}";
$handle = opendir($dir);
When $remote_dir is the full path from root then open_dir works. If $remote_dir is just '/' or '', then I get the 'unable to open' error as you did.
In my case, it seems ssh connects at the root folder instead of the 'home' directory as ftp does. You mentioned that it worked on a different server, so I wonder if it is just a config issue.

the most easiest way to get SFTP working within PHP (even on windows) without installing any extension etc is PHPSECLIB: http://phpseclib.sourceforge.net/ . The SSH stuff is completely implemented in a PHP class.
You use is like this:
<?php
include('Net/SFTP.php');
$sftp = new Net_SFTP('www.domain.tld');
if (!$sftp->login('username', 'password')) {
exit('Login Failed');
}
echo $sftp->pwd();
?>

The documentation on that page contains an error. Take a look at the example here instead: http://php.net/ssh2_sftp - what you actually need to do is to open a special SFTP resource using ssh2_sftp() prior to using it with fopen(). And yes, it looks just like that, e.g. "Resource #24" when converted to string... a bit weird but apparently it works.
Another caveat is that SFTP starts in the root directory rather than the home directory of the remote user, so your remote path in the URI should always be an absolute one.

I just had the same issue, but I could figure out the problem.
On my case, when connecting to the server, I was going to the root of the account, and due to server configs I wasn't able to write there.
I have connected to the account using a fireFTP, and so I could see where the root of the account was...it was the root of the server.
I had to include the whole path until the folder where I am allowed to write, and so I could solve the issue.
So, my advice is to get the path using a graphic interface (I have used fireFTP), and add the whole path to your code.
$pathFromAccountRootFolderToMyDestinationFolder = '/Account/Root/Folder/To/My/Folder';
$stream = fopen("ssh2.sftp://".$sftp."/".$pathFromAccountRootFolderToMyDestinationFolder."/myFile.ext", 'r');
Hope this will help you and other people with the same issue!
Cheers!

I recently tried to get SFTP on PHP working and found that phpseclib was a lot easier to use:
http://phpseclib.sourceforge.net/
If you have the luxury of not being on a shared host and can install whatever extensions you want to maybe the PECL extension would be better but not all of us are so lucky. Plus, phpseclib's API looks a bit more intuitive, being OOP and all.

My issue was, that I was connecting in function and returning string URL with resource inside. Unfortunatelly resource is than created in function context and garbage collector is disconnecting resource on function end. Solution: return resource by reference and unset it manually in more complex context.

Solved my issue by enabling sftp support on the (Powershell) server

This is a bug in the ssh2 package that I found years ago and posted a patch to php.net. It fixes this issue but requires a rebuild of the ssh2 pecl package. You can read more here: https://bugs.php.net/bug.php?id=69981. I included a patch there to the ssh2_fopen_wrappers.c file in the package to fix the issue. Here is a comment I included:
Here is a line of code from ssh2_fopen_wrappers.c that causes this bug: (comment included)
/*
Find resource->path in the path string, then copy the entire string from the original path.
This includes ?query#fragment in the path string
*/
resource->path = estrdup(strstr(path, resource->path));
This line of code -- and therefore this bug -- was introduced as a fix for bug #59794. That line of code is attempting to get a string containing the part, query and fragment from the path variable.
Consider this value for the path variable:
ssh2.sftp://Resource id #5/topdir?a=something#heading1
When resource->path is "/topdir", the result is that "/topdir?a=something#heading1" gets assigned to resource->path just like the comment says.
Now consider the case when resource->path is "/". After the line of code is executed, resource->path becomes "//Resource id#5/topdir#heading1". This is clearly not what you want. Here's a line of code that does:
resource->path = estrdup( strstr( strstr( path, "//" ) + 2, "/" ) );
You may also need to apply the patch for bug # 73597 which removes "Resource id #" from the path string before calling php_url_parse().

Related

PHP SSH2 PECL extension issues

Before I begin, I'm aware of phpseclib and from what I've ready it's the way you should normally go. Unfortunately I'm working on an older script, and I'm trying to avoid the time needed to set up phpseclib, test on local environment, put on production, test on production, refactor my script, etc. I'm hoping someone can help resolve my issue with ssh2.
I've used the following script to ssh a file to a client's server. It has worked fine for years.
<?php
$url = 'their.url.com';
$userName = 'name';
$password = 'pass';
$conn = ssh2_connect($url, 22);
$auth = ssh2_auth_password($conn, $userName, $password);
// Determine whether this is a file path that needs to be opened or not
$localFilePath = 'test123.txt';
$openFile = fopen($localFilePath, "r");
$fileName = 'test.txt';
// Their server path
$remoteFilePath = "/Orders/".$fileName;
if ($auth === true) {
// Transfer file
$sftp = ssh2_sftp($conn);
file_put_contents("ssh2.sftp://".$sftp.$remoteFilePath, $openFile);
}
WHAT HAPPENED:
My client moved his stuff to a different server. It broke the connection, he freaked out, etc., etc. He gave me updated credentials to the new server, and I have confirmed the protocal is SFTP - SSH File Transfer Protocal.
THE ISSUE:
The file transfer is no longer working after updating the ssh combo url/credentials.
WHAT I'VE TRIED:
I tried a test file transfer using Linux (not PHP) and it worked. I also ssh'd in using Filezilla and transferred a file that way, no problem. so I have confirmed that the host/user/pass combo is correct.
I dumped out the result of the file_put_contents, and it is false.
I dumped out $conn, $auth, and $sftp, and what "ssh2.sftp://".$sftp.$remoteFilePath looks like, and the results are (in order)
resource(27) of type (SSH2 Session)
bool(true)
resource(30) of type (SSH2 SFTP)
ssh2.sftp://Resource id #30/Orders/test.txt
Note that test.txt is a very tiny file to rule out file size issues.
I have also uninstalled and reinstalled the ssh2 PECL extension.
I am convinced this is due to an issue on their end, because this script has always worked before, I'm just baffled as to why things are failing now that they're on their new server, and I'm not sure what to do further to diagnose the problem. I can't think of any change that's been made to my server that would affect this.
Any ideas would be greatly appreciated.
Why not just use the ssh2.sftp:// stream directly?
file_put_contents("ssh2.sftp://{$userName}:{$password}#{$url}:22/{$remoteFilePath}", $openFile);
It should work without all the ssh connection, authentication, moving to sftp, etc..

PHP Read/Write files on remote server

I'm making a utility that provides a GUI to easy edit certain values in a csv file on a remote server. My boss wants the utility in php running on the private webserver. I'm new to php, but I was able to get the GUI file modifier working locally without issues. The final piece now is rather than the local test file I need to grab a copy of the requested file off of the remote server, edit it, and then replace the old file with the edited one. My issue is uploading and downloading the file.
When I searched for a solution I found the following:
(note in each of these I am just trying to move a test file)
$source = "http://<IP REMOTE SERVER>/index.html";
$dest = $_SERVER['DOCUMENT_ROOT']."index.html";
copy($source, $dest);
This solution ran into a permissions error.
$source ="http://<IP REMOTE SERVER>/index.html";
$destination = $_SERVER['DOCUMENT_ROOT']."newfile.html";
$data = file_get_contents($source);
$handle = fopen($destination, "w");
fwrite($handle, $data);
fclose($handle);
This also had a permissions error
$connection = ssh2_connect('<IP REMOTE SERVER>', 22);
ssh2_auth_password($connection, 'cahenk', '<PASSWORD>');
ssh2_scp_recv($connection, '/tmp/CHenk/CHenk.csv', 'Desktop/CHenk.csv');
This solution has the error Fatal error: Call to undefined function ssh2_connect() which I have learned is because the function is not a part of the default php installation.
In closing, is there any easy way to read/write files to the remote server through php either by changing permissions, having the php extension installed, or a different way entirely that will work. Basically I'm trying to find the solution that requires the least settings changes to the server because I am not the administrator and would have to go through a round about process of getting any changes done. If something does need to be changed instructions on doing so or a link to instructions would be greatly appreciated.
Did you set the enable-url-fopen-wrapper in your php.ini?(only if your php version is older)
Please look # php remote files storing in example 2

Extract a Zip Archive using PHP

what i'm trying to do is to basically extract the contents of Zip archives on my server.Here is some code:
$entry="test.zip";
$zip = new ZipArchive;
if ($zip->open($entry,ZIPARCHIVE::OVERWRITE) === TRUE)
{
$zip->extractTo('unpacked');
$zip->close();
}else
{
echo ‘failed’;
}
the directory "unpacked" is writeable for everyone and all the used methods of the ZipArchive Class return true. However nothing is being extracted. Does anyone happen to have an idea what could cause this behaviour? Any hint will be highly appreciated...Thanks in advance!
If you are using PHP 5.2.0 or later can you check zlib extension first http://www.zlib.net/
You also check PECL extensions, In order to access ZipArchive, you can also try zip_open, zip_read just for checking.
If this code is in-house, and you can safely make the assumption that you won't move this code from Linux to Windows (or vice versa), you also have the option to execute local system commands, which may help solve your problem.
<?php
echo `unzip myarchive.zip`;
echo `tar -xzf myotherarchive.tar.gz`;
?>
When developing internal-use and/or maintenance scripts, I used to opt for straight-up system calls, as it was more in-line with the commands sysadmins were used to using.
In case of failure you should echo out $zip as it contains the error.
Furthermore I'd guess that you may not have the needed permissions for test.zip
If your zip archive is big, sometimes you cannot extract all files during the maximum allowed execution time of your server.
The only solution, if you cannot change the maximum_execution_time in your php.ini, is to use a javascript to extract one file after the other. On the first javascript request you take the number of files in the archive
$nbr_of_files = $zip->numFiles;
And after you extract one file after another using the id number in the zip archive for each file
$zip->extractTo('unpacked', array($zip->getNameIndex($file_nbr)));
Please try removing the ZIPARCHIVE::OVERWRITE flag from the ZipArchive open method. (The flag may not be functioning as expected and may be the root of the issue if you have followed the advice in the other answers.)
I had the same issue too. The solution:
$zip->extractTo(public_path() .'/restoreDb/extracted/');
add the public_path() helper function.

PHP - Create directory on different server

I have Wamp (server called emerald) running and Mamp running on my Mac. People register on Mamp. Emerald is basically file hosting.
Emerald connects to Mamp's mysql database, to login users. However, I want to create a directories for new registrations on Emerald using PHP.
How can I do this? I have tried using this code:
$thisdir = "192.168.1.71";
$name = "Ryan-Hart";
if(mkdir($thisdir ."/documents/$name" , 0777))
{
echo "Directory has been created successfully...";
}
But had no luck. It basically needs to connect the other server and create a directory, in the name of the user.
I hope this is clear.
You can't create directories through http. You need a filesystem connection to the remote location (a local hard disk, or a network share for example).
The easiest way that doesn't require setting up FTP, SSH or a network share would be to put a PHP script on Emerald:
<?php
// Skipping sanitation because it's only going to be called
// from a friendly script. If "dir" is user input, you need to sanitize
$dirname = $_GET["dir"];
$secret_token = "10210343943202393403";
if ($_GET["token"] != $secret_token) die ("Access denied");
// Alternatively, you could restrict access to one IP
error_reporting(0); // Turn on to see mkdir's error messages
$success = mkdir("/home/www/htdocs/docs/".$dirname);
if ($success) echo "OK"; else echo "FAIL";
and call it from the other server:
$success = file_get_contents("http://192.168.1.71/create_script.php?token=10210343943202393403&dir=HelloWorld");
echo $success; // "OK" or "FAIL"
Create a script on another server that creates the dir and call it remotely.
Make sure you have security check (+a simple password at least)
There is no generic method to access remote server filesystems. You have to use a file transfer protocol and server software to do so. One option would be SSH, which however requires some setup.
$thisdir = "ssh2.sftp://user:pass#192.168.1.71/directory/";
On Windows you might get FTP working more easily, so using an ftp:// url as directory might work.
As last alternative you could enable WebDAV (the PUT method alone works for file transfers, not creating directories) on your WAMP webserver. (But then you probably can't use the raw PHP file functions, probably needs a wrapper class or curl to utilize it.)
I know this is old but i think this might me useful, in my experience:
if(mkdir($thisdir ."/documents/name" , 0777))
doesn't work, i need to do it:
mkdir($thisdir, 0777);
mkdir($thisdir ."/documents" , 0777);
mkdir($thisdir ."/documents/name" , 0777));
hope it helps :)

PHP fopen returning null on files that work fine with include and get_file_contents

I have XAMPP installed on a windows 2000 server. everything is working great except the PHP fopen function. I can neither create nor open files with it. the strange thing is that I can include/require/file_get_contents/other file related functions; also fopen does not generate any errors or notices it just returns NULL.
I have gone as far as to grant full control of the file and all enclosing folders to everybody but i still get NULL instead of a file pointer.
I have tried this on php 5.2.9, 5.2.13, and 5.3.1 with the same effect. I have gone through the php.ini file looking for something that is breaking it; I have even tried deleting and using the basic ini file from a linux box where fopen is working, and still nothing.
I know I have to restart apache after changing my ini and all that and have been (I have even restarted the server) so thats not it.
I am at this poing assuming it is an apache configuration issue somehow, tomorrow im going to run a test through php-cli to make sure.
I really don't want to bruise my head anymore over this can some apache/php wizard come to my aid?
Thanks for the responses. You are right is is not any config problem. The problem has to be with one of my dlls or one of my included files. I just tried the same code that isn't working in a new file without any include. I disabled my custom libraries and it worked.
For the record here is what I was doing that wasn't working:
$test_file = 'c:\\test.csv';//everybody has full control. is very large.
if(file_exists($test_file) && is_readable($test_file)){
$fp = fopen($test_file, 'r');
echo var_export($fp, true);//outputs NULL. on my linux box this is a number.
if($fp !== false){
//do the work
fread($fp, 10);//throws the error that $fp is not a valid file handle
}
}
something that I am including must be breaking fopen somehow. Works as expected in new file with no includes.
Thanks for the responses. You are right as it is not any config problem. The problem has to be with one of my dlls or one of my included files. I just tried the same code that isn't working in a new file without any include and i disabled my custom libraries and it worked.
for the record here is what I was doing that wasn't working:
$test_file = 'c:\\test.csv';//everybody has full control. is very large.
if(file_exists($test_file) && is_readable($test_file)){
$fp = fopen($test_file, 'r');
echo var_export($fp, true);//outputs NULL. on my linux box this is a number.
if($fp !== false){
//do the work
fread($fp, 10);//throws the error that $fp is not a valid file handle
}
}
something that i am including must be breaking fopen somehow. works as expected in new file with no includes.
So we're to assume you're using this kind of code:
$fp = fopen( "my_file.txt", "r" );
$contents = fread($fp, filesize( "my_file.txt" ) );
fclose($fp);
Am I to assume the filesize() function returns null? Also try the stat($filename) function to see if you get a relatively complete array.
Let me know your results.

Categories