Location: PHPKode > scripts > File Exchange Protocol > file-exchange-protocol/FEP_server.php
<?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();
    }
} 


?>
Return current item: File Exchange Protocol