Rasiel
Rasiel

Reputation: 2913

ftp_ssl_connect with implicit ftp over tls

Can ftp_ssl_connect handle Implicit FTP over TLS? By default it uses explicit.

I'm trying to upload to a server that accepts only Implicit ftp over tls on port 990; has anybody run into this as of yet? How did you fix it?

Upvotes: 16

Views: 26334

Answers (3)

Nico Westerdale
Nico Westerdale

Reputation: 2186

Based on frustrum and Steven Jeffries's answers I've extended it further. This reuses the curl connection and has some directory listing functions, including one to sort files by last modified.

This is for PHP 7, for lower you will have to rewrite the <=> operator line.

<?php
/**
 * Implicit FTP 
 * @author Nico
 * Based on
 * http://stackoverflow.com/questions/6589730/ftp-ssl-connect-with-implicit-ftp-over-tls
 * http://stackoverflow.com/questions/845220/get-the-last-modified-date-of-a-remote-file
 */
class ImplicitFtp {

    private $server;
    private $username;
    private $password;
    private $curlhandle;

    public function __construct($server, $username, $password) {
        $this->server = $server;
        $this->username = $username;
        $this->password = $password;
        $this->curlhandle = curl_init();
    }

    public function __destruct() {
        if (!empty($this->curlhandle))
            @curl_close($this->curlhandle);
    }

    /**
     * @param string $remote remote path
     * @return resource a cURL handle on success, false on errors.
     */
    private function common($remote) {
        curl_reset($this->curlhandle);
        curl_setopt($this->curlhandle, CURLOPT_URL, 'ftps://' . $this->server . '/' . $remote);
        curl_setopt($this->curlhandle, CURLOPT_USERPWD, $this->username . ':' . $this->password);
        curl_setopt($this->curlhandle, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($this->curlhandle, CURLOPT_SSL_VERIFYHOST, FALSE);
        curl_setopt($this->curlhandle, CURLOPT_FTP_SSL, CURLFTPSSL_TRY);
        curl_setopt($this->curlhandle, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
        return $this->curlhandle;
    }

    public function download($remote, $local = null) {
        if ($local === null) {
            $local = tempnam('/tmp', 'implicit_ftp');
        }

        if ($fp = fopen($local, 'w')) {
            $this->curlhandle = self::common($remote);
            curl_setopt($this->curlhandle, CURLOPT_UPLOAD, 0);
            curl_setopt($this->curlhandle, CURLOPT_FILE, $fp);

            curl_exec($this->curlhandle);

            if (curl_error($this->curlhandle)) {
                return false;
            } else {
                return $local;
            }
        }
        return false;
    }

    public function upload($local, $remote) {
        if ($fp = fopen($local, 'r')) {
            $this->curlhandle = self::common($remote);
            curl_setopt($this->curlhandle, CURLOPT_UPLOAD, 1);
            curl_setopt($this->curlhandle, CURLOPT_INFILE, $fp);

            curl_exec($this->curlhandle);
            $err = curl_error($this->curlhandle);

            return !$err;
        }
        return false;
    }

    /**
     * Get file/folder names
     * @param string $remote
     * @return string[]
     */
    public function listnames($remote) {
        if (substr($remote, -1) != '/')
            $remote .= '/';
        $this->curlhandle = self::common($remote);
        curl_setopt($this->curlhandle, CURLOPT_UPLOAD, 0);
        curl_setopt($this->curlhandle, CURLOPT_FTPLISTONLY, 1);
        curl_setopt($this->curlhandle, CURLOPT_RETURNTRANSFER, 1);

        $result = curl_exec($this->curlhandle);

        if (curl_error($this->curlhandle)) {
            return false;
        } else {
            $files = explode("\r\n", trim($result));
            return $files;
            return $local;
        }
    }

    /**
     * Get file/folder names ordered by modified date
     * @param string $remote
     * @return string[]
     */
    public function listbydate($remote) {
        $files = $this->listnames($remote);
        if (empty($files))
            return null;
        $filedata = array();
        foreach ($files as $file) {

            $this->curlhandle = self::common($remote . '/' . $file);
            curl_setopt($this->curlhandle, CURLOPT_NOBODY, 1);
            curl_setopt($this->curlhandle, CURLOPT_FILETIME, 1);
            curl_setopt($this->curlhandle, CURLOPT_RETURNTRANSFER, 1);
            $result = curl_exec($this->curlhandle);

            if ($result) {
                $timestamp = curl_getinfo($this->curlhandle, CURLINFO_FILETIME);
                $fileobj = array();
                $fileobj['name'] = $file;
                $fileobj['lastmodified'] = ($timestamp != -1) ? date("Y-m-d H:i:s", $timestamp) : null;
                $filedata[] = $fileobj;
            }
        }

        usort($filedata, function ($item1, $item2) {
            return date($item2['lastmodified']) <=> date($item1['lastmodified']);
        });

        return $filedata;
    }



    /**
     * Get file/folder raw data
     * @param string $remote
     * @return string[]
     */
    public function rawlist($remote) {
        if (substr($remote, -1) != '/')
            $remote .= '/';
        $this->curlhandle = self::common($remote);
        curl_setopt($this->curlhandle, CURLOPT_UPLOAD, 0);
        curl_setopt($this->curlhandle, CURLOPT_RETURNTRANSFER, 1);

        $result = curl_exec($this->curlhandle);

        if (curl_error($this->curlhandle)) {
            return false;
        } else {
            $files = explode("\n", trim($result));
            return $files;
            return $local;
        }
    }

    /**
     * Get file/folder parsed data into an array
     * @param string $remote
     * @return array[]
     */
    public function list($remote) {
        $this->curlhandleildren = $this->rawlist($remote);
        if (!empty($this->curlhandleildren)) {
            $items = array();
            foreach ($this->curlhandleildren as $this->curlhandleild) {
                $chunks = preg_split("/\s+/", $this->curlhandleild);
                list($item['rights'], $item['number'], $item['user'], $item['group'], $item['size'], $item['month'], $item['day'], $item['time']) = $chunks;
                array_splice($chunks, 0, 8);
                $item['name'] = trim(implode(" ", $chunks));
                $item['type'] = $chunks[0]{0} === 'd' ? 'directory' : 'file';
                $items[] = $item;
            }
            return $items;
        }
        return false;
    }

}
?>

Upvotes: 11

Steven Jeffries
Steven Jeffries

Reputation: 3572

For anyone who happens to google upon this page and wants a quick solution:

I expanded on frustrum's answer and made a simple class for basic upload/download using this method. I hope it helps!

<?php

class ImplicitFtp {

    private $server;
    private $username;
    private $password;

    public function __construct($server, $username, $password) {
        $this->server = $server;
        $this->username = $username;
        $this->password = $password;
    }

    public function download($remote, $local = null) {
        if ($local === null) {
            $local = tempnam('/tmp', 'implicit_ftp');
        }

        if ($fp = fopen($local, 'w')) {
            $ftp_server = 'ftps://' . $this->server . '/' . $remote;
            $ch = curl_init();

            curl_setopt($ch, CURLOPT_URL, $ftp_server);
            curl_setopt($ch, CURLOPT_USERPWD, $this->username . ':' . $this->password);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($ch, CURLOPT_FTP_SSL, CURLFTPSSL_TRY);
            curl_setopt($ch, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
            curl_setopt($ch, CURLOPT_UPLOAD, 0);
            curl_setopt($ch, CURLOPT_FILE, $fp);

            curl_exec($ch);

            if (curl_error($ch)) {
                curl_close($ch);
                return false;
            } else {
                curl_close($ch);
                return $local;
            }
        }
        return false;
    }

    public function upload($local, $remote) {
        if ($fp = fopen($local, 'r')) {
            $ftp_server = 'ftps://' . $this->server . '/' . $remote;
            $ch = curl_init();

            curl_setopt($ch, CURLOPT_URL, $ftp_server);
            curl_setopt($ch, CURLOPT_USERPWD, $this->username . ':' . $this->password);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
            curl_setopt($ch, CURLOPT_FTP_SSL, CURLFTPSSL_TRY);
            curl_setopt($ch, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
            curl_setopt($ch, CURLOPT_UPLOAD, 1);
            curl_setopt($ch, CURLOPT_INFILE, $fp);

            curl_exec($ch);
            $err = curl_error($ch);
            curl_close($ch);

            return !$err;
        }
        return false;
    }

}

Upvotes: 7

frustrum
frustrum

Reputation: 361

ftp_ssl_connect is only explicit

if you need implicit, use curl

$fp = fopen($path, 'r');
$ftp_server = 'ftps://'.$server.'/'.$filename; 
$ch = curl_init(); 
curl_setopt($ch, CURLOPT_URL, $ftp_server);
curl_setopt($ch, CURLOPT_USERPWD,$user.':'.$pass);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
curl_setopt($ch, CURLOPT_FTP_SSL, CURLFTPSSL_TRY);
curl_setopt($ch, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
curl_setopt($ch, CURLOPT_UPLOAD, 1);
curl_setopt($ch, CURLOPT_INFILE, $fp);

$output = curl_exec($ch);
$error_no = curl_errno($ch);
//var_dump(curl_error($ch));
curl_close($ch);

Upvotes: 23

Related Questions