<?php /** * An FTP transfer wrapper using the OSInet Finite_State_Machine * * @copyright (c) 2007 OSI * @author Frédéric G. MARAND * @license Licensed under the CeCILL 2.0 * @version CVS: $Id: Ftp_Client.php,v 1.3 2007-06-10 19:39:54 marand Exp $ * @link * @since Not applicable yet * @package osinetoffice * @subpackage bo_up_ingram * */ /** * Save current reporting level while we set it */ $_ftpEr = error_reporting(E_ALL | E_STRICT); /** * Class implements a finite-state-machine-based * FTP client with *very* limited functionality, * but that can display progress information * states are: * - init: not yet set up * - offline: no link established * - online: client connected to server * - live: client connected and logged in * - active: a data transfer operation is under way * - unsafe: something failed, disconnect can happen out of our control * * @package osinetoffice * @subpackage bo_up_ingram */ class Ftp_Client extends Finite_State_Machine { /** * Code currently depends on the actual FSM location. Future versions should * probably locate it on their own. */ const FTP_FSM = 'e:/src/osinetoffice/lib/Ftp.xml'; /** * remote properties */ private $fRemoteFile; private $fRemoteHost; private $fRemotePass; private $fRemoteUser; private $fRemoteWd; /** * local properties */ private $fLocalWd; private $fLocalFile; private $fLocalFp; // file pointer to local file, used in get/put/continue /** * FTP properties */ private $fFtpCallback; // name of callback function for progress information private $fFtpGoal; // bytes to be transferred private $fFtpConnection; // connection /** * Initialize parameters from an array * * @todo should really check names and values. * @param array $params * @return Fsm_Result */ public function setParameters($params) { foreach ($params as $name => $value) { $field_name = "f$name"; $this->$field_name = $value; } $ret = $this->apply_event('CheckParameters'); return $ret; } /** * ============ utility functions ============ */ /** * format a boolean value as a string * * @param boolean $val * @return string */ static function stringFromBoolean($val) { return $val ? 'true' : 'false'; } /** * Enter description here... * * @param unknown_type $status * @return unknown */ static function stringFromFtp($status) { switch ($status) { case FTP_FINISHED: $ret = 'FTP_FINISHED'; break; case FTP_MOREDATA: $ret = 'FTP_MOREDATA'; break; case FTP_FAILED: $ret = 'FTP_MOREDATA'; break; default: $ret = 'FTP_INVALID_STATUS'; break; } return $ret; } /** * ============ event handlers ============ */ /** * implement change remote directory * @return string (boolean) */ protected function eventChdir() { $ret = ftp_chdir($this->fFtpConnection, $this->fRemoteWd); return ftp_client::stringFromBoolean($ret); } /** * does the instance have all necessary info for a FTP transfer ? * * @return string (boolean) */ public function eventCheckParameters() { $ret = isset($this->fRemoteHost) && isset($this->fRemoteUser) && isset($this->fRemotePass) && isset($this->fRemoteWd) && isset($this->fRemoteFile) && isset($this->fLocalWd) && isset($this->fLocalFile) && isset($this->fFtpCallback) ; $ret = ftp_client::stringFromBoolean($ret); // echo func_name() . ", ret = $ret\n"; return $ret; } /** * implementation of Close event * * @return string (boolean) */ protected function eventClose() { $ret = 'true'; // echo "Trying to close socket..."; if (is_resource($this->fFtpConnection)) { try { // echo "Trying to close connection..."; ftp_close($this->fFtpConnection); // echo "Done."; } catch (Exception $e) { print_r($e); $ret = 'false'; } } // echo "Socket closed\n"; return $ret; } /** * implementation of Connect event * * @return string (boolean) */ protected function eventConnect() { // echo func_name() . "\n"; $this->fFtpConnection = ftp_connect($this->fRemoteHost); // default port, default timeout $ret = is_resource($this->fFtpConnection); return Ftp_Client::stringFromBoolean($ret); } /** * implementation of continue * @return string FTP_FINISHED | FTP_MOREDATA | FTP_FAILED */ protected function eventContinue() { // echo func_name(); if ($this->fFtpCallback) { call_user_func($this->fFtpCallback, $this, 'pre'); } $ret = ftp_nb_continue($this->fFtpConnection); if ($ret == FTP_FINISHED) { fclose($this->fLocalFp); } if ($this->fFtpCallback) call_user_func($this->fFtpCallback, $this, 'post'); $ret = Ftp_Client::stringFromFtp($ret); return $ret; } /** * implementation of get * * @return string FTP_FINISHED | FTP_MOREDATA | FTP_FAILED * @throws Exception fail creating local file */ protected function eventGet() { // echo func_name() . "\n"; $this->fLocalFp = fopen($this->fLocalFile, "wb"); if (!is_resource($this->fLocalFp)) { $ret = Ftp_Client::stringFromFtp(FTP_FAILED); // throw new Exception(func_name() . ": could not create local file $this->fLocalFile"); return $ret; } $this->fFtpGoal = ftp_size($this->fFtpConnection, $this->fRemoteFile); $ret = ftp_nb_fget($this->fFtpConnection, $this->fLocalFp, $this->fRemoteFile, FTP_BINARY); if ($ret == FTP_FINISHED) { fclose($this->fLocalFp); } call_user_func($this->fFtpCallback, $this, 'post'); // echo func_name() . " => $ret\n"; flush(); return Ftp_Client::stringFromFtp($ret); } /** * implementation of login * @return string (boolean) */ protected function eventLogin() { // echo func_name() . "\n"; $ret = ftp_login($this->fFtpConnection, $this->fRemoteUser, $this->fRemotePass); return Ftp_Client::stringFromBoolean($ret); } /** * handler must be implemented, but does nothing * @return void */ protected function eventProgress() { return; // the FSM needs nothing for this } /** * @return void */ public function __construct() { $this->load_fsm(Ftp_Client::FTP_FSM); // print_r($this->f_transitions); parent::__construct(); // print_r($this);die(); } /** * close connection if it hasn't been done, to prevent connection * lingering on the server if avoidable * @return void */ public function __destruct() { // echo func_name() . PHP_EOL; if ($this->is_event_allowed('Close')) { $this->apply_event('Close'); } if(is_resource($this->fLocalFp)) try { echo "Trying to close file..."; fclose($this->fLocalFp); echo "Done\n"; } catch (Exception $e) { print_r($e); } // echo "End of " . func_name() . "\n"; } /** * Make sure name can be resolved * ignore hosts that don't resolve in DNS or hosts file * * @param string $host * @return boolean */ public function isHostValid($host = null) { $ret = is_array(gethostbynamel($host)); return $ret; } /** * If a transfer is under way, return the progress percentile, otherwise 0.0 * * @return float */ public function getProgress() { if ((!$this->f_state == 'active') || (!is_resource($this->fLocalFp))) { $ret = 0.0; } else { $pos = ftell($this->fLocalFp); $ret = $pos / $this->fFtpGoal; } return $ret; } } /** * Restore previous reporting level */ error_reporting($_ftpEr);