diff --git a/php/classes/EpnTapMgr.php b/php/classes/EpnTapMgr.php
index 2ea9140..c296685 100644
--- a/php/classes/EpnTapMgr.php
+++ b/php/classes/EpnTapMgr.php
@@ -6,13 +6,6 @@
 class EpnTapMgr {
 	private $logEpnTap;
 
-	private function addLog($msg)
-	{
-		// fwrite($this->logEpnTap, date("h:i:s A") . ": " . $msg . "\n");
-		error_log("INFO " . $msg);
-		// print "test";
-	}
-
 	private function dateToJD($gregorian_date) {
 		list($day, $month, $year, $hours, $minutes, $seconds) = preg_split('/[\s\/:]+/', $gregorian_date);
 		return GregorianToJD($month, $day, $year) + $hours/24 + $minutes/(24*60) + $seconds/(24*60*60);
@@ -46,7 +39,7 @@ class EpnTapMgr {
 		$url = $access_url . '/sync?' . $params . '&QUERY=' . urlencode(preg_replace('/\s+/', ' ', $query)); // remove also multiple whitespaces
 
 		$res = $votMgr->load($url);
-		$this->addLog("Query URL: " . $url);
+		error_log("Query URL: " . $url);
 		$result = $votMgr->parseStream();
 		return $votMgr->getVotableError() ? array('error' => $votMgr->getVotableError()) : $result;
 	}
@@ -54,7 +47,6 @@ class EpnTapMgr {
 	/* filter order: product type, target name, time min, time max */
 	public function getGranules($table_name, $access_url, $filter, $select, $limit, $offset) {
 		$query = "SELECT TOP {$limit} " . join(', ', $select) . " FROM {$table_name}.epn_core " . $this->createFilter($filter[0], $filter[1], $filter[2], $filter[3]) . " OFFSET {$offset}";
-		// return $query;
 		$result = $this->request($access_url, $query);
 		if(! array_key_exists("error", $result)) {
 			for ($i = 0 ; $i < sizeof($result) ; $i++) {
@@ -67,10 +59,37 @@ class EpnTapMgr {
 		return $result;
 	}
 
+	/* Return the list of available services by querying some usual registries. */
+	public function getServices() {
+		$registriesURL = ["http://registry.euro-vo.org/regtap/tap", "http://dc.zah.uni-heidelberg.de/tap", "http://gavo.aip.de/tap", "http://reg.g-vo.org/tap"];
+		$columns = ['short_name', 'res_title', 'ivoid', 'access_url', 'table_name', 'content_type', 'creator_seq', 'content_level', 'reference_url', 'created', 'updated'];
+		$getServicesQuery = "SELECT DISTINCT " . implode(', ', $columns) . " FROM rr.resource
+		                     NATURAL JOIN rr.res_schema NATURAL JOIN rr.res_table NATURAL JOIN rr.interface NATURAL JOIN rr.res_detail NATURAL JOIN rr.capability
+		                     WHERE standard_id='ivo://ivoa.net/std/tap' AND intf_type='vs:paramhttp' AND detail_xpath='/capability/dataModel/@ivo-id'
+		                     AND 1=ivo_nocasematch(detail_value, 'ivo://vopdc.obspm/std/EpnCore%') AND table_name LIKE '%.epn_core' ORDER BY short_name, table_name";
+
+		for($i=0; $i<count($registriesURL); $i++) {
+			$services = $this->request($registriesURL[$i], $getServicesQuery);
+			if(! array_key_exists("error", $services)) {
+				for($j=0; $j<count($services); $j++) {
+					$services[$j]['id'] = $this->generateServiceId($services[$j]);
+				}
+				return $services;
+			} else if($i === count($registriesURL)-1) {
+				error_log("Can not access any of these services : " . implode(', ', $registriesURL) . ".");
+				return;
+			}
+		}
+	}
+
+	/* Generate a unique service identifier from the service ivoid and the table name. */
+	public function generateServiceId($service) {
+		return str_replace(['ivo://', '.epn_core'], '', $service['ivoid'] . '/' . $service['table_name']);
+	}
+
 	/* filter order: product type, target name, time min, time max */
 	public function getNbRows($table_name, $access_url, $filter) {
 		$query = "SELECT COUNT(*) AS nb_rows FROM {$table_name}.epn_core " . $this->createFilter($filter[0], $filter[1], $filter[2], $filter[3]);
-		// return $query;
 		return $this->request($access_url, $query)[0]['nb_rows'];
 	}
 
diff --git a/php/classes/VOTableMgr.php b/php/classes/VOTableMgr.php
index 73d1bca..aa4982a 100644
--- a/php/classes/VOTableMgr.php
+++ b/php/classes/VOTableMgr.php
@@ -4,9 +4,8 @@
  * @version $Id: VOTableMgr.php 2916 2015-05-19 13:08:33Z elena $
  */
 
-//set DEBUG_MODE to TRUE to have some log information in user data dir
-define("DEBUG_MODE", TRUE);
-
+//set DEBUG_MODE to TRUE to have some log information
+define("DEBUG_MODE", FALSE);
 
 class VOTableMgr {
 	private $xml, $xp;
@@ -16,14 +15,8 @@ class VOTableMgr {
 	private $is_little_endian;
 	private $votable_error = false;
 
-	function addLog($msg) {
-		if (DEBUG_MODE) {
-			error_log("INFO " . $msg);
-		}
-	}
-
 	function load($fileName) {
-		$this->addLog("File name " . $fileName);
+		// error_log("Loading " . $fileName);
 		$this->is_little_endian = array_values(unpack('L1L', pack('V', 1)))[0] == 1;
 
 		// see http://php.net/manual/en/domdocument.load.php#91384
@@ -31,35 +24,28 @@ class VOTableMgr {
 			'http' => array(
 				'method' => 'GET',
 				'timeout' => '5',
-				'user_agent' => 'PHP libxml agent'
+				'user_agent' => 'PHP libxml agent',
+				// If the query is wrong, epn-tap service returns an HTTP error code 400, along with xml containing some usefull informations.
+				'ignore_errors' => true
 			)
 		);
 		$context = stream_context_create($options);
 		libxml_set_streams_context($context);
-
 		$this->xml = new DomDocument();
-		$res = $this->xml->load($fileName);
 
-		if(!$res) {
-			$this->votable_error = "Can not load the XML file. Maybe the service is not accessible.";
-			$this->addLog("Can not load XML.");
+		try {
+			$res = $this->xml->load($fileName);
+		} catch (Exception $e) {
+			$this->votable_error = $e->message;
 			return false;
 		}
 
 		$this->checkIDAttribute();
-		/*if ($this->xml->namespaceURI == '')
-		{
-			$this->addLog("File don't have a namespace defined\n");
-			if (!$this->xml->createAttributeNS('http://www.ivoa.net/xml/VOTable/v1.1','xmlns'))
-			$this->addLog("Cannot create namespace attribute\n");
-		}
-		$this->addLog($this->xml->namespaceURI."\n");*/
 
 		$rootNamespace = $this->xml->lookupNamespaceUri($this->xml->namespaceURI);
 		$this->xp = new domxpath($this->xml);
 		$this->xp->registerNameSpace('x', $rootNamespace);
 
-		$this->addLog($fileName . " loaded.");
 		return true;
 	}
 
@@ -69,11 +55,13 @@ class VOTableMgr {
 
 	function isValidSchema() {
 		if ($this->votable_error != false) {
+			error_log("There is an error on the VOTable: " . $this->votable_error);
 			return false;
 		}
 
 		if (!$this->xml) {
 			$this->votable_error = "The returned file is not XML.";
+			error_log("There is an error on the VOTable: " . $this->votable_error);
 			return false;
 		}
 
@@ -81,6 +69,7 @@ class VOTableMgr {
 		foreach($infos as $info) {
 			if($info->getAttribute('value') == 'ERROR') {
 				$this->votable_error = $info->textContent;
+				error_log("There is an error on the VOTable: " . $this->votable_error);
 				return false;
 			}
 		}
@@ -92,9 +81,6 @@ class VOTableMgr {
 			 libxml_use_internal_errors(true);
 
 		 $vers = $this->getVersion();
-
-		 $this->addLog("VOTable version : ".$vers."\n");
-
 		 $result = FALSE;
 
 		 switch ($vers)
@@ -128,8 +114,7 @@ class VOTableMgr {
 						break;
 				}
 				$msg .= ($error->message." - In line : ".$error->line." - Of file : ".$error->file."\n");
-
-				$this->addLog($msg);
+				error_log($msg);
 			 }
 
 			 libxml_use_internal_errors(false);
@@ -270,6 +255,7 @@ class VOTableMgr {
 	public function parseStream() {
 		if (! $this->isValidSchema()) {
 			return null;
+			// TODO: check this
 		}
 		$data = Array();
 		$fields = $this->xp->query($this->queryFields());
@@ -292,8 +278,6 @@ class VOTableMgr {
 			$col_id = $n_value % $nb_columns;
 			$field_node = $fields[$col_id];
 
-			// echo "c=$this->c, n_value=$n_value, nb_columns=$nb_columns, col_id=$col_id, column_name=" . $field_node->getAttribute("ID") . "\n";
-
 			if($col_id == 0) {
 				$row = Array();
 			}
@@ -302,7 +286,6 @@ class VOTableMgr {
 				array_push($data, $row);
 			}
 			$n_value+=1;
-			// echo "Char pos: $this->c\n";
 		}
 		return $data;
 	}
@@ -312,41 +295,6 @@ class VOTableMgr {
 		return "$day/$month/$year";
 	}
 
-	# because unpack for floats is hardware dependant
-	// private function big_endian_unpack ($format, $data) {
-	// 	$ar = unpack ($format, $data);
-	// 	$vals = array_values ($ar);
-	// 	echo "vals: " . json_encode($vals) . "\n";
-	// 	$f = explode ('/', $format);
-	// 	$i = 0;
-	// 	foreach ($f as $f_k => $f_v) {
-	// 		$repeater = intval (substr ($f_v, 1));
-	//
-	// 		if ($repeater == 0) {
-	// 			$repeater = 1;
-	// 		}
-	// 		if ($f_v{1} == '*') {
-	// 			$repeater = count ($ar) - $i;
-	// 		}
-	// 		if ($f_v{0} != 'd') {
-	// 			$i += $repeater; continue;
-	// 		}
-	// 		$j = $i + $repeater;
-	//
-	// 		for ($a = $i; $a < $j; ++$a) {
-	// 			$p = pack ('d', $vals[$i]);
-	// 			$p = strrev ($p);
-	// 			++$i;
-	// 		}
-	// 	}
-	// 	$a = 0;
-	// 	foreach ($ar as $ar_k => $ar_v) {
-	// 		$ar[$ar_k] = $vals[$a];
-	// 		++$a;
-	// 	}
-	// 	return $ar;
-	// }
-
 	private function process_datablock($field_node) {
 		$data_type = $field_node->getAttribute("datatype");
 		$row_size = $this->get_row_size($field_node);
@@ -390,11 +338,10 @@ class VOTableMgr {
 				break;
 			default:
 				$res = NULL;
-				$this->addLog("Unknown character: $data_type");
+				error_log("Unknown character: $data_type");
 				break;
 		}
 		$this->c+=$row_size;
-		// echo $field_node->getAttribute("ID") . "($data_type $row_size) = " . json_encode($res) . "\n";
 		return $res;
 	}
 
--
libgit2 0.21.2