<?php
/***************************************************************************
* Copyright (C) 2007 by Cesar D. Rodas *
* hide@address.com *
* *
* Permission is hereby granted, free of charge, to any person obtaining *
* a copy of this software and associated documentation files (the *
* "Software"), to deal in the Software without restriction, including *
* without limitation the rights to use, copy, modify, merge, publish, *
* distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to *
* the following conditions: *
* *
* The above copyright notice and this permission notice shall be *
* included in all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, *
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR *
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
* OTHER DEALINGS IN THE SOFTWARE. *
***************************************************************************/
/**
* File Exchange Protocol
*
* This is a PHP class based on Amazon S3 like transference protocol.
* The Idea is to develope a fexible, plataform indepente, file transfer
* that works over HTTP(s). Basically for exchange files between servers.
*
* @package File Exchange Protocol
* @version 1.0
* @author Cesar D. Rodas <hide@address.com>
* @license BSD License
*/
require_once( dirname(__FILE__).'/FEP_base.php'); /* include base class*/
(include_once dirname(__FILE__).'/safeIO.php') or die('gCache is missing! Please <a href="http://cesars.users.phpclasses.org/browse/package/3972.html">download</a> and copy "safeIO.php" into this folder!');
/* Error numbers */
define('ERR_PROTOCOL' ,1);
define('ERR_AUTH' ,2);
define('ERR_MKDIR' ,3);
define('ERR_RMDIR' ,4);
define('ERR_LIST' ,5);
define('ERR_UNLINK' ,6);
define('ERR_NOTEXIST' ,7);
/* Error text */
define('PROTOCOL_ERR_TEXT','Error in protocol');
define('PROTOCOL_ERR_AUTH','Unautorized user');
define('PROTOCOL_ERR_MKDIR','Imposible to create folder, OS problems, please check your config.');
define('PROTOCOL_ERR_RMDIR','Imposible to delete the folder, the folder must be empty for delete');
define('PROTOCOL_ERR_LIST','Imposible to list files the folder');
define('PROTOCOL_ERR_UNLINK','Imposible to delete file');
define('PROTOCOL_ERR_NOTEXIST','The request file does not exist');
/**
* FEP SERVER
*
* This class is a File Exchange Protocol Server.
*
* @package File Exchange Protocol
* @version 1.0
* @author Cesar D. Rodas <hide@address.com>
* @license BSD License
*/
class FEP_server extends FEP_base {
/**
* Working directory
*
* @var string
* @access private
*/
var $directory;
/**
* Authentication function
*
* @var string
* @access private
*/
var $auth;
function FEP_server($auth='') {
set_time_limit(0); /* disable php time limit! */
$this->auth=$auth;
$this->directory=getcwd();
}
/**
* Start Handling client request
*
*
*/
function start() {
/* Needed Http Params */
$_needed = array('HTTP_FEP_USER','HTTP_FEP_KEY','HTTP_FEP_ACTION','HTTP_FEP_PATH','HTTP_FEP_DATE','HTTP_FEP_VERSION');
/* See if needed params are present, if not, get out. */
foreach($_needed as $v) {
if ( ! isset($_SERVER[$v]) ) $this->FEP_error(ERR_PROTOCOL,__LINE__);
$info[] = $_SERVER[$v];
}
$user = $info[0];
$key = $info[1];
$action = strtoupper($info[2]);
$path = $info[3];
$date = $info[4];
$version = $info[5];
/* The request file is transform to a Filesystem dir */
$file = realpath($this->directory).'/'.$path;
/**
* Avoiding basic hacker attack! by checking if the quest
* file is on the $this->directory or not (it should be hacked
* by asking "../../file".
* This portion of code is very important, because avoid that
* kind of attacks.
*/
$f = realpath($this->directory);
if ( $f != substr(dirname($file),0,strlen($f)) ) {
die("Avoiding hacker attack!, please be smarter to hackme");
}
if ( !$this->FEP_auth($user, $key, $action, $path, $date) ) $this->FEP_error(ERR_AUTH,__LINE__);
/* The request request file is now $file */
header("FEP-RESPONSE: OK");
switch ( $action ) {
/* Read a file! */
case "READ":
/**
* If client did not send us an Start and offset of bytes to read
* this is an error!, We cannot send them the whole file!.
*/
if ( !isset($_SERVER['HTTP_FEP_START']) || !isset($_SERVER['HTTP_FEP_LENGTH']) )
$this->FEP_error(ERR_PROTOCOL,__LINE__);
/**
* Open file with safeIO. This class will lock
* the whole file with a shared lock.
*/
clearstatcache();
$bytesOffSet = $_SERVER['HTTP_FEP_START'];
if (! is_file($file) ) {
$this->FEP_error( ERR_NOTEXIST, __LINE__);
}
if ( filesize($file) < $bytesOffSet + $_SERVER['HTTP_FEP_LENGTH']) {
header("FEP-EOF: true");
}
$fp = new safeIO;
$fp->open($file,READ);
$fp->seek($_SERVER['HTTP_FEP_START'], SEEK_SET);
$buf = $fp->read( $_SERVER['HTTP_FEP_LENGTH'] );
$e = strlen($buf);
header("FEP-LENGTH: ".$e);
header("Content-Length: ".$e);
echo $buf;flush();
$fp->close();
break;
case "WRITE":
/**
* If client did not send us an Start and offset of bytes to read
* this is an error!, We cannot send them the whole file!.
*/
if ( !isset($_SERVER['HTTP_FEP_START']) || !isset($_SERVER['CONTENT_LENGTH']) )
$this->FEP_error(ERR_PROTOCOL,__LINE__);
/* Get the HTTP body*/
$fp = fopen('php://input',"r");
$body='';
while ( strlen($body) < $_SERVER['CONTENT_LENGTH'])
$body .= fread($fp,8192);
fclose($fp);
echo strlen($body).$_SERVER['CONTENT_LENGTH'];
/**
* Open file with safeIO. This class will lock
* the whole file with a shared lock.
*/
$fp = new safeIO;
$fp->open($file,APPEND);
$fp->seek($_SERVER['HTTP_FEP_START'], SEEK_SET);
$fp->write($body, $_SERVER['CONTENT_LENGTH'] );
$fp->close();
break;
case "INFO":
$e = stat($file);
echo "${e['mode']}|${e['size']}|${e['atime']}|${e['mtime']}|${e['ctime']}";
break;
case "LIST":
if ( !is_dir($file) ) {
$this->FEP_error(ERR_LIST,__LINE__);
}
$f = opendir($file);
while ( $r = readdir($f) ) {
if ( $r == "." || $r == "..") continue;
echo $r."|".( is_dir($file.'/'.$r) ? "dir" : "file")."\r\n";
}
closedir($f);
break;
case "UNLINK":
unlink($file) or $this->FEP_error(ERR_UNLINK,__LINE__);
break;
case "RMDIR":
if ( is_dir($file) == true) {
rmdir($file) or $this->FEP_error(ERR_RMDIR,__LINE__);
die();
}
$this->FEP_error(ERR_RMDIR,__LINE__);
break;
case "MKDIR":
if ( is_dir($file) == true) {
die();//dir already exist, just exit
}
mkdir($file) or $this->FEP_error(ERR_MKDIR,__LINE__);
break;
default:
$this->FEP_error(ERR_PROTOCOL,__LINE__);
}
//phpinfo();
}
/**
* Set the $pwd the working directory, the
* directory where files will be written. This will be
* root ("/") of the FEP_server.
*
* @param string $pwd Directory
* @return bool True is sucess
* @access public
*/
function setWorkingDir($pwd) {
if ( is_dir($pwd) ) {
$this->directory=$pwd;
return true;
}
trigger_error(NOT_VALID_DIR,E_USER_WARNING);
return false;
}
/**
* Authentication
*
* Validate the remote user and also check permission of
* the user
*
* @param string $user
* @param string $key
* @param string $action
* @param string $path
* @param string $clientDate
* @return bool
* @access private
*/
function FEP_auth($user,$key, $action, $path, $clientDate) {
if ( $this->auth != '') {
$f = & $this->auth;
$pass='';
$ret = $f($user, $pass, $action,$path);
if (!$ret) return false; /* username error */
//echo "cesar:".$this->genAuthKey($user,$pass,$path,$action,$clientDate)."\r\n";
if ( $key != $this->genAuthKey($user,$pass,$path,$action,$clientDate)) return false; /* key error */
}
return true; /* return ok*/
}
/**
* Display Error
*
* Send error to the client, and stop the execution
* of the script.
*
* @param int Number of error
* @param int Line of error (Only on debug)
*/
function FEP_error($errNro,$line=__LINE__) {
$t=''; /* text to print */
$h='ERR';
switch($errNro) {
case ERR_PROTOCOL:
$h='ERR';
$t=PROTOCOL_ERR_TEXT;
break;
case ERR_AUTH:
$h='UNAUTH';
$t=PROTOCOL_ERR_AUTH;
break;
case ERR_MKDIR:
$h='ERR_OS';
$t=PROTOCOL_ERR_MKDIR;
break;
case ERR_RMDIR:
$t=PROTOCOL_ERR_RMDIR;
break;
case ERR_LIST:
$t=PROTOCOL_ERR_LIST;
break;
case ERR_UNLINK:
$t=PROTOCOL_ERR_UNLINK;
break;
case ERR_NOTEXIST :
$t = PROTOCOL_ERR_NOTEXIST;
break;
}
header("FEP-RESPONSE: ${h}");
echo $t;
echo " ".$line;
die();
}
}
?>