| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 | 
							- <?php
 
- /**
 
-  * Classe de gestion du serveur Caldav
 
-  * Les méthodes suivantes sont utilisables comme des callbacks a définir avant le start();
 
-  * calendarLastUpdate(user,calendar) : doit retourner le timestamp de la derniere modification sur le calendrier ciblé (le client s'en sert pour la gestion des performances)
 
-  * searchEvents(user,calendar) : doit retourner un tableau d'évenements sur le calendrier ciblé
 
-  * saveEvent(user,calendar,event,infos) : doit effectuer une sauvegarde de l'évenement ciblé
 
-  * deleteEvent(user,calendar,event) : doit effectuer une supression de l'évenement ciblé
 
-  * @author valentin carruesco
 
-  * @category Planning
 
-  * @license cc by nc sa
 
-  */
 
- class CalDavServer{
 
- 	public $root,$searchEvents,$deleteEvent,$saveEvent,$calendarLastUpdate,$log;
 
- 	public function start(){
 
- 		
 
- 		$server = $_SERVER;
 
- 		$request = $_REQUEST;
 
- 		$method = strtoupper($server['REQUEST_METHOD']);
 
- 		$body = stream_get_contents(fopen('php://input','r+'));
 
- 		$user = 'default';
 
- 		$calendar = 'default';
 
- 		
 
- 		$target = str_replace($this->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 = '<?xml version="1.0" encoding="utf-8"?><d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/">';
 
- 			$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 .= '<d:response>
 
- 				<d:href>'.$this->root.$user.'/'.$calendar.'/'.$event['id'].'.ics</d:href>
 
- 				<d:propstat>
 
- 				<d:prop>
 
- 				<cal:calendar-data>';
 
- 				$stream .= IcalEvent::toString($event);
 
- 				$stream .='</cal:calendar-data>
 
- 				<d:getetag>'.$event['updated'].'</d:getetag>
 
- 				</d:prop>
 
- 				<d:status>HTTP/1.1 200 OK</d:status>
 
- 				</d:propstat>
 
- 				</d:response>';
 
- 			}
 
- 			$stream .='</d:multistatus>';
 
- 			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 = '<?xml version="1.0" encoding="utf-8"?>
 
- 			<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/">
 
- 				<d:response>
 
- 					<d:href>'.$this->root.'</d:href>
 
- 					<d:propstat>
 
- 						<d:prop>
 
- 							<d:current-user-principal>
 
- 								<d:href>'.$this->root.$user.'</d:href>
 
- 							</d:current-user-principal>
 
- 							<d:owner>
 
- 								<d:href>'.$this->root.$user.'</d:href>
 
- 							</d:owner>
 
- 							<cal:supported-calendar-component-set>
 
- 								<cal:comp name="VEVENT"/>
 
- 								<cal:comp name="VTODO"/>
 
- 							</cal:supported-calendar-component-set>
 
- 							<cs:getctag>'.$lastUpdate.'</cs:getctag>
 
- 							<d:resourcetype>
 
- 								<d:collection/>
 
- 								<cal:calendar/>
 
- 							</d:resourcetype>
 
- 							<d:supported-report-set>
 
- 								<d:supported-report>
 
- 									<d:report>
 
- 										<d:expand-property/>
 
- 									</d:report>
 
- 								</d:supported-report>
 
- 								<d:supported-report>
 
- 									<d:report>
 
- 										<d:principal-property-search/>
 
- 									</d:report>
 
- 								</d:supported-report>
 
- 								<d:supported-report>
 
- 									<d:report>
 
- 										<d:principal-search-property-set/>
 
- 									</d:report>
 
- 								</d:supported-report>
 
- 								<d:supported-report>
 
- 									<d:report>
 
- 										<cal:calendar-multiget/>
 
- 									</d:report>
 
- 								</d:supported-report>
 
- 								<d:supported-report>
 
- 									<d:report>
 
- 										<cal:calendar-query/>
 
- 									</d:report>
 
- 								</d:supported-report>
 
- 								<d:supported-report>
 
- 									<d:report>
 
- 										<cal:free-busy-query/>
 
- 									</d:report>
 
- 								</d:supported-report>
 
- 							</d:supported-report-set>
 
- 						</d:prop>
 
- 						<d:status>HTTP/1.1 200 OK</d:status>
 
- 					</d:propstat>
 
- 				</d:response>
 
- 			</d:multistatus>';
 
- 		}
 
- 		//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 = '<?xml version="1.0" encoding="utf-8"?>
 
- 				<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/">
 
- 				<d:response>
 
- 				<d:href>'.$this->root.$user.'/'.$calendar.'/</d:href>
 
- 				<d:propstat>
 
- 				<d:prop>
 
- 				<d:resourcetype>
 
- 				<d:collection/>
 
- 				<cal:calendar/>
 
- 				</d:resourcetype>
 
- 				</d:prop>
 
- 				<d:status>HTTP/1.1 200 OK</d:status>
 
- 				</d:propstat>
 
- 				<d:propstat>
 
- 				<d:prop>
 
- 				<d:getcontenttype/>
 
- 				<d:getetag/>
 
- 				</d:prop>
 
- 				<d:status>HTTP/1.1 404 Not Found</d:status>
 
- 				</d:propstat>
 
- 				</d:response>';
 
- 				foreach($events as $event){
 
- 					$stream .= '<d:response>
 
- 					<d:href>'.$this->root.$user.'/'.$calendar.'/'.$event['id'].'.ics</d:href>
 
- 					<d:propstat>
 
- 					<d:prop>
 
- 					<d:getcontenttype>text/calendar; charset=utf-8</d:getcontenttype>
 
- 					<d:resourcetype/>
 
- 					<d:getetag>'.$event['updated'].'</d:getetag>
 
- 					</d:prop>
 
- 					<d:status>HTTP/1.1 200 OK</d:status>
 
- 					</d:propstat>
 
- 					</d:response>';
 
- 				}
 
- 				$stream .= '</d:multistatus>';
 
- 			}
 
- 			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;
 
- 	}
 
- }
 
- ?>
 
 
  |