Transfer files using SFTP and either PHP or shell/terminal script - php

I need to write a script that is run as a cron job every night which transfers some report files via sftp to another server.
The report files are created every night using another cron in the format 'support_[date].csv' & 'download_[date].csv'.
I'm wondering if you had any pointers on how to do the following:
Find the 2 files created on latest [date]
Copy these files to another server using SFTP
I've tried several PHP scripts utilising the ssh2 extension, but to no avail. Is there a way to do it using a shell script? It's not something I am hugely familiar with to be honest (hence going down the PHP route initially)
This was one of my PHP scripts which didn't work:
$src = 'test.csv';
$filename = 'test.csv';
$dest = '/destination_directory_on_server/'.$filename;
$connection = ssh2_connect('example.com', 22);
ssh2_auth_password($connection, 'username', 'password');
// Create SFTP session
$sftp = ssh2_sftp($connection);
$sftpStream = fopen('ssh2.sftp://'.$sftp.$dest, 'w');
try {
if (!$sftpStream) {
throw new Exception("Could not open remote file: $dest<br>");
}
$data_to_send = file_get_contents($src);
if ($data_to_send === false) {
throw new Exception("Could not open local file: $src.<br>");
}
if (fwrite($sftpStream, $data_to_send) === false) {
throw new Exception("Could not send data from file: $src.<br>");
} else {
//Upload was successful, post-upload actions go here...
}
fclose($sftpStream);
} catch (Exception $e) {
//error_log('Exception: ' . $e->getMessage());
echo 'Exception: ' . $e->getMessage();
if($sftpStream) {fclose($sftpStream);}
}
This were the error messages I got:
Warning: fopen() [function.fopen]: URL
file-access is disabled in the server
configuration in
/path_to_script/sftp-test.php on line
17
Warning: fopen(ssh2.sftp://Resource id
3/destination_directory_on_server/test.csv)
[function.fopen]: failed to open
stream: no suitable wrapper could be
found in /path_to_script/sftp-test.php
on line 17 Exception: Could not open
remote file:
/destination_directory_on_server/test.csv

using the terminal to find latest date of your file, you can use ls -1tr . Then use scp (not sftp) to copy/transfer files over
example,
#!/bin/bash
latest_download=$(ls -1tr download*csv | tail -1)
latest_support=$(ls -1tr support*csv | tail -1)
scp $latest_download user#somehost.com:somedir # syntax from memory, check man page for correct syntax
scp $latest_support user#somehost.com:somedir
check the man page of scp for usage

Muchos kudos to ghostdog74! Managed to get this working, but with sftp.
First I managed to set up key authentication, then partly using ghostdog74's script I did this and it worked perfectly!
cd /directorywithfilesin
latest_download=$(ls -1tr download* | tail -1)
latest_support=$(ls -1tr support* | tail -1)
sftp username#example.com <<EOF
cd /dir_to_copy_to
put $latest_download
put $latest_support
EOF
Thanks!

Among other problems with ghostdog74's method is that it's non-portable. My recommendation would be to use phpseclib, a pure PHP SFTP implementation.

This will not work from PHP from your server because your php.ini has disabled remote wrappers
allow_url_fopen boolean
This option enables the URL-aware fopen wrappers that enable accessing URL object like files. Default wrappers are provided for the access of remote files using the ftp or http protocol, some extensions like zlib may register additional wrappers.
Note: This setting can only be set in php.ini due to security reasons.
However, you could simply let your cron job call a shell script that that uses sftp or rsync directly. You don't have to do this with PHP.
I'm voting to move this to ServerFault to get better support for shell scripting.

The answer is right there, in the error message:
Warning: fopen() [function.fopen]: URL file-access is disabled in the server configuration
means that file-access through URL wrappers is disabled in the server configuration.
Check your PHP config, especially allow_url_fopen. PHP documentation says "This setting can only be set in php.ini due to security reasons", so check it there.
See also fopen: "If PHP has decided that filename specifies a registered protocol, and that protocol is registered as a network URL, PHP will check to make sure that allow_url_fopen is enabled. If it is switched off, PHP will emit a warning and the fopen call will fail." As far as I can tell, that's exactly what is happening there.
If you can't or won't enable allow_url_fopen, you still have some options:
call sftp directly
mount a share with sshfs and then use it as a normal folder

Try as follows (Shell)
SFTP=<sftp path>
KEY_FILE=<your key>
USERNAME=<remote username>
SERVER =<remote server>
REMOTE_DIR=<remote location>
APP_HOME =<App location>
FILENAME=<file name>
${SFTP} -o IdentityFile=${KEY_FILE} ${USERNAME}#${SERVER} <<_COMMAND
lcd ${APP_HOME}
cd ${REMOTE_DIR}
put ${FILENAME}
bye
_COMMAND

Related

PHP FTP can't connect when containerized

I'm trying to containerize my PHP script.
But for some reason it's unable to connect to my FTP server when it's running in the container.
Running the script locally on my machine runs without any issues.
Dockerfile:
FROM php:8.1-cli
EXPOSE 8080
COPY . /var/www/html/
WORKDIR /var/www/html
ENTRYPOINT php -f serve.php
PHP Script:
$connection = ftp_connect(FTP_HOST, 21, 90);
ftp_pasv($connection, true);
if(ftp_login($connection, FTP_USERNAME, FTP_PASSWORD))
{
$stream = fopen('data://text/plain,','r');
ftp_fput($connection, $filename, $stream);
}
ftp_close($connection);
After 90 seconds it gives the following warning:
ftp_fput(): Connection timed out in /var/www/html/ftp.php on line 16
I tried bashing into the container and installing an FTP client.
It gives me a similar timeout error - I can connect to the host, but running any commands results in a stall.
I also have tried running the container on a VPS to eliminate any local firewall/network issues.
But the issue is the same.
Please let me know if any additional information is needed to solve the issue.
Mind the documentation of PHP's ftp_pasv function (emphasis mine):
Please note that ftp_pasv() can only be called after a successful login or otherwise it will fail.
Also note that you do not check the status return value of your ftp_pasv call, so you won't notice if that call actually succeeds (which is most likely won't). Because of that, your script will attempt to establish an active FTP connection. That won't work in a container (unless started with --network=host), because the container runs in a private network which is NATed by your host machine.
Solution: Login first, enabling passive mode second (and also, always check your error return values; many of the older functions from the PHP standard library do not throw exceptions, but rely on error return values):
if (ftp_login($connection, FTP_USERNAME, FTP_PASSWORD))
{
if (ftp_pasv($connection, true) === false) {
throw new \Exception("could not enable passive mode")
}
$stream = fopen('data://text/plain,','r');
ftp_fput($connection, $filename, $stream);
}

PHP SSH2 operations failing when attempting to upload file to server

I am currently struggling with using the SSH2 built-in libraries for PHP (running version 5.5). I am trying to upload a file to an SFTP server as the title states however I keep getting a "stream operation failed" error message.
After attempting to debug the code itself the connection works, the sftp resource is assigned an ID correctly, however when fopen is called for writing the file directly to the remote server it fails.
// open Live environment if we are not in dev
$connection = ssh2_connect($this->_settings['source_host'], 22);
$authSuccess = ssh2_auth_password($connection, $this- >_settings['source_user'], $this->_settings['source_password']);
$sftp = ssh2_sftp($connection);
And finally the fopen() call:
if($operation == 'export') {
$handle = fopen("ssh2.sftp://".$sftp."/remotecopy/IN/".$filename, $mode);
}
I added debug messages in my own code to verify if the data from the _settings array is also used correctly and it is, however I can't explain the stream error.
Message: fopen(): Unable to open ssh2.sftp://Resource id #173/PATH GOES HERE/filename.xxx on remote host
Message: fopen(ssh2.sftp://Resource id #173/PATH GOES HERE/filename.xxx): failed to open stream: operation failed
As a note the file does not exist on the remote host but according to my knowledge 'w' mode in PHP fopen() should create the file if it does not exist.
I can't use the other PHP library as our whole project uses the builtin ssh2 libraries and the person in charged told me to not use it as it works fine everywhere else.
i think you'd have an easier time if you used phpseclib, a pure PHP SFTP implementation. eg.
<?php
include('Net/SFTP.php');
$sftp = new Net_SFTP('www.domain.tld');
if (!$sftp->login('username', 'password')) {
exit('Login Failed');
}
// puts a three-byte file named filename.remote on the SFTP server
$sftp->put('filename.remote', 'xxx');
// puts an x-byte file named filename.remote on the SFTP server,
// where x is the size of filename.local
$sftp->put('filename.remote', 'filename.local', NET_SFTP_LOCAL_FILE);
?>
One of the nice things about phpseclib is it's logging so if that doesn't work you can do define('NET_SSH2_LOGGING', NET_SSH2_LOG_COMPLEX); after including Net/SFTP.php and then do echo $sftp->getLog() after the point where it fails. That might provide some insight into what's going on if it still isn't working.
The answer was easy, I had incorrectly formatted path on the remote server. After verifying my settings it works just fine.
Thank you all for the hints and help.

Is it possible to download a file from one server (sftp) and upload it to my server using php?

I have been told this cannot be done but I want to get some other opinions here. I am a bit of a newbie when it comes to things like this.
My Site: ExampleSiteA.com
File to download: ExampleSiteB.com
Basically, I am downloading a csv file from ExampleSiteB.com to make updates to my site, ExampleSiteA.com. To do this, I am downloading the csv file manually through CoreFTP and then uploading it manually to ExampleSiteA.com. The file changes daily and I would like to skip this step so I can automate the process.
Keep in mind that I need to download the csv file from ExampleSiteB.com through SFTP...
I am not sure if it is possible to directly download/upload a file from one server to another if one is SFTP. The file size is also quite large, it averages about 25,000 KB / 25 MB.
Another option that I haven't explored yet is requiring or including a file from another server... is that an option or a possibility? The file is located in a folder exclusively for my site and a login is required for SFTP download.
Any insight will be appreciated. Thanks in advance!
Go here and download what you need: http://phpseclib.sourceforge.net/
UPDATE
FOR SFTP
Then in your script:
<?php
include('Net/SFTP.php');
$url = 'http://www.downloadsite.com';
$fileToDownload = "yourCSV.csv";
$cmd = "wget -q \"$url\" -O $fileToDownload";
exec($cmd);
$sftp = new Net_SFTP('www.uploadsite.com');
if (!$sftp->login('username', 'password')) {
exit('Login Failed');
}
echo $sftp->pwd() . "\r\n";
$sftp->put('remote.file.csv', 'yourCSV.csv', NET_SFTP_LOCAL_FILE);
print_r($sftp->nlist());
?>
If you need to connect to a second server for download:
$sftp2 = new Net_SFTP('www.serverFromWhichToDownload.com');
if (!$sftp2->login('username', 'password')) {
exit('Login Failed');
}
echo $sftp2->pwd() . "\r\n";
$sftp2->get('localFileName.csv', 'remoteFileName.csv');
print_r($sftp2->nlist());
Read the docs for further help and examples: http://phpseclib.sourceforge.net/documentation/net.html#net_sftp_get
To Log what your connection is doing if it fails, etc. use this:
include('Net/SSH2.php');
define('NET_SSH2_LOGGING', true);
$ssh = new Net_SSH2('www.domain.tld');
$ssh->login('username','password');
echo $ssh->getLog();
FOR FTP upload - SO has gone crazy, does not want to format my code, but here it is anyway:
$file = 'somefile.txt';
$remote_file = 'readme.txt';
$conn_id = ftp_connect($ftp_server);
$login_result = ftp_login($conn_id, $ftp_user_name, $ftp_user_pass);
if (ftp_put($conn_id, $remote_file, $file, FTP_ASCII)) {
echo "successfully uploaded $file\n";
} else {
echo "There was a problem while uploading $file\n";
}
ftp_close($conn_id);
Yes, that's possible using ssh2_sftp.
http://php.net/manual/en/function.ssh2-sftp.php
I have had good luck with cURL in the past. If you are on a Linux box, it would be trivial to set up a CRON job to do this update process for you. A good reference for CLI HTTP scripting in cURL can be found here, however you may need the -T flag (for file transport) to accomplish the upload portion. Speaking of uploading, if you can run the script/process/crontab from the server you would like to update, I would recommend downloading from the web server to obviate one trip and a third party. Or, if you need to update on demand, you could write a PHP script that uses the built in PHP cURL functions. If you take the Linux+CLI route, you could also use sftp.
Update: In testing curl with sftp (curl -u uname:pword sftp://domain.tld) I get the following error: curl: (1) Protocol sftp not supported or disabled in libcurl on Kubuntu 12.04. So cURL may not be a good idea. I also tested CLI sftp (sftp uname#domain.tld:/dir/file.ext) but could not find a way (short of using ssh keys) to send authentication. Thus, this would necessarily be a manual process unless you did set up ssh keys between the servers. As it does not sound like you have that kind of access to ExampleSiteB.com, this probably isn't acceptable.
Update 2: Since my initial answer turned out to be of little use, I figured I would expand upon one of the above answers. I was trying to find a solution that did not involve a PECL extension, but I did not have much luck with ftp_ssh_connect(). I recommend trying it, you may have better luck and could forgo the PECL extension route.
Sigh, on further reading, it appears ftp_ssh_connect is, understandably, incompatible with the sftp protocol. However, I found a nice blog post about utilizing ssh2_connect() and ssh2_sftp() (as mentioned in a previous answer) and figured I would post that to give you some additional assistance. It is not as simple as calling the functions for most PHP distributions. Here is the blog post. Some of those steps may not be necessary or you may need to do some additional things listed in another blog post I ran across, here.
On my system, all I had to do was run apt-get install libssh2-1-dev libssh2-php and I was able to find ssh2 in my php -m output.
Having an include, as long as you have read/write permissions on the website you're getting the file from should work, however this is just guess work atm as i don't have any means of checking it. Good luck though!
Yes, you should be able to do this.
Whoever told you that you can't do this might be getting confused with JavaScript and cross-site scripting browser restrictions which prevent JavaScript downloaded from one domain to access content in a different domain.
That being said, if you are using PHP which to me implies that you are talking about PHP running on a web sever, you should be able to use PHP or any other scripting or programming language to download the file from SiteB.com, then update the file, and then finally FTP the file to a different web server (SiteA.com).

editing a file on external server with php

I am trying to edit an external file. I have changed file's chmods (666) and I can read the file using file_get_contents but i am unable to put content:
[function.file-put-contents]: failed to open stream: HTTP wrapper does not support writeable connections
how can I solve that problem?
is there any way to edit files on external servers? maybe you can suggest better method
CHMOD the file(s) on the external server to 0777, then they can be changed externally:
<?php
chmod("/somedir/somefile", 0777);
?>
Then use for editing:
<?php
$file = fopen ("ftp://login:password#server", "w");
if (!$file) {
echo "<p>Unable to open remote file for writing.\n";
exit;
}
/* Write the data here. */
fwrite ($file, "blablabla");
fclose ($file);
?>
Be aware that this can create a major security risk.
Check the 'Notes' section of http://us.php.net/manual/en/function.file-put-contents.php for some clues on writing files with a URL filespec. tl;dr: Not as easy as reading remote files via file_get_contents() might make you believe.
This is a BAD idea, though, from a security standpoint. A couple slightly better ideas:
Check out ssh2_scp_send and scp the locally-changed copy of the file to the remote server. This may involve installing the SSH2 PECL extension either through your OS vendor (e.g. installing them in 'apt' for Debian-type Linux machines, 'yum' for Red Hat et al, or similar) or from PECL.
POST to a script on the remote server and have it update the file for you.

How do I use PHP CLI to automate FTP when I don't have access to PHP's native FTP handle?

I'm writing an automation script on a production server that, among other things, needs to grab a list of remote files via FTP (FTP is the only option for interacting with the remote filesystem) and selectively download them.
Why I can't use PHP's native FTP wrappers
This is a production server in a very brittle environment. I'm writing it using PHP CLI, since most of the existing automation scripts are written this way. However, although I have a very new PHP 5.1.2 installation, I'm not able to recompile it with --with-ftp, and that option is not enabled.
The remaining options
So, my options are to connect, get my file list, and selectively download using shell_exec() or the php_filesystem functions using an FTP stream and the PHP native filesystem functions.
Unfortunately, I'm not able to find good code examples of either. When I try to shell_exec using FTP commands, the program hangs, presumably because control stays at the shell once I open up the FTP prompt.
$ftp_connect_command = "ftp -v -n $bl_ftp_host";
$ftp_login_command = "user $bl_ftp_user $bl_ftp_password";
$ftp_bye_command = "bye";
$ftp_connect_response = shell_exec("$ftp_connect_command");
// this never executes, because it hangs here waiting for a return to shell
$ftp_login_response = shell_exec($ftp_login_command);
Or, I imagine the stream based way to do this would be:
$ftp_path = "ftp://$bl_ftp_user:$bl_ftp_user#$bl_ftp_host/";
$stream_options = array('ftp' => array('overwrite' => false));
$context = stream_context_create();
if ($dh = opendir($ftp_path, $context))
{
while (filename = readdir($dh))
{
print($filename);
}
}
But I'm not sure if this is considered a reliable method.
Can anyone provide code samples showing how to capture a directory list and download files by either of these methods?
Apparently, wordpress uses pemftp for pure-PHP FTP (on systems compiled without FTP support)
Have you looked at the native PHP FTP library?
http://us2.php.net/ftp

Categories