Ftp_Client.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. <?php
  2. /**
  3. * An FTP transfer wrapper using the OSInet Finite_State_Machine
  4. *
  5. * @copyright (c) 2007 OSI
  6. * @author Frédéric G. MARAND
  7. * @license Licensed under the CeCILL 2.0
  8. * @version CVS: $Id: Ftp_Client.php,v 1.3 2007-06-10 19:39:54 marand Exp $
  9. * @link
  10. * @since Not applicable yet
  11. * @package osinetoffice
  12. * @subpackage bo_up_ingram
  13. *
  14. */
  15. /**
  16. * Save current reporting level while we set it
  17. */
  18. $_ftpEr = error_reporting(E_ALL | E_STRICT);
  19. /**
  20. * Class implements a finite-state-machine-based
  21. * FTP client with *very* limited functionality,
  22. * but that can display progress information
  23. * states are:
  24. * - init: not yet set up
  25. * - offline: no link established
  26. * - online: client connected to server
  27. * - live: client connected and logged in
  28. * - active: a data transfer operation is under way
  29. * - unsafe: something failed, disconnect can happen out of our control
  30. *
  31. * @package osinetoffice
  32. * @subpackage bo_up_ingram
  33. */
  34. class Ftp_Client extends Finite_State_Machine
  35. {
  36. /**
  37. * Code currently depends on the actual FSM location. Future versions should
  38. * probably locate it on their own.
  39. */
  40. const FTP_FSM = 'e:/src/osinetoffice/lib/Ftp.xml';
  41. /**
  42. * remote properties
  43. */
  44. private $fRemoteFile;
  45. private $fRemoteHost;
  46. private $fRemotePass;
  47. private $fRemoteUser;
  48. private $fRemoteWd;
  49. /**
  50. * local properties
  51. */
  52. private $fLocalWd;
  53. private $fLocalFile;
  54. private $fLocalFp; // file pointer to local file, used in get/put/continue
  55. /**
  56. * FTP properties
  57. */
  58. private $fFtpCallback; // name of callback function for progress information
  59. private $fFtpGoal; // bytes to be transferred
  60. private $fFtpConnection; // connection
  61. /**
  62. * Initialize parameters from an array
  63. *
  64. * @todo should really check names and values.
  65. * @param array $params
  66. * @return Fsm_Result
  67. */
  68. public function setParameters($params)
  69. {
  70. foreach ($params as $name => $value)
  71. {
  72. $field_name = "f$name";
  73. $this->$field_name = $value;
  74. }
  75. $ret = $this->apply_event('CheckParameters');
  76. return $ret;
  77. }
  78. /**
  79. * ============ utility functions ============
  80. */
  81. /**
  82. * format a boolean value as a string
  83. *
  84. * @param boolean $val
  85. * @return string
  86. */
  87. static function stringFromBoolean($val)
  88. {
  89. return $val ? 'true' : 'false';
  90. }
  91. /**
  92. * Enter description here...
  93. *
  94. * @param unknown_type $status
  95. * @return unknown
  96. */
  97. static function stringFromFtp($status)
  98. {
  99. switch ($status)
  100. {
  101. case FTP_FINISHED: $ret = 'FTP_FINISHED'; break;
  102. case FTP_MOREDATA: $ret = 'FTP_MOREDATA'; break;
  103. case FTP_FAILED: $ret = 'FTP_MOREDATA'; break;
  104. default: $ret = 'FTP_INVALID_STATUS'; break;
  105. }
  106. return $ret;
  107. }
  108. /**
  109. * ============ event handlers ============
  110. */
  111. /**
  112. * implement change remote directory
  113. * @return string (boolean)
  114. */
  115. protected function eventChdir()
  116. {
  117. $ret = ftp_chdir($this->fFtpConnection, $this->fRemoteWd);
  118. return ftp_client::stringFromBoolean($ret);
  119. }
  120. /**
  121. * does the instance have all necessary info for a FTP transfer ?
  122. *
  123. * @return string (boolean)
  124. */
  125. public function eventCheckParameters()
  126. {
  127. $ret = isset($this->fRemoteHost)
  128. && isset($this->fRemoteUser)
  129. && isset($this->fRemotePass)
  130. && isset($this->fRemoteWd)
  131. && isset($this->fRemoteFile)
  132. && isset($this->fLocalWd)
  133. && isset($this->fLocalFile)
  134. && isset($this->fFtpCallback)
  135. ;
  136. $ret = ftp_client::stringFromBoolean($ret);
  137. // echo func_name() . ", ret = $ret\n";
  138. return $ret;
  139. }
  140. /**
  141. * implementation of Close event
  142. *
  143. * @return string (boolean)
  144. */
  145. protected function eventClose()
  146. {
  147. $ret = 'true';
  148. // echo "Trying to close socket...";
  149. if (is_resource($this->fFtpConnection))
  150. {
  151. try
  152. {
  153. // echo "Trying to close connection...";
  154. ftp_close($this->fFtpConnection);
  155. // echo "Done.";
  156. }
  157. catch (Exception $e)
  158. {
  159. print_r($e);
  160. $ret = 'false';
  161. }
  162. }
  163. // echo "Socket closed\n";
  164. return $ret;
  165. }
  166. /**
  167. * implementation of Connect event
  168. *
  169. * @return string (boolean)
  170. */
  171. protected function eventConnect()
  172. {
  173. // echo func_name() . "\n";
  174. $this->fFtpConnection = ftp_connect($this->fRemoteHost); // default port, default timeout
  175. $ret = is_resource($this->fFtpConnection);
  176. return Ftp_Client::stringFromBoolean($ret);
  177. }
  178. /**
  179. * implementation of continue
  180. * @return string FTP_FINISHED | FTP_MOREDATA | FTP_FAILED
  181. */
  182. protected function eventContinue()
  183. {
  184. // echo func_name();
  185. if ($this->fFtpCallback)
  186. {
  187. call_user_func($this->fFtpCallback, $this, 'pre');
  188. }
  189. $ret = ftp_nb_continue($this->fFtpConnection);
  190. if ($ret == FTP_FINISHED)
  191. {
  192. fclose($this->fLocalFp);
  193. }
  194. if ($this->fFtpCallback)
  195. call_user_func($this->fFtpCallback, $this, 'post');
  196. $ret = Ftp_Client::stringFromFtp($ret);
  197. return $ret;
  198. }
  199. /**
  200. * implementation of get
  201. *
  202. * @return string FTP_FINISHED | FTP_MOREDATA | FTP_FAILED
  203. * @throws Exception fail creating local file
  204. */
  205. protected function eventGet()
  206. {
  207. // echo func_name() . "\n";
  208. $this->fLocalFp = fopen($this->fLocalFile, "wb");
  209. if (!is_resource($this->fLocalFp))
  210. {
  211. $ret = Ftp_Client::stringFromFtp(FTP_FAILED);
  212. // throw new Exception(func_name() . ": could not create local file $this->fLocalFile");
  213. return $ret;
  214. }
  215. $this->fFtpGoal = ftp_size($this->fFtpConnection, $this->fRemoteFile);
  216. $ret = ftp_nb_fget($this->fFtpConnection, $this->fLocalFp,
  217. $this->fRemoteFile, FTP_BINARY);
  218. if ($ret == FTP_FINISHED)
  219. {
  220. fclose($this->fLocalFp);
  221. }
  222. call_user_func($this->fFtpCallback, $this, 'post');
  223. // echo func_name() . " => $ret\n"; flush();
  224. return Ftp_Client::stringFromFtp($ret);
  225. }
  226. /**
  227. * implementation of login
  228. * @return string (boolean)
  229. */
  230. protected function eventLogin()
  231. {
  232. // echo func_name() . "\n";
  233. $ret = ftp_login($this->fFtpConnection, $this->fRemoteUser, $this->fRemotePass);
  234. return Ftp_Client::stringFromBoolean($ret);
  235. }
  236. /**
  237. * handler must be implemented, but does nothing
  238. * @return void
  239. */
  240. protected function eventProgress()
  241. {
  242. return; // the FSM needs nothing for this
  243. }
  244. /**
  245. * @return void
  246. */
  247. public function __construct()
  248. {
  249. $this->load_fsm(Ftp_Client::FTP_FSM);
  250. // print_r($this->f_transitions);
  251. parent::__construct();
  252. // print_r($this);die();
  253. }
  254. /**
  255. * close connection if it hasn't been done, to prevent connection
  256. * lingering on the server if avoidable
  257. * @return void
  258. */
  259. public function __destruct()
  260. {
  261. // echo func_name() . PHP_EOL;
  262. if ($this->is_event_allowed('Close'))
  263. {
  264. $this->apply_event('Close');
  265. }
  266. if(is_resource($this->fLocalFp))
  267. try
  268. {
  269. echo "Trying to close file...";
  270. fclose($this->fLocalFp);
  271. echo "Done\n";
  272. }
  273. catch (Exception $e)
  274. {
  275. print_r($e);
  276. }
  277. // echo "End of " . func_name() . "\n";
  278. }
  279. /**
  280. * Make sure name can be resolved
  281. * ignore hosts that don't resolve in DNS or hosts file
  282. *
  283. * @param string $host
  284. * @return boolean
  285. */
  286. public function isHostValid($host = null)
  287. {
  288. $ret = is_array(gethostbynamel($host));
  289. return $ret;
  290. }
  291. /**
  292. * If a transfer is under way, return the progress percentile, otherwise 0.0
  293. *
  294. * @return float
  295. */
  296. public function getProgress()
  297. {
  298. if ((!$this->f_state == 'active') || (!is_resource($this->fLocalFp)))
  299. {
  300. $ret = 0.0;
  301. }
  302. else
  303. {
  304. $pos = ftell($this->fLocalFp);
  305. $ret = $pos / $this->fFtpGoal;
  306. }
  307. return $ret;
  308. }
  309. }
  310. /**
  311. * Restore previous reporting level
  312. */
  313. error_reporting($_ftpEr);