<?php
# $Id: class_layer_monitor.php 791 2007-08-10 10:36:04Z baudson $
# http://www.mapbender.org/index.php/
# Copyright (C) 2002 CCGIS 
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

//require_once(dirname(__FILE__)."/../../conf/mapbender.conf");
require_once(dirname(__FILE__)."/../http/classes/class_connector.php");
require_once(dirname(__FILE__)."/../http/classes/class_mb_exception.php");
require_once(dirname(__FILE__)."/../http/extensions/DifferenceEngine.php");
require_once(dirname(__FILE__) . "/../http/classes/class_administration.php");

class Monitor {
	/**
	 *  1 = reachable and in sync with db
	 *  0 = reachable and out of sync with db
	 * -1 = unreachable
	 * -2 = monitoring in progress
	 * 
	 */
	var $result = -1;

	/**
	 * 1  = the get map request DEFINITELY returns a valid map image
	 * 0  = the WMS doesn't support XML error format. Who knows if the image is really a map?
	 * -1 = the get map request doesn't return an image
	 */
	var $returnsImage;

	var $comment = "";
	var $updated = "0";
	var $supportsXMLException = false;
	
	var $timestamp;
	var $timestamp_cap_begin;
	var $timestamp_cap_end;
	var $capabilitiesURL;
	var $mapURL;
	
	var $remoteXML;
	var $localXML;
	var $capabilitiesDiff;
	
	var $tmpDir = null;
	
	function __construct($reportFile, $autoUpdate, $tmpDir) {
		$this->tmpDir = $tmpDir;
		
		$this->reportFile = $tmpDir.$reportFile;
		//$this->reportFile = $reportFile;
		$this->wmsId = $this->getTagOutOfXML($this->reportFile,'wms_id');
		$this->uploadId = $this->getTagOutOfXML($this->reportFile,'upload_id');
		$this->autoUpdate = $autoUpdate;
		$e=new mb_notice("Monitor Report File: ".$this->reportFile);
		$e=new mb_notice("WMS ID: ".$this->wmsId);
		$this->capabilitiesURL = urldecode($this->getTagOutOfXML($this->reportFile,'getcapurl'));//read out from xml
		$e=new mb_notice("GetCapURL: ".$this->capabilitiesURL);

		set_time_limit(TIME_LIMIT);
		
		$this->timestamp = microtime(TRUE);
		//get authentication info for service
		$admin = new administration();
		$auth = $admin->getAuthInfoOfWMS($this->wmsId);
		if ($auth['auth_type']==''){
			unset($auth);
		}
		if ($this->capabilitiesURL) {
		
			//$remoteWms = new wms(); #exchange by other handling
			

			$this->timestamp_cap_begin=microtime(TRUE);//ok
			

			//$remoteWms->createObjFromXML($this->capabilitiesURL);#exchange by other handling
			

			//$this->remoteXML = $remoteWms->wms_getcapabilities_doc;
			if (isset($auth)) {
				$capObject = new connector($this->capabilitiesURL,$auth);
			} else {
				$capObject = new connector($this->capabilitiesURL);
			}
			//decode and encode to have the same behavior as loading caps to database
			$this->remoteXML = $capObject->file;

			$this->timestamp_cap_end=microtime(TRUE);
			//read local copy out of xml
			$this->localXML = urldecode($this->getTagOutOfXML($this->reportFile,'getcapdoclocal'));
//			$e=new mb_notice("Remote Caps: ".$this->remoteXML);
			// service unreachable
			if (!$this->remoteXML) {
				$this->result = -1;
				$this->comment = "Connection failed.";
				//$e=new mb_exception("Connection failed");
			}
			/*
			 * service available;
			 * no local copy of capabilities file,
			 * so it has to be updated anyway
			 */
			elseif (!$this->localXML) {
				$this->result = 0;
				//$e=new mb_exception("No local Copy of Caps available");
			}
			/*
			 * service available;
			 * check if local copy is different
			 * to remote capabilties document
			 */
			else {
				/*
				 * compare to local capabilities document
				 */
				// capabilities files match
				if ($this->localXML == $this->remoteXML) {
					$this->result = 1;
					$this->comment = "WMS is stable.";
					//$e=new mb_exception("Compare ok - Docs ident");
				
				}
				// capabilities files don't match
				else {
					$this->result = 0;
					$this->comment = "WMS is not up to date.";
					$localXMLArray = explode("\n", htmlentities($this->localXML));
					$remoteXMLArray = explode("\n", htmlentities($this->remoteXML));
					$this->capabilitiesDiff = $this->outputDiffHtml($localXMLArray,$remoteXMLArray);
					//$e=new mb_exception("Problem Docs are out of sync");
				}
			}
			/*
			 * if the WMS is available,
			 * 1) get a map image
			 * 2) update the local backup of the capabilities doc if necessary
			 */
			if ($this->result != -1) {
				
				$this->mapURL = urldecode($this->getTagOutOfXML($this->reportFile,'getmapurl'));
				//$e=new mb_exception("mapurl:".$this->mapURL);
			if (isset($auth)) {
				if ($this->isImage($this->mapURL,$auth)) {
					$this->returnsImage = 1;
				}
				else {
					$this->returnsImage = -1;
				}
				
			} else {
				if ($this->isImage($this->mapURL)) {
					$this->returnsImage = 1;
					//$e=new mb_exception("Returns image");
				}
				else {
					$this->returnsImage = -1;
					//$e=new mb_exception("Returns no image!");
				}
			}



				//Check for valid XML - validate it again wms 1.1.1 -some problems occur?
				#$dtd = "../schemas/capabilities_1_1_1.dtd";
				#$dom = new domDocument;
				#$dom->loadXML($this->remoteXML);
				#if (!$dom->validate($dtd)) {
					#$this->result = -1;
					#$this->comment = "Invalid getCapabilities request/document or service exception.";
				#}
					#else {
					#$this->comment = "WMS is not up to date but valid!";
				#}

				//Do a simple check if <WMT_MS_Capabilities version="1.1 is part of the remote Cap Dokument
				
				$searchString  = 'WMT_MS_Capabilities';
				$pos = strpos($this->remoteXML, $searchString);
				if ($pos === false) {
    					$this->result = -1;
					$this->comment = "Invalid getCapabilities request/document or service exception.";
				}


				/*
				 * if the local backup of the capabilities document
				 * is deprecated, update the local backup
				 */
				#if ($this->result == 0) {
					//$mywms = new wms();
		
					/* 
					 * if the capabilities document is valid,
					 * update it OR mark it as "not up to date"
					 */ 
					#if ($this->localXML==) {//check validation of capabilities document
						#if ($this->autoUpdate) {
							#$mywms->updateObjInDB($this->wmsId);
							#$this->updated = "1";
							#$this->comment = "WMS has been updated.";
							
						#}
						#else {
						#	$this->comment = "WMS is not up to date.";
						#}
					#}
					// capabilities document is invalid
					#else {
					#	$this->result = -1;
					#	$this->comment = "Invalid getCapabilities request/document or service exception.";
					#}    
				#}
			}
		}
		else {
			$this->result = -1;
			$this->comment = "Invalid upload URL.";
		}
		#$e = new mb_notice("class_monitor: constructor: result = " . $this->result);
		#$e = new mb_notice("class_monitor: constructor: comment = " . $this->comment);
		#$e = new mb_notice("class_monitor: constructor: returnsImage = " . $this->returnsImage);
	}
	
	/**
	 * 
	 */
	#function toString() {
		#$str = "";
		#$str .= "wmsid: " . $this->wmsId . "\nupload_id: " . $this->uploadId . "\n";
		#$str .= "autoupdate: " . $this->autoUpdate . "\n";
		#$str .= "result: " . $this->result . "\ncomment: " . $this->comment . "\n";
		#$str .= "timestamp: " . $this->timestamp . " (".date("F j, Y, G:i:s", $this->timestamp).")\n";
		#$str .= "getCapabilities URL: " . $this->capabilitiesURL . "\nupdated: " . $this->updated . "\n\n";
		#$str .= "getMap URL: " . $this->mapURL . "\nis image: " . $this->returnsImage . "\n\n";
		#$str .= "-------------------------------------------------------------------\n";
		#$str .= "remote XML:\n\n" . $this->remoteXML . "\n\n";
		#$str .= "-------------------------------------------------------------------\n";
		#$str .= "local XML:\n\n" . $this->localXML . "\n\n";
		#$str .= "-------------------------------------------------------------------\n";
		#return (string) $str;
	#}

	/**
	 * Update database
	 */
	function updateInDB() {
		$sql = "UPDATE mb_monitor SET updated = $1, status = $2, image = $3, status_comment = $4, upload_url = $5, timestamp_end = $6, map_url = $7 , timestamp_begin = $10 WHERE upload_id = $8 AND fkey_wms_id=$9";
		$v = array($this->updated, $this->result, $this->returnsImage, $this->comment, $this->capabilitiesURL, $this->timestamp_cap_end, $this->mapURL, $this->uploadId, $this->wmsId, $this->timestamp_cap_begin);
		$t = array('s', 'i', 'i', 's', 's', 's', 's', 's', 'i','s');
		$res = db_prep_query($sql,$v,$t);		
	}
	/**
	 * Update xml
	 */
	function updateInXMLReport() {
		//create text for diff
		$difftext = "<html>\n";
		$difftext .= "<head>\n";
		$difftext .= "<title>Mapbender - monitor diff results</title>\n";
		$difftext .= "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n";
		$difftext .= "<meta http-equiv=\"pragma\" content=\"no-cache\">\n";
		$difftext .= "<meta http-equiv=\"expires\" content=\"0\">\n";
		$difftext .= "<meta http-equiv=\"Content-Type\" content=\"text/html; charset='.CHARSET.'\">";	
		$difftext .= "<style type=\"text/css\">\n";
		$difftext .= "* {font-family: Arial, \"Courier New\", monospace; font-size: small;}\n";
		$difftext .= ".diff-context {background: #eee;}\n";
		$difftext .= ".diff-deletedline {background: #eaa;}\n";
		$difftext .= ".diff-addedline {background: #aea;}\n";
		$difftext .= ".diff-blockheader {background: #ccc;}\n";
		$difftext .= "</style>\n";
		$difftext .= "</head>\n";
		$difftext .= "<body>\n";
		$difftext .= "<table cellpadding=3 cellspacing=0 border=0>\n";
		$difftext .= "<tr><td align='center' colspan='2'>Local</td><td align='center' colspan='2'>Remote</td></tr>\n";		
		$difftext .= $this->capabilitiesDiff;
		$difftext .= "\n\t</table>\n\t";
		$difftext .= "</body></html>";
		//write to report
		$xml=simplexml_load_file($this->reportFile);	
		$xml->wms->image=$this->returnsImage;
		$xml->wms->status=$this->result;
		$xml->wms->getcapduration = intval(($this->timestamp_cap_end-$this->timestamp_cap_begin)*1000);
		$xml->wms->getcapdocremote = rawurlencode($this->remoteXML);
		$xml->wms->getcapdiff = rawurlencode($difftext);
		$xml->wms->comment=$this->comment;
		$xml->wms->getcapbegin=$this->timestamp_cap_begin;
		$xml->wms->getcapend=$this->timestamp_cap_end;
		$xml->asXML($this->reportFile);
	}
	/*
	 * Checks if the mapUrl returns an image or an exception
	 */
	function isImage($url) {
		#$headers = get_headers($url, 1);#controll this function TODO
		//$e = new mb_notice("class_monitor: isImage: map URL is " . $url);
		#$e = new mb_notice("class_monitor: isImage: Content-Type is " . $headers["Content-Type"]);
		#if (preg_match("/xml/", $headers["Content-Type"])) {
		#	return false;
		#}


		if (func_num_args() == 2) { //new for HTTP Authentication
        	    	$auth = func_get_arg(1);
			$imgObject = new connector($url, $auth);
		}
		else {
			$imgObject = new connector($url);
		}
		$image = $imgObject->file;
		//write images to tmp folder
		$imageName=$this->tmpDir."/"."monitor_getmap_image_".md5(uniqid()).".png";
		$fileMapImg = fopen($imageName, 'w+');
		$bytesWritten = fwrite($fileMapImg, $image);
		fclose($fileMapImg);

		//$e = new mb_notice("class_monitor: isImage: path: ".$imageName);
		//$e = new mb_notice("class_monitor: isImage: Content-Type is " . mime_content_type($image));
		//$e = new mb_notice("class_monitor: isImage: Content-Type (file) is " . mime_content_type($imageName));
		if (mime_content_type($imageName)=="image/png") {
			return true;
		}
		return false;
	}

	/**
	 * Returns the objects out of the xml file
 	 */
       private function getTagOutOfXML($reportFile,$tagName) {
		#$e = new mb_notice("class_monitor: getUploadURL: wms = " . $wmsId);
		#$e = new mb_notice("class_monitor: getUploadURL: upload_id = " . $upload_id);
		#$sql = "SELECT upload_url FROM mb_monitor WHERE fkey_wms_id = $1 AND upload_id = $2";
		#$v = array($wmsId, $upload_id);
		#$t = array('i', 'i');
		#$res = db_prep_query($sql,$v,$t);
		#$someArray = db_fetch_array($res);
		#$e = new mb_notice("class_monitor: getUploadURL: url = " . $someArray["upload_url"]);
		#$xmlobj = new DOMDocument('1.0');
  		#$xmlobj->load($reportFile);
		$xml=simplexml_load_file($reportFile);
		$result=(string)$xml->wms->$tagName;
		return $result;
	}
	/*
	* creates a html diff of the xml documents
	*/
	private function outputDiffHtml($localXMLArray,$remoteXMLArray) {
		$diffObj = new Diff($localXMLArray,$remoteXMLArray);
		$dft = new TableDiffFormatter();
		return $dft->format($diffObj);
	}		
	//TODO get this things out of administration - but without database connection	
	function is_utf8_string($string) {
		return preg_match('%(?:
		[\xC2-\xDF][\x80-\xBF]        # non-overlong 2-byte
		|\xE0[\xA0-\xBF][\x80-\xBF]               # excluding overlongs
		|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}      # straight 3-byte
		|\xED[\x80-\x9F][\x80-\xBF]               # excluding surrogates
		|\xF0[\x90-\xBF][\x80-\xBF]{2}    # planes 1-3
		|[\xF1-\xF3][\x80-\xBF]{3}                  # planes 4-15
		|\xF4[\x80-\x8F][\x80-\xBF]{2}    # plane 16
		)+%xs', $string);
	}
//function to get documents with authentication
	function getDocumentContent($url){
		if (func_num_args() == 2) { //new for HTTP Authentication
        	    	$auth = func_get_arg(1);
			$d = new connector($url, $auth);
		}
		else {
			$d = new connector($url);
		}
		return $d->file;
	}

	function is_utf8_xml($xml) {
		return preg_match('/<\?xml[^>]+encoding="utf-8"[^>]*\?>/is', $xml);
	}
	
	function is_utf8 ($data) {
		return ($this->is_utf8_xml($data) || $this->is_utf8_string($data));
	}
	
	function char_encode($data) {
		if (CHARSET == "UTF-8") {
			if (!$this->is_utf8($data)) {
				$e = new mb_notice("Conversion: ISO-8859-1 to UTF-8");
				return utf8_encode($data);
			}
		}
		else {
			if ($this->is_utf8($data)) {
				$e = new mb_notice("Conversion: UTF-8 to ISO-8859-1");
				return utf8_decode($data);
			}
		}
		$e = new mb_notice("No conversion: is " . CHARSET);
		return $data;
	}

	function char_decode($data) {
		if (CHARSET == "UTF-8") {
			if ($this->is_utf8($data)) {
				$e = new mb_notice("Conversion: UTF-8 to ISO-8859-1");
				return utf8_decode($data);
			}
		}
		$e = new mb_notice("no conversion: is " . CHARSET);
		return $data;
	}
}