<?php
  set_time_limit ( 300 );

	require_once ("Crypt.php");
	require_once ("../admin/classes/cCourse.php");	
	require_once ("../admin/classes/cUser.php");	
	
	define("errNone",               0);
	define("errIncorect",           1);
	define("errDBExists",           2);
	define("errDBError",            3);
	
	/*
	*	class cExchangeManager
	*	-import/export dat zo servera na prihlasovanie na skusky
	*	-struktura xml suborov je definovana v *.dtd suboroch v adresari ./dtd/
	*
	*	public metody:
	*		ImportUsers() 	- import uzivatelov
	*		ImportCourses() - import predmetov spolu so studentmi zapisanymi na tento predmet + ucitelmi
	*/
	class cExchangeManager
	{
		private $xmlParser;
		private $db;
		
		function __construct( $db )
		{
			$this->db = $db;
		}
		
		private function GetContent( $actionParams )
		{
      $content = "";      
      $host = gethostbyaddr($_SERVER['REMOTE_ADDR']);
      
      $_SESSION["sessKey"] = $this->GenerateSessionKey();
      
		  $query = "SELECT * FROM tImportData";
		  $result = $this->db->QueryToRow($query);
      if ( $result )
      {
        $url = $result["url"];
        $id = $result["id"];
        $idPrefix = $result["idPrefix"];
        $key = $result["pubKey"];
        $mod = $result["pubMod"];
      }
      else 
        return "";
      
      $this->GetHostUriFromUrl( $url, $host, $service_uri );

      $td = mcrypt_module_open("rijndael-256", "", "ofb", "");
      $ivSize = mcrypt_enc_get_iv_size($td);
      $keySize = mcrypt_enc_get_key_size($td);
      $iv = $this->GenerateSessionKey();
      $_SESSION["sessKey"] = substr( $_SESSION["sessKey"], 0, $keySize );
      
      $cryptedData = base64_encode( AsymEncrypt( $id.$_SESSION["sessKey"], $key, $mod ) );       
      
      $vars = "data=".urlencode($cryptedData)."&".$actionParams."&iv=".urlencode($iv)."&idp=".$idPrefix;
      
      $header = "Host: ".$host."\r\n";
      $header .= "User-Agent: PHP Script\r\n";
      $header .= "Content-Type: application/x-www-form-urlencoded\r\n";
      $header .= "Content-Length: ".strlen($vars)."\r\n";
      $header .= "Connection: close\r\n\r\n";

      $fp = fsockopen($host, 80, $errno, $errstr);
      $file = fopen(FPATH."tempdata/temp.dat","w");
      if ($fp && $file)
      {
        fputs($fp, "POST ".$service_uri." HTTP/1.1\r\n");
        fputs($fp, $header.$vars);
        fwrite($fp, $out);
        while (!feof($fp)) 
        {
          $result = fgets($fp);
          fwrite($file, $result);      
        }
        fclose($file);
        fclose($fp);      	

        $bChunked = false;
        $bChunkSize = 0;
        $bActualChunkSize = 0;
        $file = fopen(FPATH."tempdata/temp.dat","r");
        do 
        {
          $result = fgets($file);
          if ( strstr( $result, "chunked" ) )
            $bChunked = true;
        } while ( $file && !feof($file) && ord($result[0]) != 10 && ord($result[1]) != 10 );  
        do
        {          
          $temp = fgets($file);
          if ( $bChunked )
          {
            if ( $bChunkSize == 0 )
            {
              $bChunkSize = hexdec($temp);
              if ( $bChunkSize == 0 )
                break;
            }
            else
            {
              $bActualChunkSize += strlen($temp);
              $content .= $temp;
              if ( $bActualChunkSize == $bChunkSize )
              {
                $bActualChunkSize = 0;
                $bChunkSize = 0;
              }
            }
          }
          else
          {
            $content .= $temp;
          }
        } while ( !feof($file) );
        fclose($file);
        
        if ( !empty($content) && mcrypt_generic_init($td, $_SESSION["sessKey"], $iv) != -1 )        
        {
           $content = mdecrypt_generic($td, $content );
           $lastItemPos = strrpos( $content, '>' );
           $content = substr( $content, 0, $lastItemPos+1 );
           mcrypt_generic_deinit($td);
        }
        else 
          $cotnent = "";

        mcrypt_module_close($td);        
        return $content;
      }
		}		
		
		private function ParseContent($actionParams, &$values, &$indexes, &$error)
		{
			$error = "";		
			$this->xmlParser = xml_parser_create('UTF-8');
    	xml_parser_set_option( $this->xmlParser, XML_OPTION_CASE_FOLDING, 0 );
			xml_parser_set_option( $this->xmlParser, XML_OPTION_SKIP_WHITE, 1 );
      
      $content = $this->GetContent($actionParams);
      if ( empty( $content ) )
      {
        $error = "Import service unavailable!";
        return false;
      }
      
			if ( $this->ValidateXml($content, $error) )
			{
				xml_parse_into_struct( $this->xmlParser, $content, $values, $indexes );
			}
			else
			{
  			xml_parser_free( $this->xmlParser );
				return false;
			}				
			xml_parser_free( $this->xmlParser );
			return true;
		}
		
		/*
		*	overi, ci je xml subor validny (schema, dtd)
		*	ak ano, vrati true, inak false a v $message bude chybova hlaska
		*/
		private static function ValidateXml($xml, &$message)
		{
      libxml_use_internal_errors(true);

      $doc = new DOMDocument('1.0', 'utf-8');
      $doc->loadXML($xml);
      
      $errors = libxml_get_errors();
      if (empty($errors))
      {	
    		if ( !$doc->validate() )
    		{
    			$message = "Invalid xml file<br />";
    			return false;
    		}
   			return true;
      }

      $error = $errors[ 0 ];
      if ($error->level < 3)
      {
        return true;
      }

      $lines = explode("r", $xml);
      $line = $lines[($error->line)-1];

      $message = $error->message.' at line '.$error->line.':<br />'.htmlentities($line);

      return false;
		}
		
    function ImportUsers(&$importResult)
		{
			$importResult["valid"] = array();
			$importResult["invalid"] = array();
			
			if ( $this->ParseContent("action=users", $values, $indexes, $error) )
			{	
			  $teachers = array();
			  $specs = array();
				  
				foreach ( $indexes as $key=>$indexArray)
				{				  
	        if ($key == "teacher")
	        {
	          for ( $i = 0; $i < count($indexArray); $i++ )
	          {
	            if ( $values[$indexArray[$i]]["type"] == "close" )
	              continue;
	              
        			$offset = $indexArray[$i] + 1;
       				$len = $indexArray[$i + 1] - $offset;
       				$error = $this->ParseTeacher(array_slice($values, $offset, $len), $result);
       				if ( $error )
       				  return $error;
       				
              $teachers[$result["type"]][count($teachers[$result["type"]])] = $result;
				      $result = array();
	          }
          }
          else if ($key == "specialization")
          {
            for ( $i = 0; $i < count($indexArray); $i++ )
            {
              if ( $values[$indexArray[$i]]["type"] == "close" )
                continue;
  	              
        			$offset = $indexArray[$i] + 1;
       				$len = $indexArray[$i + 1] - $offset;
       				$studentsOffset = $indexes["students"][$i] + 1;
       				$studentsLen = $indexes["students"][$i+1] - $studentsOffset;
       				$students = array_slice($values, $studentsOffset, $studentsLen);
       				$error = $this->ParseSpecialization(array_slice($values, $offset, $len), $students, $result);
       				if ( $error )
       				  return $error;
       			
              $specs[count($specs)] = $result;              
				      $result = array();
            }
          }          
        }
			  return "";
			}
			else
				return $error;
		}
		
		function ImportCourses(&$importResult)
		{
      if ( $this->ParseContent("action=courses", $values, $indexes, $error) )
      {
        foreach ( $indexes as $key=>$indexArray)
        {
          if ($key == "course")
          {
            for ( $i = 0; $i < count($indexArray); $i += 2 )   		
            {
            	$offset = $indexArray[$i] + 1;
            	$len = $indexArray[$i + 1] - $offset;
            	$error = $this->ParseCourse(array_slice($values, $offset, $len), $importResult[$i]);
            	if ( $error )
            		return $error;
            }
          }
        }
        return "";
      }
      return $error;
		}
		
		function ImportExams( $teacherID )
		{
		  if ( $this->ParseContent("action=exams&param1=".$teacherID, $values, $indexes, $error) )
		  {
        foreach ( $indexes as $key=>$indexArray)
        {
          if ($key == "exam")
          {
            for ( $i = 0; $i < count($indexArray); $i += 2 )   		
            {
            	$offset = $indexArray[$i] + 1;
            	$len = $indexArray[$i + 1] - $offset;
            	$error = $this->ParseExam(array_slice($values, $offset, $len));
            	if ( $error )
            		return $error;
            }
          }
        }
        return "";
		  }
		  return $error;
		}
		
		private function ParseTeacher($values, &$result)
		{
	    for ($i=0; $i < count($values); $i++)
	    {
	        $result[$values[$i]["tag"]] = $values[$i]["value"];
	    }
	    
      if ( empty($result["ISIC"]) || strlen($result["ISIC"]) != 10 || 
           empty($result["name"]) || empty($result["surname"]) )
      {
        $result["type"] = errIncorect;
        return "";
      }
      
      $teacher = new cTeacher(0, $this->db);   
      if ( !$teacher->LoadByISIC($result["ISIC"], $error) && empty($error) )
      {        
        if ( !$teacher->DBCreate($result, $error) )
        {          
          $result["type"] = errDBError;
      	  return error;
      	}
      	$result["type"] = errNone;
      }
      else if ( empty($error) )
      {
        if ( !$teacher->DBEdit($result, $error) )
        {          
          $result["type"] = errDBError;
      	  return $error;
      	}
        
        $result["type"] = errDBExists;
      }	    
		  return $error;
		}
		
		private function ParseSpecialization($values, $students, &$result)
		{
		  $result["name"] = $values[0]["value"];
		  $garantISIC = $values[1]["value"];
		  $garantID = 0;
		  
		  $garant = new cTeacher(0, $this->db);
		  $spec = new cSpecialization(0, $this->db);
		  
		  if ( $garant->LoadByISIC( $garantISIC, $error ) )
		  {
		    $garantID = $garant->GetID();
		    $result["garant"] = $garant->GetProperties();
		  }
		  else if ( empty($error) )
		  {		    
		    $result["garant"] = NULL;
		  }
		  else
		  {
		    return $error;
		  }
      
      $dbSpecValues["name"] = $result["name"];
      $dbSpecValues["id_garant"] = $garantID;
      
      if ( $spec->LoadByName( $result["name"], $error ) )
      {
        if ( !$spec->DBEdit( $dbSpecValues, $error ) )
          return $error;
        $result["type"] = errDBExists;
      }
      else if ( empty( $error ) )
      {
        if ( !$spec->DBCreate( $dbSpecValues, $error ) )
          return $error; 
        $result["type"] = errNone;
      }
      else 
      {
        return $error;
      }
      
      $studentValues = array(); 
      $year = 0;
      for ( $i = 0; $i < count($students); $i++ )
      {
        if ( $students[$i]["tag"] == "student" && $students[$i]["type"] == "open" )
        {
          $studentValues = array();
        }
        else if ( $students[$i]["tag"] == "student" && $students[$i]["type"] == "close" )
        {
          $student = new cStudent(0, $this->db);
          if ( $student->LoadByISIC($studentValues["ISIC"], $error) )
          {
           if( !$student->DBEdit( $studentValues, $spec->GetID(), $year, $error ) )
              return $error;
            $result["students"][errDBExists][count($result["students"][errDBExists])] = $student->GetProperties();
          }
          else
          {
            if ( !$student->DBCreate( $studentValues, $spec->GetID(), $year, $error ) )
              return $error;
            $result["students"][errNone][count($result["students"][errNone])] = $student->GetProperties();
          }
        }
        else if ( $students[$i]["tag"] == "id" )
        {
          $studentValues["ISIC"] = $students[$i]["value"];
        }
        else if ( $students[$i]["tag"] == "year" )
        {
          $year = $students[$i]["value"];
        }
        else
        {
          $studentValues[$students[$i]["tag"]] = $students[$i]["value"];
        }
      }
      		  
		  return "";
		}

    private function ParseUser($values, $type, &$importResult)
    {
      $userValues = array();
      
	    for ($i=0; $i < count($values); $i++)
	    {
	        $userValues[$values[$i]["tag"]] = $values[$i]["value"];
	    }
  		$userValues["type"] = $type;
  		
  		$user = $this->AddUser($userValues, $bUserExists, $error);
  		if ( $user && !$bUserExists )
  		  $importResult["valid"][count($importResult["valid"])] = $user->GetProperties();
  		else if ( $user )
  		  $importResult["existing"][count($importResult["existing"])] = $user->GetProperties();
  		else
  		  $importResult["invalid"][count($importResult["invalid"])] = $userValues;
  		return $error;
    }
    	
		/*
		*  $userValues - associative array (namePrefix, name, surname, nameSuffix, ISIC)
		*/
		function AddUser($userValues, &$bUserExists, &$error)
		{	 
      if ( empty($userValues["ISIC"]) )
        return NULL;   
      $query = "SELECT * FROM tUser WHERE ISIC=".$userValues["ISIC"].";";
      $result = $this->db->QueryToRow($query);
      
      if ($result == 0 && $this->db->IsError() )
      {
      	$error = $this->db->GetError();
      	return NULL;
      }
      
      if ( strlen($userValues["ISIC"]) == 10 && 
    	    !empty($userValues["name"]) && !empty($userValues["surname"]))
    	{
    	  $user = NULL;
        $id = $result == 0 ? 0 : $result["id"];

        if ( $id > 0 && $this->GetUserType( $id, $error ) != $userValues["type"] )
          return NULL;
        
      	if ( $userValues["type"] == "student" )
      		$user = new cStudent($id, $this->db);
      	else if ( $userValues["type"] == "teacher" )
      		$user = new cTeacher($id, $this->db);
      	else if ( !empty( $error ) )
      	 return NULL;
        	
        if ( $id == 0 )
          if ( !$user->DBCreate($userValues,$error) )
        	  return NULL;

        $bUserExists = $id > 0;
        return $user;
      }
      return NULL;
		}
		
		private function ParseCourse($courseValues, &$importResult)
		{
			$course = NULL;
			$courseData = array();
			$students = array();
			$studentCount = 0;
			$teacher = NULL;
			$idType = "teacher";

      $importResult["invalidUsers"] = array();
			
			//pozn. v nasledujucom cykle sa vyuziva znalost toho, ze xml pre course je validne
			//preto aj obsah $courseValues ma pevne danu strukturu
			for ( $i = 0; $i < count($courseValues); $i++ )
			{
				if ( $courseValues[$i]["tag"] == "label" )
				{
				  $courseData["course"]["name"] = $courseValues[$i]["value"];
					$importResult["name"] = $courseValues[$i]["value"];
				}
				else if ( $courseValues[$i]["tag"] == "code" )
				{
					$courseData["course"]["code"] = $courseValues[$i]["value"];
				}
				else if ( $courseValues[$i]["tag"] == "description" )
				{
					$courseData["course"]["description"] = $courseValues[$i]["value"];
				}
				else if ( $courseValues[$i]["tag"] == "students" )
				{
				  $idType = "student";
				}
				else if ( $courseValues[$i]["tag"] == "id" && !empty($courseValues[$i]["value"]) )
				{
				  if ( $idType == "student" )
          { 
				    $student = new cStudent(0, $this->db);
  				  if ( $student->LoadByISIC($courseValues[$i]["value"], $error) )
		  		    $students[$studentCount++] = $student;
		  		  else if ( empty( $error ) )
		  		    $importResult["invalidUsers"][count($importResult["invalidUsers"])] = $courseValues[$i]["value"];
				  }
				  else
				  {
				    $teacher = new cTeacher(0, $this->db);
				    if ( $teacher->LoadByISIC($courseValues[$i]["value"], $error ) )
		  		    $importResult["invalidTeacher"] = $courseValues[$i]["value"];
		  		  else if ( empty($error) )				      
				      $importResult["teacher"] = $teacher->GetProperties();
				  }
				    
				  if ( !empty( $error ) )
				    return $error;
				}
			}      
      $importResult["invalidUserCount"] = count($importResult["invalidUsers"]);
			
			$query = "SELECT id FROM tCourse WHERE name='".$courseData["course"]["name"]."';";
			$result = $this->db->QueryToRow($query);			
		  if ( $this->db->IsError() )
		  	return $this->db->GetError();
		    
		  //ak predmet este neexistuje v db, pridaj ho s defaultnym popisom ziskanym z xml suboru	
		  if ( $result == 0 )		    
		  {
		    $course = new cCourse(0, $this->db);
		    $importResult["existing"] = false;
		    
		    if ( !$course->DBCreate($courseData, $error) )
		      return $error;
		  }
      else
      {
		    $importResult["existing"] = true;
	    	$course = new cCourse($result["id"], $this->db);
		  }

      if ( !empty( $students ) )
      {        
    	  foreach ( $students as $student )
    	  {
    	    if ( $student->Loaded() )
          {    	      
      	    if ( !$student->HasCourse( $course->GetID(), $error ) )
      	      $student->AddCourse( $course->GetID(), $error );
      	      
      	    if ( !empty($error) )
      	      return $error;
      	  }
    	  }
      }    
      $teacherID = $teacher->Loaded() ? $teacher->GetID() : 0;
      $course->DBChangeTeacher( $teacherID, $error );              		  
		  return $error;
		}
		
    private function ParseExam($examValues)
    {
      $examData;
			$studentCount = 0;
			
	    for ($i=0; $i < count($examValues); $i++)
	    {
				if ( $examValues[$i]["tag"] == "courseName" )
				{				  				  
				  $examData["course"] = new cCourse(0, $this->db);
				  if ( !$examData["course"]->LoadByName($examValues[$i]["value"], $error) )
				    return $error;
				}
				else if ( $examValues[$i]["tag"] == "date" )
				{
					$examData["dateUnix"] = $examValues[$i]["value"];
					$examData["dateFormated"] = date("Y-m-d H:i:s", $examData["dateUnix"]);
				}
				else if ( $examValues[$i]["tag"] == "id" )
				{
  		    $student = new cStudent(0, $this->db);
				    
				  if ( $student->LoadByISIC($examValues[$i]["value"], $error) )
				    $examData["students"][$studentCount++] = $student;
				  else if ( !empty( $error ) )
				    return $error;
				}
	    }
      $examId = 0;	    
      $query = "SELECT * FROM tExam WHERE date='".$examData["dateFormated"]."' AND id_course=".$examData["course"]->GetID().";";
      $result = $this->db->QueryToRow($query);
      if ( $this->db->IsError() )
        return $this->db->GetError();
        
      if ( $result )
      {
        $examId = $result["id"];
      }
      else
      {
        $query = "INSERT INTO tExam VALUES (0,".$examData["course"]->GetID().",'".$examData["dateFormated"]."','scheduled', '');";
        if ( !$this->db->Query($query) )
          return $this->db->GetError();
        
        $examId = $this->db->InsertId();
      }
      
      for ( $i = 0; $i < $studentCount; $i++ )
      {
        $query = "SELECT * FROM tExamToUser WHERE id_exam=".$examId." AND id_user=".$examData["students"][$i]->GetID().";";
        $result = $this->db->QueryToRow($query);
        
        if ( $this->db->IsError() )
          return $this->db->GetError();
          
        if ( !$result )
        {
          $query = "INSERT INTO tExamToUser VALUES (".$examId.",".$examData["students"][$i]->GetID().");";
          if ( !$this->db->Query($query) )
            return $this->db->GetError();
        }         
      }      
  		return $error;
    }
		
		
    private function GetUserType($id, &$error)
    {
      $query = "SELECT * FROM tStudents WHERE id=".$id.";";
      if ( $this->db->QueryToRow($query) != 0 )
        return "student";
        
      if ( $this->db->IsError() )
      {
        $error = $this->db->GetError();
        return "";
      }

      $query = "SELECT * FROM tTeachers WHERE id=".$id.";";
      if ( $this->db->QueryToRow($query) != 0 )
        return "teacher";
      
      $error = $this->db->GetError();
      return "";
    }
    
    private function GenerateSessionKey()
		{
      $values = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
                 			'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
                 			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
                   		'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
                   		'1', '2', '3', '4', '5', '6', '7', '8', '9', '0');
          
      for($i = 0; $i < 32; $i++)
      {
        $r = rand(0, count($values)-1);
        $key[$i] = $values[$r];
      }      
      return join("", $key);
		}
		
		private function GetHostUriFromUrl( $url, &$host, &$uri )
		{
		  $posHost = 0;
		  $posUri = strpos( $url, "/" );
		  if ( $url[$posUri+1] == "/" )
		  {
		    $posHost = $posUri+2;
		    $posUri = strpos( $url, "/", $posUri+2 );
		  }
		    
		  $host = substr( $url, $posHost, $posUri-$posHost );
		  $uri = substr( $url, $posUri, strlen($url) - $posUri );
    }
  }
?>
