Browse Source

- Dav : start

idleman 4 years ago
parent
commit
fc1fd9873c

+ 676 - 0
plugin/hackpoint/WebDav.class.php

@@ -0,0 +1,676 @@
+<?php
+
+class WebDav{
+	public $root,$folder,$logs,$version = 1,$user,$lockfile,$ignoreFiles = array();
+	public $on = array(
+		'login' => null,
+		'log' => null,
+		'edit' => null,
+		'move' => null,
+		'delete' => null,
+		'list' => null,
+		'properties' => null,
+		'copy' => null
+	);
+	public $do = array(
+		'edit' => null,
+		'move' => null,
+		'delete' => null,
+		'list' => null,
+		'copy' => null
+	);
+
+	public static function logPath() {
+		return __DIR__.SLASH.'logs';
+	}
+
+	function start(){
+
+		$server = $_SERVER;
+		$method = strtoupper($server['REQUEST_METHOD']);
+		$headers = getallheaders();
+		$body = file_get_contents("php://input");
+
+		$requestUri = explode('/',$server['REQUEST_URI']);
+		$requestUri = array_filter($requestUri);
+		$requestUri = '/'.implode('/',$requestUri);
+		$server['REQUEST_URI'] =  $requestUri;
+
+		$log = PHP_EOL.PHP_EOL.'---------- '.$method.' --------------';
+		$log .= PHP_EOL.'Fichier : '.$requestUri;
+		foreach($headers as $key => $header){
+			$log .=  PHP_EOL."\t - ".$key.' : '.$header;
+		}
+		$log .= PHP_EOL."\t === ".'Requête : '.PHP_EOL. $body;
+
+		$this->logit($log);
+
+	
+		
+
+		if(!file_exists(self::logPath()))
+			mkdir(self::logPath(),0755,true);
+
+		if($method!='OPTIONS' && isset($this->do['login']) ){
+			try{
+				$function = $this->do['login'];
+
+				$user = isset($server['PHP_AUTH_USER']) ? $server['PHP_AUTH_USER']: '';
+				$password = isset($server['PHP_AUTH_PW']) ? $server['PHP_AUTH_PW']: '';
+
+				$header = apache_request_headers(); 
+			
+				if(isset($header['Authorization']) && $header['Authorization']!=''){
+					$authorization = explode(' ',$header['Authorization']);
+					if(count($authorization)==2){
+						$credentials = explode(':',base64_decode($authorization[1]));
+						if(isset($credentials[0]))  $user = $credentials[0];
+						if(isset($credentials[1]))  $password = $credentials[1];
+					}
+				}
+
+				$user = $user =='' ? 'anonymous': $user;
+				$password = $password =='' ? 'anonymous': $password;
+
+				$this->user = $function($user,$password);
+				
+			}catch(Exception $e){		    
+		    	$this->logit('REQUEST URI : '.$requestUri.PHP_EOL.'REQUEST HEADERS : '.json_encode($headers));
+				$this->logit('Error '.self::headerFromCode(401).'  for -'. $user.' :'.$e->getCode().' '.$e->getMessage() );
+				$this->logit(json_encode($_SERVER,JSON_PRETTY_PRINT));
+				$header = apache_request_headers(); 
+				$header = $this->logit(json_encode($header,JSON_PRETTY_PRINT));
+				$header = $this->logit(json_encode($_REQUEST,JSON_PRETTY_PRINT));
+					
+				self::emit_header($e->getCode());
+	    		if($e->getCode()==401)
+					header('WWW-Authenticate: Basic realm="Software Webdav gate"');
+			    
+		    	echo $e->getMessage();
+		    	exit();
+		    }
+		}
+
+		
+
+		try{
+
+	        
+			switch($method) {
+
+				//Returns a list of properties of a file, a directory of a directory and its files.
+				case 'PROPFIND':
+					$log= "\t === ".'Réponse : '.PHP_EOL;
+					try{
+						$depth = isset($headers['Depth']) ? $headers['Depth'] : 0;
+
+						if(in_array(mt_basename($server['REQUEST_URI']), $this->ignoreFiles)){
+							$this->logit('File in blacklist ignored'.PHP_EOL);
+							return self::emit_header(404);
+						}
+
+						$scanned = $this->folder.$this->get_path($server);
+
+						$files = array();
+						
+						if(isset($this->do['list']) ){
+							$function = $this->do['list'];
+							$files = $function($scanned,$depth);
+						}
+						
+						$locks = $this->lock();
+						$xml = '';
+						
+						foreach ($files as $i=>$file) {
+							$url = $requestUri.'/';
+							//Si on est pas sur le premier dossier/fichier d'origine, c'est qu'on est sur un enfant du dossier, on concatene son url au dossier
+							if($i!=0) $url .= rawurlencode($file['label']);
+
+							$xml .= self::xml_file($url,$file,$locks);
+						}
+
+						$stream = '<D:multistatus xmlns:D="DAV:">'.$xml.'</D:multistatus>';
+
+						self::emit_header(207);
+						header('DAV: '.$this->version);
+						header('Content-Type: text/xml');
+						echo $stream;
+
+						$log.= "\t".'DAV: '.$this->version.PHP_EOL;
+						$log.= "\t\t".'Content-Type: text/xml'.PHP_EOL;
+						$log.= "\t\t".$stream.PHP_EOL;
+						$this->logit($log);
+
+					}catch(Exception $e){
+
+						$log.= "\t".$e->getCode().' : '.$e->getMessage().PHP_EOL;
+						$this->logit($log);
+				    	self::emit_header($e->getCode());
+			    	}
+		      	break;
+
+		      	//Récuperation d'un fichier (ou dossier ?)
+		      	case 'GET':
+		      		$log= "\t === ".'Réponse : '.PHP_EOL;
+			      	try{
+				        $file = $this->folder.$this->get_path($server);
+
+				      	$this->logit('Download '.$file);
+						if(!isset($this->do['download']) ) throw new Exception("Not implemented", 501);
+						
+						$function = $this->do['download'];
+						$stream = $function($file);
+						self::emit_header(200);
+						header('Content-Length: '.strlen($stream));
+						echo $stream;
+
+						$log.= "\t".'- 200 OK'.PHP_EOL;
+						$log.= "\t".'- Content-Length: '.strlen($stream);
+						$log.= "\t\t".$stream.PHP_EOL;
+						$this->logit($log);
+			        }catch(Exception $e){
+			        	$log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
+						$this->logit($log);
+				    	self::emit_header($e->getCode());
+				    	header('Content-Type: text/xml');
+				    }
+			    break;
+
+			    //Envoi d'un fichier
+			    case 'PUT':
+			    	$log= "\t === ".'Réponse : '.PHP_EOL;
+			      	try{
+				        $target = $this->folder.$this->get_path($server);
+				        
+
+						$this->logit('PUT '.$target);
+						if(!isset($this->do['edit']) ) throw new Exception("Not implemented", 501);
+						
+						$function = $this->do['edit'];
+						$function($target, $body,'file');
+						self::emit_header(201);
+						
+
+						$log.= "\t".'- 201 Created'.PHP_EOL;
+						$this->logit($log);
+
+				    }catch(Exception $e){
+				   
+				    	$log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
+						$this->logit($log);
+				    	self::emit_header($e->getCode());
+				    }
+			    break;
+
+			    //Suppression dossier/fichier
+			    case 'DELETE':
+			    	$log= "\t === ".'Réponse : '.PHP_EOL;
+			      	try{
+				        $path = $this->folder.$this->get_path($server);
+				        if(!isset($this->do['delete']) ) throw new Exception("Error Processing Request", 501);
+						$function = $this->do['delete'];
+						$function($path);
+						self::emit_header(200);
+
+					}catch(Exception $e){
+				    	$log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
+						$this->logit($log);
+				    	self::emit_header($e->getCode());
+				    }
+
+			    break;
+
+			    //Déplacement/renommage dossier/fichier
+			    case 'MOVE':
+			    	$log= "\t === ".'Réponse : '.PHP_EOL;
+			      	try{
+				      	
+
+				        $target = $this->folder.$this->get_path($server);
+
+				        $toUrl = rawurldecode($server['HTTP_DESTINATION']);
+				        $toUrl = parse_url($toUrl, PHP_URL_PATH);
+
+				        $this->logit('Move to '.$toUrl);
+
+				        $to = $this->folder.str_replace('/',SLASH,utf8_decode(str_replace($this->root,'',$toUrl)));
+				        $to = substr($to, -1,1) == SLASH ? substr($to, 0,strlen($to)-1): $to;
+
+				        $this->logit('Move '.$target.' to '.$to.' (url :'.$toUrl.')');
+				        
+				        if(!isset($this->do['move']) ) throw new Exception("Not implemented", 501);
+				        
+						$function = $this->do['move'];
+						$function($target,$to);
+						self::emit_header(200);
+		
+			        }catch(Exception $e){
+				    	$log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
+						$this->logit($log);
+				    	self::emit_header($e->getCode());
+				    }
+			    break;
+
+		        //Creation de dossier
+		        case 'MKCOL':
+		        	$log= "\t === ".'Réponse : '.PHP_EOL;
+			      	try{
+				      	
+				        $path = $this->folder.$this->get_path($server);
+				        $this->logit('Create folder '.$path);
+
+						if(isset($this->do['edit']) ){
+							$function = $this->do['edit'];
+							$function($path, '','create','directory');
+							self::emit_header(200);
+							break;
+						}
+						self::emit_header(200);
+				        mkdir($path,0755,true);
+			        }catch(Exception $e){
+				    	$log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
+						$this->logit($log);
+				    	self::emit_header($e->getCode());
+				    }
+		    	break;
+
+		    	//The OPTIONS method allows an http client to find out what HTTP methods are supported on a specific url.
+		    	case 'OPTIONS':
+		    		$log= "\t === ".'Réponse : '.PHP_EOL;
+		        //header('Allows: options get head post delete trace propfind proppatch copy mkcol put lock unlock');
+		        	if (ob_get_length()) ob_end_clean();
+		        	header('Allows: options get head delete propfind copy mkcol put lock unlock proppatch');
+		        	$log.= "\t".'- Allows: options get head delete propfind copy mkcol put lock unlock proppatch'.PHP_EOL;
+					$this->logit($log);
+		      	break;
+
+		      	case 'HEAD':
+		      		$log= "\t === ".'Réponse : '.PHP_EOL;
+		      		self::emit_header(200);
+		      		$log.= "\t".'- 200 Ok'.PHP_EOL;
+					$this->logit($log);
+		      	break;
+
+		      	case 'POST':
+		      		$log= "\t === ".'Réponse : '.PHP_EOL;		      	
+		        	self::emit_header(501);
+		        	$log.= "\t".'- 501 Not implemented'.PHP_EOL;
+					$this->logit($log);
+		      	break;
+
+		      	case 'TRACE':
+		      		$log= "\t === ".'Réponse : '.PHP_EOL;		      	
+		        	self::emit_header(501);
+		        	$log.= "\t".'- 501 Not implemented'.PHP_EOL;
+					$this->logit($log);
+		      	break;
+
+		      	//Updates properties of a resource or collection.
+		      	case 'PROPPATCH':
+		      		$log= "\t === ".'Réponse : '.PHP_EOL;
+					$properties = array();
+					$namespaces = array();
+				   	$ns = '';
+				   	preg_match_all('|xmlns:([^\=]*)="([^"]*)"|iUs', $body, $matches,PREG_SET_ORDER);
+				   	foreach ($matches as $match) {
+				   		if(count($match)!=3) continue;
+				   		$namespaces[$match[1]] = $match[2];
+
+				   		$ns.=' xmlns:'.$match[1].' = "'.$match[2].'" ';
+				   	}
+
+				   	preg_match('|<D:prop>(.*)<\/D:prop>|iUs', $body, $matches);
+				   	if(isset($matches[1])){
+					   	preg_match_all('|<([^:]*):([^\>]*)>([^\<]*)<\/\1:\2>|iUs', $matches[1], $matches,PREG_SET_ORDER);
+					   	foreach ($matches as $match) {
+					   		if(count($match)==4)
+					   			$properties[$match[2]] = array('ns'=>$match[1],'value'=>$match[3],'key'=>$match[2]);
+					   	}
+				   	}
+
+				   	$this->logit('Set properties '.json_encode($properties,JSON_PRETTY_PRINT));
+					if(isset($this->on['properties']) ){
+						self::emit_header(200);
+						$function = $this->do['properties'];
+						$function($properties,$namespaces);
+						
+						break;
+					}
+
+				   	$response = '<?xml version="1.0" encoding="utf-8"?>
+					<d:multistatus xmlns:d="DAV:" '.$ns.'>
+					  <d:response>
+					    <d:href>/</d:href>
+					    <d:propstat>
+					      <d:status>HTTP/1.1 200 OK</d:status>
+					      <d:prop>
+					      ';
+					        foreach ($properties as $key => $value) {
+					        	$response .= '<'.$value['ns'].':'.$value['key'].' />';
+					        }
+					$response .= '
+					       
+					      </d:prop>
+					    </d:propstat>
+					  </d:response>
+					</d:multistatus>';
+
+				   	$this->logit('[RESPONSE]'.PHP_EOL.$response);
+			       	self::emit_header(207);
+			       	header('Content-Type: text/xml');
+			       	echo $response;
+			       	$log.= "\t".'- 207 Multi status'.PHP_EOL;
+			       	$log.= "\t".'- Content-Type: text/xml'.PHP_EOL;
+			       	$log.= "\t\t".$response.PHP_EOL;
+					$this->logit($log);
+			    break;
+
+		    	//Copie d'un élement vers un nouvel emplacement
+		    	case 'COPY':
+		    		$log= "\t === ".'Réponse : '.PHP_EOL;
+			      	try{
+				        $target = $this->folder.$this->get_path($server);
+
+				        $toUrl = rawurldecode($server['HTTP_DESTINATION']);
+				        $toUrl = parse_url($toUrl, PHP_URL_PATH);
+				        $to = $this->folder.str_replace('/',SLASH,utf8_decode(str_replace($this->root,'',$toUrl)));
+				        $this->logit('Copy '.$target.' to '.$to.' (url :'.$toUrl.')');
+				        
+				        if(!isset($this->do['copy'])) throw new Exception("Not implemented", 501);
+				        
+						$function = $this->do['copy'];
+						$function($target,$to);
+						self::emit_header(200);
+
+						$log.= "\t".'- 200 OK'.PHP_EOL;
+						$this->logit($log);
+					
+			        }catch(Exception $e){
+				    	$log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
+						$this->logit($log);
+				    	self::emit_header($e->getCode());
+				    }
+		    	break;
+
+		    	//Verouillage d'un élement
+		    	case 'LOCK':
+		    		$log= "\t === ".'Réponse : '.PHP_EOL;
+		    		try{
+			    		$target = $this->folder.$this->get_path($server);
+						$response = 200;
+						$locks = $this->lock();
+
+						//Renouvellement de lock ou demande par un autre user
+						if(isset($locks[base64_encode($requestUri)])){
+							$lock = $locks[base64_encode($requestUri)];
+
+							if($lock['owner']!=$this->user->login) {
+								$response = 423;
+							}else{
+								$properties = array(
+									'owner'=>$this->user->login,
+									'created'=>time(),
+									'depth'=>isset($headers['Depth']) ? $headers['Depth'] : 'Infinity',
+									'timeout'=>isset($headers['Timeout']) ? $headers['Timeout'] : 'Second-3600',
+								);
+								$lock = $this->lock($requestUri,$properties);
+							}
+						//Création d'un nouveau lock
+						}else{
+							$properties = array(
+								'owner'=>$this->user->login,
+								'created'=>time(),
+								'depth'=>isset($headers['Depth']) ? $headers['Depth'] : 'Infinity',
+								'timeout'=>isset($headers['Timeout']) ? $headers['Timeout'] : 'Second-3600',
+							);
+							$lock = $this->lock($requestUri,$properties);
+						}
+
+				        self::emit_header($response);
+				        header('Lock-Token: <'.$lock['token'].'>');
+
+				        $requestUri = str_replace('&','%26',$requestUri);
+				        $xml = '<?xml version="1.0" encoding="utf-8" ?>
+						   <D:prop xmlns:D="DAV:">
+						     <D:lockdiscovery>
+						       <D:activelock>
+						         <D:locktype><D:write/></D:locktype>
+						         <D:lockscope><D:exclusive/></D:lockscope>
+						         <D:depth>'.$lock['depth'].'</D:depth>
+						         <D:owner>
+						           <D:href>'.$lock['owner'].'</D:href>
+						         </D:owner>
+						         <D:timeout>'.$lock['timeout'].'</D:timeout>
+						         <D:locktoken>
+						           <D:href>'.$lock['token'].'</D:href>
+						         </D:locktoken>
+						         <D:lockroot>
+						           <D:href>'.$requestUri.'</D:href>
+						         </D:lockroot>
+						       </D:activelock>
+						     </D:lockdiscovery>
+						   </D:prop>';
+				        echo $xml;
+
+				        $log.= "\t".'- '.$response.' '.self::headerFromCode($response).PHP_EOL;
+				        $log.= "\t".'-  Lock-Token: <'.$lock['token'].'>'.PHP_EOL;
+				        $log.= "\t\t".$xml.PHP_EOL;
+						$this->logit($log);
+
+				    }catch(Exception $e){
+				    	$log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
+						$this->logit($log);
+				    	self::emit_header($e->getCode());
+				    }
+		    	break;
+
+		    	//Déverouillage d'un élement
+		    	case 'UNLOCK':
+		    		$log= "\t === ".'Réponse : '.PHP_EOL;
+		    		try{
+			    		$target = $this->folder.$this->get_path($server);
+			    		$token = isset($headers['Lock-Token']) ? $headers['Lock-Token'] : 'no-token';
+			    		$token = preg_replace('|[\<\>]|is','',$token);
+			    		$locks = $this->lock();
+
+			    		if(isset($locks[base64_encode($requestUri)])){
+			    			if($locks[base64_encode($requestUri)]['token'] == $token){
+					    		$this->lock($requestUri,false);
+			    			}
+			    		}
+			    		self::emit_header(204);
+			    		$log.= "\t".'- 204 No Content'.PHP_EOL;
+						$this->logit($log);
+		    		}catch(Exception $e){
+				    	$log.= "\t".'- '.$e->getCode().' '.$e->getMessage().PHP_EOL;
+						$this->logit($log);
+				    	self::emit_header($e->getCode());
+				    }
+		    	break;
+		    }
+		}catch(Exception $e){
+			self::emit_header(500);
+		}
+	    exit();
+	}
+
+	//Méthode de gestion du manifest des locks fichiers (ajout, supression, listing)
+	// Si properties est a false : supression d'un verrou pour 'file'
+	// Si file + properties est renseigné : Création d'un verrou pour 'file' avec ces propriétés
+	// Si file et properties sont a vide : on retourne juste la liste des verrou non expirés
+	public function lock($file=null,$properties = array()){
+		if(!file_exists($this->lockfile)) file_put_contents($this->lockfile,json_encode(array()));
+		$locks = json_decode(file_get_contents($this->lockfile),true);
+
+		foreach($locks as $key=>$lock){
+			$timeoutInfos = explode('-',$lock['timeout']);
+
+      		//Calcul du délais d'expiration d'un verrou
+      		$secondNumber = 60;
+      		if(count($timeoutInfos)==2){
+      			list($unity,$number) = $timeoutInfos;
+      			$secondNumber = $number;
+      			if($unity == 'Minut') $secondNumber *= 60;
+      			if($unity == 'Hour') $secondNumber *= 3600;
+      			if($unity == 'Day') $secondNumber *= 86400;
+      		}
+
+      		//Supression du verrou fichier si expiré
+  			if(time() <= ($secondNumber + $lock['created'])) continue;
+			unset($locks[$key]);
+		}
+		//Si aucun parametre n'est spécifié, on retourne la liste des verrous
+		if($file==null) return $locks;
+		
+		//Si les properties sont a false, on supprime le verrou
+		if($properties===false){
+			unset($locks[base64_encode($file)]);
+		}else{
+			//Si les properties du verrou sont renseigné, on le save
+			$properties['token'] = isset($properties['token']) && $properties['token'] != '' ? $properties['token'] : 'fr.sys1:'.sha1($file);
+			$locks[base64_encode($file)] = $properties;
+		}
+
+		file_put_contents($this->lockfile, json_encode($locks));
+
+		return !$properties ? '': $locks[base64_encode($file)];
+	}
+
+	/*
+
+		create_time : timestamp de création
+		update_time : timestamp dernière modification
+		length : taille du dossier ou du fichier
+		type : directory/file
+
+	*/
+	public static function xml_file($url,$fileInfos,$locks){
+		
+
+		if(!isset($fileInfos['create_time'])) $fileInfos['create_time'] =  time();
+		if(!isset($fileInfos['update_time'])) $fileInfos['update_time']  =  time();
+		if(!isset($fileInfos['length'])) $fileInfos['length'] = 0;
+	
+		
+		$fileInfos['create_time'] = $fileInfos['create_time'] - (3600*2);
+		$fileInfos['update_time'] = $fileInfos['update_time'] - (3600*2);
+
+		$fileInfos['update_time'] = date('D, j M Y H:i:s',  $fileInfos['update_time']).' GMT';
+		$fileInfos['create_time'] = date('D, j M Y H:i:s',  $fileInfos['create_time']).' GMT';
+
+		$url = str_replace('&','%26',$url);
+
+
+		$xml = '<D:response>
+					<D:href>'.$url.'</D:href>';
+
+	    $xml .= '<D:propstat>
+	              <D:prop>';
+	            if($fileInfos['type']=='directory'){
+	            $xml .='<D:resourcetype>
+	                  <D:collection/>
+	                </D:resourcetype>';
+	            }else{
+	            	$xml .='<D:resourcetype/>';
+	            	$xml .='<D:supportedlock>
+                    <D:lockentry>
+			            <D:lockscope><D:exclusive/></D:lockscope>
+			            <D:locktype><D:write/></D:locktype>
+			          </D:lockentry>
+                  </D:supportedlock>';
+	            }
+
+	            if(isset($locks[base64_encode($url)])){
+	              		$lock = $locks[base64_encode($url)];
+	      				//Envoi du verrou au client si un verrou est en place
+	              		$xml .='<D:lockdiscovery>
+                        	<D:activelock>
+                        	    <D:locktype><D:write/></D:locktype>
+                        	    <D:lockscope><D:exclusive/></D:lockscope>
+                        	    <D:depth>'.$lock['depth'].'</D:depth>
+                        	    <D:owner>'.$lock['owner'].'</D:owner>
+                        	    <D:timeout>'.$lock['timeout'].'</D:timeout>
+                        	    <D:locktoken>
+                        	        <D:href>'.$lock['token'].'</D:href>
+                        	    </D:locktoken>
+                        	</D:activelock>
+	                    </D:lockdiscovery>';
+	            }
+	             
+
+              	if(isset($fileInfos['length']))
+	              	 $xml.= '<D:getcontentlength>'.$fileInfos['length'].'</D:getcontentlength>';
+	           $xml .='
+	                <D:getetag>"'.sha1($fileInfos['update_time']).'"</D:getetag>
+	                <D:getlastmodified>'.$fileInfos['update_time'].'</D:getlastmodified>
+	                <D:creationdate>'.$fileInfos['create_time'].'</D:creationdate>
+	              </D:prop>
+	              <D:status>HTTP/1.1 200 OK</D:status>
+	           </D:propstat>';
+	           
+	           if(!isset($fileInfos['length'])){
+	           		$xml .=  '<D:propstat>
+	              <D:prop>
+	                <D:getcontentlength/>
+	              </D:prop>
+	              <D:status>HTTP/1.1 404 Not Found</D:status>
+	           		</D:propstat>';
+	           }
+	 		
+	    $xml .= '</D:response>';
+	    return $xml;
+	}
+
+	function get_path($server){
+		$path = $this->get_parameter($server['REQUEST_URI']);
+		$path = rawurldecode(utf8_decode($path));
+		return str_replace('/',SLASH,$path);
+	}
+
+	public function get_parameter($parameter){
+		$path = rawurldecode($parameter);
+		$path = str_replace('\\', '/', $path);
+		$path = str_replace('//', '/', $path);
+		$path = substr($path,strlen($this->root));
+		
+		if(substr($path, -1)=='/') $path = substr($path, 0,-1);
+		return $path;
+	}
+
+
+	function logit($message){
+		if(isset($this->on['log']) ){
+			$function = $this->on['log'];
+			$function($message);
+		}
+
+		if($this->logs=='') return;
+		file_put_contents($this->logs, $message.PHP_EOL,FILE_APPEND);
+	}
+
+	public static function emit_header($code){
+		if (ob_get_length()) ob_end_clean();
+		header(self::headerFromCode($code));
+	}
+
+	public static function headerFromCode($code){
+		$codes = array(
+			200 => 'HTTP/1.1 200 Ok',
+	        201 => 'HTTP/1.1 201 Created',
+	        204 => 'HTTP/1.1 204 No Content',
+	        207 => 'HTTP/1.1 207 Multi-Status',
+	        401 => 'HTTP/1.0 401 Unauthorized',
+	        403 => 'HTTP/1.1 403 Forbidden',
+	        404 => 'HTTP/1.1 404 Not Found',
+	        406 => 'HTTP/1.1 406 Not acceptable',
+	        409 => 'HTTP/1.1 409 Conflict',
+	        415 => 'HTTP/1.1 415 Unsupported Media Type',
+	        423 => 'HTTP/1.1 423 Locked',
+	        500 => 'HTTP/1.1 500 Internal Server Error',
+	        501 => 'HTTP/1.1 501 Method not implemented',
+		);
+		return isset($codes[$code]) ? $codes[$code] : $codes[500];
+	}
+}
+
+?>

+ 188 - 1
plugin/hackpoint/hackpoint.plugin.php

@@ -93,6 +93,193 @@ Configuration::setting('hackpoint',array(
  }
 
 
+function hackpoint_dav($requested){
+	global $conf,$myUser;
+	$requested = trim($requested, '/');
+
+	if(substr($requested, 0,13)!='dav/hackpoint') return;
+	if(empty($requested)) throw new Exception("Unspecified DAV path");
+
+
+	require_once(__ROOT__.'common.php');
+	require_once(__DIR__.SLASH.'WebDav.class.php');
+
+	$projectPath = preg_replace('|https?\:\/\/'.$_SERVER['HTTP_HOST'].'|i', '', ROOT_URL);
+
+	$server = new WebDav();
+	$server->logs = WebDav::logPath().SLASH.'dav-logs.txt';
+	$server->lockfile = WebDav::logPath().SLASH.'dav-lock.json';
+	$server->root = str_replace('//','/',$projectPath.'/dav/hackpoint/');
+	$server->folder = '';
+
+	//Windows cherche desktop.ini a chaque ouverture de dossier pour le custom
+	$server->ignoreFiles[] = 'desktop.ini';
+	//Tortoise et autre client git d'explorer cherchent les .git a chaques ouverture
+	$server->ignoreFiles[] = '.git';
+
+
+
+	$server->do['login'] = function($login,$password){
+		global $myUser;
+
+		if($myUser->login !=''){
+			return $myUser;
+		}
+		
+		$myUser = User::check($login,$password);
+
+		if(!$myUser)
+			throw new Exception("Problème lors de la connexion, veuillez contacter l'administrateur",401);
+		
+		if(file_exists('enabled.maintenance') && $myUser->superadmin != 1)
+			throw new Exception('Seul un compte Super Admin peut se connecter en mode maintenance',401);
+		
+		if(!$myUser->connected())
+			throw new Exception('Identifiant ou mot de passe incorrect',401);
+
+		if(is_numeric($myUser->preference('default_firm')) && $myUser->haveFirm($myUser->preference('default_firm'))){
+			$_SESSION['firm'] = serialize(Firm::getById($myUser->preference('default_firm')));
+			if(!$_SESSION['firm'])
+				throw new Exception("Problème lors de la connexion, veuillez contacter l'administrateur",401);
+			
+		}else if(count($myUser->firms)!=0){
+			$_SESSION['firm'] = serialize(reset($myUser->firms));
+			if(!$_SESSION['firm'])
+				throw new Exception(" Problème lors de la connexion, veuillez contacter l'administrateur",401);
+		}else{
+			throw new Exception('Ce compte n\'est actif sur aucune firm',401);
+		}			
+		
+		$myFirm = isset($_SESSION['firm']) ? unserialize($_SESSION['firm']) : new Firm();
+		if(!$myFirm)
+			throw new Exception("Problème lors de la connexion, veuillez contacter l'administrateur",401);
+
+		$_SESSION['currentUser'] = serialize($myUser);
+		if(!$_SESSION['currentUser'])
+			throw new Exception("Problème lors de la connexion, veuillez contacter l'administrateur",401);
+	};
+
+	$server->do['delete'] = function($isoPath){
+		global $myUser,$conf;
+		$utf8Path = utf8_encode($isoPath);
+		
+		
+	};
+
+	$server->do['download'] = function($isoPath){
+		global $myUser,$_,$conf;
+		$utf8Path = utf8_encode($isoPath);
+
+		//pour verfiier si le fichier existe, on récupere son chemin système avec le bon encodage
+		$osPath = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? $isoPath: $utf8Path; 
+
+		if(!file_exists($osPath))  throw new Exception("Fichier inexistant",404);
+	    if(!is_file($osPath)) throw new Exception("Méthode non autorisée sur autre chose qu'un fichier",501);
+
+		$stream = 'not implemented';
+
+		return $stream;
+	};
+
+	$server->do['edit'] = function($isoPath,$body,$type){
+		global $myUser,$conf;
+		User::check_access('document','edit');
+		$utf8Path = utf8_encode($isoPath);
+
+		if($type=='file'){
+			$maxSize = $conf->get('document_allowed_size');
+			$extension = getExt($utf8Path);
+			$extensions = explode(',',str_replace(' ', '', $conf->get('document_allowed_extensions')));
+			if(strlen($body) > $maxSize) throw new Exception("Taille du fichier  trop grande, taille maximum :".readable_size($maxSize).' ('.$maxSize.' octets)',406);
+			if(!in_array($extension , $extensions)) throw new Exception("Extension '".$extension."' du fichier ".$path." non permise, autorisé :".implode(', ',$extensions),415);
+			$element = Element::addFile($utf8Path, $body);
+			if($conf->get('document_enable_logs') || $conf->get('document_enable_logs_verbose')) Log::put('Enregistrement fichier '.$utf8Path,'document');
+		}else{
+			
+			if($conf->get('document_enable_logs') || $conf->get('document_enable_logs_verbose')) Log::put('Enregistrement dossier '.$utf8Path,'document');
+		}
+
+		
+	};
+
+	$server->do['move'] = function($isoPath,$isoTo){
+		global $myUser,$conf;
+		$utf8Path = utf8_encode($isoPath);
+		$utf8To = utf8_encode($isoTo);
+		User::check_access('document','edit');
+		if(!document_check_element_name(mt_basename($utf8To), ENT_QUOTES)) throw new Exception("Caractères interdits : \\/:*?\"<>|");
+		
+	};
+
+	$server->do['copy'] = function($isoPath,$isoTo){
+		global $myUser,$conf;
+		$utf8Path = utf8_encode($isoPath);
+		$utf8To = utf8_encode($isoTo);
+		User::check_access('document','edit');
+		if(!document_check_element_name(mt_basename($utf8To), ENT_QUOTES)) throw new Exception("Caractères interdits : \\/:*?\"<>|");
+		
+	};
+
+	$server->do['list'] = function($isoPath,$depth =0){
+		global $myUser,$conf;
+		$utf8Path = utf8_encode($isoPath);
+		User::check_access('document','read');
+
+		//pour verfier si le fichier existe, on récupere son chemin système avec le bon encodage
+		$osPath = strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' ? $isoPath: $utf8Path; 
+		/*if(!file_exists($osPath)) throw new Exception("Not found", 404);
+
+		$files = array();
+
+		//Si la cible est la racine dav, on la retourne sous forme d'element fictif, sinon on utilise le systeme d'element standard
+		if($utf8Path == __DIR__.SLASH.'hackpoint'.SLASH.'dav'.SLASH.'hackpoint'){
+			$file = (object) array();
+			$file->label = '';
+			$file->type = 'directory';
+			$file->path = '';
+			$toScan = array($file);
+		}else{
+			
+			$file = (object) array();
+			$file->label = '';
+			$file->type = 'directory';
+			$file->path = '';
+			$toScan = array($file);
+		}
+		*/
+		
+		
+		//Si le dav demande un scan en profondeur du dossier, on scan les enfants du dossier ciblé
+		if($depth>0)
+			$toScan = array_merge($toScan,Element::browse($utf8Path.SLASH.'*'));
+		
+		foreach($toScan as $element){
+			
+		    $file = array(
+				'type' => $element->type,
+				'label' =>  $element->label
+		    );
+
+		    if($element->type == 'directory'){
+		    	
+				$file['create_time']  = time();
+				$file['update_time']  =  time();
+				$file['length'] =  1000;
+			}else{
+				$file['create_time']  = time();
+				$file['update_time']  =  time();
+				$file['length'] =  1000;
+			}
+
+			$files[] = $file;
+		}
+
+		return $files;
+	};
+
+	$server->start();
+}
+
 //Déclation des assets
 Plugin::addCss("/css/main.css?v=1"); 
 Plugin::addCss("/css/codemirror.css?v=1"); 
@@ -119,6 +306,6 @@ Plugin::addHook("action", "hackpoint_action");
 Plugin::addHook("menu_setting", "hackpoint_menu_setting");    
 Plugin::addHook("content_setting", "hackpoint_content_setting");   
 Plugin::addHook("hackpoint_resource_type", "hackpoint_manage_types");   
-    
+ Plugin::addHook("rewrite", "hackpoint_dav");
 
 ?>

+ 63 - 0
plugin/hackpoint/logs/dav-logs.txt

@@ -0,0 +1,63 @@
+
+
+---------- GET --------------
+Fichier : /hackpoint/dav/hackpoint
+	 - Host : 127.0.0.1
+	 - User-Agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.75 Safari/537.36
+	 - Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+	 - Accept-Language : en-US,en;q=0.5
+	 - Accept-Encoding : gzip,deflate,sdch
+	 - DNT : 1
+	 - Authorization : Basic YWRtaW46YWRtaW4=
+	 - Connection : keep-alive
+	 - Cookie : erp-core=jcegqlk2pan2tc2v57e57nlfm3
+	 - Upgrade-Insecure-Requests : 1
+	 - Cache-Control : max-age=0
+	 === Requête : 
+
+Download 
+	 === Réponse : 
+	- 404 Fichier inexistant
+
+
+
+---------- GET --------------
+Fichier : /hackpoint/dav/hackpoint
+	 - Host : 127.0.0.1
+	 - User-Agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.75 Safari/537.36
+	 - Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+	 - Accept-Language : en-US,en;q=0.5
+	 - Accept-Encoding : gzip,deflate,sdch
+	 - DNT : 1
+	 - Authorization : Basic YWRtaW46YWRtaW4=
+	 - Connection : keep-alive
+	 - Cookie : erp-core=jcegqlk2pan2tc2v57e57nlfm3
+	 - Upgrade-Insecure-Requests : 1
+	 - Cache-Control : max-age=0
+	 === Requête : 
+
+Download 
+	 === Réponse : 
+	- 404 Fichier inexistant
+
+
+
+---------- GET --------------
+Fichier : /hackpoint/dav/hackpoint
+	 - Host : 127.0.0.1
+	 - User-Agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.75 Safari/537.36
+	 - Accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+	 - Accept-Language : en-US,en;q=0.5
+	 - Accept-Encoding : gzip,deflate,sdch
+	 - DNT : 1
+	 - Authorization : Basic YWRtaW46YWRtaW4=
+	 - Connection : keep-alive
+	 - Cookie : erp-core=jcegqlk2pan2tc2v57e57nlfm3
+	 - Upgrade-Insecure-Requests : 1
+	 - Cache-Control : max-age=0
+	 === Requête : 
+
+Download 
+	 === Réponse : 
+	- 404 Fichier inexistant
+