<?php
define('idDiGIR_DiGIRm','$Id: DiGIRm.php,v 1.7 2004/05/18 17:51:29 rdg Exp $');
if (defined('idDIGIR_VERSION_REQUEST'))
    return;

define('DIGIR_REQUEST',false);
define('DIGIRM_CLIENT_ID',$_SERVER['REMOTE_ADDR']);
define('DiGIRVERSION','$Revision: 1.7 $');

$ScriptCalledDirect = ((isset($_SERVER['SCRIPT_NAME']) &&
                        preg_match("/.\/DiGIRm.php/i",$_SERVER['SCRIPT_NAME'])) ||
                       (isset($_SERVER['argv'][0]) &&
                        strcasecmp($_SERVER['argv'][0],'DiGIRm.php') === 0));

if (!$ScriptCalledDirect)
{
    //The script was included.  Do not continue.
    //The defines made above will still be valid.
    return;
}

define('MY_ADDRESS',"http://".$_SERVER['SERVER_NAME'].$_SERVER['SCRIPT_NAME']);

//turn this on only if you want a messy response with a bunch of diagnostic stuff
define('__DEBUG',FALSE);
function decho($s)
{
	if (__DEBUG)
		echo $s."\n";
}


/**
* Generates the header(s) for a DiGIRm response
*/
function DiGIRmHeader($requestType,$aheaders = NULL)
{
    if (is_null($aheaders))
    {
        return DiGIRHeader($requestType,DIGIRM_CLIENT_ID);
    }
}


/**
* class DiGIRmTarget contains the information for a single request
* used to help parse a DiGIRm request.
*/
class DiGIRmTarget
{
	var $clientID;
	var $endpoint;
	var $payload;
	var $opcode; //'search' | 'metadata' | 'inventory'
	var $resource;

	/**
	* Constructor for a DiGIRmTarget instance
	* @param string $id ClientID value that identifies this target within the 
	*	context of this DiGIRm operation
	* @param URL $ep The URL of the target endpoint
	* @param string $op The operation to be performed.  One of search, metadata, inventory
	* @param string $res The name of the resource that is the subject of the operation (not 
	*	used in metadata operations)
	*/		
	function DiGIRmTarget($id,$ep,$op,$res='')
	{
		$this->clientID = $id;
		$this->endpoint = $ep;
		$this->payload = '';
		$this->opcode = $op;
		$this->resource = $res;
	}
	
	/**
	* makePayload creates a request document for this target
	* @param array $attrs List of attributes (such as namespaces) included in the request
	* @param string $version The contents of the header version element
	* @param string $sendtime A ISO-8601 formatted time stamp for the sentime element of the header
	* @param string $source The contents of the source tag for the header
	* @param string $payload The XML payload for the request document
	*/
	function makePayload($attrs,$version,$sendtime,$source,$payload)
	{
		$res = "<request";
		foreach ($attrs as $k=>$v)
		{
			$res .= " ".$k."='".$v."'";
		}
		$res .= ">\n";
		$res .= "<header clientKey='".$this->clientID."'>\n";
		$res .= "<version>$version</version>\n";
		$res .= "<sendTime>$sendtime</sendTime>\n";
		$res .= "<destination resource='".$this->resource."'>";
		$res.= htmlentities($this->endpoint);
		$res .= "</destination>\n";
		$res .= "<type>".$this->opcode."</type>\n";
		$res .= "</header>";
		$res .= $payload;
		$res .= "</request>";
		$this->payload = $res;
	}
}

/**
* initParams initializes the DiGIRm operating parameters
* 
* @return 
*/
function initParams()
{
	$params = array();
	$params['request'] = '';
	$params['debug'] = false;
	$params['operation'] = 'metadata';
	$params['status'] = 1;
	$params['targets'] = NULL;
	return $params;
}

/**
* loadNVPairs
*
* @return 
*/
function loadNVPairs(&$params)
{
}

/**
* loadRequest
*
* @return 
*/
function loadRequest($request,&$params)
{
    global $g_LOGINFO;
    $params['request'] = $request;
    $g_LOGINFO['request'] = str_replace("\r"," ",str_replace("\n"," ",$request));

	//create XPath Parser
    $xparser = new XPath();
    $xparser->setVerbose(0);
    $xparser->setXmlOption(XML_OPTION_CASE_FOLDING,false);
    $xparser->setXmlOption(XML_OPTION_SKIP_WHITE,true);
	$res = NULL;

	//load the request document from a URL or from the HTTP reqeust data	
	if (isURL($request))
	{
    	$res = $xparser->importFromFile($request);
	}
	else
	{
    	$res = $xparser->importFromString($request);
    }
	if (!$res)
	{
		//problem loading the document.
		$params['status'] = 0;
		appendError(DC_INVALIDREQUEST,'Could not load request document',DIAG_ERROR);
		return;
	}
	
	//extract the operation type
	$qry = "//request/header/type";
	$pths = $xparser->match($qry);
	if (count($pths)<=0)
	{
		$params['status'] = 0;
		appendError(DC_INVALIDREQUEST,'Could not determine operation type',DIAG_ERROR);
		return;
	}
	$params['operation'] = trim(strtolower($xparser->getData($pths[0])));
	if ($params['operation'] == 'scan')
		$params['operation'] = 'inventory';
	elseif ($params['operation'] == 'getdata')
		$params['operation'] = 'search';
	elseif ($params['operation'] == 'getcontent')
		$params['operation'] = 'search';

	$g_LOGINFO['operation'] = $params['operation'];

	//get the operation payload
	$payload = "";
	if ($params['operation'] == 'metadata')
	{
	}
	elseif($params['operation'] == 'search')
	{
            $qry = "//request/search";
            $pths = $xparser->match($qry);
            if (count($pths) <=0)
            {
                $params['status'] = 0;
                appendError(DC_INVALIDREQUEST,'Could not operation load payload',DIAG_ERROR);
                return;			
            }
            $payload = $xparser->exportAsXml($pths[0]);
	}
	elseif ($params['operation'] = 'inventory')
	{
            $qry = "//request/inventory | //request/scan";
            $pths = $xparser->match($qry);
            if (count($pths) <=0)
            {
                $params['status'] = 0;
                appendError(DC_INVALIDREQUEST,'Could not operation load payload',DIAG_ERROR);
                return;			
            }
            $payload = $xparser->exportAsXml($pths[0]);
	}
	decho("payload=\n".htmlentities($payload));

	//Get the header data
	$qry = "//request[1]";
	$requestattrs = $xparser->getAttributes($qry);
	$version = $xparser->getData("//request[1]/header[1]/version[1]");
	$sendTime = $xparser->getData("//request[1]/header[1]/sendTime[1]");
	$source = $xparser->getData("//request[1]/header[1]/source[1]");

	//extract the list of targets and construct individual payloads
	$qry = "//request/header/destination";
	$pths = $xparser->match($qry);
	$cid = 0;
	$targets = array();
	foreach ($pths as $pth)
	{
		$resource = '';
		$clientID = '';
		$endpoint = '';
		$attrs = $xparser->getAttributes($pth);
		if (isset($attrs['resource']))
		{
			$resource = $attrs['resource'];
		}
		if (isset($attrs['clientID']))
			$clientID = $attrs['clientID'];
		else
		{
			$clientID = $cid;
			$cid ++;
		}
		$endpoint = $xparser->getData($pth);
		//check to make sure the endpoint is not the same URL as this script
		if (strcasecmp($endpoint,MY_ADDRESS) === 0)
		{
		    appendError(DC_RECURSIVE,'Circular reference attemped in DiGIRm',DIAG_ERROR);
		    $params['status'] = 0;
		    return;
		}
		if (strlen($endpoint) > 0)
		{			
			$target = new DiGIRmTarget($clientID,$endpoint,$params['operation'],$resource);
			$target->makePayload($requestattrs,
								 $version,
								 $sendTime,
								 $source,
								 $payload);
			array_push($targets,$target);
		}
	}
	$params['targets'] = $targets;		
	$params['status'] = 1;
}

/**
* 
*
* @return 
*/
function loadParams()
{
    global $HTTP_GET_VARS,
           $HTTP_POST_VARS,
           $QUERY_STRING;

    $params = initParams();
	
    //The request can be an XML document, a reference to an XML document, or
    //a bunch of name + value pairs.  In either case, the data may be sent by
    //POST or GET, and may be identified by the name "doc" or "request", or may
    //simply form the entire body of the submitted data (i.e. the default
    //parameter).

    $request = NULL;

    //Check to see if request is the "default" parameter for GET or POST
    //if so then it will be a url encoded xml request document or a URL pointing
    //to a request document
    $request = getDefaultParameter();
    
    //$request = file_get_contents("test/digirm_metadata.xml");
    
    if (strlen($request) < 1)
    	$request = NULL;
    
    //maybe the request is called "request"?
    if (is_null($request))
	   $request = stripcslashes(getVar("request",NULL));
    if (strlen($request) < 1)
    	$request = NULL;

    //could be called "doc"?
    if (is_null($request))
	   $request = stripcslashes(getVar("doc",NULL));

    //strip any whitespace from the request
	if (!is_null($request))
	    $request=trim($request);

    if (is_null($request))
    {
    	//the request was probably submitted as a set of NV pairs.
    	//Try and parse the request.
        loadNVPairs($params);
    }
    else
    {
        loadRequest($request,$params);
    }
      
    //reduce ambiguities in operation names.
    if (strcasecmp($params['operation'],'getcontent') == 0)
    {
        $params['operation'] = 'search';
    }
    elseif (strcasecmp($params['operation'],'getcapabilities') == 0)
    {
        $params['operation'] = 'metadata';
    }
	return $params;
}

/////////////////////////////////
// Data Retrieval code
////////////////////////////////

/**
* retrieveData
*
* @return 
*/
function retrieveData(&$targets,$nfh,$nfr,$nfd,&$nameSpaces)
{
    global $g_LOGINFO;
    $res = "";
    set_magic_quotes_runtime(0);
    include_once('DiGIR_searchResponse.php');
    $fh = fopen($nfh,"wb");
    $fr = fopen($nfr,"wb");
    $fd = fopen($nfd,"wb");

    decho("create multitarget...\n");
    $tjob = new multiTarget(DIGIRM_MAX_SIMULTANEOUS);
    decho("init targets...\n");
    foreach ($targets as $k=>$t)
    {
        decho("endpoint = ".$t->endpoint);
    	$cid = $t->clientID;
    	$url = $t->endpoint;
    	$payload = "doc=".urlencode($t->payload);
        $tg = new DiGIRSearchFiler2($cid,$url,$payload,$fh,$fr,$fd,'GET');
        $tjob->addTarget($tg);
    }

    decho("start job...\n");
    $tjob->doJob();
    decho("job end.\n");
    foreach ($tjob->nameSpaces as $n=>$s)
    {
    	decho($n."==".$s);
        $nameSpaces[$n] = $s;
    }
    
    $totcount = 0;
    foreach ($tjob->targets as $t)
    {
    	$totcount += $t->recCount;
    }
    $g_LOGINFO['totalRecs'] = $totcount;

    fclose($fh);
    fclose($fr);
    fclose($fd);
    
    return $res;
}
	
/**
* 
*
* @return 
*/
function doWork(&$params)
{
    //create temporary files for caching the response(s)
    $fHead = tempnam(DIGIRM_CACHE,"hea");
    $fRec = tempnam(DIGIRM_CACHE,"rec");
    $fDiag = tempnam(DIGIRM_CACHE,"dia");
    $nameSpaces = array();
    
    decho("Starting retrieval...");
    decho($fHead);
    decho($fRec);
    decho($fDiag);

    if (__DEBUG)
        flush();

    retrieveData($params['targets'],$fHead,$fRec,$fDiag,$nameSpaces);
    
    decho("done.");
    decho("pushing responses...");
    

    //start the response document
	if (!headers_sent())
		header ("Content-type: text/xml");
	echo "<?xml version='1.0' encoding='utf-8' ?".">\n";
    echo "<response";
    foreach ($nameSpaces as $k=>$v)
    {
    	echo " ".$k."='".htmlentities($v)."'";
    }
    echo ">";
    
    decho("stream head...");
    //stream the response data
    $ff = fopen($fHead,'r');
    while ($data = fread($ff, 4096))
    {
    	echo $data;
    }
    fclose($ff);
    unlink($fHead);

    decho("stream data...");
    echo "<content>\n";
    $ff = fopen($fRec,'r');
    while ($data = fread($ff, 4096))
    {
    	echo $data;
    }
    fclose($ff);
    unlink($fRec);
    echo "</content>\n";

    decho("stream diag...");
    echo "<diagnostics>\n";
    $ff = fopen($fDiag,'r');
    while ($data = fread($ff, 4096))
    {
    	echo $data;
    }
    fclose($ff);
    unlink($fDiag);
    
    //dump any diagnostics from DiGIRm
    global $g_LastError;
    foreach ($g_LastError as $diag)
    {
    	echo $diag->toDiagnostic('DiGIRm');
    }
    
    echo "</diagnostics>\n";
    echo "</response>";
}


////////////////////////////////////////////////////
//Start of main block of code
////////////////////////////////////////////////////
	
/*error_reporting(E_ALL
                & ~E_NOTICE
                & ~E_WARNING
                & ~E_CORE_WARNING
                & ~E_COMPILE_WARNING);*/
                
define('PHP_SELF',$_SERVER['PHP_SELF']);

include_once('DiGIR_globals.php');
include_once('DiGIR_utils.php');
include_once('DiGIR_errors.php');
include_once(DIGIR_XPATH_LIBRARY);
include_once('DiGIR_Log.php');
$libsok = loadlibrary_x('sockets');

//install a new error handler
$old_error_handler = set_error_handler("DiGIRErrorHandler");

if (!$libsok)
{
    appendError(DC_GENERAL_ERROR,
        'A problem was encountered loading the necessary libraries',
        DIAG_FATAL);
}

//Create the log singleton
//log entries have ID = client (portal) IP address
$g_LOG = &Log::singleton('file',
                         realpath(DIGIRM_LOG_PATH.'/'.DIGIRM_LOG_NAME),
                         DIGIRM_CLIENT_ID,
                         array(),
                         DIGIRM_LOG_LEVEL);
$g_LOGINFO = array();

$params = loadParams();

//
include_once('DiGIR_status.php');
$statusopcode = 0;
if (strcasecmp($params["operation"],"metadata") == 0)
    $statusopcode = 1;
elseif (strcasecmp($params["operation"],"search") == 0)
    $statusopcode = 2;
elseif (strcasecmp($params["operation"],"inventory") == 0)
    $statusopcode = 3;

//Check to see if we are even allowed to run
//If not, set an error and bail with minimal header info.
if (!DIGIRM_ALLOW)
{
	$g_LOG->log('DiGIRm request received, but service disabled.',PEAR_LOG_WARNING);
	appendError(DC_NOTALLOWED,'DiGIRm service has been disabled.',DIAG_ERROR);
	
	//this should be the first output.
	if (!headers_sent())
		header ("Content-type: text/xml");
	echo "<?xml version='1.0' encoding='utf-8' ?".">\n";
	//set the default namespace of the response document
	echo "<response xmlns='".DiGIRmNameSpace."'>\n";
	echo DiGIRmHeader($statusopcode);
	echo "<content />";
	echo DiGIRErrors();
	echo "</response>";
	die();
}

decho("<pre>");
decho("Loading libraries...");


//set the maximum script run time
set_time_limit(DIGIRM_MAX_RUNTIME);

$digirstatus = getStatus(DIGIRM_STATUSINTERVAL,DIGIRM_STATUSPICKLE,$statusopcode);
AppendError(DC_STATUS_INTERVAL,DIGIR_STATUSINTERVAL,DIAG_INFO);
AppendError(DC_STATUS_DATA,
            sprintf("%d,%d,%d",$digirstatus[1],$digirstatus[2],$digirstatus[3]),
            DIAG_INFO);

if (ErrorCountA(array(DIAG_ERROR,DIAG_FATAL)) > 0)
//if ($params['status'] <= 0)
{
	$g_LOG->log('DiGIRm request received, but failed parsing.',PEAR_LOG_WARNING);
	
	//this should be the first output.
	if (!headers_sent())
		header ("Content-type: text/xml");
	echo "<?xml version='1.0' encoding='utf-8' ?".">\n";
	//set the default namespace of the response document
	echo "<response xmlns='".DiGIRmNameSpace."'>\n";
	echo DiGIRmHeader($statusopcode);
	echo "<content />";
	echo DiGIRErrors();
	echo "</response>";
	die();
}

//Log the request
$g_LOG->log("request_params=".str_replace("\r"," ",str_replace("\n"," ",serialize($params)),PEAR_LOG_DEBUG));

//Do the work!
doWork($params);

$g_LOG->log("data=".str_replace("\n"," ",serialize($g_LOGINFO)),PEAR_LOG_INFO);

decho("</pre>");
?>