<?
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ALBasic
//
// Author: Christian Mähler
// Version: 9
// Last update: December 2002
//
// Base library for map&guide TCP/IP communication to 
// address locator server and map&guide intranet server
// Batch Control is not supported (!)
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

class ALBasic
{
   // common parameter as documented for the public user interface
	 
   var $BPParaBCPath;			 	// old batch control path
   var $BPParaRequestFileName;			// filename of the file to be generated
   var $BPBatchUrl;				// URL where to find the file
   var $BPPictureFormat;			// format of picture to be generated
   var $BPLogFilePath;				// logfile path including filename!!
   var $BPUseTCPIP;        			// use TCP/IP? should be always true because Batch Control is not supported with this class
   var $BPMaximumFileAge;  			// maximum time for a file to stay on hard disk (in minutes)
   var $BPStreamDirectory; 			// directory where to write the maps from the stream
   var $BPMapsByStream;				// send map as stream?
   var $BPStreamToDisk;				// write stream to hard disk?
   var $BPMapStream;				// contains map stream	 
   var $eATCor;            			// AnswerType for BFGetParam = 1
   var $eATAns;            			// AnswerType for BFGetParam = 0
   var $BPALS;					// list of all server as string
   var $BPUrlNameMap;      			// name of map file (URL from docroot with filename)
   var $BPMaxSocketWait;			// time to wait for a server socket connect 
   var $BPCalcWait;				// time to wait for the whole server request to finish including the search for this server
	 		 															// this is not yet supported in PHP
	 
    // constants

   var $BPStat_PropriError;      		// 0
   var $BPStat_Error;            		// 1
   var $BPStat_Success;          		// 2
   var $BPGeoStat_Select;        		// 3
   var $BPRouteStat_NoLocFound;   		// 4

   // private variables
	 
   var $BPTCPAnswer;       			// TCP answer; every row in an array (BPTCPAnswer[ANS][0] == row 1 of server answer)
   var $BPBCAnswer;        			// Hier steht die BC-Antwort. Jede Zeile in einem Array (BPBCAnswer[COR][0] == Zeile 1 der Zwischendatei)
   var $BPFPTCP;           			// FileHandle for TCP communication
   var $BPFileCount;       			// unique number for file to generate
   var $BPRequest;         			// internal request string (necessary for BFAddParam)
   var $BPALSTCP;          			// assoc. array with Name, IP, Port and state of the server
   var $BCAnswerList;			 	// contains an associative array with the content of the answer file
   var $BPWINOS;           			// true if operating system is windows
   var $BPUNIXOS;          			// true if operating system is UNIX
   var $TCPtemp;				// temporary variable for TCP/IP answer from server
	 
   // the following internal variables define the service to request at server side
   // defined by ACTION or SUBACTION in request

   var $BPGeoServiceNum;
   var $NR_MAPREQUEST;  			// 2
   var $NR_GEO;   				// 7
   var $NR_ROUTEREQUEST_ROUTEID; 		// 30
   var $NR_ROUTEREQUEST; 	 		// 3


//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ALBasic
// constructor for class ALBasic
   
function ALBasic()
{     
      set_time_limit(60);		 // set maximum execution time for PHP script
      $this->BPMapsByStream=false;	 // maps are coming as stream by default
      $this->BPLogFilePath="";		 // set LogFilePath					
      $this->eATCor = 1;		 // const for correction part of server answer
      $this->eATAns = 0;		 // const for answer part of server answer
      $this->BPMaxSocketWait=60; 	 // wait 60 seconds per server for a socket connect	
      $this->BPMaximumFileAge=10; 	 // leave the generated maps 10 seconds as a maximum on hard disk      
      $this->BPStat_PropriError= 0; 	 // unknown error
      $this->BPStat_Error      = 1; 	 // known error
      $this->BPStat_Success    = 2; 	 // success (one hit - geocoding)
      $this->BPGeoStat_Select  = 3; 	 // success (multiple hits - geocoding)
      $this->BPRouteStat_Select= 3; 	 // should never happen (old value!)
      $this->BPRouteStat_NoLocFound = 4; // should never happen (old value!)
      $this->BPWINOS  = false;		 // not used at the moment
      $this->BPUNIXOS = false;		 // not used at the moment
      $this->BFSetFileCount();		 // get file count
			$this->BPStreamToDisk=true;
			// BPUrlNameMap = URL to map if correct parameters were passed
      $this->BPUrlNameMap  = $this->BPParaBCUrl  . $this->BPParaRequestFileName . $this->BPFileCount;  // . "." . $this->BPPictureFormat;
			
		  $this->Log_to_file("ALBasic.ALBasic: Passed successfully.");
}
 
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// BFGetTCPAnsLength
// Gets the length from the response stream in byte. The length is defined
// as a 12 byte string with leading 0s. E.g.: #0000000111#, answer is 111
// byte long
   
function BFGetTCPAnsLength($fp)
{
 		if (!$fp || feof($fp)) return 0;
 		$bufferArray = explode("#",fread($fp,12));
    return ((int)$bufferArray[1])-11;
		$this->Log_to_file("ALBasic.BFGetTCPAnsLength: Passed Succesfully");		
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// BFReadALSAnswer
// Reads complete answer from TCP/IP stream and splits stream into
// COR (correction) and ANS (answer) parts. Every line is read into
// a single associative array

function BFReadALSAnswer($servicenum)
{     
	if($this->TCPtemp=="") return false;
	
	 // read complete answer stream into arrays
	 if ($servicenum==$this->NR_NEXTREQUEST || $servicenum==$this->NR_ROUTEREQUEST || $servicenum==$this->NR_GEOREQUEST)
	 {
	    $this->BPTCPAnswer["COR"] = explode("^", $this->TCPtemp[2]);
	    $this->BPTCPAnswer["ANS"] = explode("^", $this->TCPtemp[6]);	 
	 }
	 else
	    $this->BPTCPAnswer["ANS"] = explode("^", $this->TCPtemp[2]);
	
	 // read every single line of answer section ANS into an assoc. array		
     for ($i = 0; $i < count($this->BPTCPAnswer["ANS"])-1 ;$i++)
     {				 		
       $tmp = explode("~", $this->BPTCPAnswer["ANS"][$i]);
       $this->BCAnswerList["ANS"][$tmp[0]][$tmp[1]]=$tmp[2];
     }
		 
		 // read every single line of correction section COR into an assoc. array		
		 if(is_array($this->BPTCPAnswer["COR"]))
		   for ($i = 0; $i < count($this->BPTCPAnswer["COR"])-1 ;$i++)
       {				 		
         $tmp = explode("~", $this->BPTCPAnswer["COR"][$i]);
         $this->BCAnswerList["COR"][$tmp[0]][$tmp[1]]=$tmp[2];
       }
		
		 // read maps from stream if applicable
		 if($this->BPMapsByStream)
		 {
		 	 //[MAPS]
			 //MAPSTREAMS=<Picture name>,<Size>|<Picture name>,<Size>|<Picture name>,<Size>|...
			 $maps=explode("|",$this->BFGetParam($this->eATAns,"MAPS","MAPSTREAMS",""));
			 
			 if(count($maps)>0)
			 {
			 		for($i=0;$i<count($maps);$i++)
					{		
					   // read one dummy byte between ASCII stream and binary stream
						 // this strange behavior is necessary			 
						 if($i==0) $temp=fread($this->BPFPTCP,1);
						 
					   // read map from stream
					 	 $mapstream=explode(",",$maps[$i]);
						 $temp = fread($this->BPFPTCP, $mapstream[1]);
						 
						 $this->BPMapStream=array($i => $temp);
						 
						 // write map to file
						 if($this->BPStreamToDisk && $this->BPStreamDirectory!="")
						 {
  						 $fp=@fopen($this->BPStreamDirectory."/".$mapstream[0],"wb");
  						 if($fp)
  						 {
  						   fwrite($fp,$temp);
  						   fclose($fp);
  						 }
  						 else
  						 	 $this->Log_to_file("ALBasic.BFReadALSAnswer: Error while writing map to hard disk: MapFile ".$this->BPStreamDirectory."/".$mapstream[0]);
						 }
					}
					
					srand ((double)microtime()*1000000);
 	  		  if(rand(0,100)%2 == 0) // deletion is performed
							$this->BFDeleteOldFiles();
			 }
		 }
		 
		 		
  	$this->Log_to_file("ALBasic.BFReadALSAnswer: Passed Succesfully");		 	 
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// BFSetFileCount
// Get single name for the naming of the generated files. Has to be 
// explicitly used by the user. This file count is not automatically 
// added to a file name!

function BFSetFileCount()
{
	$mytime=gettimeofday();
	$this->BPFileCount = $mytime["sec"]."--".$mytime["usec"];
  $this->Log_to_file("ALBasic.BFSetFileCount: Passed Succesfully");
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// BFBuildOrder()
// assembles request to server according to the content of the associative
// arrays containing the properties and their values

function BFBuildOrder()
{
    $request="";
		$separator="'^'";
    
		// add user defined parameter for variables of ALBasic
		if($this->BPPictureFormat!="")
				$this->BPRequest["CLIENT"]["BPPICTUREFORMAT"]=$this->BPPictureFormat;
		
		// build all values concerning SERVER section   
    reset($this->BPRequest["SERVER"]);
    while (list($key, $value) = each ($this->BPRequest["SERVER"]))
     $request .= "SERVER~$key~$value" . $separator;
   
	  // build all values concerning CLIENT section
    reset($this->BPRequest["CLIENT"]);
    while (list($key, $value) = each ($this->BPRequest["CLIENT"]))
      $request .= "CLIENT~$key~$value" . $separator;
    
		// build all values concerning special section (login) if applicable
		// it's necesary for the function WRITE_OPTIONS where a section
		// called after the login name is available
	  $login=strtoupper($this->BPRequest["CLIENT"]["LOGIN"]);
		if (sizeof($this->BPRequest[$login])>0)
		{
			 reset($this->BPRequest[$login]);
		   $numo=0;
       while (list($key, $value) = each ($this->BPRequest[$login]))
       {
 				  $numo++;
         	$request .= $login."~OPTION".$numo."~$key^$value".$separator;
       }
			 $request .= $login."~NUMOPTIONS~$numo".$separator;
		 }
		 
		 // return ready built request
		 
		 $this->Log_to_file("ALBasic.BFBuildOrder: Passed Succesfully: Request: #BAL##FN#".$this->BPParaRequestFileName."#ENDFN#" . $request . "#SEP#" . $separator . "#ENDSEP##ENDBAL#");		

     return  "#BAL##FN#".$this->BPParaRequestFileName."#ENDFN#" . $request . "#SEP#" . $separator . "#ENDSEP##ENDBAL#\r\n";
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// BFWriteOrder
// Opens TCP/IP connection and sends request to server. 

function BFWriteOrder($request)
{
 	  // split list of all available servers
    $serverlist=explode("^",$this->BPALS);
 		
		// check every server in round robin manner until timeout or connect
		for($x=0;$x<count($serverlist);$x++) 
		{
		   // get next server from list
		 	 list($this->BPALSTCP["NAME"],$this->BPALSTCP["IP"],$this->BPALSTCP["PORT"],$this->BPALSTCP["ACTIVE"])=explode(",",$serverlist[$x]);
			 // if server is set to "active" in server list, try to connect
			 if((int)$this->BPALSTCP["ACTIVE"]==1)
			 {
			 			// reset connection
			 			$this->BPFPTCP = false;
						// connect to IP and port of server
			 		  $this->BPFPTCP = @fsockopen ($this->BPALSTCP["IP"], $this->BPALSTCP["PORT"], &$errno, &$errstr, $this->BPMaxSocketWait);
 
 					  // if connection was enabled, write request
  	 	 			if ($this->BPFPTCP && !feof($this->BPFPTCP))
			 			{
						   $answerlength=0;
							 $starttime=time();
							 
						   while($answerlength<100 && (time()-$starttime<5) && $this->BPFPTCP && !feof($this->BPFPTCP))
							 {
    						   // write request
    	      		 	 @fputs ($this->BPFPTCP,$request);
    							 $this->Log_to_file("ALBasic.BFWriteOrder: succesful server connect: Server: ".$this->BPALSTCP["IP"].":".$this->BPALSTCP["PORT"]);
    							 
    							 // get answer length 					 
    							 $answerlength=$this->BFGetTCPAnsLength($this->BPFPTCP);
    							 
    							 // #0000000024##DISABLED##ENDDISABLED#
    							 // #0000000037##BUSY#<Milliseconds_working>~<TreadID>#ENDBUSY#
    							 // #0000000042##ERROR#<Error description>#ENDERROR#
    							 $this->TCPtemp="";
    							 $errno=$answerlength;
    							 // if answer length is less than 100, we received an error from server
    							 if($answerlength<100)
									 {
									 		if($answerlength>0)
									 		 $tcptmp=fgets($this->BPFPTCP, $answerlength);
											 		 					 
    							     if($answerlength==24)
    							 	 	   		$this->Log_to_file("ALBasic.BFWriteOrder: Server is disabled: Server: ".$this->BPALSTCP["IP"].":".$this->BPALSTCP["PORT"]."   server answer: ".$tcptmp);
											 else
											 		 if($answerlength>0)
    								 	 			 $this->Log_to_file("ALBasic.BFWriteOrder: Server error: Server: ".$this->BPALSTCP["IP"].":".$this->BPALSTCP["PORT"]."   server answer: ".$tcptmp);
													 else
													   $this->Log_to_file("ALBasic.BFWriteOrder: Server error: Server: ".$this->BPALSTCP["IP"].":".$this->BPALSTCP["PORT"]."   server answer: none");
											
											 fclose($this->BPFPTCP);
									 		 usleep(300);	// dos not work with windows													 										
											 $this->BPFPTCP = @fsockopen ($this->BPALSTCP["IP"], $this->BPALSTCP["PORT"], &$errno, &$errstr, $this->BPMaxSocketWait);											 											 
									 }
    							 else
    							 {
    							    $this->TCPtemp = explode("#", fgets($this->BPFPTCP, $answerlength));
    							 	  return $answerlength;
    								  break;
    							 }

    					 	 } // try to send request to one server (end while)									
			 		  }
						else
						{
							$this->Log_to_file("ALBasic.BFWriteOrder: Error while writing to socket: Server: ".$this->BPALSTCP["IP"].":".$this->BPALSTCP["PORT"]);
						} // end server connect
				} // server active				
		} // end for 

		if($this->TCPtemp=='')
				$this->Log_to_file("ALBasic.BFWriteOrder: Passed without server connect");
		else
				$this->Log_to_file("ALBasic.BFWriteOrder: Passed Succesfully");
						 
		return $errno;			
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// BFCheckParam
// Check whether all necessary parameters are available dependent on
// the service requested by the server. 
// returns clear text error message

function BFCheckParam($servicenum)
{		
    $err_msg='';
		
    // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // common parameters

    if ($this->BPALS == "")
       $err_msg = "Warning: No server defined!";
    elseif (($servicenum==$this->NR_MAPREQUEST || $servicenum==$this->NR_ROUTINGREQUEST) && $this->BPParaRequestFileName == "")
       $err_msg = "Warning: No name for file to generate!";

  	$this->Log_to_file("ALBasic.BFCheckParam: Regular error message: ".$err_msg);

    if($err_msg!='')
    {
      if ($servicenum == $this->NR_GEOREQUEST) 		 
				 $this->GeoResponse   = $err_msg;
      if ($servicenum == $this->NR_MAPREQUEST) 		 
				 $this->MapResponse   = $err_msg;
      if ($servicenum == $this->NR_ROUTINGREQUEST) 
				 $this->RouteResponse = $err_msg;				 
      return $this->BPStat_Error;
    }

    // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    // error messages for geocoding service
    
    if ($servicenum == $this->NR_GEOREQUEST)
    {
      if ($this->ParaRequestType == "")
       {
          if ($servicenum == $this->NR_GEOREQUEST) 
	      $this->GeoResponse = "Warning: Parameter ParaRequestType not defined!";
	      $this->Log_to_file("ALBasic.BFCheckParam: Regular error message (geocoding): ".$this->GeoResponse);
          return $this->BPStat_Error;
       }
     }
		 
     // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
     // error messages for mapping service

     if ($servicenum == $this->NR_MAPREQUEST)
     {
       if ($this->MapRequestRect == "" and $this->MapObjectList == "" and $this->BPRequest[CLIENT][LOGICALRECT]=="" and $this->BPRequest[CLIENT][CST1] =="")
       {
           $this->MapResponse = "Warning: No bounding rect and no objects defined!";
					 $this->Log_to_file("ALBasic.BFCheckParam: Regular error message (geocoding): ".$this->GeoResponse);
           return $this->BPStat_Error;
       }
     }
		 
    // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
		// error messages for routing service

     if ($servicenum == $this->NR_ROUTINGREQUEST)
     {
       if (($this->RouteStation[1] == "" and $this->RouteStation[2] == "")
       and (($this->BPRequest["CLIENT"]["STATION1"]=="") and ($this->BPRequest["CLIENT"]["STATION2"]=="") ))
       {
          $this->RouteResponse = "Warning: No starting and destination point defined!";
					$this->Log_to_file("ALBasic.BFCheckParam: Regular error message (routing): ".$this->RouteResponse);
          return $this->BPStat_Error;
       }
     }

    // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
		// error messages for next search service

     if ($servicenum == $this->NR_NEXTREQUEST)
     {
       if($this->SearchByCoord=="" && $this->SearchByTown=="")
       {
          $this->Response = "Warning: No search statement as SearchByTown or SearchByCoord defined!";
					$this->Log_to_file("ALBasic.BFCheckParam: Regular error message (next search): ".$this->Response);
          return $this->BPStat_Error;
       }
     }
		 
		 $this->Log_to_file("ALBasic.BFCheckParam: Passed Succesfully");

     return $this->BPStat_Success;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// BFAddParam
// add a property to the request without recheck by system

function BFAddParam($app, $key, $value)
{
   $this->BPRequest[strtoupper($app)][strtoupper($key)] = $value;
   $this->Log_to_file("ALBasic.BFAddParam: Passed successfully: ".$app." ".$key." ".$value);
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// BFGetParam
// read a property directly from COR or ANS part of the TCP/IP answer
   
function BFGetParam($answer_type, $section, $name, $def_value)
{
   (($answer_type == $this->eATAns) ? $file = "ANS" : $file = "COR");

	 if($this->BCAnswerList[$file][strtoupper($section)][$name]!="")
	 			return $this->BCAnswerList[$file][strtoupper($section)][$name];
	
	$this->Log_to_file("ALBasic.BFGetParam: Passed successfully: ".$answer_type." ".$section." ".$name." ".$def_value);
  return $def_value;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// BFDeleteOldFiles
// delete old files which are older than BPMaximumFileAge by random
// because it takes a lot of time to check and delete files, we only
// delete every second map generation. 
   
function BFDeleteOldFiles()
{
 		 if(!$this->BPMapsByStream || $this->BPStreamDirectory=="") return false;
		 
	   $handle = opendir($this->BPStreamDirectory."/");	 
	   while (($file = readdir($handle))!==false) 
 	   if($file!="." && $file!="..")
	 	 	if( (time()-filemtime($this->BPStreamDirectory."/".$file))>$this->BPMaximumFileAge*60)
		 		if(!unlink($this->BPStreamDirectory."/".$file))
   			  $this->Log_to_file("AlBasic.BFDeleteOldFiles: Error while deltin old map files on hard disk. Path: ".$this->BPStreamDirectory);
	
	   $this->Log_to_file("AlBasic.BFDeleteOldFiles: Passed successfully . Path: ".$this->BPStreamDirectory);
	   // deletion was performed
	   return true;
}

//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Log_to_file
// write log output to file defined by BPLogFilePath
   
function Log_to_file($logtext)
{
 if($this->BPLogFilePath=='') 
  return false;
	
	$fp=@fopen($this->BPLogFilePath,"a");
	if($fp)
	{
	   fwrite($fp,$logtext."\r\n");
	   fclose($fp);
	}
	else
	 	 echo "<br>Error while writing to log file<br>";
	
	return false;
}

} // end of class definition

?>
