Subversion Repositories Applications.annuaire

Rev

Rev 521 | Rev 536 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
69 aurelien 1
<?php
2
/**
3
 * Classe mère abstraite contenant les méthodes génériques des services.
4
 * Encodage en entrée : utf8
5
 * Encodage en sortie : utf8
6
 *
7
 * @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
8
 * @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
9
 * @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
10
 * @version $Id$
11
 * @copyright 2009
12
 */
13
abstract class JRestService {
14
 
15
	public $config;
16
	protected $bdd;
331 jpm 17
	protected $ressources;
69 aurelien 18
	protected $log = array();
19
	protected $messages = array();
20
	protected $debug = array();
21
	protected $distinct = false;
22
	protected $orderby = null;
23
	protected $formatRetour = 'objet';
24
	protected $start = 0;
25
	protected $limit = 150;
26
 
519 mathias 27
	/** pour l'envoi de XML : éventuelle balise dans laquelle placer tout le contenu */
28
	protected $baliseMaitresse;
29
 
69 aurelien 30
	public function __construct($config, $demarrer_session = true) {
31
		// Tableau contenant la config de Jrest
32
		$this->config = $config;
33
 
34
		// Connection à la base de données
35
		$this->bdd = $this->connecterPDO($this->config, 'appli');
36
 
37
		// Nettoyage du $_GET (sécurité)
38
		if (isset($_GET)) {
39
			$get_params = array('orderby', 'distinct', 'start', 'limit', 'formatRetour');
40
			foreach ($get_params as $get) {
41
				$verifier = array('NULL', "\n", "\r", "\\", "'", '"', "\x00", "\x1a", ';');
42
				$_GET[$get] = str_replace($verifier, '', $_GET[$get]);
43
				if (isset($_GET[$get]) && $_GET[$get] != '') {
44
					$this->$get = $_GET[$get];
45
				} else {
46
					$_GET[$get] = null;
47
				}
48
			}
49
		}
50
	}
51
 
52
	/**
53
	 * Méthode appelée quand aucun paramètre n'est passé dans l'url et avec une requête de type GET.
54
	 */
55
	public function getRessource() {
56
		$this->getElement(array());
57
	}
58
 
331 jpm 59
	//+----------------------------------------------------------------------------------------------------------------+
60
	// GESTION de l'ENVOIE au NAVIGATEUR
61
 
62
	protected function envoyerJson($donnees, $encodage = 'utf-8') {
63
		$contenu = json_encode($donnees);
64
		$this->envoyer($contenu, 'application/json', $encodage, false);
65
	}
390 jpm 66
 
519 mathias 67
	/** à l'arrache pour rétrocompatibilité avec le service "annuaire_tela" de eFlore_chatin */
68
	protected function envoyerXml($donnees, $encodage = 'utf-8') {
69
		$xml = '<?xml version="1.0" encoding="' . strtoupper($encodage) . '"?>';
70
		if ($this->baliseMaitresse) {
71
			$xml .= '<' . $this->baliseMaitresse . '>';
72
		}
73
		$xml .= $this->genererXmlAPartirDeTableau($donnees);
74
		if ($this->baliseMaitresse) {
75
			$xml .= '</' . $this->baliseMaitresse . '>';
76
		}
77
		$this->envoyer($xml, 'application/xml', $encodage, false);
78
	}
79
 
80
	/**
81
	 * Génère un XML minimaliste à partir d'un tableau associatif
82
	 * Note : gère mal les indices numériques
83
	 * @TODO utiliser une vraie lib
84
	 */
85
	protected function genererXmlAPartirDeTableau($tableau) {
86
		$xml = '';
87
		foreach ($tableau as $balise => $donnee) {
88
			$xml .= '<' . $balise . '>';
89
			if (is_array($donnee)) {
90
				// récurer, balayer, que ce soit toujours pimpant
91
				$xml .= $this->genererXmlAPartirDeTableau($donnee);
92
			} else {
93
				$xml .= $donnee;
94
			}
95
			$xml .= '</' . $balise . '>';
96
		}
97
		return $xml;
98
	}
99
 
331 jpm 100
	protected function envoyerJsonVar($variable, $donnees = null, $encodage = 'utf-8') {
101
		$contenu = "var $variable = ".json_encode($donnees);
102
		$this->envoyer($contenu, 'text/html', $encodage, false);
103
	}
390 jpm 104
 
331 jpm 105
	protected function envoyerJsonp($donnees = null, $encodage = 'utf-8') {
106
		$contenu = $_GET['callback'].'('.json_encode($donnees).');';
107
		$this->envoyer($contenu, 'text/html', $encodage, false);
108
	}
390 jpm 109
 
331 jpm 110
	protected function envoyerTxt($donnees, $encodage = 'utf-8') {
111
		$this->envoyer($contenu, 'text/html', $encodage, false);
112
	}
113
 
69 aurelien 114
	protected function envoyer($donnees = null, $mime = 'text/html', $encodage = 'utf-8', $json = true) {
115
		// Traitements des messages d'erreurs et données
116
		if (count($this->messages) != 0) {
117
			header('HTTP/1.1 500 Internal Server Error');
118
			$mime = 'text/html';
119
			$encodage = 'utf-8';
120
			$json = true;
121
			$sortie = $this->messages;
122
		} else {
123
			$sortie = $donnees;
124
			if (is_null($donnees)) {
125
				$sortie = 'OK';
126
			}
127
		}
128
 
129
		// Gestion de l'envoie du déboguage
130
		$this->envoyerDebogage();
131
 
132
		// Encodage au format et JSON et envoie sur la sortie standard
133
		$contenu = $json ? json_encode($sortie) : $sortie;
134
		$this->envoyerContenu($encodage, $mime, $contenu);
135
	}
136
 
331 jpm 137
	private function envoyerDebogage() {
69 aurelien 138
		if (!is_array($this->debug)) {
139
			$this->debug[] = $this->debug;
140
		}
141
		if (count($this->debug) != 0) {
142
			foreach ($this->debug as $cle => $val) {
143
				if (is_array($val)) {
144
					$this->debug[$cle] = print_r($val, true);
145
				}
146
			}
147
			header('X-DebugJrest-Data:'.json_encode($this->debug));
148
		}
149
	}
150
 
331 jpm 151
	private function envoyerContenu($encodage, $mime, $contenu) {
152
		if (!is_null($mime) && !is_null($encodage)) {
153
			header("Content-Type: $mime; charset=$encodage");
154
		} else if (!is_null($mime) && is_null($encodage)) {
155
			header("Content-Type: $mime");
156
		}
69 aurelien 157
		print $contenu;
158
	}
390 jpm 159
 
331 jpm 160
	private function envoyerAuth($message_accueil, $message_echec) {
161
		header('HTTP/1.0 401 Unauthorized');
162
		header('WWW-Authenticate: Basic realm="'.mb_convert_encoding($message_accueil, 'ISO-8859-1', 'UTF-8').'"');
163
		header('Content-type: text/plain; charset=UTF-8');
164
		print $message_echec;
165
		exit(0);
166
	}
390 jpm 167
 
473 jpm 168
	protected function envoyerMessageErreur($msg, $code) {
169
		$textHttp = $this->getCodeHttpText($code);
170
		header("HTTP/1.0 $code $textHttp");
171
		header("Content-Type: text/plain; charset=utf-8");
172
		die($msg);
173
	}
174
 
175
	private function getCodeHttpText($code) {
176
		$text = '';
177
		switch ($code) {
178
			case 100: $text = 'Continue'; break;
179
			case 101: $text = 'Switching Protocols'; break;
180
			case 200: $text = 'OK'; break;
181
			case 201: $text = 'Created'; break;
182
			case 202: $text = 'Accepted'; break;
183
			case 203: $text = 'Non-Authoritative Information'; break;
184
			case 204: $text = 'No Content'; break;
185
			case 205: $text = 'Reset Content'; break;
186
			case 206: $text = 'Partial Content'; break;
187
			case 300: $text = 'Multiple Choices'; break;
188
			case 301: $text = 'Moved Permanently'; break;
189
			case 302: $text = 'Moved Temporarily'; break;
190
			case 303: $text = 'See Other'; break;
191
			case 304: $text = 'Not Modified'; break;
192
			case 305: $text = 'Use Proxy'; break;
193
			case 400: $text = 'Bad Request'; break;
194
			case 401: $text = 'Unauthorized'; break;
195
			case 402: $text = 'Payment Required'; break;
196
			case 403: $text = 'Forbidden'; break;
197
			case 404: $text = 'Not Found'; break;
198
			case 405: $text = 'Method Not Allowed'; break;
199
			case 406: $text = 'Not Acceptable'; break;
200
			case 407: $text = 'Proxy Authentication Required'; break;
201
			case 408: $text = 'Request Time-out'; break;
202
			case 409: $text = 'Conflict'; break;
203
			case 410: $text = 'Gone'; break;
204
			case 411: $text = 'Length Required'; break;
205
			case 412: $text = 'Precondition Failed'; break;
206
			case 413: $text = 'Request Entity Too Large'; break;
207
			case 414: $text = 'Request-URI Too Large'; break;
208
			case 415: $text = 'Unsupported Media Type'; break;
209
			case 500: $text = 'Internal Server Error'; break;
210
			case 501: $text = 'Not Implemented'; break;
211
			case 502: $text = 'Bad Gateway'; break;
212
			case 503: $text = 'Service Unavailable'; break;
213
			case 504: $text = 'Gateway Time-out'; break;
214
			case 505: $text = 'HTTP Version not supported'; break;
215
			default:
216
				exit('Unknown http status code "' . htmlentities($code) . '"');
217
			break;
218
		}
219
		return $text;
220
	}
221
 
331 jpm 222
	//+----------------------------------------------------------------------------------------------------------------+
223
	// GESTION de la BASE de DONNÉES
390 jpm 224
 
69 aurelien 225
	private function connecterPDO($config, $base = 'database') {
390 jpm 226
  		$cfg = $config[$base];
473 jpm 227
		// ATTENTION : la connexin à la bdd peut échouer si l'host vaut localhost. Utiliser 127.0.0.1 à la place.
69 aurelien 228
		$dsn = $cfg['phptype'].':dbname='.$cfg['database'].';host='.$cfg['hostspec'];
229
		try {
473 jpm 230
		// Création de la connexion en UTF-8 à la BDD
390 jpm 231
			$PDO = new PDO($dsn, $cfg['username'], $cfg['password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8'"));
69 aurelien 232
		} catch (PDOException $e) {
473 jpm 233
			echo 'La connexion à la base de donnée via PDO a échouée : ' .$dsn. $e->getMessage();
69 aurelien 234
		}
235
		// Affiche les erreurs détectées par PDO (sinon mode silencieux => aucune erreur affiché)
236
		$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
473 jpm 237
		return $PDO;
69 aurelien 238
	}
239
 
240
	protected function getTxt($id) {
241
		$sortie = '';
242
		switch ($id) {
243
			case 'sql_erreur' : $sortie = 'Requête echec. Fichier : "%s". Ligne : "%s". Message : %s'; break;
244
			default : $sortie = $id;
245
		}
246
		return $sortie;
247
	}
248
 
331 jpm 249
	//+----------------------------------------------------------------------------------------------------------------+
250
	// TRAITEMENT des URLs et des PARAMÊTRES
390 jpm 251
 
331 jpm 252
	protected function traiterNomMethodeGet($nom) {
253
		$methode = 'get';
254
		$methode .= str_replace(' ', '', ucwords(str_replace('-', ' ', strtolower($nom))));
255
		return $methode;
256
	}
390 jpm 257
 
347 jpm 258
	protected function traiterNomMethodePost($nom) {
259
		$methode = 'update';
260
		$methode .= str_replace(' ', '', ucwords(str_replace('-', ' ', strtolower($nom))));
261
		return $methode;
262
	}
390 jpm 263
 
430 aurelien 264
	protected function traiterNomMethodePut($nom) {
265
		$methode = 'create';
266
		$methode .= str_replace(' ', '', ucwords(str_replace('-', ' ', strtolower($nom))));
267
		return $methode;
394 jpm 268
	}
269
 
69 aurelien 270
	protected function traiterParametresUrl($params_attendu, $params, $pourBDD = true) {
271
		$sortie = array();
272
		foreach ($params_attendu as $num => $nom) {
273
			if (isset($params[$num]) && $params[$num] != '*') {
274
				if ($pourBDD) {
275
					$params[$num] = $this->bdd->quote($params[$num]);
276
				}
277
				$sortie[$nom] = $params[$num];
278
			}
279
		}
280
		return $sortie;
281
	}
282
 
283
	protected function traiterParametresPost($params) {
284
		$sortie = array();
285
		foreach ($params as $cle => $valeur) {
286
			$sortie[$cle] = $this->bdd->quote($valeur);
287
		}
288
		return $sortie;
289
	}
290
 
331 jpm 291
	//+----------------------------------------------------------------------------------------------------------------+
292
	// GESTION DE L'IDENTIFICATION
390 jpm 293
 
473 jpm 294
	public function controlerIpAutorisees() {
295
		$ipsAutorisees = $this->config['jrest_admin']['ip_autorisees'];
296
 
297
		$remoteIp = filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP);
298
		$serverIp = filter_input(INPUT_SERVER, 'SERVER_ADDR', FILTER_VALIDATE_IP);
299
		if (in_array($remoteIp, $ipsAutorisees) == false) {
300
			if ($remoteIp != $serverIp) {// ATTENTION : maintenir ce test à l'intérieur du précédent
301
				$message = "Accès interdit. \n".
302
					"Vous n'êtes pas autorisé à accéder à ce service depuis '$remoteIp' !\n";
303
				$this->envoyerMessageErreur($message, 401);
304
			}
305
		}
306
		return true;
307
	}
308
 
430 aurelien 309
	protected function getIdentification(&$params) {
310
		// Initialisation des variables
311
		$utilisateur = array(0, session_id());
312
 
313
		// L'id utilisateur est soit passé par le POST soit dans l'url
314
		if (is_array($params) && isset($params['cmhl_ce_modifier_par'])) {
315
		   	$utilisateur[0] = $params['cmhl_ce_modifier_par'];
316
			unset($params['cmhl_ce_modifier_par']);
317
		} else if (is_string($params)) {
318
			$utilisateur[0] = $params;
319
		}
320
 
321
		return $utilisateur;
322
	}
323
 
324
	protected function etreAutorise($id_utilisateur) {
325
		$autorisation = false;
326
		if (($_SESSION['coel_utilisateur'] != '') && $_SESSION['coel_utilisateur']['id'] != $id_utilisateur) {
327
			$this->messages[] = 'Accès interdit.';
328
		} else if ($_SESSION['coel_utilisateur'] == '') {
329
			$this->messages[] = 'Veuillez vous identifiez pour accéder à cette fonction.';
330
		} else {
331
			$autorisation = true;
332
		}
333
		return $autorisation;
334
	}
335
 
336
	private function gererIdentificationPermanente() {
337
		// Pour maintenir l'utilisateur tjrs réellement identifié nous sommes obligé de recréer une SESSION et de le recharger depuis la bdd
338
		if ($this->getUtilisateur() == ''
339
				&& isset($_COOKIE['coel_login'])
340
				&& ($utilisateur = $this->chargerUtilisateur($_COOKIE['coel_login'], $_COOKIE['coel_mot_de_passe']))) {
341
			$this->setUtilisateur($utilisateur, $_COOKIE['coel_permanence']);
342
		}
343
	}
344
 
345
	protected function getUtilisateur() {
346
		return (isset($_SESSION['coel_utilisateur']) ? $_SESSION['coel_utilisateur'] : '');
347
	}
348
 
331 jpm 349
	protected function authentifier() {
350
		if (!isset($_SERVER['PHP_AUTH_USER'])) {
351
			header('WWW-Authenticate: Basic realm="www.tela-botanica.org"');
352
			header('HTTP/1.0 401 Unauthorized');
353
			header('Content-type: text/html; charset=UTF-8');
354
			echo 'Accès interdit';
355
			exit;
356
		} else {
394 jpm 357
			if ($this->verifierAcces()) {
331 jpm 358
				return ;
359
			} else {
360
				header('WWW-Authenticate: Basic realm="www.tela-botanica.org"');
361
				header('HTTP/1.0 401 Unauthorized');
362
				header('Content-type: text/html; charset=UTF-8');
363
				echo 'Accès interdit';
364
				exit ;
365
			}
366
		}
367
	}
69 aurelien 368
 
394 jpm 369
	protected function verifierAcces($id = null, $mdp = null) {
370
		$id = is_null($id) ? $_SERVER['PHP_AUTH_USER'] : $id;
371
		$mdp = is_null($mdp) ? $_SERVER['PHP_AUTH_PW'] : $mdp;
372
 
373
		$requete = 'SELECT '.$this->config['database_ident']['ann_id'].' AS courriel '.
331 jpm 374
			'FROM '.$this->config['database_ident']['database'].'.'.$this->config['database_ident']['annuaire'].' '.
375
			'WHERE '.$this->config['database_ident']['ann_id'].' = '.$this->bdd->quote($id).' '.
394 jpm 376
			'	AND '.$this->config['database_ident']['ann_pwd'].' = '.$this->config['database_ident']['pass_crypt_funct'].'('.$this->bdd->quote($mdp).')' ;
532 aurelien 377
 
394 jpm 378
		$resultat = $this->bdd->query($requete)->fetch();
390 jpm 379
 
394 jpm 380
		$identifie = false;
381
		if (isset($resultat['courriel'])) {
382
			$identifie = true;
331 jpm 383
		}
394 jpm 384
		return $identifie;
385
	}
331 jpm 386
 
521 mathias 387
	/**
388
	 * Envoie une demande d'authentification HTTP puis compare le couple
389
	 * login / mot de passe envoyé par l'utilisateur, à ceux définis dans
390
	 * la config (section database_ident).
391
	 * En cas d'erreur, sort du programme avec un entête HTTP 401
392
	 * @TODO redondant avec les trucs du dessus :'(
393
	 */
394
	protected function authentificationHttpSimple() {
395
		$autorise = true;
396
		// contrôle d'accès
397
		$nomUtil = $_SERVER['PHP_AUTH_USER'];
398
		$mdp = $_SERVER['PHP_AUTH_PW'];
399
		$autorise = (($nomUtil == $this->config['database_ident']['username']) && ($mdp == $this->config['database_ident']['password']));
400
		// entêtes HTTP
401
		if (! $autorise) {
402
			header('WWW-Authenticate: Basic realm="Annuaire de Tela Botanica"');
403
			header('HTTP/1.0 401 Unauthorized');
404
			echo 'Veuillez vous authentifier pour utiliser ce service';
405
			exit;
406
		}
407
	}
408
 
396 jpm 409
	protected function creerCookiePersistant($duree = null, $id = null, $mdp = null) {
430 aurelien 410
		$id = is_null($id) ? $_SERVER['PHP_AUTH_USER'] : $id;
394 jpm 411
		$mdp = is_null($mdp) ? $_SERVER['PHP_AUTH_PW'] : $mdp;
396 jpm 412
		$duree = (int) is_null($duree) ? time()+3600*24*30 : $duree;
331 jpm 413
 
394 jpm 414
		$nomCookie = $this->config['database_ident']['nom_cookie_persistant'];
430 aurelien 415
		$valeurCookie = md5($mdp).$id;
416
 
396 jpm 417
		setcookie($nomCookie, $valeurCookie, $duree, '/');
331 jpm 418
	}
390 jpm 419
 
430 aurelien 420
	protected function creerCookieUtilisateur($duree = null, $id = null, $mdp = null) {
400 jpm 421
		$id = is_null($id) ? $_SERVER['PHP_AUTH_USER'] : $id;
430 aurelien 422
		$mdp = is_null($mdp) ? $_SERVER['PHP_AUTH_PW'] : $mdp;
396 jpm 423
		$duree = (int) is_null($duree) ? 0 : $duree;
430 aurelien 424
 
425
		$nomCookie = $this->config['database_ident']['nom_cookie_utilisateur'];
400 jpm 426
		$valeurCookie = md5($mdp).$id;
430 aurelien 427
 
428
		setcookie($nomCookie, $valeurCookie, $duree, '/');
394 jpm 429
	}
532 aurelien 430
 
431
	protected function supprimerCookieUtilisateur() {
432
		session_destroy();
433
		setcookie($this->config['database_ident']['nom_cookie_utilisateur'], "", time()-7200, "/");
434
		setcookie($this->config['database_ident']['nom_cookie_persistant'], "", time()-7200, "/");
435
	}
394 jpm 436
 
388 aurelien 437
	protected function estAutoriseMessagerie($adresse) {
438
		$utilisateurs_messagerie = explode(',', $this->config['messagerie']['utilisateurs_autorises']);
439
		return in_array($adresse, $utilisateurs_messagerie);
440
	}
394 jpm 441
 
331 jpm 442
	//+----------------------------------------------------------------------------------------------------------------+
443
	// GESTION DES SQUELETTES PHP
390 jpm 444
 
69 aurelien 445
	/**
446
	 * Méthode prenant en paramètre un chemin de fichier squelette et un tableau associatif de données,
447
	 * en extrait les variables, charge le squelette et retourne le résultat des deux combinés.
448
	 *
449
	 * @param String $fichier	le chemin du fichier du squelette
450
	 * @param Array  $donnees	un tableau associatif contenant les variables a injecter dans le squelette.
451
	 *
452
	 * @return boolean false si le squelette n'existe pas, sinon la chaine résultat.
453
	 */
454
	public static function traiterSquelettePhp($fichier, Array $donnees = array()) {
455
		$sortie = false;
456
		if (file_exists($fichier)) {
457
			// Extraction des variables du tableau de données
458
			extract($donnees);
459
			// Démarage de la bufferisation de sortie
460
			ob_start();
461
			// Si les tags courts sont activés
462
			if ((bool) @ini_get('short_open_tag') === true) {
463
				// Simple inclusion du squelette
464
				include $fichier;
465
			} else {
466
				// Sinon, remplacement des tags courts par la syntaxe classique avec echo
467
				$html_et_code_php = self::traiterTagsCourts($fichier);
468
				// Pour évaluer du php mélangé dans du html il est nécessaire de fermer la balise php ouverte par eval
469
				$html_et_code_php = '?>'.$html_et_code_php;
470
				// Interprétation du html et du php dans le buffer
471
				echo eval($html_et_code_php);
472
			}
473
			// Récupèration du contenu du buffer
474
			$sortie = ob_get_contents();
475
			// Suppression du buffer
476
			@ob_end_clean();
477
		} else {
478
			$msg = "Le fichier du squelette '$fichier' n'existe pas.";
479
			trigger_error($msg, E_USER_WARNING);
480
		}
481
		// Retourne le contenu
482
		return $sortie;
483
	}
484
 
485
	/**
486
	 * Fonction chargeant le contenu du squelette et remplaçant les tags court php (<?= ...) par un tag long avec echo.
487
	 *
488
	 * @param String $chemin_squelette le chemin du fichier du squelette
489
	 *
490
	 * @return string le contenu du fichier du squelette php avec les tags courts remplacés.
491
	 */
492
	private static function traiterTagsCourts($chemin_squelette) {
493
		$contenu = file_get_contents($chemin_squelette);
494
		// Remplacement de tags courts par un tag long avec echo
495
		$contenu = str_replace('<?=', '<?php echo ',  $contenu);
496
		// Ajout systématique d'un point virgule avant la fermeture php
497
		$contenu = preg_replace("/;*\s*\?>/", "; ?>", $contenu);
498
		return $contenu;
499
	}
500
}
501
?>