args = $args;
$this->repositoryIdentier = $repositoryIdentifier;
$this->identifyResponse = $identifyResponse;
$this->respond();
}
private function respond() {
if (!isset($this->args['verb']) || empty($this->args['verb'])) {
$this->errors[] = oai_error('noVerb');
} else {
switch ($this->args['verb']) {
case 'Identify':
// we never use compression in Identify
$compress = FALSE;
$this->outputObj = $this->identify();
break;
case 'ListMetadataFormats':
$this->outputObj = $this->listMetadataFormats();
break;
case 'ListSets':
$this->outputObj = $this->listSets();
break;
case 'ListIdentifiers':
case 'ListRecords':
$this->outputObj = $this->listRecords();
break;
case 'GetRecord':
$this->outputObj = $this->getRecord();
break;
default:
// we never use compression with errors
$compress = FALSE;
$this->errors[] = oai_error('badVerb', $this->args['verb']);
}
}
if (empty($this->errors)) {
$this->display();
} else {
$this->errorResponse();
}
}
function errorResponse() {
$e = new ANDS_Error_XML($this->args,$this->errors);
header(CONTENT_TYPE);
$e->display();
exit();
}
function display() {
if (isset($this->outputObj)) {
if ($compress) {
ob_start('ob_gzhandler');
}
header(CONTENT_TYPE);
$this->outputObj->display();
if ($compress) {
ob_end_flush();
}
} else {
exit("Nothing to output. May be a bug.");
}
}
/**
* Response to Verb Identify
*
* Tell the world what the data provider is. Usually it is static once the provider has been set up.
*
* http://www.openarchives.org/OAI/2.0/guidelines-oai-identifier.htm for details
*/
public function identify($show_identifier, $repositoryIdentifier, $delimiter, $sampleIdentifier) {
if (count($this->args) > 1) {
foreach($args as $key => $val) {
if(strcmp($key,"verb")!=0) {
$this->errors[] = oai_error('badArgument', $key, $val);
}
}
}
$outputObj = new ANDS_Response_XML($this->args);
foreach($this->identifyResponse as $key => $val) {
$outputObj->add2_verbNode($key, $val);
}
// A description MAY be included.
// Use this if you choose to comply with a specific format of unique identifiers
// for items.
// See http://www.openarchives.org/OAI/2.0/guidelines-oai-identifier.htm
// for details
// As they will not be changed, using string for simplicity.
$output = '';
if ($this->show_identifier && $this->repositoryIdentifier && $this->delimiter && $this->sampleIdentifier) {
$output .=
' oai'.$repositoryIdentifier.''.$delimiter.''.$sampleIdentifier.''."\n";
}
// A description MAY be included.
// This example from arXiv.org is used by the e-prints community, please adjust
// see http://www.openarchives.org/OAI/2.0/guidelines-eprints.htm for details
// To include, change 'false' to 'true'.
if (false) {
$output .=
' Author self-archived e-prints'."\n";
}
// If you want to point harvesters to other repositories, you can list their
// base URLs. Usage of friends container is RECOMMENDED.
// see http://www.openarchives.org/OAI/2.0/guidelines-friends.htm
// for details
// To include, change 'false' to 'true'.
if (false) {
$output .=
' http://naca.larc.nasa.gov/oai2.0/http://techreports.larc.nasa.gov/ltrs/oai2.0/http://physnet.uni-oldenburg.de/oai/oai2.phphttp://cogprints.soton.ac.uk/perl/oaihttp://ub.uni-duisburg.de:8080/cgi-oai/oai.plhttp://rocky.dlib.vt.edu/~jcdlpix/cgi-bin/OAI1.1/jcdlpix.pl'."\n";
}
// If you want to provide branding information, adjust accordingly.
// Usage of friends container is OPTIONAL.
// see http://www.openarchives.org/OAI/2.0/guidelines-branding.htm
// for details
// To include, change 'false' to 'true'.
if (false) {
$output .=
' http://my.site/icon.png
http://my.site/homepage.html
MySite(tm)8831http://some.where/DCrender.xslhttp://another.place/MARCrender.css'."\n";
}
if(strlen($output)>10) {
$des = $outputObj->doc->createDocumentFragment();
$des->appendXML($output);
$outputObj->verbNode->appendChild($des);
}
return $outputObj;
}
/**
* Response to Verb ListMetadataFormats
*
* The information of supported metadata formats :
* try database table $SQL['table']
* else try $METADATAFORMATS array from config-metadataformats.php
*/
public function listMetadataFormats() {
global $DSN, $DB_USER, $DB_PASSWD, $METADATAFORMATS, $SQL;
$checkList = array("ops"=>array("identifier"));
$this->checkArgs($checkList);
// Create a PDO object
try {
$db = new PDO($DSN, $DB_USER, $DB_PASSWD);
} catch (PDOException $e) {
exit('Connection failed: ' . $e->getMessage());
}
if (isset($this->args['identifier'])) {
$identifier = $this->args['identifier'];
$query = 'select '.$SQL['metadataPrefix'].' FROM '.$SQL['table']. " WHERE ".$SQL['identifier']." = '".$id."'";
$res = $db->query($query);
if ($res==false) {
if (SHOW_QUERY_ERROR) {
echo __FILE__.','.__LINE__." ";
echo "Query: $query \n";
die($db->errorInfo());
} else {
$this->errors[] = oai_error('idDoesNotExist','', $identifier);
}
} else {
$record = $res->fetch();
if($record===false) {
$this->errors[] = oai_error('idDoesNotExist', '', $identifier);
} else {
$mf = explode(",",$record[$SQL['metadataPrefix']]);
}
}
}
//break and clean up on error
if (!empty($this->errors)) {
$this->errorResponse();
}
$outputObj = new ANDS_Response_XML($this->args);
if (isset($mf)) {
foreach($mf as $key) {
$val = $METADATAFORMATS[$key];
$this->addMetedataFormat($outputObj,$key, $val);
}
} elseif (is_array($METADATAFORMATS)) {
foreach($METADATAFORMATS as $key=>$val) {
$this->addMetedataFormat($outputObj,$key, $val);
}
} else { // a very unlikely event
$this->errors[] = oai_error('noMetadataFormats');
$this->errorResponse();
}
return $outputObj;
}
/**
* Response to Verb ListSets
*
* Lists what sets are available to records in the system.
* This variable is filled in config-sets.php
*/
public function listSets() {
global $SETS;
$sets = $SETS;
if (isset($this->args['resumptionToken']) && count($this->args) > 2) {
$this->errors[] = oai_error('exclusiveArgument');
}
$checkList = array("ops"=>array("resumptionToken"));
$this->checkArgs($checkList);
if (is_array($sets)) {
$outputObj = new ANDS_Response_XML($this->args);
foreach($sets as $set) {
$setNode = $outputObj->add2_verbNode("set");
foreach($set as $key => $val) {
if($key=='setDescription') {
$desNode = $outputObj->addChild($setNode,$key);
$des = $outputObj->doc->createDocumentFragment();
$des->appendXML($val);
$desNode->appendChild($des);
} else {
$outputObj->addChild($setNode,$key,$val);
}
}
}
} else {
$this->errors[] = oai_error('noSetHierarchy');
oai_exit();
}
return $outputObj;
}
/**
* Response to Verb GetRecord
*
* Retrieve a record based its identifier.
*
* Local variables $metadataPrefix and $identifier need to be provided through global array variable $args
* by their indexes 'metadataPrefix' and 'identifier'.
* The reset of information will be extracted from database based those two parameters.
*/
public function getRecord() {
global $METADATAFORMATS, $DSN, $DB_USER, $DB_PASSWD, $SQL;
$checkList = array("required"=>array("metadataPrefix","identifier"));
$this->checkArgs($checkList);
$metadataPrefix = $this->args['metadataPrefix'];
if (!isset($METADATAFORMATS[$metadataPrefix])) {
$this->errors[] = oai_error('cannotDisseminateFormat', 'metadataPrefix', $metadataPrefix);
}
// Create a PDO object
try {
$db = new PDO($DSN, $DB_USER, $DB_PASSWD);
} catch (PDOException $e) {
exit('Connection failed: ' . $e->getMessage());
}
$identifier = $this->args['identifier'];
$query = selectallQuery($metadataPrefix, $identifier);
$res = $db->query($query);
if ($res===false) {
$this->errors[] = oai_error('idDoesNotExist', '', $identifier);
} elseif (!$res->rowCount()) { // based on PHP manual, it might only work for some DBs
$this->errors[] = oai_error('idDoesNotExist', '', $identifier);
}
if (!empty($this->errors)) {
oai_exit();
}
$record = $res->fetch(PDO::FETCH_ASSOC);
if ($record===false) {
$this->errors[] = oai_error('idDoesNotExist', '', $identifier);
}
$identifier = $record[$SQL['identifier']];;
$datestamp = formatDatestamp($record[$SQL['datestamp']]);
$status_deleted = (isset($record[$SQL['deleted']]) && ($record[$SQL['deleted']] == 'true') &&
($this->identifyResponse['deletedRecord'] == 'transient' || $this->identifyResponse['deletedRecord'] == 'persistent'));
$outputObj = new ANDS_Response_XML($this->args);
$cur_record = $outputObj->create_record();
$cur_header = $outputObj->create_header($identifier, $datestamp,$record[$SQL['set']],$cur_record);
// return the metadata record itself
if ($status_deleted) {
$cur_header->setAttribute("status","deleted");
} else {
call_user_func(array($this, "{$metadataPrefix}_create_metadata"),
$outputObj, $cur_record, $identifier, $record[$SQL['set']], $db);
}
return $outputObj;
}
/**
* Response to Verb ListRecords
*
* Lists records according to conditions. If there are too many, a resumptionToken is generated.
* - If a request comes with a resumptionToken and is still valid, read it and send back records.
* - Otherwise, set up a query with conditions such as: 'metadataPrefix', 'from', 'until', 'set'.
* Only 'metadataPrefix' is compulsory. All conditions are accessible through global array variable $args by keywords.
*/
public function listRecords() {
global $SQL, $METADATAFORMATS, $DSN, $DB_USER, $DB_PASSWD, $SETS;
$sets = $SETS;
if(isset($this->args['resumptionToken'])) {
if (count($this->args) > 2) {
$this->errors[] = oai_error('exclusiveArgument');
}
$checkList = array("ops"=>array("resumptionToken"));
} else {
$checkList = array("required"=>array("metadataPrefix"),"ops"=>array("from","until","set"));
}
$this->checkArgs($checkList);
// Resume previous session?
if (isset($this->args['resumptionToken'])) {
if (!file_exists(TOKEN_PREFIX.$this->args['resumptionToken'])) {
$this->errors[] = oai_error('badResumptionToken', '', $this->args['resumptionToken']);
} else {
$readings = readResumToken(TOKEN_PREFIX.$this->args['resumptionToken']);
if ($readings == false) {
$this->errors[] = oai_error('badResumptionToken', '', $this->args['resumptionToken']);
} else {
list($deliveredrecords, $extquery, $metadataPrefix) = $readings;
}
}
} else { // no, we start a new session
$deliveredrecords = 0;
$extquery = '';
$metadataPrefix = $this->args['metadataPrefix'];
if (isset($args['from'])) {
$from = checkDateFormat($this->args['from']);
$extquery .= fromQuery($from);
}
if (isset($args['until'])) {
$until = checkDateFormat($this->args['until']);
$extquery .= untilQuery($until);
}
if (isset($args['set'])) {
if (is_array($sets)) {
$extquery .= setQuery($this->args['set']);
} else {
$this->errors[] = oai_error('noSetHierarchy');
}
}
}
if (!isset($METADATAFORMATS[$metadataPrefix])) {
$this->errors[] = oai_error('cannotDisseminateFormat', 'metadataPrefix', $metadataPrefix);
}
if (!empty($this->errors)) {
$this->errorResponse();
}
// Create a PDO object
try {
$db = new PDO($DSN, $DB_USER, $DB_PASSWD);
} catch (PDOException $e) {
exit('Connection failed: ' . $e->getMessage());
}
$query = selectallQuery($metadataPrefix) . $extquery;
$res = $db->prepare($query, array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL));
$r = $res->execute();
if ($r===false) {
$this->errors[] = oai_error('noRecordsMatch');
} else {
$r = $res->setFetchMode(PDO::FETCH_ASSOC);
if ($r===false) {
exit("FetchMode is not supported");
}
$num_rows = rowCount($metadataPrefix, $extquery, $db);
if ($num_rows==0) {
$this->errors[] = oai_error('noRecordsMatch');
}
}
if (!empty($this->errors)) {
$this->errorResponse();
}
// Will we need a new ResumptionToken?
if($this->args['verb']=='ListRecords') {
$maxItems = MAXRECORDS;
} elseif($this->args['verb']=='ListIdentifiers') {
$maxItems = MAXIDS;
} else {
exit("Check ".__FILE__." ".__LINE__.", there is something wrong.");
}
$maxrec = min($num_rows - $deliveredrecords, $maxItems);
if ($num_rows - $deliveredrecords > $maxItems) {
$cursor = (int)$deliveredrecords + $maxItems;
$restoken = createResumToken($cursor, $extquery, $metadataPrefix);
$expirationdatetime = gmstrftime('%Y-%m-%dT%TZ', time()+TOKEN_VALID);
} elseif (isset($args['resumptionToken'])) {
// Last delivery, return empty ResumptionToken
$restoken = $args['resumptionToken']; // just used as an indicator
unset($expirationdatetime);
}
if (isset($this->args['resumptionToken'])) {
$record = $res->fetch(PDO::FETCH_ASSOC, PDO::FETCH_ORI_ABS, $deliveredrecords);
}
// Record counter
$countrec = 0;
// Publish a batch to $maxrec number of records
$outputObj = new ANDS_Response_XML($this->args);
while ($countrec++ < $maxrec) {
$record = $res->fetch(PDO::FETCH_ASSOC);
if ($record===false) {
if (SHOW_QUERY_ERROR) {
echo __FILE__.",". __LINE__." ";
print_r($db->errorInfo());
exit();
}
}
$identifier = $oaiprefix.$record[$SQL['identifier']];
$datestamp = formatDatestamp($record[$SQL['datestamp']]);
$setspec = $record[$SQL['set']];
$status_deleted = (isset($record[$SQL['deleted']]) && ($record[$SQL['deleted']] === true) &&
($this->identifyResponse['deletedRecord'] == 'transient' || $this->identifyResponse['deletedRecord'] == 'persistent'));
if($this->args['verb']=='ListRecords') {
$cur_record = $outputObj->create_record();
$cur_header = $outputObj->create_header($identifier, $datestamp,$setspec,$cur_record);
// return the metadata record itself
if (!$status_deleted) {
call_user_func(array($this, "{$metadataPrefix}_create_metadata"),
$outputObj, $cur_record, $identifier, $setspec, $db);
}
} else { // for ListIdentifiers, only identifiers will be returned.
$cur_header = $outputObj->create_header($identifier, $datestamp,$setspec);
}
if ($status_deleted) {
$cur_header->setAttribute("status","deleted");
}
}
// ResumptionToken
if (isset($restoken)) {
if(isset($expirationdatetime)) {
$outputObj->create_resumpToken($restoken,$expirationdatetime,$num_rows,$cursor);
} else {
$outputObj->create_resumpToken('',null,$num_rows,$deliveredrecords);
}
}
return $outputObj;
}
/**
* Add a metadata format node to an ANDS_Response_XML
* \param &$outputObj
* type: ANDS_Response_XML. The ANDS_Response_XML object for output.
* \param $key
* type string. The name of new node.
* \param $val
* type: array. Values accessable through keywords 'schema' and 'metadataNamespace'.
*
*/
private function addMetedataFormat(&$outputObj,$key,$val) {
$cmf = $outputObj->add2_verbNode("metadataFormat");
$outputObj->addChild($cmf,'metadataPrefix',$key);
$outputObj->addChild($cmf,'schema',$val['schema']);
$outputObj->addChild($cmf,'metadataNamespace',$val['metadataNamespace']);
}
private function rif_create_metadata($outputObj, $cur_record, $identifier, $setspec, $db) {
$metadata_node = $outputObj->create_metadata($cur_record);
$obj_node = new ANDS_TPA($outputObj, $metadata_node, $db);
try {
$obj_node->create_obj_node($setspec, $identifier);
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), " when adding $identifier\n";
}
}
private function oai_dc_create_metadata($outputObj, $cur_record, $identifier, $setspec, $db) {
$sql = "SELECT dc_title, dc_creator, dc_subject, dc_description, dc_contributor, dc_publisher,
dc_date , dc_type , dc_format , dc_identifier , dc_source , dc_language,
dc_relation , dc_coverage , dc_rights
FROM oai_records
WHERE oai_set = '{$setspec}'
AND oai_identifier = '{$identifier}'";
$res = exec_pdo_query($db,$sql);
$record = $res->fetch(PDO::FETCH_ASSOC);
$meta_node = $outputObj->addChild($cur_record ,"metadata");
$schema_node = $outputObj->addChild($meta_node, 'oai_dc:dc');
$schema_node->setAttribute('xmlns:oai_dc', "http://www.openarchives.org/OAI/2.0/oai_dc/");
$schema_node->setAttribute('xmlns:dc',"http://purl.org/dc/elements/1.1/");
$schema_node->setAttribute('xmlns:xsi',"http://www.w3.org/2001/XMLSchema-instance");
$schema_node->setAttribute('xsi:schemaLocation',
'http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd');
foreach ($record as $r => $v) {
if (!empty($v)) {
$outputObj->addChild($schema_node, str_replace('_', ':', $r), $v);
}
}
}
/** Check if provided correct arguments for a request.
*
* Only number of parameters is checked.
* metadataPrefix has to be checked before it is used.
* set has to be checked before it is used.
* resumptionToken has to be checked before it is used.
* from and until can easily checked here because no extra information
* is needed.
*/
private function checkArgs($checkList) {
global $METADATAFORMATS;
// "verb" has been checked before, no further check is needed
$verb = $this->args["verb"];
$test_args = $this->args;
unset($test_args["verb"]);
if(isset($checkList['required'])) {
for($i = 0; $i < count($checkList["required"]); $i++) {
if(isset($test_args[$checkList['required'][$i]])==false) {
$this->errors[] = oai_error('missingArgument', $checkList["required"][$i]);
} else {
// if metadataPrefix is set, it is in required section
if(isset($test_args['metadataPrefix'])) {
$metadataPrefix = $test_args['metadataPrefix'];
// Check if the format is supported, it has enough infor (an array), last if a handle has been defined.
if (!array_key_exists($metadataPrefix, $METADATAFORMATS) ||
!(is_array($METADATAFORMATS[$metadataPrefix]) ||
!isset($METADATAFORMATS[$metadataPrefix]['myhandler']))) {
$this->errors[] = oai_error('cannotDisseminateFormat', 'metadataPrefix', $metadataPrefix);
}
}
unset($test_args[$checkList["required"][$i]]);
}
}
}
if (!empty($this->errors)) return;
// check to see if there is unwanted
foreach($test_args as $key => $val) {
if(!in_array($key, $checkList["ops"])) {
$this->errors[] = oai_error('badArgument', $key, $val);
}
switch ($key) {
case 'from':
case 'until':
if(!checkDateFormat($val)) {
$this->errors[] = oai_error('badGranularity', $key, $val);
}
break;
case 'resumptionToken':
// only check for expairation
if((int)$val+TOKEN_VALID < time())
$this->errors[] = oai_error('badResumptionToken');
break;
}
}
}
}