Author: james@rezo.net
Date: Mon Aug 21 16:00:02 2006
New Revision: 4793
Log:
ortho_serveur.php
Added:
_acotes_/ortho/
_acotes_/ortho/ortho_serveur.php
Added: _acotes_/ortho/ortho_serveur.php
--- _acotes_/ortho/ortho_serveur.php (added)
+++ _acotes_/ortho/ortho_serveur.php Mon Aug 21 16:00:02 2006
@@ -0,0 +1,417 @@
+<?php
+
+/*
+ortho_serveur.php - Serveur d'orthographe en PHP
+Copyright (C) 2004 Antoine Pitrou
+Distribue sous licence GNU GPL
+cf. http://lab.spip.net/spikini/?wiki=CorrecteurOrthographique
+
+----------------------------------------------
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+----------------------------------------------
+
+*/
+
+define('E_LANG_ABSENT', 1);
+define('E_TOO_MANY_MISTAKES', 2);
+
+ignore_user_abort(true);
+
+function _dl($ext) {
+ if (extension_loaded($ext)) return true;
+ $so = $ext.".so";
+ return @dl($so);
+}
+
+$dir_data = "data/";
+if (!chdir($dir_data)) {
+ echo "<h1>Veuillez créer le sous-répertoire data et le protéger contre l'accès distant (.htaccess).</h1>";
+ exit;
+}
+
+_dl('pspell');
+_dl('sockets');
+
+class Resultat {
+ var $erreurs;
+ var $retours;
+
+ function Resultat() {
+ $this->erreurs = array();
+ $this->retours = array();
+ }
+
+ function erreur($code) {
+ $this->erreurs[] = $code;
+ }
+ function retour($texte) {
+ $this->retours[] = $texte;
+ }
+ function xml($debug = "") {
+ $xml = '<'.'?xml version="1.0" encoding="utf-8"?'.'>'."\n";
+ $xml .= "<ortho>\n";
+ if ($debug) {
+ $xml .= "\t<debug>$debug</debug>\n";
+ }
+ foreach ($this->erreurs as $erreur) {
+ $xml .= "\t<erreur><code>$erreur</code><texte>$erreur</texte></erreur>\n";
+ }
+ foreach ($this->retours as $retour) {
+ $xml .= "\t<ok>$retour</ok>\n";
+ }
+ $xml .= "</ortho>\n";
+ return $xml;
+ }
+}
+
+class Correcteur {
+ var $lang;
+ var $ok;
+ var $p;
+
+ function Correcteur($lang) {
+ $this->lang = $lang;
+ $this->p = @pspell_new($lang, "", "", "utf-8", PSPELL_FAST);
+ $this->ok = !!$this->p;
+ }
+
+ function corriger($texte, &$resultat) {
+ if (!$this->p) {
+ $resultat->erreur('E_LANG_ABSENT');
+ return;
+ }
+ $r = "";
+ $mots = explode(" ", $texte);
+ $bad = array();
+ $suggest = true;
+ foreach ($mots as $mot) {
+ if (!pspell_check($this->p, $mot)) {
+ $bad[$mot] = $mot;
+ }
+ }
+ if (count($bad) > 250) {
+ $resultat->erreur('E_TOO_MANY_MISTAKES');
+ $suggest = false;
+ }
+ foreach ($bad as $mot) {
+ $r .= "<mot>".htmlspecialchars($mot)."</mot>\n";
+ $r .= "<suggest>";
+ if ($suggest) $r .= htmlspecialchars(join(", ", pspell_suggest($this->p, $mot)));
+ $r .= "</suggest>\n";
+ }
+ $resultat->retour($r);
+ }
+}
+
+function corriger($texte, $lang, $debug = "") {
+ static $cache;
+
+ // Gestion d'un cache de correcteurs afin de minimiser les appels a pspell_new()
+ if (!$cache) $cache = array();
+ if (!$cache[$lang]) $cache[$lang] = new Correcteur($lang);
+ $resultat = new Resultat;
+ $cache[$lang]->corriger($texte, $resultat);
+ $body = $resultat->xml($debug);
+ return $body;
+}
+
+
+//
+// Gestion de l'IPC (via sockets Unix)
+//
+
+function creer_client($fichier_socket) {
+ $client = "";
+ // Essayer de se connecter a la socket
+ if (file_exists($fichier_socket)) {
+ $client = fsockopen("unix://".realpath($fichier_socket), 0);
+ if (!$client) $client = fsockopen($fichier_socket, 0);
+ }
+ return $client;
+}
+
+function corriger_client($client, $texte, $lang) {
+ $texte = str_replace("\0", "", $texte);
+ $params = array('texte' => $texte, 'lang' => $lang);
+ // Pour marquer la fin des donnees envoyees, on utilise le caractere NUL
+ $buf = serialize($params)."\0";
+ while ($l = fwrite($client, $buf) AND $l < strlen($buf))
+ $buf = substr($buf, $l);
+ $result = "";
+ // Lire le resultat jusqu'a terminaison de la socket
+ while (!feof($client)) $result .= fread($client, 50000);
+ return $result;
+}
+
+function creer_serveur($fichier_socket) {
+ // Essayer de creer une socket Unix en mode connecte
+ $ss = socket_create(AF_UNIX, SOCK_STREAM, 0);
+ if (!$ss) return false;
+ @unlink($fichier_socket);
+ if (!socket_bind($ss, $fichier_socket)) return false;
+ if (!socket_listen($ss)) return false;
+ chmod($fichier_socket, 0666);
+ return $ss;
+}
+
+function corriger_serveur($msg) {
+ $params = unserialize($msg);
+ $result = corriger($params['texte'], $params['lang'], "serveur");
+ return $result;
+}
+
+function boucle_serveur($serveur) {
+ $t = time();
+ $max = intval(ini_get('max_execution_time'));
+ if (!$max) $max = 30;
+ //while ($socket = socket_accept($serveur)) {
+ while (socket_select($r = array($serveur), $w = NULL, $e = NULL, max(0, $max - 5 - (time() - $t)))) {
+ $socket = socket_accept($serveur);
+ $msg = "";
+ // Lire les donnees envoyees par le client jusqu'a renconter le caractere de fin
+ while ($msg .= socket_read($socket, 10000) AND substr($msg, -1, 1) != "\0");
+ $msg = corriger_serveur(substr($msg, 0, -1));
+ // Envoyer le resultat au client
+ while ($l = socket_write($socket, $msg) AND $l < strlen($msg))
+ $msg = substr($msg, $l);
+ socket_close($socket);
+ if (time() - $t > $max - 5) {
+ flush();
+ break;
+ }
+ }
+ socket_close($serveur);
+ exit;
+}
+
+
+//
+// Repondre a la requete POST
+//
+
+$flag_sockets = function_exists("socket_create");
+$fichier_lock = "pipeline.lock";
+$fichier_socket = "pipeline.sock";
+
+
+function magic_unquote($s) {
+ if (get_magic_quotes_gpc()) return stripslashes($s);
+ else return $s;
+}
+
+function ajout_log($log, $ligne) {
+ $max_logs = 4;
+
+ $f = fopen($log, "ab");
+ flock($f, LOCK_EX);
+ if (@filemtime($log.'.1') < time() - 7 * 24 * 3600) {
+ @unlink($log.'.'.$max_logs);
+ for ($i = $max_logs; $i > 1; $i--) {
+ @rename($log.'.'.($i - 1), $log.'.'.$i);
+ }
+ copy($log, $log.'.1');
+ ftruncate($f, 0);
+ }
+ fwrite($f, $ligne."\n");
+ flock($f, LOCK_UN);
+ fclose($f);
+}
+
+// Cette fonction remplit un fichier de log avec une syntaxe similaire
+// a celle d'un log Apache (permet de l'analyser avec AWStats et al.)
+function ortho_log($op, $lang, $taille, $duree, $mode) {
+ if (!$ip = $_SERVER['HTTP_X_FORWARDED_FOR'])
+ $ip = $_SERVER['REMOTE_ADDR'];
+
+ $referer = 'http://'.$ip.'/'.$op.','.$lang;
+ if (!$method = $_SERVER['REQUEST_METHOD'])
+ $method = 'POST';
+ $uri = 'http://'.$_SERVER['HTTP_HOST'].':'.$_SERVER['SERVER_PORT'].'/'.$op.','.$lang.','.$mode;
+ $user_agent = $_SERVER['HTTP_USER_AGENT'];
+ $request = $method.' '.$uri.' HTTP/1.0';
+ $date = gmdate('d/m/Y:H:i:s').' +0000';
+
+ $ligne = "$ip - - [$date] \"$request\" 200 $taille \"$referer\" \"$user_agent\"";
+ ajout_log("access.log", $ligne);
+
+ $ligne = "$ip - - [$date] \"$request\" $taille $duree";
+ ajout_log("time.log", $ligne);
+}
+
+
+if ($op = magic_unquote($_REQUEST['op'])) {
+ switch($op) {
+ case 'spell':
+ $log_debut = explode(" ", microtime());
+ $log_taille = 0;
+ $log_mode = '';
+
+ $client = $serveur = "";
+
+ // Debut verrou
+ if (!file_exists($fichier_lock)) {
+ touch($fichier_lock);
+ @chmod($fichier_lock, 0666);
+ }
+ $fp = fopen($fichier_lock, "w+");
+ if ($fp) flock($fp, LOCK_EX);
+
+ if ($flag_sockets) {
+ $client = creer_client($fichier_socket);
+ if (!$client) $serveur = creer_serveur($fichier_socket);
+ }
+
+ // Fin verrou
+ if ($fp) flock($fp, LOCK_UN);
+ fclose($fp);
+
+ // Correction ortho proprement dite
+ $texte = magic_unquote($_REQUEST['texte']);
+ $log_taille += strlen($texte);
+ if ($gz = $_REQUEST['gz']) {
+ // PHP n'aime pas le caractere NUL dans les multipart/form-data ...
+ $echap = magic_unquote($_REQUEST['nul_echap']);
+ if (!$echap) $echap = "\xFF\xFF\xFF\xFF";
+ $texte = gzuncompress(str_replace($echap, "\x00", $texte));
+ }
+ $lang = magic_unquote($_REQUEST['lang']);
+ $body = "";
+ if ($client) {
+ $body = corriger_client($client, $texte, $lang);
+ $log_mode = 'client';
+ }
+ if (!strpos($body, "</ortho>")) {
+ $body = corriger($texte, $lang);
+ $log_mode = '';
+ }
+ if ($gz) $body = gzcompress($body);
+ $length = strlen($body);
+ Header("Content-Type: text/xml; charset=utf-8");
+ Header("Content-Length: ".$length);
+ Header("Connection: close");
+ echo $body;
+ flush();
+
+ $log_fin = explode(" ", microtime());
+ $log_taille += $length;
+ $log_duree = floor(1000 * ($log_fin[0] + $log_fin[1] - $log_debut[0] - $log_debut[1])) / 1000;
+ ortho_log($op, $lang, $log_taille, $log_duree, $log_mode);
+
+ if ($serveur) {
+ boucle_serveur($serveur);
+ }
+ break;
+ }
+ exit;
+}
+
+
+
+// --------------------------------------------------------------------------
+// Ici routine de test manuel
+
+if ($_REQUEST['op']) exit;
+
+$uri = $_SERVER['REQUEST_URI'];
+if ($p = strpos($uri, '?')) $uri = substr($uri, 0, $p);
+
+$texte = stripslashes($_POST['texte']);
+$lang = $_POST['lang'];
+
+Header("Content-Type: text/html; charset=utf-8");
+
+echo "<html><head><title>Test du serveur d'orthographe</title></head>";
[... 81 lines stripped ...]