<?php
//
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available through the world-wide-web at the following url: |
// | http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | hide@address.com so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
/**
@version $Id: Download.php,v 1.1 2004/01/28 11:39:51 ordnas Exp $
@copyright Copyright © 2001-2004 ZZ/OSS GbR, http://www.zzoss.com
@license http://opensource.org/licenses/lgpl-license.php GNU Lesser General Public License
*/
/**
* Helper methods for several occasions.
*/
class ZZOSS_FileDownload
{
var $src;
var $dest_dir;
var $save_as;
var $log;
var $proxy = array();
var $callback = NULL;
function setCallback($callback)
{
if(is_array($callback)){
$this->callback = $callback;
} else {
PEAR::raiseError('Callback parameters need to be passed in an array');
}
}
function setProxy($host, $port, $user, $pass)
{
$this->proxy['host'] = $host;
$this->proxy['port'] = $port;
$this->proxy['user'] = $user;
$this->proxy['pass'] = $pass;
}
function download($src, $dest, $replace = true)
{
$this->src = $src;
if($replace && is_file($dest)) {
@unlink($dest);
}
if(!is_dir($dest)){
$this->save_as = basename($dest);
$this->dest_dir = dirname($dest);
} else {
$this->save_as = basename($src);
$this->dest_dir = $dest;
}
if(is_null($this->callback)){
$this->callback = array(&$this, '_downloadCallback');
}
$result = $this->downloadHttp($this->src, $this->dest_dir, $this->callback);
// WORKAROUND, did not understand how to define
// the full file path in PEAR's method, so we
// copy the saved file to the defined location,
// but only if the differ
//echo "$dest != $result";
if(is_object($result)) {
// TODO: trigger error since we got a pear error object
return PEAR::raiseError("Unable to get file $src");
}/* elseif(!is_dir($dest) && $dest != $result){
copy($result, $dest);
unlink($result);
}*/
return $result;
}
function getLog()
{
return $this->log;
}
/**
* Taken and modified from PEAR_Common.
*/
function _downloadCallback($msg, $params = null)
{
switch ($msg) {
case 'done':
$this->log .= '...done: ' . number_format($params, 0, '', ',') . ' bytes'."\n";
break;
case 'bytesread':
static $bytes;
if (empty($bytes)) {
$bytes = 0;
}
if (!($bytes % 10240)) {
$this->log .= '.';
}
$bytes += $params;
break;
case 'start':
$this->log .= "Starting to download {$params[0]} (".number_format($params[1], 0, '', ',')." bytes)"."\n";
break;
}
}
/**
* Taken and modified from PEAR_Common.
*
* Download a file through HTTP. Considers suggested file name in
* Content-disposition: header and can run a callback function for
* different events. The callback will be called with two
* parameters: the callback type, and parameters. The implemented
* callback types are:
*
* 'message' the parameter is a string with an informational message
* 'start' download is starting, parameter is number of bytes
* that are expected, or -1 if unknown
* 'bytesread' parameter is the number of bytes read so far
* 'done' download is complete, parameter is the total number
* of bytes read
* 'connfailed' if the TCP connection fails, this callback is called
* with array(host,port,errno,errmsg)
* 'writefailed' if writing to disk fails, this callback is called
* with array(destfile,errmsg)
*
* If an HTTP proxy has been configured (http_proxy PEAR_Config
* setting), the proxy will be used.
*
* @param string $url the URL to download
* @param object $config PEAR_Config instance
* @param string $save_dir (optional) directory to save file in
* @param mixed $callback (optional) function/method to call for status
* updates
*
* @return string Returns the full path of the downloaded file or a PEAR
* error on failure. If the error is caused by
* socket-related errors, the error object will
* have the fsockopen error code available through
* getCode().
*
* @access public
*/
function downloadHttp($url, $save_dir = '.', $callback = null)
{
if ($callback) {
call_user_func($callback, 'setup');
}
if (preg_match('!^http://([^/:?#]*)(:(\d+))?(/.*)!', $url, $matches)) {
list(,$host,,$port,$path) = $matches;
}
$proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
if (count($this->proxy)) {
$proxy_host = @$this->proxy['host'];
$proxy_port = @$this->proxy['port'];
$proxy_user = @$this->proxy['user'];
$proxy_pass = @$this->proxy['pass'];
if ($proxy_port == '') {
$proxy_port = 8080;
}
if ($callback) {
call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
}
}
if (empty($port)) {
$port = 80;
}
if ($proxy_host != '') {
$fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
if (!$fp) {
if ($callback) {
call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
$errno, $errstr));
}
return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
}
$request = "GET $url HTTP/1.0\r\n";
} else {
$fp = @fsockopen($host, $port, $errno, $errstr);
if (!$fp) {
if ($callback) {
call_user_func($callback, 'connfailed', array($host, $port,
$errno, $errstr));
}
return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
}
$request = "GET $path HTTP/1.0\r\n";
}
$request .= "Host: $host:$port\r\n".
"User-Agent: PHP/".PHP_VERSION."\r\n";
if ($proxy_host != '' && $proxy_user != '') {
$request .= 'Proxy-Authorization: Basic ' .
base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
}
$request .= "\r\n";
fwrite($fp, $request);
$headers = array();
while (trim($line = fgets($fp, 1024))) {
if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) {
$headers[strtolower($matches[1])] = trim($matches[2]);
} elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
if ($matches[1] != 200) {
return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)");
}
}
}
if (isset($headers['content-disposition']) &&
preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|$)/', $headers['content-disposition'], $matches)) {
$save_as = basename($matches[1]);
} else {
$save_as = basename($url);
}
if (strlen($this->save_as)) {
$save_as = $this->save_as;
}
$dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
if (!$wp = @fopen($dest_file, 'wb')) {
fclose($fp);
if ($callback) {
call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
}
return PEAR::raiseError("could not open $dest_file for writing");
}
if (isset($headers['content-length'])) {
$length = $headers['content-length'];
} else {
$length = -1;
}
$bytes = 0;
if ($callback) {
call_user_func($callback, 'start', array(basename($dest_file), $length));
}
while ($data = @fread($fp, 1024)) {
$bytes += strlen($data);
if ($callback) {
call_user_func($callback, 'bytesread', $bytes);
}
if (!@fwrite($wp, $data)) {
fclose($fp);
if ($callback) {
call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
}
return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
}
}
fclose($fp);
fclose($wp);
if ($callback) {
call_user_func($callback, 'done', $bytes);
}
return $dest_file;
}
}
?>