root,'',$server['REQUEST_URI']);
		$infos = explode('/',$target);
		if(isset($infos[0]))$user = $infos[0];
		if(isset($infos[1])) $calendar = $infos[1];
		$logFunction  = $this->log;
		if(isset($logFunction)) $logFunction('notice','receive : '.$method.', body : '.PHP_EOL.$body.PHP_EOL.PHP_EOL.' --- target : '.$server['REQUEST_URI'].PHP_EOL.PHP_EOL);
		
		switch($method) {
			//Le client veux la liste des évenements de $calendar pour le user $user au format ical
			case 'REPORT':
			$stream = '';
			$callback = $this->searchEvents;
			$events = $callback($user,$calendar);
			if(isset($logFunction)) $logFunction('notice','fetch '.count($events).' events, parsing to xml ...'.PHP_EOL.PHP_EOL);
			foreach($events as $event){
				$stream .= '
				'.$this->root.$user.'/'.$calendar.'/'.$event['id'].'.ics
				
				
				';
				$stream .= IcalEvent::toString($event);
				$stream .='
				'.$event['updated'].'
				
				HTTP/1.1 200 OK
				
				';
			}
			$stream .='';
			if(isset($logFunction)) $logFunction('notice','return stream : '.$stream.' '.PHP_EOL.PHP_EOL,true);
			header('HTTP/1.1 207 Multi-Status');
			echo $stream;
			exit();
			break;
			case 'PROPFIND':
		//Le client souhaite avoir le ctag (timestamp de derniere mise a jour du calendrier ciblé), si ce ctag est égal au dernier ctag fournis, le client ne met pas a jour les events
		if(strpos($body, 'getctag')!==false) {
			$callback = $this->calendarLastUpdate;
			$lastUpdate = $callback($user,$calendar);
			$stream = '
			
				
					'.$this->root.'
					
						
							
								'.$this->root.$user.'
							
							
								'.$this->root.$user.'
							
							
								
								
							
							'.$lastUpdate.'
							
								
								
							
							
								
									
										
									
								
								
									
										
									
								
								
									
										
									
								
								
									
										
									
								
								
									
										
									
								
								
									
										
									
								
							
						
						HTTP/1.1 200 OK
					
				
			';
		}
		//pour le calenderier ciblé, le client souhaite voir tous les etag (tag de derniere maj d'un évenement) pour voir lesquels il doit demander au serveur
		if(strpos($body, 'getetag')!==false) {
	
				$f = $this->searchEvents;
				$events = $f($user,$calendar);
				$stream = '
				
				
				'.$this->root.$user.'/'.$calendar.'/
				
				
				
				
				
				
				
				HTTP/1.1 200 OK
				
				
				
				
				
				
				HTTP/1.1 404 Not Found
				
				';
				foreach($events as $event){
					$stream .= '
					'.$this->root.$user.'/'.$calendar.'/'.$event['id'].'.ics
					
					
					text/calendar; charset=utf-8
					
					'.$event['updated'].'
					
					HTTP/1.1 200 OK
					
					';
				}
				$stream .= '';
			}
			if(isset($logFunction)) $logFunction('notice','return stream : '.$stream.' '.PHP_EOL.PHP_EOL,true);
			header('HTTP/1.1 207 Multi-Status');
			echo $stream;
			exit();
			break;
      //Récuperation d'un fichier (ou dossier ?)
			case 'GET':
			header('HTTP/1.1 200 Ok');
			break;
      //Envois d'un evenement
			case 'PUT':
			header('HTTP/1.1 201 Created');
			$path = $_SERVER['REQUEST_URI'];
			$pathinfos = explode('/',$path);
			preg_match('|([0-9]*)\.ics|i',$server['REQUEST_URI'],$m);
			
			$callback = $this->saveEvent;
			$events = $callback($user,$calendar,$m[1],IcalEvent::fromString($body));
			if(isset($logFunction)) $logFunction('notice','event saved '.$user.'/'.$calendar.', id: '.$m[1].PHP_EOL.PHP_EOL);
			break;
      //Supression dossier/fichier
			case 'DELETE':
        //header('HTTP/1.1 501 Method not implemented');
			header('HTTP/1.1 200 Ok');
			$callback = $this->deleteEvent;
			preg_match('|([0-9]*)\.ics|i',$server['REQUEST_URI'],$m);
			try{
				if(!isset($m[1])) throw new Exception("Event id non récuperable: ".$server['REQUEST_URI']);
				
				$callback($user,$calendar,$m[1]);
				if(isset($logFunction)) $logFunction('notice','event deleted '.$user.'/'.$calendar.', id: '.$m[1].PHP_EOL.PHP_EOL);
				header('HTTP/1.1 200 Ok');
			}catch(Exeption $e){
				if(isset($logFunction)) $logFunction('error','unable to delete event: '.$e->getMessage().' '.PHP_EOL.PHP_EOL);
				header('HTTP/1.1 403 Forbidden');
			}
      /*
       200 => 'Ok',
        201 => 'Created',
        204 => 'No Content',
        207 => 'Multi-Status',
        403 => 'Forbidden',
        404 => 'Not Found',
        409 => 'Conflict',
        415 => 'Unsupported Media Type',
        500 => 'Internal Server Error',
        501 => 'Method not implemented',
     );
     return 'HTTP/1.1 ' . $code . ' ' . $msg[$code];
      */
     break;
      //Déplacement/renommage dossier/fichier
     case 'MOVE':
        //header('HTTP/1.1 501 Method not implemented');
     header('HTTP/1.1 200 Ok');
     break;
      //The OPTIONS method allows an http client to find out what HTTP methods are supported on a specific url.
     case 'OPTIONS':
     header('Allows: options get head post delete trace propfind proppatch copy mkcol put');
        //header('Allows: options get post delete trace propfind proppatch copy mkcol put');
     break;
     case 'HEAD':
     header('HTTP/1.1 200 Ok');
        //header('HTTP/1.1 501 Method not implemented');
     break;
     case 'POST':
     header('HTTP/1.1 501 Method not implemented');
     break;
     case 'TRACE':
     header('HTTP/1.1 501 Method not implemented');
     break;
      //Updates properties of a resource or collection.
     case 'PROPPATCH':
     header('HTTP/1.1 501 Method not implemented');
     break;
      //Copie d'un élement vers un nouvel emplacement
     case 'COPY':
        //header('HTTP/1.1 501 Method not implemented');
     header('HTTP/1.1 200 Ok');
     break;
      //Verouillage d'un élement
     case 'LOCK':
     header('HTTP/1.1 501 Method not implemented');
     break;
      //Déverouillage d'un élement
     case 'UNLOCK':
     header('HTTP/1.1 501 Method not implemented');
     break;
 }
}
}
class IcalEvent{
	public $title,$description,$start,$end,$frequency,$location,$categories,$alarms,$ics,$uid;
	
	public static function fromString($ical){
		$event = new self();
		$lines = array();
		foreach(explode("\n",$ical) as $line):
			$columns = explode(":",trim($line));
			if(!isset($columns[1])) continue;
			$key = $columns[0];
			$value = $columns[1];
			
			$keyvalues = explode(';',$key);
			$key = array_shift($keyvalues);
			//Ne prendre que la premiere description
			if($key=='DESCRIPTION' && isset($lines['DESCRIPTION'])) continue;
			$lines[$key] = $value;
		endforeach;
		
		if(isset($lines['CATEGORIES']))  $event->categories = $lines['CATEGORIES'];
		if(isset($lines['SUMMARY']))  $event->title = $lines['SUMMARY'];
		if(isset($lines['DESCRIPTION']))  $event->description = $lines['DESCRIPTION'];
		if(isset($lines['DTSTART']))  	  $event->start = strtotime($lines['DTSTART']);
		if(isset($lines['DTEND']))  $event->end = strtotime($lines['DTEND']);
		if(isset($lines['RRULE']))  $event->frequency = $lines['RRULE'];
		if(isset($lines['LOCATION']))  $event->location = $lines['LOCATION'];
		if(isset($lines['UID']))  $event->uid = $lines['UID'];
		if(isset($lines['TRIGGER'])) $event->alarms = $lines['TRIGGER'];
		return $event;
	}
	public static function toString($event){
		$ics = 'BEGIN:VCALENDAR'."\n";
		$ics .= 'PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN'."\n";
		$ics .= 'VERSION:2.0'."\n";
		$ics .= 'BEGIN:VTIMEZONE'."\n";
		$ics .= 'TZID:Europe/Paris'."\n";
		$ics .= 'BEGIN:DAYLIGHT'."\n";
		$ics .= 'TZOFFSETFROM:+0100'."\n";
		$ics .= 'TZOFFSETTO:+0200'."\n";
		$ics .= 'TZNAME:CEST'."\n";
		$ics .= 'END:DAYLIGHT'."\n";
		$ics .= 'BEGIN:STANDARD'."\n";
		$ics .= 'TZOFFSETFROM:+0200'."\n";
		$ics .= 'TZOFFSETTO:+0100'."\n";
		$ics .= 'TZNAME:CET'."\n";
		$ics .= 'DTSTART:19701025T030000'."\n";
		$ics .= 'END:STANDARD'."\n";
		$ics .= 'END:VTIMEZONE'."\n";
		$ics .= 'BEGIN:VEVENT'."\n";
		$ics .= 'DTSTART:19700329T020000'."\n";
		if(isset($event['recurrence']) && $event['recurrence']!='')
			$ics .= 'RRULE:'.$event['recurrence']."\n";
		$ics .= 'CREATED:'.date('Ymd',$event['created']).'T'.date('His',$event['created']).'Z'."\n";
		$ics .= 'LAST-MODIFIED:'.date('Ymd',$event['updated']).'T'.date('His',$event['updated']).'Z'."\n";
		$ics .= 'DTSTAMP:20181209T222306Z'."\n";
		$ics .= 'UID:'.$event['id']."\n";
		if(!empty($event['label']))
		$ics .= 'SUMMARY:'.$event['label']."\n";
		if(!empty($event['type']))
		$ics .= 'CATEGORIES:'.$event['type']."\n";
		if(!empty($event['description']))
			$ics .= 'DESCRIPTION:'.str_replace(array(PHP_EOL,"\r","\n"),'\n',$event['description'])." \n";
		if($event['street'].$event['zip'].$event['city']!='')
			$ics .= 'LOCATION:'.$event['street'].' '.$event['zip'].' '.$event['city']."\n";
		$ics .= 'DTSTART;TZID=Europe/Paris:'.date('Ymd',$event['startDate']).'T'.date('His',$event['startDate'])."\n";
		$ics .= 'DTEND;TZID=Europe/Paris:'.date('Ymd',$event['endDate']).'T'.date('His',$event['endDate'])."\n";
		$ics .= 'TRANSP:OPAQUE'."\n"; //TRANSP : Définit si la ressource affectée à l'événement est rendu indisponible (OPAQUE, TRANSPARENT)
		if($event['notificationNumber']!='0'){
			if($event['notificationUnity'] == 'minut') $notification= 'PT'.$event['notificationNumber'].'M';
			if($event['notificationUnity'] == 'hour') $notification = 'PT'.$event['notificationNumber'].'H';
			if($event['notificationUnity'] == 'day') $notification = 'P'.$event['notificationNumber'].'D';
			$ics .= 'BEGIN:VALARM'."\n";
			$ics .= 'ACTION:DISPLAY'."\n";
			$ics .= 'TRIGGER;VALUE=DURATION:-'.$notification.''."\n";
			$ics .= 'DESCRIPTION:Rappel erp'."\n";
			$ics .= 'END:VALARM'."\n";
		}
		$ics .= 'STATUS:CONFIRMED'."\n"; //STATUS : Statut de l'événement (TENTATIVE, CONFIRMED, CANCELLED)
		
		$ics .= 'END:VEVENT'."\n";
		$ics .= 'END:VCALENDAR'."\n";
		return $ics;
	}
}
?>