<?php
/**
* WP-Property Customized version of PHRETS
*
* @original https://github.com/troydavisson/PHRETS
* @class WPP_RETS
*/
class WPP_RETS {
public $err;
public $capability_url = array();
private $ch;
private $server_hostname;
private $server_port;
private $server_protocol;
private $server_version;
private $server_software;
private $static_headers = array();
private $server_information = array();
private $cookie_file = "";
private $debug_file = "rets_debug.txt";
private $debug_mode;
private $allowed_capabilities = array(
"Action" => 1,
"ChangePassword" => 1,
"GetObject" => 1,
"Login" => 1,
"LoginComplete" => 1,
"Logout" => 1,
"Search" => 1,
"GetMetadata" => 1,
"ServerInformation" => 1,
"Update" => 1
);
private $last_request = array();
private $auth_support_basic = false;
private $auth_support_digest = false;
private $last_response_headers = array();
private $last_response_headers_raw = "";
private $compression_enabled = false;
private $ua_pwd = "";
private $ua_auth = false;
private $request_id = "";
private $disable_follow_location = false;
private $force_basic_authentication = false;
private $use_interealty_ua_auth = false;
private $int_result_pointer = 0;
public $error_info = array();
private $last_request_url;
private $last_server_response;
private $session_id;
private $catch_last_response = false;
private $disable_encoding_fix = false;
private $offset_support = false;
private $override_offset_protection = false;
public function WPP_RETS() {
}
public function GetLastServerResponse() {
return $this->last_server_response;
}
public function FirewallTest() {
$google = $this->FirewallTestConn( "google.com", 80 );
$crt80 = $this->FirewallTestConn( "demo.crt.realtors.org", 80 );
$crt6103 = $this->FirewallTestConn( "demo.crt.realtors.org", 6103 );
$flexmls80 = $this->FirewallTestConn( "retsgw.flexmls.com", 80 );
$flexmls6103 = $this->FirewallTestConn( "retsgw.flexmls.com", 6103 );
if ( !$google && !$crt80 && !$crt6103 && !$flexmls80 && !$flexmls6103 ) {
echo "Firewall Result: All tests failed. Possible causes:";
echo "<ol>";
echo "<li>Firewall is blocking your outbound connections</li>";
echo "<li>You aren't connected to the internet</li>";
echo "</ol>";
return false;
}
if ( !$crt6103 && !$flexmls6103 ) {
echo "Firewall Result: All port 6103 tests failed. ";
echo "Likely cause: Firewall is blocking your outbound connections on port 6103.";
return false;
}
if ( $google && $crt6103 && $crt80 && $flexmls6103 && $flexmls80 ) {
echo "Firewall Result: All tests passed.";
return true;
}
if ( ( $crt6103 && !$flexmls6103 ) || ( !$crt6103 && $flexmls6103 ) ) {
echo "Firewall Result: At least one port 6103 test passed. ";
echo "Likely cause: One of the test servers might be down but connections on port 80 and port 6103 should work.";
return true;
}
if ( !$google || !$crt80 || !$flexmls80 ) {
echo "Firewall Result: At least one port 80 test failed. ";
echo "Likely cause: One of the test servers might be down.";
return true;
}
echo "Firewall Results: Unable to guess the issue. See individual test results above.";
return false;
}
private function FirewallTestConn( $hostname, $port = 6103 ) {
$fp = @fsockopen( $hostname, $port, $errno, $errstr, 5 );
if ( !$fp ) {
echo "Firewall Test: {$hostname}:{$port} FAILED<br>\n";
return false;
} else {
@fclose( $fp );
echo "Firewall Test: {$hostname}:{$port} GOOD<br>\n";
return true;
}
}
public function GetObject( $resource, $type, $id, $photo_number = '*', $location = 0 ) {
$this->reset_error_info();
$return_photos = array();
if ( empty( $resource ) ) {
die( "Resource parameter is required for GetObject() request." );
}
if ( empty( $type ) ) {
die( "Type parameter is required for GetObject() request." );
}
if ( empty( $id ) ) {
die( "ID parameter is required for GetObject() request." );
}
if ( empty( $this->capability_url[ 'GetObject' ] ) ) {
die( "GetObject() called but unable to find GetObject location. Failed login?\n" );
}
$send_id = "";
$send_numb = "";
// check if $photo_number needs fixing
if ( preg_match( '/\,/', $photo_number ) ) {
// change the commas to colons for the request
$photo_number = preg_replace( '/\,/', ':', $photo_number );
}
if ( preg_match( '/\:/', $photo_number ) ) {
// photo number contains multiple objects
// chopping and cleaning
$requested_numbers = explode( ":", $photo_number );
if ( is_array( $requested_numbers ) ) {
foreach ( $requested_numbers as $numb ) {
$numb = trim( $numb );
if ( !empty( $numb ) || $numb == "0" ) {
$send_numb .= "{$numb}:";
}
}
}
$send_numb = preg_replace( '/\:$/', '', $send_numb );
} else {
$send_numb = trim( $photo_number );
}
if ( preg_match( '/\,/', $id ) ) {
// id contains multiple objects.
// chopping and combining with photo_number
$requested_ids = explode( ",", $id );
if ( is_array( $requested_ids ) ) {
foreach ( $requested_ids as $req_id ) {
$req_id = trim( $req_id );
if ( !empty( $req_id ) && $req_id != "0" ) {
$send_id .= "{$req_id}:{$send_numb},";
}
}
}
$send_id = preg_replace( '/\,$/', '', $send_id );
} else {
$send_id = trim( $id ) . ':' . $send_numb;
}
// make request
$result = $this->RETSRequest( $this->capability_url[ 'GetObject' ], array( 'Resource' => $resource, 'Type' => $type, 'ID' => $send_id, 'Location' => $location ) );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
// fix case issue if exists
if ( isset( $this->last_response_headers[ 'Content-type' ] ) && !isset( $this->last_response_headers[ 'Content-Type' ] ) ) {
$this->last_response_headers[ 'Content-Type' ] = $this->last_response_headers[ 'Content-type' ];
}
if ( !isset( $this->last_response_headers[ 'Content-Type' ] ) ) {
$this->last_response_headers[ 'Content-Type' ] = "";
}
// check what type of response came back
if ( preg_match( '/multipart/', $this->last_response_headers[ 'Content-Type' ] ) ) {
// help bad responses be more multipart compliant
$body = "\r\n{$body}\r\n";
// multipart
preg_match( '/boundary\=\"(.*?)\"/', $this->last_response_headers[ 'Content-Type' ], $matches );
if ( isset( $matches[ 1 ] ) ) {
$boundary = $matches[ 1 ];
} else {
preg_match( '/boundary\=(.*?)(\s|$|\;)/', $this->last_response_headers[ 'Content-Type' ], $matches );
$boundary = $matches[ 1 ];
}
// strip quotes off of the boundary
$boundary = preg_replace( '/^\"(.*?)\"$/', '\1', $boundary );
// clean up the body to remove a reamble and epilogue
$body = preg_replace( '/^(.*?)\r\n--' . $boundary . '\r\n/', "\r\n--{$boundary}\r\n", $body );
// make the last one look like the rest for easier parsing
$body = preg_replace( '/\r\n--' . $boundary . '--/', "\r\n--{$boundary}\r\n", $body );
// cut up the message
$multi_parts = array();
$multi_parts = explode( "\r\n--{$boundary}\r\n", $body );
// take off anything that happens before the first boundary (the preamble)
array_shift( $multi_parts );
// take off anything after the last boundary (the epilogue)
array_pop( $multi_parts );
// go through each part of the multipart message
foreach ( $multi_parts as $part ) {
// default to processing headers
$on_headers = true;
$on_body = false;
$first_body_found = false;
$this_photo = array();
// go through the multipart chunk line-by-line
$body_parts = array();
$body_parts = explode( "\r\n", $part );
$this_photo[ 'Data' ] = "";
foreach ( $body_parts as $line ) {
if ( empty( $line ) && $on_headers == true ) {
// blank line. switching to processing a body and moving on
$on_headers = false;
$on_body = true;
continue;
}
if ( $on_headers == true ) {
// non blank line and we're processing headers so save the header
@list( $header, $value ) = explode( ':', $line, 2 );
$header = trim( $header );
$value = trim( $value );
if ( !empty( $header ) ) {
if ( $header == "Description" ) {
// for servers where the implementors didn't read the next word in the RETS spec.
// 'Description' is the BNF term. Content-Description is the correct header.
// fixing for sanity
$header = "Content-Description";
}
// fix case issue if exists
if ( $header == "Content-type" ) {
$header = "Content-Type";
}
$this_photo[ $header ] = $value;
}
}
if ( $on_body == true ) {
if ( $first_body_found == true ) {
// here again because a linebreak in the body section which was cut out in the explode
// add the CRLF back
$this_photo[ 'Data' ] .= "\r\n";
}
// non blank line and we're processing a body so save the line as part of Data
$first_body_found = true;
$this_photo[ 'Data' ] .= $line;
}
}
// done with parsing out the multipart response
// check for errors and finish up
$this_photo[ 'Success' ] = true; // assuming for now
if ( preg_match( '/xml/', $this_photo[ 'Content-Type' ] ) ) {
// this multipart might include a RETS error
$xml = $this->ParseXMLResponse( $this_photo[ 'Data' ] );
if ( $xml[ 'ReplyCode' ] == 0 || empty( $this_photo[ 'Data' ] ) ) {
// success but no body
$this_photo[ 'Success' ] = true;
} else {
// RETS error in this multipart section
$this_photo[ 'Success' ] = false;
$this_photo[ 'ReplyCode' ] = "{$xml['ReplyCode']}";
$this_photo[ 'ReplyText' ] = "{$xml['ReplyText']}";
}
}
// add information about this multipart to the returned array
$return_photos[ ] = $this_photo;
}
} else {
// all we know is that the response wasn't a multipart so it's either a single photo or error
$this_photo = array();
$this_photo[ 'Success' ] = true; // assuming for now
if ( isset( $this->last_response_headers[ 'Content-ID' ] ) ) {
$this_photo[ 'Content-ID' ] = $this->last_response_headers[ 'Content-ID' ];
}
if ( isset( $this->last_response_headers[ 'Object-ID' ] ) ) {
$this_photo[ 'Object-ID' ] = $this->last_response_headers[ 'Object-ID' ];
}
if ( isset( $this->last_response_headers[ 'Content-Type' ] ) ) {
$this_photo[ 'Content-Type' ] = $this->last_response_headers[ 'Content-Type' ];
}
if ( isset( $this->last_response_headers[ 'MIME-Version' ] ) ) {
$this_photo[ 'MIME-Version' ] = $this->last_response_headers[ 'MIME-Version' ];
}
if ( isset( $this->last_response_headers[ 'Location' ] ) ) {
$this_photo[ 'Location' ] = $this->last_response_headers[ 'Location' ];
}
if ( isset( $this->last_response_headers[ 'Preferred' ] ) ) {
$this_photo[ 'Preferred' ] = $this->last_response_headers[ 'Preferred' ];
}
if ( isset( $this->last_response_headers[ 'Description' ] ) ) {
if ( !empty( $this->last_response_headers[ 'Description' ] ) ) {
// for servers where the implementors didn't read the next word in the RETS spec.
// 'Description' is the BNF term. Content-Description is the correct header.
// fixing for sanity
$this_photo[ 'Content-Description' ] = $this->last_response_headers[ 'Description' ];
}
}
if ( isset( $this->last_response_headers[ 'Content-Description' ] ) ) {
$this_photo[ 'Content-Description' ] = $this->last_response_headers[ 'Content-Description' ];
}
$this_photo[ 'Length' ] = strlen( $body );
$this_photo[ 'Data' ] = $body;
if ( isset( $this->last_response_headers[ 'Content-Type' ] ) ) {
if ( preg_match( '/xml/', $this->last_response_headers[ 'Content-Type' ] ) ) {
// RETS error maybe?
$xml = $this->ParseXMLResponse( $body );
if ( $xml[ 'ReplyCode' ] == 0 || empty( $body ) ) {
// false alarm. we're good
$this_photo[ 'Success' ] = true;
} else {
// yes, RETS error
$this->last_request[ 'ReplyCode' ] = "{$xml['ReplyCode']}";
$this->last_request[ 'ReplyText' ] = "{$xml['ReplyText']}";
$this_photo[ 'ReplyCode' ] = "{$xml['ReplyCode']}";
$this_photo[ 'ReplyText' ] = "{$xml['ReplyText']}";
$this_photo[ 'Success' ] = false;
}
}
}
// add information about this photo to the returned array
$return_photos[ ] = $this_photo;
}
// return everything
return $return_photos;
}
public function IsMaxrowsReached( $pointer_id = "" ) {
if ( empty( $pointer_id ) ) {
$pointer_id = $this->int_result_pointer;
}
return $this->search_data[ $pointer_id ][ 'maxrows_reached' ];
}
public function TotalRecordsFound( $pointer_id = "" ) {
if ( empty( $pointer_id ) ) {
$pointer_id = $this->int_result_pointer;
}
return $this->search_data[ $pointer_id ][ 'total_records_found' ];
}
public function NumRows( $pointer_id = "" ) {
if ( empty( $pointer_id ) ) {
$pointer_id = $this->int_result_pointer;
}
return $this->search_data[ $pointer_id ][ 'last_search_returned' ];
}
public function SearchGetFields( $pointer_id ) {
if ( !empty( $pointer_id ) ) {
return $this->search_data[ $pointer_id ][ 'column_names' ];
} else {
return false;
}
}
public function FreeResult( $pointer_id ) {
if ( !empty( $pointer_id ) ) {
unset( $this->search_data[ $pointer_id ][ 'data' ] );
unset( $this->search_data[ $pointer_id ][ 'delimiter_character' ] );
unset( $this->search_data[ $pointer_id ][ 'column_names' ] );
return true;
} else {
return false;
}
}
public function FetchRow( $pointer_id ) {
$this_row = false;
if ( !empty( $pointer_id ) ) {
if ( isset( $this->search_data[ $pointer_id ][ 'data' ] ) ) {
$field_data = current( $this->search_data[ $pointer_id ][ 'data' ] );
next( $this->search_data[ $pointer_id ][ 'data' ] );
}
if ( !empty( $field_data ) ) {
$this_row = array();
// split up DATA row on delimiter found earlier
$field_data = preg_replace( "/^{$this->search_data[ $pointer_id ]['delimiter_character']}/", "", $field_data );
$field_data = preg_replace( "/{$this->search_data[ $pointer_id ]['delimiter_character']}\$/", "", $field_data );
$field_data = explode( $this->search_data[ $pointer_id ][ 'delimiter_character' ], $field_data );
foreach ( $this->search_data[ $pointer_id ][ 'column_names' ] as $key => $name ) {
// assign each value to it's name retrieved in the COLUMNS earlier
$this_row[ $name ] = $field_data[ $key ];
}
}
}
return $this_row;
}
public function SearchQuery( $resource, $class, $query = "", $optional_params = array() ) {
$this->reset_error_info();
if ( empty( $resource ) ) {
die( "Resource parameter is required in SearchQuery() request." );
}
if ( empty( $class ) ) {
die( "Class parameter is required in SearchQuery() request." );
}
if ( empty( $this->capability_url[ 'Search' ] ) ) {
die( "SearchQuery() called but unable to find Search location. Failed login?\n" );
}
$this->int_result_pointer++;
$this->search_data[ $this->int_result_pointer ][ 'last_search_returned' ] = 0;
$this->search_data[ $this->int_result_pointer ][ 'total_records_found' ] = 0;
$this->search_data[ $this->int_result_pointer ][ 'column_names' ] = "";
$this->search_data[ $this->int_result_pointer ][ 'delimiter_character' ] = "";
$this->search_data[ $this->int_result_pointer ][ 'search_requests' ] = 0;
// setup request arguments
$search_arguments = array();
$search_arguments[ 'SearchType' ] = $resource;
$search_arguments[ 'Class' ] = $class;
// due to a lack of forward-thinking, reversing a previous decision
// check if the query passed is missing the outer parenthesis
// if so, add them
if ( empty( $query ) ) {
// do nothing. http://retsdoc.onconfluence.com/display/rcpcenter/RCP+80+-+Optional+Query
} elseif ( $query == "*" || preg_match( '/^\((.*)\)$/', $query ) ) {
$search_arguments[ 'Query' ] = $query;
} else {
$search_arguments[ 'Query' ] = '(' . $query . ')';
}
if ( isset( $search_arguments[ 'Query' ] ) ) {
$search_arguments[ 'QueryType' ] = "DMQL2";
}
// setup additional, optional request arguments
$search_arguments[ 'Count' ] = empty( $optional_params[ 'Count' ] ) ? 1 : $optional_params[ 'Count' ];
$search_arguments[ 'Format' ] = empty( $optional_params[ 'Format' ] ) ? "COMPACT-DECODED" : $optional_params[ 'Format' ];
$search_arguments[ 'Limit' ] = empty( $optional_params[ 'Limit' ] ) ? 99999999 : $optional_params[ 'Limit' ];
if ( !empty( $optional_params[ 'Offset' ] ) ) {
$search_arguments[ 'Offset' ] = $optional_params[ 'Offset' ];
} elseif ( $this->offset_support && empty( $optional_params[ 'Offset' ] ) ) {
// start auto-offset looping with Offset at 1
$search_arguments[ 'Offset' ] = 1;
} else {
}
if ( !empty( $optional_params[ 'Select' ] ) ) {
$search_arguments[ 'Select' ] = $optional_params[ 'Select' ];
}
if ( !empty( $optional_params[ 'RestrictedIndicator' ] ) ) {
$search_arguments[ 'RestrictedIndicator' ] = $optional_params[ 'RestrictedIndicator' ];
}
$search_arguments[ 'StandardNames' ] = empty( $optional_params[ 'StandardNames' ] ) ? 0 : $optional_params[ 'StandardNames' ];
$continue_searching = true; // Keep searching if MAX ROWS is reached and offset_support is true
while ( $continue_searching ) {
$this->search_data[ $this->int_result_pointer ][ 'maxrows_reached' ] = false;
$this->search_data[ $this->int_result_pointer ][ 'search_requests' ]++;
if ( $this->search_data[ $this->int_result_pointer ][ 'search_requests' ] == 300 && !$this->override_offset_protection ) {
// this call for SearchQuery() has resulted in X number of search requests
// which is considered excessive. stopping the process in order to prevent
// abuse against the server. almost ALWAYS happens when the user thinks Offset
// is supported by the server when it's actually NOT supported
$this->set_error_info( "phrets", -1, "Last SearchQuery() has resulted in 300+ requests to the server. Stopping to prevent abuse" );
return false;
}
// make request
$result = $this->RETSRequest( $this->capability_url[ 'Search' ], $search_arguments );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
$body = $this->fix_encoding( $body );
$xml = $this->ParseXMLResponse( $body );
if ( !$xml ) {
return false;
}
// log replycode and replytext for reference later
$this->last_request[ 'ReplyCode' ] = "{$xml['ReplyCode']}";
$this->last_request[ 'ReplyText' ] = "{$xml['ReplyText']}";
if ( $xml[ 'ReplyCode' ] != 0 ) {
$this->set_error_info( "rets", "{$xml['ReplyCode']}", "{$xml['ReplyText']}" );
return false;
}
if ( isset( $xml->DELIMITER ) ) {
// delimiter found so we have at least a COLUMNS row to parse
$delimiter_character = chr( "{$xml->DELIMITER->attributes()->value}" );
$this->search_data[ $this->int_result_pointer ][ 'delimiter_character' ] = $delimiter_character;
$column_names = "{$xml->COLUMNS[0]}";
$column_names = preg_replace( "/^{$delimiter_character}/", "", $column_names );
$column_names = preg_replace( "/{$delimiter_character}\$/", "", $column_names );
$this->search_data[ $this->int_result_pointer ][ 'column_names' ] = explode( $delimiter_character, $column_names );
}
if ( isset( $xml->DATA ) ) {
foreach ( $xml->DATA as $key ) {
$field_data = "{$key}";
// split up DATA row on delimiter found earlier
$this->search_data[ $this->int_result_pointer ][ 'data' ][ ] = $field_data;
$this->search_data[ $this->int_result_pointer ][ 'last_search_returned' ]++;
}
}
if ( isset( $xml->MAXROWS ) ) {
// MAXROWS tag found. the RETS server withheld records.
// if the server supports Offset, more requests can be sent to page through results
// until this tag isn't found anymore.
$this->search_data[ $this->int_result_pointer ][ 'maxrows_reached' ] = true;
}
if ( isset( $xml->COUNT ) ) {
// found the record count returned. save it
$this->search_data[ $this->int_result_pointer ][ 'total_records_found' ] = "{$xml->COUNT->attributes()->Records}";
}
if ( isset( $xml ) ) {
unset( $xml );
}
if ( $this->IsMaxrowsReached( $this->int_result_pointer ) && $this->offset_support ) {
$continue_searching = true;
$search_arguments[ 'Offset' ] = $this->NumRows( $this->int_result_pointer ) + 1;
} else {
$continue_searching = false;
}
}
return $this->int_result_pointer;
}
public function Search( $resource, $class, $query = "", $optional_params = array() ) {
$data_table = array();
$int_result_pointer = $this->SearchQuery( $resource, $class, $query, $optional_params );
while ( $row = $this->FetchRow( $int_result_pointer ) ) {
$data_table[ ] = $row;
}
return $data_table;
}
public function GetAllLookupValues( $resource ) {
$this->reset_error_info();
if ( empty( $resource ) ) {
die( "Resource parameter is required in GetAllLookupValues() request." );
}
if ( empty( $this->capability_url[ 'GetMetadata' ] ) ) {
die( "GetAllLookupValues() called but unable to find GetMetadata location. Failed login?\n" );
}
// make request
$result = $this->RETSRequest( $this->capability_url[ 'GetMetadata' ], array( 'Type' => 'METADATA-LOOKUP_TYPE', 'ID' => $resource . ':*', 'Format' => 'STANDARD-XML' ) );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
$xml = $this->ParseXMLResponse( $body );
if ( !$xml ) {
return false;
}
if ( $xml[ 'ReplyCode' ] != 0 ) {
$this->set_error_info( "rets", "{$xml['ReplyCode']}", "{$xml['ReplyText']}" );
return false;
}
$this_table = array();
// parse XML into a nice array
if ( $xml->METADATA ) {
foreach ( $xml->METADATA->{'METADATA-LOOKUP_TYPE'} as $key ) {
if ( !empty( $key->attributes()->Lookup ) ) {
$this_lookup = array();
$lookup_xml_array = array();
if ( $this->server_version == "RETS/1.7.2" ) {
$lookup_xml_array = $key->LookupType;
} else {
$lookup_xml_array = $key->Lookup;
}
foreach ( $lookup_xml_array as $look ) {
$metadataentryid = isset( $look->MetadataEntryID ) ? "{$look->MetadataEntryID}" : "";
$value = isset( $look->Value ) ? "{$look->Value}" : "";
$shortvalue = isset( $look->ShortValue ) ? "{$look->ShortValue}" : "";
$longvalue = isset( $look->LongValue ) ? "{$look->LongValue}" : "";
$this_lookup[ ] = array( 'MetadataEntryID' => $metadataentryid, 'Value' => $value, 'ShortValue' => $shortvalue,
'LongValue' => $longvalue );
}
$this_table[ ] = array( 'Lookup' => "{$key->attributes()->Lookup}", 'Values' => $this_lookup );
}
}
}
// return the big array
return $this_table;
}
public function GetLookupValues( $resource, $lookupname ) {
$this->reset_error_info();
if ( empty( $resource ) ) {
die( "Resource parameter is required in GetLookupValues() request." );
}
if ( empty( $lookupname ) ) {
die( "Lookup Name parameter is required in GetLookupValues() request." );
}
if ( empty( $this->capability_url[ 'GetMetadata' ] ) ) {
die( "GetLookupValues() called but unable to find GetMetadata location. Failed login?\n" );
}
// make request
$result = $this->RETSRequest( $this->capability_url[ 'GetMetadata' ], array( 'Type' => 'METADATA-LOOKUP_TYPE', 'ID' => $resource . ':' . $lookupname, 'Format' => 'STANDARD-XML' ) );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
$xml = $this->ParseXMLResponse( $body );
if ( !$xml ) {
return false;
}
if ( $xml[ 'ReplyCode' ] != 0 ) {
$this->set_error_info( "rets", "{$xml['ReplyCode']}", "{$xml['ReplyText']}" );
return false;
}
$this_table = array();
// parse XML into a nice array
if ( $xml->METADATA ) {
$lookup_xml_array = array();
if ( $this->server_version == "RETS/1.7.2" ) {
$lookup_xml_array = $xml->METADATA->{'METADATA-LOOKUP_TYPE'}->LookupType;
} else {
$lookup_xml_array = $xml->METADATA->{'METADATA-LOOKUP_TYPE'}->Lookup;
}
foreach ( $lookup_xml_array as $key ) {
if ( isset( $key->Value ) ) {
$metadataentryid = isset( $key->MetadataEntryID ) ? "{$key->MetadataEntryID}" : "";
$value = isset( $key->Value ) ? "{$key->Value}" : "";
$shortvalue = isset( $key->ShortValue ) ? "{$key->ShortValue}" : "";
$longvalue = isset( $key->LongValue ) ? "{$key->LongValue}" : "";
$this_table[ ] = array( 'MetadataEntryID' => $metadataentryid, 'Value' => $value, 'ShortValue' => $shortvalue,
'LongValue' => $longvalue );
}
}
}
// return the big array
return $this_table;
}
public function GetMetadataResources( $id = 0 ) {
$this->reset_error_info();
if ( empty( $this->capability_url[ 'GetMetadata' ] ) ) {
die( "GetMetadataResources() called but unable to find GetMetadata location. Failed login?\n" );
}
// make request
$result = $this->RETSRequest( $this->capability_url[ 'GetMetadata' ], array( 'Type' => 'METADATA-RESOURCE', 'ID' => $id, 'Format' => 'STANDARD-XML' ) );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
$xml = $this->ParseXMLResponse( $body );
if ( !$xml ) {
return false;
}
if ( $xml[ 'ReplyCode' ] != 0 ) {
$this->set_error_info( "rets", "{$xml['ReplyCode']}", "{$xml['ReplyText']}" );
return false;
}
$this_resource = array();
// parse XML into a nice array
if ( $xml->METADATA ) {
foreach ( $xml->METADATA->{'METADATA-RESOURCE'}->Resource as $key => $value ) {
$this_resource[ "{$value->ResourceID}" ] = array( 'ResourceID' => "{$value->ResourceID}", 'StandardName' => "{$value->StandardName}", 'VisibleName' => "{$value->VisibleName}",
'Description' => "{$value->Description}", 'KeyField' => "{$value->KeyField}", 'ClassCount' => "{$value->ClassCount}",
'ClassVersion' => "{$value->ClassVersion}", 'ClassDate' => "{$value->ClassDate}", 'ObjectVersion' => "{$value->ObjectVersion}",
'ObjectDate' => "{$value->ObjectDate}", 'SearchHelpVersion' => "{$value->SearchHelpVersion}",
'SearchHelpDate' => "{$value->SearchHelpDate}", 'EditMaskVersion' => "{$value->EditMaskVersion}",
'EditMaskDate' => "{$value->EditMaskDate}", 'LookupVersion' => "{$value->LookupVersion}", 'LookupDate' => "{$value->LookupDate}",
'UpdateHelpVersion' => "{$value->UpdateHelpVersion}", 'UpdateHelpDate' => "{$value->UpdateHelpDate}",
'ValidationExpressionVersion' => "{$value->ValidationExpressionVersion}", 'ValidationExpressionDate' => "{$value->ValidationExpressionDate}",
'ValidationLookupVersion' => "{$value->ValidationLookupVersion}", 'ValidationLookupDate' => "{$value->ValidationLookupDate}",
'ValidationExternalVersion' => "{$value->ValidationExternalVersion}", 'ValidationExternalDate' => "{$value->ValidationExternalDate}" );
}
}
// send back array
return $this_resource;
}
public function GetMetadataInfo( $id = 0 ) {
if ( empty( $this->capability_url[ 'GetMetadata' ] ) ) {
die( "GetMetadataInfo() called but unable to find GetMetadata location. Failed login?\n" );
}
return $this->GetMetadataResources( $id );
}
public function GetMetadataTable( $resource, $class ) {
$this->reset_error_info();
$id = $resource . ':' . $class;
if ( empty( $resource ) ) {
die( "Resource parameter is required in GetMetadata() request." );
}
if ( empty( $class ) ) {
die( "Class parameter is required in GetMetadata() request." );
}
if ( empty( $this->capability_url[ 'GetMetadata' ] ) ) {
die( "GetMetadataTable() called but unable to find GetMetadata location. Failed login?\n" );
}
// request specific metadata
$result = $this->RETSRequest( $this->capability_url[ 'GetMetadata' ], array( 'Type' => 'METADATA-TABLE', 'ID' => $id, 'Format' => 'STANDARD-XML' ) );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
$xml = $this->ParseXMLResponse( $body );
if ( !$xml ) {
return false;
}
// log replycode and replytext for reference later
$this->last_request[ 'ReplyCode' ] = "{$xml['ReplyCode']}";
$this->last_request[ 'ReplyText' ] = "{$xml['ReplyText']}";
if ( $xml[ 'ReplyCode' ] != 0 ) {
$this->set_error_info( "rets", "{$xml['ReplyCode']}", "{$xml['ReplyText']}" );
return false;
}
$this_table = array();
// parse XML into a nice array
if ( $xml->METADATA ) {
foreach ( $xml->METADATA->{'METADATA-TABLE'}->Field as $key ) {
$this_table[ ] = array( 'SystemName' => "{$key->SystemName}", 'StandardName' => "{$key->StandardName}", 'LongName' => "{$key->LongName}", 'DBName' => "{$key->DBName}",
'ShortName' => "{$key->ShortName}", 'MaximumLength' => "{$key->MaximumLength}", 'DataType' => "{$key->DataType}", 'Precision' => "{$key->Precision}",
'Searchable' => "{$key->Searchable}", 'Interpretation' => "{$key->Interpretation}", 'Alignment' => "{$key->Alignment}",
'UseSeparator' => "{$key->UseSeparator}", 'EditMaskID' => "{$key->EditMaskID}", 'LookupName' => "{$key->LookupName}",
'MaxSelect' => "{$key->MaxSelect}", 'Units' => "{$key->Units}", 'Index' => "{$key->Index}", 'Minimum' => "{$key->Minimum}",
'Maximum' => "{$key->Maximum}", 'Default' => "{$key->Default}", 'Required' => "{$key->Required}", 'SearchHelpID' => "{$key->SearchHelpID}",
'Unique' => "{$key->Unique}", 'MetadataEntryID' => "{$key->MetadataEntryID}", 'ModTimeStamp' => "{$key->ModTimeStamp}",
'ForeignKeyName' => "{$key->ForiengKeyName}", 'ForeignField' => "{$key->ForeignField}", 'InKeyIndex' => "{$key->InKeyIndex}" );
}
}
// return the big array
return $this_table;
}
public function GetMetadata( $resource, $class ) {
if ( empty( $this->capability_url[ 'GetMetadata' ] ) ) {
die( "GetMetadata() called but unable to find GetMetadata location. Failed login?\n" );
}
return $this->GetMetadataTable( $resource, $class );
}
public function GetMetadataObjects( $id ) {
$this->reset_error_info();
if ( empty( $id ) ) {
die( "ID parameter is required in GetMetadataObjects() request." );
}
if ( empty( $this->capability_url[ 'GetMetadata' ] ) ) {
die( "GetMetadataObjects() called but unable to find GetMetadata location. Failed login?\n" );
}
// request basic metadata information
$result = $this->RETSRequest( $this->capability_url[ 'GetMetadata' ], array( 'Type' => 'METADATA-OBJECT', 'ID' => $id, 'Format' => 'STANDARD-XML' ) );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
$xml = $this->ParseXMLResponse( $body );
if ( !$xml ) {
return false;
}
// log replycode and replytext for reference later
$this->last_request[ 'ReplyCode' ] = "{$xml['ReplyCode']}";
$this->last_request[ 'ReplyText' ] = "{$xml['ReplyText']}";
if ( $xml[ 'ReplyCode' ] != 0 ) {
$this->set_error_info( "rets", "{$xml['ReplyCode']}", "{$xml['ReplyText']}" );
return false;
}
$return_data = array();
if ( isset( $xml->METADATA->{'METADATA-OBJECT'} ) ) {
// parse XML into a nice array
foreach ( $xml->METADATA->{'METADATA-OBJECT'} as $key => $value ) {
foreach ( $value->Object as $key ) {
if ( !empty( $key->ObjectType ) ) {
$return_data[ ] = array( 'MetadataEntryID' => "{$key->MetadataEntryID}", 'VisibleName' => "{$key->VisibleName}", 'ObjectTimeStamp' => "{$key->ObjectTimeStamp}",
'ObjectCount' => "{$key->ObjectCount}", 'ObjectType' => "{$key->ObjectType}", 'StandardName' => "{$key->StandardName}",
'MimeType' => "{$key->MimeType}", 'Description' => "{$key->Description}" );
}
}
}
}
// send back array
return $return_data;
}
public function GetMetadataClasses( $id ) {
$this->reset_error_info();
if ( empty( $id ) ) {
die( "ID parameter is required in GetMetadataClasses() request." );
}
if ( empty( $this->capability_url[ 'GetMetadata' ] ) ) {
die( "GetMetadataClasses() called but unable to find GetMetadata location. Failed login?\n" );
}
// request basic metadata information
$result = $this->RETSRequest( $this->capability_url[ 'GetMetadata' ], array( 'Type' => 'METADATA-CLASS', 'ID' => $id, 'Format' => 'STANDARD-XML' ) );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
$xml = $this->ParseXMLResponse( $body );
if ( !$xml ) {
return false;
}
// log replycode and replytext for reference later
$this->last_request[ 'ReplyCode' ] = "{$xml['ReplyCode']}";
$this->last_request[ 'ReplyText' ] = "{$xml['ReplyText']}";
if ( $xml[ 'ReplyCode' ] != 0 ) {
$this->set_error_info( "rets", "{$xml['ReplyCode']}", "{$xml['ReplyText']}" );
return false;
}
$return_data = array();
// parse XML into a nice array
if ( $xml->METADATA ) {
foreach ( $xml->METADATA->{'METADATA-CLASS'} as $key => $value ) {
foreach ( $value->Class as $key ) {
if ( !empty( $key->ClassName ) ) {
$return_data[ ] = array( 'ClassName' => "{$key->ClassName}", 'VisibleName' => "{$key->VisibleName}", 'StandardName' => "{$key->StandardName}",
'Description' => "{$key->Description}", 'TableVersion' => "{$key->TableVersion}", 'TableDate' => "{$key->TableDate}",
'UpdateVersion' => "{$key->UpdateVersion}", 'UpdateDate' => "{$key->UpdateDate}", 'ClassTimeStamp' => "{$key->ClassTimeStamp}",
'DeletedFlagField' => "{$key->DeletedFlagField}", 'DeletedFlagValue' => "{$key->DeletedFlagValue}",
'HasKeyIndex' => "{$key->HasKeyIndex}" );
}
}
}
}
// send back array
return $return_data;
}
public function GetMetadataTypes( $id = 0 ) {
$this->reset_error_info();
if ( empty( $this->capability_url[ 'GetMetadata' ] ) ) {
die( "GetMetadataTypes() called but unable to find GetMetadata location. Failed login?\n" );
}
// request basic metadata information
$result = $this->RETSRequest( $this->capability_url[ 'GetMetadata' ], array( 'Type' => 'METADATA-CLASS', 'ID' => $id, 'Format' => 'STANDARD-XML' ) );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
$xml = $this->ParseXMLResponse( $body );
if ( !$xml ) {
return false;
}
// log replycode and replytext for reference later
$this->last_request[ 'ReplyCode' ] = "{$xml['ReplyCode']}";
$this->last_request[ 'ReplyText' ] = "{$xml['ReplyText']}";
if ( $xml[ 'ReplyCode' ] != 0 ) {
$this->set_error_info( "rets", "{$xml['ReplyCode']}", "{$xml['ReplyText']}" );
return false;
}
$return_data = array();
// parse XML into a nice array
if ( $xml->METADATA ) {
foreach ( $xml->METADATA->{'METADATA-CLASS'} as $key => $value ) {
$resource = $value[ 'Resource' ];
$this_resource = array();
foreach ( $value->Class as $key ) {
if ( !empty( $key->ClassName ) ) {
$this_resource[ ] = array( 'ClassName' => "{$key->ClassName}", 'VisibleName' => "{$key->VisibleName}", 'StandardName' => "{$key->StandardName}",
'Description' => "{$key->Description}", 'TableVersion' => "{$key->TableVersion}", 'TableDate' => "{$key->TableDate}",
'UpdateVersion' => "{$key->UpdateVersion}", 'UpdateDate' => "{$key->UpdateDate}" );
}
}
// prepare 2-deep array
$return_data[ ] = array( 'Resource' => "{$resource}", 'Data' => $this_resource );
}
}
// send back array
return $return_data;
}
public function GetServerSoftware() {
return $this->server_software;
}
public function GetServerVersion() {
return $this->server_version;
}
public function CheckAuthSupport( $type = "" ) {
if ( $type == "basic" ) {
return $this->auth_support_basic;
}
if ( $type == "digest" ) {
return $this->auth_support_digest;
}
$this->set_error_info( "phrets", -1, "Unknown auth type requested." );
return false;
}
public function GetAllTransactions() {
// read through capability_urls read during the Login and return
$transactions = array();
if ( is_array( $this->capability_url ) ) {
foreach ( $this->capability_url as $key => $value ) {
$transactions[ ] = $key;
}
}
return $transactions;
}
public function LastRequestURL() {
return $this->last_request_url;
}
public function GetLoginURL() {
// see if the saved Login URL has a hostname included.
// if not, make it based on the URL given in the Connect() call
$parse_results = parse_url( $this->capability_url[ 'Login' ], PHP_URL_HOST );
if ( empty( $parse_results ) ) {
// login transaction gave a relative path for this action
$request_url = $this->server_protocol . '://' . $this->server_hostname . ':' . $this->server_port . '' . $this->capability_url[ 'Login' ];
} else {
// login transaction gave an absolute path for this action
$request_url = $this->capability_url[ 'Login' ];
}
if ( empty( $request_url ) ) {
$this->set_error_info( "phrets", -1, "Unable to find a login URL. Did initial login fail?" );
return false;
}
return $request_url;
}
public function GetServerInformation() {
$this->reset_error_info();
if ( empty( $this->capability_url[ 'GetMetadata' ] ) ) {
die( "GetServerInformation() called but unable to find GetMetadata location. Failed login?\n" );
}
// request server information
$result = $this->RETSRequest( $this->capability_url[ 'GetMetadata' ], array( 'Type' => 'METADATA-SYSTEM', 'ID' => 0, 'Format' => 'STANDARD-XML' ) );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
$xml = $this->ParseXMLResponse( $body );
if ( !$xml ) {
return false;
}
if ( $xml[ 'ReplyCode' ] != 0 ) {
$this->set_error_info( "rets", "{$xml['ReplyCode']}", "{$xml['ReplyText']}" );
return false;
}
if ( $this->is_server_version( "1_5_or_below" ) ) {
$system_id = isset( $xml->METADATA->{'METADATA-SYSTEM'}->System->SystemID ) ? "{$xml->METADATA->{'METADATA-SYSTEM'}->System->SystemID}" : "";
$system_description = isset( $xml->METADATA->{'METADATA-SYSTEM'}->System->SystemDescription ) ? "{$xml->METADATA->{'METADATA-SYSTEM'}->System->SystemDescription}" : "";
$timezone_offset = "";
} else {
$system_id = isset( $xml->METADATA->{'METADATA-SYSTEM'}->SYSTEM->attributes()->SystemID ) ? "{$xml->METADATA->{'METADATA-SYSTEM'}->SYSTEM->attributes()->SystemID}" : "";
$system_description = isset( $xml->METADATA->{'METADATA-SYSTEM'}->SYSTEM->attributes()->SystemDescription ) ? "{$xml->METADATA->{'METADATA-SYSTEM'}->SYSTEM->attributes()->SystemDescription}" : "";
$timezone_offset = isset( $xml->METADATA->{'METADATA-SYSTEM'}->SYSTEM->attributes()->TimeZoneOffset ) ? "{$xml->METADATA->{'METADATA-SYSTEM'}->SYSTEM->attributes()->TimeZoneOffset}" : "";
}
$system_comments = isset( $xml->METADATA->{'METADATA-SYSTEM'}->SYSTEM->Comments ) ? "{$xml->METADATA->{'METADATA-SYSTEM'}->SYSTEM->Comments}" : "";
return array( 'SystemID' => $system_id, 'SystemDescription' => $system_description, 'TimeZoneOffset' => $timezone_offset, 'Comments' => $system_comments );
}
public function Disconnect() {
$this->reset_error_info();
if ( empty( $this->capability_url[ 'Logout' ] ) ) {
die( "Disconnect() called but unable to find Logout location. Failed login?\n" );
}
// make request
$result = $this->RETSRequest( $this->capability_url[ 'Logout' ] );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
// close cURL connection
curl_close( $this->ch );
if ( $this->debug_mode == true ) {
// close cURL debug log file handler
fclose( $this->debug_log );
}
if ( file_exists( $this->cookie_file ) ) {
@unlink( $this->cookie_file );
}
return true;
}
public function Connect( $login_url, $username, $password, $ua_pwd = "" ) {
$this->reset_error_info();
if ( empty( $login_url ) ) {
die( "PHRETS: Login URL missing from Connect()" );
}
if ( empty( $username ) ) {
die( "PHRETS: Username missing from Connect()" );
}
if ( empty( $password ) ) {
die( "PHRETS: Password missing from Connect()" );
}
if ( empty( $this->static_headers[ 'RETS-Version' ] ) ) {
$this->AddHeader( "RETS-Version", "RETS/1.5" );
}
if ( empty( $this->static_headers[ 'User-Agent' ] ) ) {
$this->AddHeader( "User-Agent", "PHRETS/1.0" );
}
if ( empty( $this->static_headers[ 'Accept' ] ) && $this->static_headers[ 'RETS-Version' ] == "RETS/1.5" ) {
$this->AddHeader( "Accept", "*/*" );
}
// chop up Login URL to use for later requests
$url_parts = parse_url( $login_url );
$this->server_hostname = $url_parts[ 'host' ];
$this->server_port = ( empty( $url_parts[ 'port' ] ) ) ? 80 : $url_parts[ 'port' ];
$this->server_protocol = $url_parts[ 'scheme' ];
$this->capability_url[ 'Login' ] = $url_parts[ 'path' ];
if ( isset( $url_parts[ 'query' ] ) && !empty( $url_parts[ 'query' ] ) ) {
$this->capability_url[ 'Login' ] .= "?{$url_parts['query']}";
}
$this->username = $username;
$this->password = $password;
if ( !empty( $ua_pwd ) ) {
// force use of RETS 1.7 User-Agent Authentication
$this->ua_auth = true;
$this->ua_pwd = $ua_pwd;
}
if ( empty( $this->cookie_file ) ) {
$this->cookie_file = tempnam( sys_get_temp_dir(), "phrets" );
}
@touch( $this->cookie_file );
if ( !is_writable( $this->cookie_file ) ) {
$this->set_error_info( "phrets", -1, "Cookie file \"{$this->cookie_file}\" cannot be written to. Must be an absolute path and must be writable" );
return false;
}
// start cURL magic
$this->ch = curl_init();
curl_setopt( $this->ch, CURLOPT_HEADERFUNCTION, array( &$this, 'read_custom_curl_headers' ) );
if ( $this->debug_mode == true ) {
// open file handler to be used by cURL debug log
$this->debug_log = fopen( $this->debug_file, 'a' );
curl_setopt( $this->ch, CURLOPT_VERBOSE, 1 );
curl_setopt( $this->ch, CURLOPT_STDERR, $this->debug_log );
}
curl_setopt( $this->ch, CURLOPT_HEADER, false );
if ( $this->force_basic_authentication == true ) {
curl_setopt( $this->ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC );
} else {
curl_setopt( $this->ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST | CURLAUTH_BASIC );
}
if ( $this->disable_follow_location != true ) {
curl_setopt( $this->ch, CURLOPT_FOLLOWLOCATION, 1 );
}
curl_setopt( $this->ch, CURLOPT_USERPWD, $this->username . ":" . $this->password );
curl_setopt( $this->ch, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $this->ch, CURLOPT_COOKIEFILE, $this->cookie_file );
// make request to Login transaction
$result = $this->RETSRequest( $this->capability_url[ 'Login' ] );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
// parse body response
$xml = $this->ParseXMLResponse( $body );
if ( !$xml ) {
return false;
}
// log replycode and replytext for reference later
$this->last_request[ 'ReplyCode' ] = "{$xml['ReplyCode']}";
$this->last_request[ 'ReplyText' ] = "{$xml['ReplyText']}";
// chop up login response
// if multiple parts of the login response aren't found splitting on \r\n, redo using just \n
$login_response = array();
if ( $this->server_version == "RETS/1.0" ) {
if ( isset( $xml ) ) {
$login_response = explode( "\r\n", $xml );
if ( empty( $login_response[ 3 ] ) ) {
$login_response = explode( "\n", $xml );
}
}
} else {
if ( isset( $xml->{'RETS-RESPONSE'} ) ) {
$login_response = explode( "\r\n", $xml->{'RETS-RESPONSE'} );
if ( empty( $login_response[ 3 ] ) ) {
$login_response = explode( "\n", $xml->{'RETS-RESPONSE'} );
}
}
}
// parse login response. grab all capability URLs known and ones that begin with X-
// otherwise, it's a piece of server information to save for reference
foreach ( $login_response as $line ) {
@list( $name, $value ) = @explode( "=", $line, 2 );
$name = trim( $name );
$value = trim( $value );
if ( !empty( $name ) && !empty( $value ) ) {
if ( isset( $this->allowed_capabilities[ $name ] ) || preg_match( '/^X\-/', $name ) == true ) {
$this->capability_url[ $name ] = $value;
} else {
$this->server_information[ $name ] = $value;
}
}
}
// if 'Action' capability URL is provided, we MUST request it following the successful Login
if ( isset( $this->capability_url[ 'Action' ] ) && !empty( $this->capability_url[ 'Action' ] ) ) {
$result = $this->RETSRequest( $this->capability_url[ 'Action' ] );
if ( !$result ) {
return false;
}
list( $headers, $body ) = $result;
}
if ( $this->compression_enabled == true ) {
curl_setopt( $this->ch, CURLOPT_ENCODING, "gzip" );
}
if ( $this->last_request[ 'ReplyCode' ] == 0 ) {
return true;
} else {
$this->set_error_info( "rets", $this->last_request[ 'ReplyCode' ], $this->last_request[ 'ReplyText' ] );
return false;
}
}
public function LastRequest() {
// return replycode and replytext from last request
return $this->last_request;
}
public function AddHeader( $name, $value ) {
// add static header for cURL requests
$this->static_headers[ $name ] = $value;
return true;
}
public function DeleteHeader( $name ) {
// delete static header from cURL requests
unset( $this->static_headers[ $name ] );
return true;
}
public function ParseXMLResponse( $data = "" ) {
$this->reset_error_info();
if ( !empty( $data ) ) {
// parse XML function. ability to replace SimpleXML with something later fairly easily
$xml = @simplexml_load_string( $data );
if ( !is_object( $xml ) ) {
$this->set_error_info( "xml", -1, "XML parsing error: {$data}" );
$this->err = "XML Parsing error: {$data}";
return false;
}
return $xml;
} else {
$this->set_error_info( "xml", -1, "XML parsing error. No data to parse" );
return false;
}
}
public function RETSRequest( $action, $parameters = "" ) {
$this->reset_error_info();
$this->err = "";
$this->last_response_headers = array();
$this->last_response_headers_raw = "";
// exposed raw RETS request function. used internally and externally
if ( empty( $action ) ) {
die ( "RETSRequest called but Action passed has no value. Failed login?\n" );
}
$parse_results = parse_url( $action, PHP_URL_HOST );
if ( empty( $parse_results ) ) {
// login transaction gave a relative path for this action
$request_url = $this->server_protocol . '://' . $this->server_hostname . ':' . $this->server_port . '' . $action;
} else {
// login transaction gave an absolute path for this action
$request_url = $action;
}
// build query string from arguments
$request_arguments = "";
if ( is_array( $parameters ) ) {
$request_arguments .= "?";
foreach ( $parameters as $key => $value ) {
$request_arguments .= "{$key}=" . urlencode( $value ) . "&";
}
$request_arguments = preg_replace( '/\&$/', '', $request_arguments );
}
// build entire URL
$request_url = $request_url . $request_arguments;
// build headers to pass in cURL
$request_headers = "";
if ( is_array( $this->static_headers ) ) {
foreach ( $this->static_headers as $key => $value ) {
$request_headers .= "{$key}: {$value}\r\n";
}
}
if ( $this->ua_auth == true ) {
$session_id_to_calculate_with = "";
// calculate RETS-UA-Authorization header
$ua_a1 = md5( $this->static_headers[ 'User-Agent' ] . ':' . $this->ua_pwd );
$session_id_to_calculate_with = ( $this->use_interealty_ua_auth == true ) ? "" : $this->session_id;
$ua_dig_resp = md5( trim( $ua_a1 ) . ':' . trim( $this->request_id ) . ':' . trim( $session_id_to_calculate_with ) . ':' . trim( $this->static_headers[ 'RETS-Version' ] ) );
$request_headers .= "RETS-UA-Authorization: Digest {$ua_dig_resp}\r\n";
}
$this->last_request_url = $request_url;
curl_setopt( $this->ch, CURLOPT_URL, $request_url );
curl_setopt( $this->ch, CURLOPT_HTTPHEADER, array( $request_headers ) );
// do it
$response_body = curl_exec( $this->ch );
if ( $response_body === false ) {
$response_body = 'Curl error: ' . curl_error( $this->ch );
}
$response_code = curl_getinfo( $this->ch, CURLINFO_HTTP_CODE );
if ( $this->debug_mode == true ) {
fwrite( $this->debug_log, $response_body . "\n" );
}
if ( $this->catch_last_response == true ) {
$this->last_server_response = $this->last_response_headers_raw . $response_body;
}
if ( isset( $this->last_response_headers[ 'WWW-Authenticate' ] ) ) {
if ( preg_match( '/Basic/', $this->last_response_headers[ 'WWW-Authenticate' ] ) ) {
$this->auth_support_basic = true;
}
if ( preg_match( '/Digest/', $this->last_response_headers[ 'WWW-Authenticate' ] ) ) {
$this->auth_support_digest = true;
}
}
if ( isset( $this->last_response_headers[ 'RETS-Version' ] ) ) {
$this->server_version = $this->last_response_headers[ 'RETS-Version' ];
}
if ( isset( $this->last_response_headers[ 'Server' ] ) ) {
$this->server_software = $this->last_response_headers[ 'Server' ];
}
if ( isset( $this->last_response_headers[ 'Set-Cookie' ] ) ) {
if ( preg_match( '/RETS-Session-ID\=(.*?)(\;|\s+|$)/', $this->last_response_headers[ 'Set-Cookie' ], $matches ) ) {
$this->session_id = $matches[ 1 ];
}
}
if ( $response_code != 200 ) {
$this->set_error_info( "http", $response_code, $response_body );
return false;
}
// return raw headers and body
return array( $this->last_response_headers_raw, $response_body );
}
private function read_custom_curl_headers( $handle, $call_string ) {
static $last_remembered_header;
$this->last_response_headers_raw .= $call_string;
$header = "";
$value = "";
@list( $header, $value ) = explode( ': ', $call_string, 2 );
$header = trim( $header );
$value = trim( $value );
if ( !empty( $header ) ) {
// new header
$this->last_response_headers[ $header ] = $value;
$last_remembered_header = $header;
} else {
// continuation of last header. append to previous
$this->last_response_headers[ $last_remembered_header ] .= $call_string;
}
return strlen( $call_string );
}
public function Error() {
if ( isset( $this->error_info[ 'type' ] ) && !empty( $this->error_info[ 'type' ] ) ) {
return $this->error_info;
} else {
return false;
}
}
private function set_error_info( $type, $code, $text ) {
$this->error_info[ 'type' ] = $type;
$this->error_info[ 'code' ] = $code;
$this->error_info[ 'text' ] = $text;
return true;
}
private function reset_error_info() {
$this->error_info[ 'type' ] = "";
$this->error_info[ 'code' ] = "";
$this->error_info[ 'text' ] = "";
return true;
}
private function is_server_version( $check_version ) {
if ( $check_version == "1_5_or_below" ) {
if ( $this->GetServerVersion() == "RETS/1.5" || $this->GetServerVersion() == "RETS/1.0" ) {
return true;
} else {
return false;
}
}
if ( $check_version == "1_7_or_higher" ) {
if ( $this->GetServerVersion() == "RETS/1.7" || $this->GetServerVersion() == "RETS/1.7.1" || $this->GetServerVersion() == "RETS/1.7.2" ) {
return true;
} else {
return false;
}
}
return false;
}
private function fix_encoding( $in_str ) {
if ( $this->disable_encoding_fix == true ) {
return $in_str;
}
$in_str = preg_replace( '/\&\s/', '& ', $in_str );
$cur_encoding = mb_detect_encoding( $in_str );
if ( $cur_encoding == "UTF-8" && mb_check_encoding( $in_str, "UTF-8" ) ) {
return $in_str;
} else {
return utf8_encode( $in_str );
}
}
public function ServerDetail( $detail ) {
if ( isset( $this->server_information[ $detail ] ) ) {
return $this->server_information[ $detail ];
} else {
return "";
}
}
public function SetParam( $name, $value ) {
switch ( $name ) {
case "cookie_file":
$this->cookie_file = $value;
break;
case "debug_file":
$this->debug_file = $value;
break;
case "debug_mode":
$this->debug_mode = $value;
break;
case "compression_enabled":
$this->compression_enabled = $value;
break;
case "force_ua_authentication":
$this->ua_auth = $value;
break;
case "disable_follow_location":
$this->disable_follow_location = $value;
break;
case "force_basic_authentication":
$this->force_basic_authentication = $value;
break;
case "use_interealty_ua_auth":
$this->use_interealty_ua_auth = $value;
break;
case "catch_last_response":
$this->catch_last_response = $value;
break;
case "disable_encoding_fix":
$this->disable_encoding_fix = $value;
break;
case "offset_support":
$this->offset_support = $value;
break;
case "override_offset_protection":
$this->override_offset_protection = $value;
break;
default:
return false;
}
return true;
}
}