<?php
/**
* @file
* The EBSCO Connector and Exception classes.
*
* Used when EBSCO API calls return error messages.
*
* Copyright [2017] [EBSCO Information Services]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* EBSCOException class.
*/
class EBSCOException extends Exception {
const CRITICAL_ERROR = 1;
/**
* Make message argument mandatory.
*/
public function __construct($message, $code = self::CRITICAL_ERROR, Exception $previous = NULL) {
parent::__construct($message, $code, $previous);
}
}
/**
* EBSCOConnector class.
*/
class EBSCOConnector {
/**
* Error codes defined by EDS API.
*/
const EDS_UNKNOWN_PARAMETER = 100;
const EDS_INCORRECT_PARAMETER_FORMAT = 101;
const EDS_INVALID_PARAMETER_INDEX = 102;
const EDS_MISSING_PARAMETER = 103;
const EDS_AUTH_TOKEN_INVALID = 104;
const EDS_INCORRECT_ARGUMENTS_NUMBER = 105;
const EDS_UNKNOWN_ERROR = 106;
const EDS_AUTH_TOKEN_MISSING = 107;
const EDS_SESSION_TOKEN_MISSING = 108;
const EDS_SESSION_TOKEN_INVALID = 109;
const EDS_INVALID_RECORD_FORMAT = 110;
const EDS_UNKNOWN_ACTION = 111;
const EDS_INVALID_ARGUMENT_VALUE = 112;
const EDS_CREATE_SESSION_ERROR = 113;
const EDS_REQUIRED_DATA_MISSING = 114;
const EDS_TRANSACTION_LOGGING_ERROR = 115;
const EDS_DUPLICATE_PARAMETER = 116;
const EDS_UNABLE_TO_AUTHENTICATE = 117;
const EDS_SEARCH_ERROR = 118;
const EDS_INVALID_PAGE_SIZE = 119;
const EDS_SESSION_SAVE_ERROR = 120;
const EDS_SESSION_ENDING_ERROR = 121;
const EDS_CACHING_RESULTSET_ERROR = 122;
const EDS_INVALID_EXPANDER_ERROR = 123;
const EDS_INVALID_SEARCH_MODE_ERROR = 124;
const EDS_INVALID_LIMITER_ERROR = 125;
const EDS_INVALID_LIMITER_VALUE_ERROR = 126;
const EDS_UNSUPPORTED_PROFILE_ERROR = 127;
const EDS_PROFILE_NOT_SUPPORTED_ERROR = 128;
const EDS_INVALID_CONTENT_PROVIDER_ERROR = 129;
const EDS_INVALID_SOURCE_TYPE_ERROR = 130;
const EDS_XSLT_ERROR = 131;
const EDS_RECORD_NOT_FOUND_ERROR = 132;
const EDS_SIMULTANEOUS_USER_LIMIT_ERROR = 133;
const EDS_NO_GUEST_ACCESS_ERROR = 134;
const EDS_DBID_NOT_IN_PROFILE_ERROR = 135;
const EDS_INVALID_SEARCH_VIEW_ERROR = 136;
const EDS_RETRIEVING_FULL_TEXT_ERROR = 137;
/**
* HTTP status codes constants
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.
*
* @global integer HTTP_OK The request has succeeded
* @global integer HTTP_NOT_FOUND The server has not found anything matching the Request-URI
*/
const HTTP_OK = 200;
const HTTP_BAD_REQUEST = 400;
const HTTP_NOT_FOUND = 404;
const HTTP_INTERNAL_SERVER_ERROR = 500;
/**
* The HTTP_Request object used for API transactions.
*
* @global object HTTP_Request
*/
private $client;
/**
* The URL of the EBSCO API server.
*
* @global string
*/
private static $end_point = 'http://eds-api.ebscohost.com/EDSAPI/rest';
/**
* The URL of the EBSCO API server.
*
* @global string
*/
private static $authentication_end_point = 'https://eds-api.ebscohost.com/AuthService/rest';
/**
* The password used for API transactions.
*
* @global string
*/
private $password;
/**
* The user id used for API transactions.
*
* @global string
*/
private $userId;
/**
* The profile ID used for API transactions.
*
* @global string
*/
private $profileId;
/**
* The interface ID used for API transactions.
*
* @global string
*/
private $interfaceId;
/**
* The customer ID used for API transactions.
*
* @global string
*/
private $orgId;
/**
* The isGuest used for API transactions.
*
* @global string 'y' or 'n'
*/
private $isGuest;
/**
* Contains the list of ip addresses.
*
* @global string
*/
private $local_ip_address;
/**
* You can log HTTP_Request requests using this option.
*
* @global bool logAPIRequests
*/
private $logAPIRequests;
/**
* The logger object.
*
* @global object Logger
*/
private $logger;
/**
* Constructor.
*
* Sets up the EBSCO API settings.
*
* @param none
*
* @access public
*/
public function __construct($config) {
$this->password = $config['password'];
$this->userId = $config['user'];
$this->interfaceId = $config['interface'];
$this->profileId = $config['profile'];
$this->orgId = $config['organization'];
$this->local_ip_address = $config['local_ip_address'];
$this->isGuest = (user_is_logged_in() || $this->isGuestIPAddress($_SERVER["REMOTE_ADDR"])) ? 'n' : 'y';
$this->logAPIRequests = ($config['log'] == 1);
if ($this->logAPIRequests) {
$writer = new Zend_Log_Writer_Stream('php://output');
$this->logger = new Zend_Log($writer);
}
}
/**
* Detects if the user is authorized based on the IP address.
*
* @return string
*/
public function isGuestIPAddress($ipUser) {
$s = $this->local_ip_address;
if (trim($s) == "") {
return FALSE;
}
// Break records.
$m = explode(",", $s);
foreach ($m as $ip) {
if (strcmp(substr($ipUser, 0, strlen(trim($ip))), trim($ip)) == 0) {
// Inside of ip address range of customer.
return TRUE;
}
}
return FALSE;
}
/**
* Public getter for private isGuest .
*
* @param none
*
* @return string isGuest
*
* @access public
*/
public function isGuest() {
return $this->isGuest;
}
/**
* Request the authentication token.
*
* @param none
*
* @return object SimpleXml or PEAR_Error
*
* @access public
*/
public function requestAuthenticationToken() {
$url = self::$authentication_end_point . '/UIDAuth';
// Add the body of the request.
$params = < < < BODY
< UIDAuthRequestMessage xmlns = "http://www.ebscohost.com/services/public/AuthService/Response/2012/06/01" >
< UserId > {$this->userId}< / UserId >
< Password > {$this->password}< / Password >
< InterfaceId > {$this->interfaceId}< / InterfaceId >
< / UIDAuthRequestMessage >
BODY;
$response = $this->request($url, $params, array(), 'POST');
return $response;
}
/**
* Request the session token.
*
* @param array $headers
* Authentication token.
*
* @return object SimpleXml or PEAR_Error
*
* @access public
*/
public function requestSessionToken($headers) {
$url = self::$end_point . '/CreateSession';
// Add the HTTP query params.
$params = array(
'profile' => $this->profileId,
'org' => $this->orgId,
'guest' => $this->isGuest,
);
$response = $this->request($url, $params, $headers);
return $response;
}
/**
* Request the search records.
*
* @param array $params
* Search specific parameters.
* @param array $headers
* Authentication and session tokens.
*
* @return object SimpleXml or PEAR_Error
*
* @access public
*/
public function requestSearch($params, $headers) {
$url = self::$end_point . '/Search';
$response = $this->request($url, $params, $headers);
return $response;
}
/**
* Request a specific record.
*
* @param array $params
* Retrieve specific parameters.
* @param array $headers
* Authentication and session tokens.
*
* @return object SimpleXml or PEAR_Error
*
* @access public
*/
public function requestRetrieve($params, $headers) {
$url = self::$end_point . '/Retrieve';
$response = $this->request($url, $params, $headers);
return $response;
}
/**
* Request the info data.
*
* @param null $params
* Not used.
* @param array $headers
* Authentication and session tokens.
*
* @return object SimpleXml or PEAR_Error
*
* @access public
*/
public function requestInfo($params, $headers) {
$url = self::$end_point . '/Info';
$response = $this->request($url, $params, $headers);
return $response;
}
/**
* Send an HTTP request and inspect the response.
*
* @param string $url
* The url of the HTTP request.
* @param array $params
* The parameters of the HTTP request.
* @param array $headers
* The headers of the HTTP request.
* @param array $body
* The body of the HTTP request.
* @param string $method
* The HTTP method, default is 'GET'.
*
* @return object SimpleXml or PEAR_Error
*
* @access protected
*/
protected function request($url, $params, $headers = array(), $method = 'GET') {
$xml = FALSE;
$return = FALSE;
$data = NULL;
if (!empty($params)) {
// Arrays of parameters are used only for GET requests.
if (is_array($params)) {
$query = http_build_query($params, '', '&');
$query = preg_replace('/\%5B\d+\%5D/', '', $query);
$url = $url . '?' . $query;
// String parameters are used only for POST requests.
}
else {
$data = $params;
$headers = array_merge(
array('content-type' => 'text/xml'),
$headers
);
}
}
// Add compression in case its not there.
$headers = array_merge(
array('Accept-Encoding' => 'gzip,deflate'),
$headers
);
$options = array(
'headers' => $headers,
'method' => $method,
'data' => $data,
);
// Send the request.
try {
$response = drupal_http_request($url, $options);
// print_r($url);
// print_r($response);
$code = $response->code;
if (isset($response->headers['content-encoding'])) {
if ($response->headers['content-encoding'] == 'gzip') {
$response->data = gzinflate(substr($response->data, 10));
}
elseif ($response->headers['content-encoding'] == 'deflate') {
$response->data = gzinflate($response->data);
}
}
switch ($code) {
case self::HTTP_OK:
$xml_str = $response->data;
try {
// Clean EMP namespace.
$xml_str = str_replace(array("< a: " , " < / a: " ) , array ( " < " , " < / " ) , $ xml_str ) ;
$xml = simplexml_load_string($xml_str);
$return = $xml;
}
catch (Exception $e) {
$return = new EBSCOException($xml);
}
break;
case self::HTTP_BAD_REQUEST:
$xml_str = $response->data;
try {
$xml = simplexml_load_string($xml_str);
// If the response is an API error.
$isError = isset($xml->ErrorNumber) || isset($xml->ErrorCode);
if ($isError) {
$error = ''; $code = 0;
if (isset($xml->DetailedErrorDescription) & & !empty($xml->DetailedErrorDescription)) {
$error = (string) $xml->DetailedErrorDescription;
}
elseif (isset($xml->ErrorDescription)) {
$error = (string) $xml->ErrorDescription;
}
elseif (isset($xml->Reason)) {
$error = (string) $xml->Reason;
}
if (isset($xml->ErrorNumber)) {
$code = (integer) $xml->ErrorNumber;
}
elseif (isset($xml->ErrorCode)) {
$code = (integer) $xml->ErrorCode;
}
$return = new EBSCOException($error, $code);
}
else {
$return = new EBSCOException("HTTP {$code} : The request could not be understood by the server due to malformed syntax. Modify your search before retrying.");
}
}
catch (Exception $e) {
$return = new EBSCOException($xml);
}
break;
case self::HTTP_NOT_FOUND:
$return = new EBSCOException("HTTP {$code} : The resource you are looking for might have been removed, had its name changed, or is temporarily unavailable.");
break;
case self::HTTP_INTERNAL_SERVER_ERROR:
$return = new EBSCOException("HTTP {$code} : The server encountered an unexpected condition which prevented it from fulfilling the request.");
break;
default:
$return = new EBSCOException("HTTP {$code} : Unexpected HTTP error.");
break;
}
}
catch (Exception $e) {
// Or $this->toString($response)
$message = $this->toString($client);
$this->logger->log($message, Zend_Log::ERR);
$return = new EBSCOException($response);
}
// Log any error
/*if ($this->logAPIRequests) {
// $client = both the HTTP request and response
// $response = only the HTTP response
$message = $this->toString($client); // or $this->toString($response)
$this->logger->log($message, Zend_Log::ERR);
}*/
return $return;
}
/**
* Capture the output of print_r into a string.
*
* @param object Any object
*
* @access private
*/
private function toString($object) {
ob_start();
print_r($object);
return ob_get_clean();
}
}