uawdijnntqw1x1x1
IP : 216.73.216.175
Hostname : webm001.cluster110.gra.hosting.ovh.net
Kernel : Linux webm001.cluster110.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
Disable Function : _dyuweyrj4,_dyuweyrj4r,dl
OS : Linux
PATH:
/
home
/
etiennec
/
www
/
b9da6
/
..
/
e6335
/
..
/
b594c
/
..
/
plugins
/
..
/
f1f54
/
inc.tar
/
/
filtres_images_compat.php000066600000010273152503000360011616 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2014 * * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ /** * Ce fichier ne sert pas * Il est maintenu pour assurer la compatibilite des anciens scripts avec les anciens nommages de fonction * */ if (!defined('_ECRIRE_INC_VERSION')) return; function couleur_dec_to_hex($red, $green, $blue) {include_spip('filtres/images_lib');return _couleur_dec_to_hex($red, $green, $blue);} function couleur_hex_to_dec($couleur) {include_spip('filtres/images_lib');return _couleur_hex_to_dec($couleur);} function couleur_rgb2hsv($R,$G,$B) {include_spip('filtres/images_lib');return _couleur_rgb2hsv($R,$G,$B);} function couleur_hsv2rgb($H,$S,$V) {include_spip('filtres/images_lib');return _couleur_hsv2rgb($H,$S,$V);} function couleur_rgb2hsl($R,$G,$B) {include_spip('filtres/images_lib');return _couleur_rgb2hsl($R,$G,$B);} function couleur_hsl2rgb($H,$S,$L) {include_spip('filtres/images_lib');return _couleur_hsl2rgb($H,$S,$L);} function image_couleur_extraire($img, $x=10, $y=6) {include_spip('filtres/images_lib');return _image_couleur_extraire($img, $x, $y);} function image_distance_pixel($xo, $yo, $x0, $y0) {include_spip('filtres/images_lib');return _image_distance_pixel($xo, $yo, $x0, $y0);} function image_decal_couleur($coul, $gamma) {include_spip('filtres/images_lib');return _image_decale_composante($coul, $gamma);} function image_decal_couleur_127($coul, $val) {include_spip('filtres/images_lib');return _image_decale_composante_127 ($coul, $val);} function image_creer_vignette($valeurs, $maxWidth, $maxHeight, $process='AUTO', $force=false, $test_cache_only = false) {include_spip('inc/filtres_images_lib'); return _image_creer_vignette($valeurs, $maxWidth, $maxHeight, $process, $force, $test_cache_only);} function image_ecrire_tag($valeurs,$surcharge){include_spip('inc/filtres_images_lib'); return _image_ecrire_tag($valeurs,$surcharge);} function image_gd_output($img,$valeurs, $qualite=_IMG_GD_QUALITE){include_spip('inc/filtres_images_lib'); return _image_gd_output($img,$valeurs, $qualite);} function image_imagepng($img,$fichier) {include_spip('inc/filtres_images_lib'); return _image_imagepng($img,$fichier);} function image_imagegif($img,$fichier) {include_spip('inc/filtres_images_lib'); return _image_imagegif($img,$fichier);} function image_imagejpg($img,$fichier,$qualite=_IMG_GD_QUALITE) {include_spip('inc/filtres_images_lib'); return image_imagejpg($img,$fichier,$qualite=_IMG_GD_QUALITE);} function image_imageico($img, $fichier) {include_spip('inc/filtres_images_lib'); return _image_imageico($img, $fichier);} function image_ratio ($srcWidth, $srcHeight, $maxWidth, $maxHeight) {include_spip('inc/filtres_images_lib');return _image_ratio ($srcWidth, $srcHeight, $maxWidth, $maxHeight);} function image_tag_changer_taille($tag,$width,$height,$style=false){include_spip('inc/filtres_images_lib');return _image_tag_changer_taille($tag,$width,$height,$style);} function image_valeurs_trans($img, $effet, $forcer_format = false, $fonction_creation = NULL) {include_spip('inc/filtres_images_lib'); return _image_valeurs_trans($img, $effet, $forcer_format, $fonction_creation);} // Pour assurer la compatibilite avec les anciens nom des filtres image_xxx // commencent par "image_" // http://code.spip.net/@reduire_image function reduire_image($texte, $taille = -1, $taille_y = -1) { return filtrer('image_graver', filtrer('image_reduire',$texte, $taille, $taille_y) ); } // http://code.spip.net/@valeurs_image_trans function valeurs_image_trans($img, $effet, $forcer_format = false) { include_spip('inc/filtres_images_lib_mini'); return _image_valeurs_trans($img, $effet, $forcer_format = false); } ?>filtres_images.php000066600000002321152503000360010246 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2014 * * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ /** * Ce fichier ne sert plus * Il est maintenu pour assurer la compatibilite des anciens scripts * */ if (!defined('_ECRIRE_INC_VERSION')) return; // prise en charge des renomage de fonction include_spip('inc/filtres_images_compat'); // prise en charge des fonctions de transformation d'image include_spip('filtres/images_transforme'); // prise en charge des fonctions d'image typo include_spip('filtres/images_typo'); // prise en charge des fonctions de transformation de couleur include_spip('filtres/couleurs'); ?>svp_decider.php000066600000105051152503074170007556 0ustar00<?php /** * Gestion du décideur : il calcule en fonction d'une demande d'action * sur les plugins tous les enchainements que cela implique sur les * dépendances. * * @plugin SVP pour SPIP * @license GPL * @package SPIP\SVP\Decideur */ if (!defined("_ECRIRE_INC_VERSION")) return; include_spip('plugins/installer'); // pour spip_version_compare() include_spip('inc/svp_rechercher'); // svp_verifier_compatibilite_spip() # include_spip('inc/plugin'); // plugin_version_compatible() [inclu dans svp_rechercher] /** * Le décideur calcule les actions qui doivent être faites en fonction * de ce qui est demandé et des différentes dépendances des plugins. * * @package SPIP\SVP\Actionner **/ class Decideur { /** * Plugins actifs en cours avant toute modification * @var array * Index 'i' : plugins triés par identifiant en base [i][32] = tableau de description * Index 'p' : plugins triés par prefixe de plugin [p][MOTS] = tableau de description */ var $start = array( 'i' => array(), 'p' => array(), ); /** * Plugins actifs à la fin des modifications effectuées * @var array * Index 'i' : plugins triés par identifiant en base [i][32] = tableau de description * Index 'p' : plugins triés par prefixe de plugin [p][MOTS] = tableau de description */ var $end = array( 'i' => array(), 'p' => array(), ); /** * Plugins procure par SPIP * @var array * Tableau ('PREFIXE' => numéro de version) */ var $procure = array(); /** * Toutes les actions à faire demandées * (ce que l'on demande à l'origine) * @var array * Tableau ('identifiant' => tableau de description) */ var $ask = array(); /** * Toutes les actions à faire demandées et consécutives aux dépendances * * @var array * Tableau ('identifiant' => tableau de description) */ var $todo = array(); /** * Toutes les actions à faire consécutives aux dépendances * * C'est à dire les actions à faire en plus de celles demandées. * * @var array * Tableau ('identifiant' => tableau de description) */ var $changes = array(); /** * Tous les plugins à arrêter (désactiver ou désinstaller) * * @var array * Tableau ('PREFIXE' => tableau de description) */ var $off = array(); /** * Tous les plugins invalidés (suite a des dependances introuvables, mauvaise version de SPIP...) * * @var array * Tableau ('PREFIXE' => tableau de description) */ var $invalides = array(); /** * Liste des erreurs * * @var array * Tableau ('identifiant' => liste des erreurs) */ var $err = array(); /** * État de santé (absence d'erreur) * * Le résultat true permettra d'effectuer toutes les actions. * Passe à false dès qu'une erreur est présente ! * * @var bool */ var $ok = true; /** * Loguer les différents éléments * * Sa valeur sera initialisée par la configuration 'mode_log_verbeux' de SVP * * @var bool */ var $log = false; /** * Générer une erreur si on demande une mise à jour d'un plugin * alors qu'on ne la connait pas. * @var bool */ var $erreur_sur_maj_introuvable = true; /** * Constructeur * * Initialise la propriété $log en fonction de la configuration */ function Decideur () { include_spip('inc/config'); $this->log = (lire_config('svp/mode_log_verbeux') == 'oui'); } /** * Liste des plugins déjà actifs * * @var array * Index 'i' : plugins triés par identifiant en base [i][32] = tableau de description * Index 'p' : plugins triés par prefixe de plugin [p][MOTS] = tableau de description */ function liste_plugins_actifs() { return $this->infos_courtes(array('pa.actif='.sql_quote('oui'), 'pa.attente=' . sql_quote('non'))); } /** * Teste si un paquet (via son identifiant) est en attente * * Les plugins en attente ont un statut spécial : à la fois dans la * liste des plugins actifs, mais désactivés. Un plugin passe 'en attente' * lorsqu'il est actif mais perd accidentellement une dépendance, * par exemple si une dépendance est supprimée par FTP. * Dès que sa dépendance revient, le plugin se réactive. * * L'interface de gestion des plugins de SVP, elle, permet pour ces plugins * de les désactiver ou réactiver (retéléchargeant alors la dépendance si possible). * * @param int $id * Identifiant du plugin * @return bool * Le plugin est-il en attente ? */ function est_attente_id($id) { static $attente = null; if (is_null($attente)) { $attente = $this->infos_courtes('pa.attente=' . sql_quote('oui')); } return isset($attente['i'][$id]) ? $attente['i'][$id] : false; } /** * Liste des plugins procurés par SPIP * * Calcule la liste des plugins que le core de SPIP déclare procurer. * * @return array * Tableau ('PREFIXE' => version) */ function liste_plugins_procure() { $procure = array(); $get_infos = charger_fonction('get_infos','plugins'); $infos['_DIR_RESTREINT'][''] = $get_infos('./',false,_DIR_RESTREINT); foreach($infos['_DIR_RESTREINT']['']['procure'] as $_procure) { $prefixe = strtoupper($_procure['nom']); $procure[$prefixe] = $_procure['version']; } return $procure; } /** * Écrit un log * * Écrit un log si la propriété $log l'autorise. * * @param mixed $quoi * La chose à logguer (souvent un texte) **/ function log($quoi) { if ($this->log) { spip_log($quoi,'decideur'); } } /** * Retourne le tableau de description d'un paquet (via son identifiant) * * @note * Attention, retourne un tableau complexe. * La description sera dans : ['i'][$id] * @param int $id * Identifiant du paquet * @return array * Index 'i' : plugins triés par identifiant en base [i][32] = tableau de description * Index 'p' : plugins triés par prefixe de plugin [p][MOTS] = tableau de description **/ function infos_courtes_id($id) { // on cache ceux la static $plug = array(); if (!isset($plug[$id])) { $plug[$id] = $this->infos_courtes('pa.id_paquet=' . sql_quote($id)); } return $plug[$id]; } /** * Récupérer les infos utiles des paquet * * Crée un tableau de description pour chaque paquet dans une * écriture courte comme index ('i' pour identifiant) tel que : * - i = identifiant * - p = prefixe (en majuscule) * - n = nom du plugin * - v = version * - e = etat * - a = actif * - du = dépendances utilise * - dn = dépendances nécessite * - dl = dépendances librairie * - maj = mise à jour * * * On passe un where ($condition) et on crée deux tableaux, l'un des paquets * triés par identifiant, l'autre par prefixe. * * @param array|string $condition * Condition where * @param bool $multiple * Si multiple, le tableau par préfixe est un sous-tableau (il peut alors * y avoir plusieurs paquets pour un même prefixe, classés par états décroissants) * @return array * Index 'i' : plugins triés par identifiant en base [i][32] = tableau de description * Index 'p' : plugins triés par prefixe de plugin [p][MOTS] = tableau de description * ou, avec $multiple=true : [p][MOTS][] = tableau de description */ function infos_courtes($condition, $multiple=false) { $plugs = array( 'i'=>array(), 'p'=>array() ); $from = array('spip_paquets AS pa', 'spip_plugins AS pl'); $orderby = $multiple ? 'pa.etatnum DESC' : ''; $where = array('pa.id_plugin = pl.id_plugin'); if (is_array($condition)) $where = array_merge($where, $condition); else $where[] = $condition; include_spip('inc/filtres'); // extraire_multi() $res = sql_allfetsel(array( 'pa.id_paquet AS i', 'pl.nom AS n', 'pl.prefixe AS p', 'pa.version AS v', 'pa.etatnum AS e', 'pa.compatibilite_spip', 'pa.dependances', 'pa.id_depot', 'pa.maj_version AS maj', 'pa.actif AS a'), $from, $where, '', $orderby); foreach ($res as $r) { $r['p'] = strtoupper( $r['p'] ); // on s'assure du prefixe en majuscule. // savoir si un paquet est en local ou non... $r['local'] = ($r['id_depot']) == 0 ? true : false; unset($r['id_depot']); $d = unserialize($r['dependances']); // voir pour enregistrer en bdd simplement 'n' et 'u' (pas la peine d'encombrer)... $deps = array('necessite'=>array(array()), 'utilise'=>array(array()), 'librairie'=>array(array())); if (!$d) $d = $deps; unset($r['dependances']); /* * On extrait les multi sur le nom du plugin */ $r['n'] = extraire_multi($r['n']); $plugs['i'][$r['i']] = $r; // pour chaque type de dependences... (necessite, utilise, librairie) // on cree un tableau unique [$dependence] = array() // au lieu de plusieurs tableaux par version de spip // en ne mettant dans 0 que ce qui concerne notre spip local foreach($deps as $cle => $defaut) { if (!isset($d[$cle])) { $d[$cle] = $defaut; } // gerer les dependences autres que dans 0 (communs ou local) !!!! // il peut exister des cles info[dn]["[version_spip_min;version_spip_max]"] de dependences if (!isset($d[$cle][0]) OR count($d[$cle]) > 1) { $dep = array(); $dep[0] = isset($d[$cle][0]) ? $d[$cle][0] : array(); unset($d[$cle][0]); foreach ($d[$cle] as $version => $dependences) { if (svp_verifier_compatibilite_spip($version)) { $dep = array_merge($dep[0], $dependences); } } $d[$cle] = $dep; } } // passer les prefixes en majuscule foreach($d['necessite'][0] as $i=>$n) { $d['necessite'][0][$i]['nom'] = strtoupper($n['nom']); } $plugs['i'][$r['i']]['dn'] = $d['necessite'][0]; $plugs['i'][$r['i']]['du'] = $d['utilise'][0]; $plugs['i'][$r['i']]['dl'] = $d['librairie'][0]; if ($multiple) { $plugs['p'][$r['p']][] = &$plugs['i'][$r['i']]; // alias } else { $plugs['p'][$r['p']] = &$plugs['i'][$r['i']]; // alias } } return $plugs; } /** * Ajoute une erreur sur un paquet * * Passe le flag OK à false : on ne pourra pas faire les actions demandées. * * @param int $id * Identifiant de paquet * @param string $texte * Texte de l'erreur */ function erreur($id, $texte = '') { $this->log("erreur: $id -> $texte"); if (!isset($this->err[$id]) OR !is_array($this->err[$id])) { $this->err[$id] = array(); } $this->err[$id][] = $texte; $this->ok = false; } /** * Teste si une erreur est présente sur un paquet (via son identifiant) * * @param int $id * Identifiant de paquet * @return bool|array * false si pas d'erreur, tableau des erreurs sinon. */ function en_erreur($id) { return isset($this->err[$id]) ? $this->err[$id] : false; } /** * Vérifie qu'un plugin plus récent existe pour un préfixe et une version donnée * * @param string $prefixe * Préfixe du plugin * @param string $version * Compatibilité à comparer, exemple '[1.0;]' * @return bool|array * false si pas de plugin plus récent trouvé * tableau de description du paquet le plus récent sinon */ function chercher_plugin_recent($prefixe, $version) { $news = $this->infos_courtes(array('pl.prefixe=' . sql_quote($prefixe), 'pa.obsolete=' . sql_quote('non'), 'pa.id_depot > '.sql_quote(0)), true); $res = false; if ($news and count($news['p'][$prefixe]) > 0) { foreach ($news['p'][$prefixe] as $new) { if (spip_version_compare($new['v'],$version,'>')) { if (!$res or version_compare($new['v'],$res['v'],'>')) { $res = $new; } } } } return $res; } /** * Vérifie qu'un plugin existe pour un préfixe et une version donnée * * @param string $prefixe * Préfixe du plugin * @param string $version * Compatibilité à comparer, exemple '[1.0;]' * @return bool|array * false si pas de plugin plus récent trouvé * tableau de description du paquet le plus récent sinon */ function chercher_plugin_compatible($prefixe, $version) { $plugin = array(); // on choisit en priorite dans les paquets locaux ! $locaux = $this->infos_courtes(array( 'pl.prefixe=' . sql_quote($prefixe), 'pa.obsolete=' . sql_quote('non'), 'pa.id_depot='.sql_quote(0)), true); if ($locaux and isset($locaux['p'][$prefixe]) and count($locaux['p'][$prefixe]) > 0) { $v = '000.000.000'; foreach ($locaux['p'][$prefixe] as $new) { if (plugin_version_compatible($version, $new['v']) and svp_verifier_compatibilite_spip($new['compatibilite_spip']) and ($new['v'] > $v)){ $plugin = $new; $v = $new['v']; } } } if (!$plugin) { // sinon dans les paquets distants $distants = $this->infos_courtes(array( 'pl.prefixe=' . sql_quote($prefixe), 'pa.obsolete=' . sql_quote('non'), 'pa.id_depot>'.sql_quote(0)), true); if ($distants and isset($distants['p'][$prefixe]) and count($distants['p'][$prefixe]) > 0) { $v = '000.000.000'; foreach ($distants['p'][$prefixe] as $new) { if (plugin_version_compatible($version, $new['v']) and svp_verifier_compatibilite_spip($new['compatibilite_spip']) and ($new['v'] > $v)){ $plugin = $new; $v = $new['v']; } } } } return ($plugin ? $plugin : false); } /** * Indique qu'un paquet passe à on (on l'active) * * @param array $info * Description du paquet **/ function add($info) { $this->end['i'][$info['i']] = $info; $this->end['p'][$info['p']] = &$this->end['i'][$info['i']]; } /** * Indique qu'un paquet passe à off (on le désactive ou désinstalle) * * @param array $info * Description du paquet * @param bool $recur * Passer à off les plugins qui en dépendent, de façon récursive ? **/ function off($info, $recur = false) { $this->log('- stopper ' . $info['p']); $this->remove($info); $this->off[$info['p']] = $info; // si recursif, on stoppe aussi les plugins dependants if ($recur) { foreach ($this->end['i'] as $id => $plug) { if (is_array($plug['dn']) and $plug['dn']) { foreach ($plug['dn'] as $n) { if ($info['p'] == $n['nom']) { $this->change($plug, 'off'); $this->off($plug, true); } } } } } } /** * Teste qu'un paquet (via son préfixe) sera passé off (désactivé ou désinstallé) * * @param string $prefixe * Prefixe du paquet * @return bool * Le paquet sera t'il off ? **/ function sera_off($prefixe) { return isset($this->off[$prefixe]) ? $this->off[$prefixe] : false; } /** * Teste qu'un paquet (via son identifiant) sera passé off (désactivé ou désinstallé) * * @param int $id * Identifiant du paquet * @return bool * Le paquet sera t'il off ? **/ function sera_off_id($id) { foreach ($this->off as $info) { if ($info['i'] == $id) { return $info; } } return false; } /** * Teste qu'un paquet (via son préfixe) sera actif * * @param string $prefixe * Préfixe du paquet * @return bool * Le paquet sera t'il actif ? **/ function sera_actif($prefixe) { return isset($this->end['p'][$prefixe]) ? $this->end['p'][$prefixe] : false; } /** * Teste qu'un paquet (via son identifiant) sera actif * * @param int $id * Identifiant du paquet * @return bool * Le paquet sera t'il actif ? **/ function sera_actif_id($id) { return isset($this->end['i'][$id]) ? $this->end['i'][$id] : false; } /** * Ajouter une action/paquet à la liste des demandées * * L'ajoute aussi à la liste de toutes les actions ! * * @param array $info * Description du paquet concerné * @param string $quoi * Type d'action (on, off, kill, upon...) */ function ask($info, $quoi) { $this->ask[$info['i']] = $info; $this->ask[$info['i']]['todo'] = $quoi; $this->todo($info, $quoi); } /** * Ajouter une action/paquet à la liste des changements en plus * par rapport à la demande initiale * * L'ajoute aussi à la liste de toutes les actions ! * * @param array $info * Description du paquet concerné * @param string $quoi * Type d'action (on, off, kill, upon...) */ function change($info, $quoi) { $this->changes[$info['i']] = $info; $this->changes[$info['i']]['todo'] = $quoi; $this->todo($info, $quoi); } /** * Annule une action (automatique) qui finalement était réellement demandée. * * Par exemple, une mise à 'off' de paquet entraîne d'autres mises à * 'off' des paquets qui en dépendent. Si une action sur un des paquets * dépendants était aussi demandée, il faut annuler l'action automatique. * * @param array $info * Description du paquet concerné */ function annule_change($info) { unset($this->changes[$info['i']]); } /** * Ajouter une action/paquet à la liste de toutes les actions à faire * * @param array $info * Description du paquet concerné * @param string $quoi * Type d'action (on, off, kill, upon...) */ function todo($info, $quoi) { $this->todo[$info['i']] = $info; $this->todo[$info['i']]['todo'] = $quoi; } /** * Retire un paquet de la liste des paquets à activer * * @param array $info * Description du paquet concerné */ function remove($info) { // aucazou ce ne soit pas les memes ids entre la demande et la bdd, // on efface aussi avec l'id donne par le prefixe. // Lorsqu'on desactive un plugin en "attente", il n'est pas actif ! // on teste tout de meme donc qu'il est la ce prefixe ! $i = false; if (isset($this->end['p'][$info['p']])) { $i = $this->end['p'][$info['p']]; } // on enleve les cles par id indique et par prefixe unset($this->end['i'][$info['i']], $this->end['p'][$info['p']]); // ainsi que l'id aucazou du prefixe if ($i) { unset($this->end['i'][$i['i']]); } } /** * Invalide un plugin (il est introuvable, ne correspond pas à notre version de SPIP...) * * @param array $info * Description du paquet concerné */ function invalider($info) { $this->log("-> invalider $info[p]"); $this->remove($info); // suffisant ? $this->invalides[$info['p']] = $info; $this->annule_change($info); unset($this->todo[$info['i']]); } /** * Teste qu'un paquet (via son préfixe) est déclaré invalide * * @param string $p * Prefixe du paquet * @return bool * Le paquet est t'il invalide ? **/ function sera_invalide($p) { return isset($this->invalides[$p]) ? $this->invalides[$p] : false; } /** * Teste qu'une librairie (via son nom) est déjà présente * * @param string $lib * Nom de la librairie * @return bool * La librairie est-elle présente ? **/ function est_presente_lib($lib) { static $libs = false; if ($libs === false) { include_spip('inc/svp_outiller'); $libs = svp_lister_librairies(); } return isset($libs[$lib]) ? $libs[$lib] : false; } /** * Ajoute les actions demandées au décideur * * Chaque action est analysée et elles sont redispatchées dans différents * tableaux via les méthodes : * - ask : ce qui est demandé (ils y vont tous) * - todo : ce qui est à faire (ils y vont tous aussi) * - add : les plugins activés, * - off : les plugins désactivés * * La fonction peut lever des erreurs sur les actions tel que : * - Paquet demandé inconnu * - Mise à jour introuvable * - Paquet à désactiver mais qui n'est pas actif * * @param array $todo * Ce qui est demandé de faire * Tableau identifiant du paquet => type d'action (on, off, up...) * @return bool * False en cas d'erreur, true sinon */ function actionner($todo = null) { if (is_array($todo)) { foreach ($todo as $id => $t) { // plusieurs choses nous interessent... Sauf... le simple telechargement // et la suppression des fichiers (qui ne peuvent etre fait // que si le plugin n'est pas actif) $this->log("-- todo: $id/$t"); switch ($t) { case 'getlib': break; case 'on': case 'geton': // ajouter ce plugin dans la liste if (!$this->sera_actif_id($id)) { $i = $this->infos_courtes_id($id); if ($i = $i['i'][$id]) { $this->log("--> $t : " . $i['p'] . ' en version : ' . $i['v'] ); // se mefier : on peut tenter d'activer // un plugin de meme prefixe qu'un autre deja actif // mais qui n'est pas de meme version ou de meme etat // par exemple un plugin obsolete ou un plugin au contraire plus a jour. // dans ce cas, on desactive l'ancien (sans desactiver les dependences) // et on active le nouveau. // Si une dependance ne suit pas, une erreur se produira du coup. if (isset($this->end['p'][ $i['p'] ])) { $old = $this->end['p'][ $i['p'] ]; $this->log("-->> off : " . $old['p'] . ' en version : ' . $old['v'] ); $this->ask($old, 'off'); $this->todo($old, 'off'); // désactive l'ancien plugin, mais pas les dépendances qui en dépendent // car normalement, ça devrait suivre... $this->off($old, false); } // pas de prefixe equivalent actif... $this->add($i); $this->ask($i, $i['local'] ? 'on' : 'geton' ); } else { // la c'est vraiment pas normal... Erreur plugin inexistant... // concurrence entre administrateurs ? $this->erreur($id, _T('svp:message_nok_plugin_inexistant',array('plugin' => $id))); } } break; case 'up': case 'upon': // le plugin peut etre actif ! // ajouter ce plugin dans la liste et retirer l'ancien $i = $this->infos_courtes_id($id); if ($i = $i['i'][$id]) { $this->log("--> $t : " . $i['p'] . ' en version : ' . $i['v'] ); // new : plugin a installer if ($new = $this->chercher_plugin_recent($i['p'], $i['v'])) { $this->log("--> maj : " . $new['p'] . ' en version : ' . $new['v'] ); // ajouter seulement si on l'active ! // ou si le plugin est actuellement actif if ($t == 'upon' or $this->sera_actif_id($id)) { $this->remove($i); $this->add($new); } $this->ask($i, $t); } else { if ($this->erreur_sur_maj_introuvable) { // on n'a pas trouve la nouveaute !!! $this->erreur($id, _T('svp:message_nok_maj_introuvable',array('plugin' => $i['n'],'id'=>$id))); } } } else { // mauvais identifiant ? // on n'a pas trouve le plugin !!! $this->erreur($id, _T('svp:message_erreur_maj_inconnu',array('id'=>$id))); } break; case 'off': case 'stop': // retirer ce plugin // (il l'est peut etre deja) if ($info = $this->sera_actif_id($id) or $info_off = $this->sera_off_id($id) // un plugin en attente (desactive parce sa dependance a disparu certainement par ftp) // peut etre desactive or $info = $this->est_attente_id($id)) { // annuler le signalement en "proposition" (due a une mise a 'off' recursive) // de cet arret de plugin, vu qu'on le demande reellement if (!$info) { $info = $info_off; $this->annule_change($info); } $this->log("--> $t : " . $info['p'] . ' en version : ' . denormaliser_version($info['v']) ); $this->ask($info, $t); $this->todo($info, $t); // désactive tous les plugins qui en dépendent aussi. $this->off($info, true); } else { // pas normal... plugin deja inactif... // concurrence entre administrateurs ? $this->erreur($id, _T('svp:message_erreur_plugin_non_actif')); } break; case 'null': case 'get': case 'kill': if ($info = $this->infos_courtes_id($id)) { $this->log("--> $t : " . $info['i'][$id]['p'] . ' en version : ' . $info['i'][$id]['v'] ); $this->ask($info['i'][$id], $t); } else { // pas normal... plugin inconnu... concurrence entre administrateurs ? $this->erreur($id, _T('svp:message_erreur_plugin_introuvable',array('plugin'=>$id,'action'=>$t))); } break; } } } return $this->ok; } /** * Initialise les listes de plugins pour le calcul de dépendances * * Les propriété $start et $end reçoivent la liste des plugins actifs * $procure celle des plugins procurés par le Core */ function start() { $this->start = $this->end = $this->liste_plugins_actifs(); $this->procure = $this->liste_plugins_procure(); } /** * Vérifier (et activer) les dépendances * * Pour chaque plugin qui sera actif, vérifie qu'il respecte * ses dépendances. * * Si ce n'est pas le cas, le plugin n'est pas activé et le calcul * de dépendances se refait sans lui. À un moment on a normalement * rapidement une liste de plugins cohérents (au pire on ne boucle * que 100 fois maximum - ce qui ne devrait jamais se produire). * * Des erreurs sont levées lorsqu'un plugin ne peut honorer son activation * à cause d'un problème de dépendance. On peut les récupérer dans la * propriété $err. * * @api * @param array $todo * Ce qui est demandé de faire * Tableau identifiant du paquet => type d'action (on, off, up...) * @return bool * False en cas d'erreur, true sinon */ function verifier_dependances($todo = null) { $this->start(); // ajouter les actions if (!$this->actionner($todo)) { $this->log("! Todo en echec !"); $this->log($decideur->err); return false; } // doit on reverifier les dependances ? // oui des qu'on modifie quelque chose... // attention a ne pas boucler infiniment ! $supersticieux = 0; do { $try_again = 0; $supersticieux++; // verifier chaque dependance de chaque plugin a activer foreach ($this->end['i'] as $info) { if (!$this->verifier_dependances_plugin($info)) { $try_again = true; } } unset($id, $info); $this->log("--------> try_again: $try_again, supersticieux: $supersticieux"); } while ($try_again > 0 and $supersticieux < 100); # and !count($this->err) $this->log("Fin !"); $this->log("Ok: " . $this->ok); # $this->log($this->todo); return $this->ok; } /** * Pour une description de paquet donnée, vérifie sa validité. * * Teste la version de SPIP, les librairies nécessitées, ses dépendances * (et tente de les trouver et ajouter si elles ne sont pas là) * * Lorsqu'une dépendance est activée, on entre en récursion * dans cette fonction avec la description de la dépendance * * @param array $info * Description du paquet * @param int $prof * Profondeur de récursion * @return bool * false si erreur (dépendance non résolue, incompatibilité...), true sinon **/ function verifier_dependances_plugin($info, $prof=0) { $this->log("- [$prof] verifier dependances " . $info['p']); $id = $info['i']; $err = false; // variable receptionnant parfois des erreurs $cache = array(); // cache des actions realisees dans ce tour // 1 // tester la version de SPIP de notre paquet // si on ne valide pas, on retourne une erreur ! // mais normalement, on ne devrait vraiment pas pouvoir tomber sur ce cas if (!svp_verifier_compatibilite_spip($info['compatibilite_spip'])) { $this->invalider($info); $this->erreur($id, _T('svp:message_incompatibilite_spip',array('plugin'=>$info['n']))); return false; } // 2 // ajouter les librairies necessaires a notre paquet if (is_array($info['dl']) and count($info['dl'])) { foreach ($info['dl'] as $l) { // $l = array('nom' => 'x', 'lien' => 'url') $lib = $l['nom']; $this->log("## Necessite la librairie : " . $lib ); // on verifie sa presence OU le fait qu'on pourra la telecharger if ($lib and !$this->est_presente_lib($lib)) { // peut on ecrire ? if (!is_writable(_DIR_LIB)) { $this->invalider($info); $this->erreur($id, _T('svp:message_erreur_ecriture_lib', array('plugin'=>$info['n'], 'lib_url'=>$l['lien'], 'lib'=>$lib))); $err = true; } // ajout, pour info // de la librairie dans la todo list else { $this->change(array( 'i' => md5(serialize($l)), 'p' => $lib, 'n' => $lib, 'v' => $l['lien'], ), 'getlib'); $this->log("- La librairie $lib sera a télécharger"); } } } if ($err) { return false; } } // 3 // Trouver les dependences aux necessites // et les activer au besoin if (is_array($info['dn']) and count($info['dn'])) { foreach ($info['dn'] as $n) { $p = $n['nom']; $v = $n['compatibilite']; if ($p == 'SPIP') { // c'est pas la que ça se fait ! // ca ne devrait plus apparaitre comme dependence a un plugin. } // le core procure le paquet que l'on demande ! elseif ((array_key_exists($p, $this->procure)) and (plugin_version_compatible($v, $this->procure[$p], 'spip'))) { // rien a faire... $this->log("-- est procure par le core ($p)"); } // pas d'autre alternative qu'un vrai paquet a activer else { $this->log("-- verifier : $p"); // nous sommes face a une dependance de plugin // on regarde s'il est present et a la bonne version // sinon on le cherche et on l'ajoute if ($ninfo = $this->sera_actif($p) and !$err = $this->en_erreur($ninfo['i']) and plugin_version_compatible($v, $ninfo['v'])) { // il est deja actif ou a activer, et tout est ok $this->log('-- dep OK pour '.$info['p'].' : '.$p); } // il faut le trouver et demander a l'activer else { // absent ou erreur ou pas compatible $etat = $err ? 'erreur' : ($ninfo ? 'conflit' : 'absent'); // conflit signifie qu'il existe le prefixe actif, mais pas a la version demandee $this->log("Dedendance " . $p . " a resoudre ! ($etat)"); switch ($etat) { // commencons par le plus simple : // en cas d'absence, on cherche ou est ce plugin ! case 'absent': // on choisit par defaut le meilleur etat de plugin. // de preference dans les plugins locaux, sinon en distant. if (!$this->sera_off($p) and $new = $this->chercher_plugin_compatible($p, $v) and $this->verifier_dependances_plugin($new, ++$prof)) { // si le plugin existe localement et possede maj_version, // c'est que c'est peut etre une mise a jour + activation a faire // si le plugin // nouveau est local => non // nouveau est distant => oui peut etre $cache[] = $new; $i = array(); if (!$new['local']) { $i = $this->infos_courtes(array( 'pl.prefixe=' . sql_quote($new['p']), 'pa.maj_version=' . sql_quote($new['v']) ), true); } if ($i and isset($i['p'][$new['p']]) and count($i['p'][$new['p']])) { // c'est une mise a jour $vieux = $i['p'][$new['p']][0]; $this->change($vieux, 'upon'); $this->log("-- update+active : $p"); } else { // tout nouveau tout beau $this->change($new, $new['local'] ? 'on' : 'geton'); if ($new['local']) { $this->log("-- nouveau present : $p"); } else { $this->log("-- nouveau distant : $p"); } } $this->add($new); } else { $this->log("-- !erreur : $p"); // on ne trouve pas la dependance ! $this->invalider($info); $this->erreur($id, $v ? _T('svp:message_dependance_plugin_version',array('plugin'=>$info['n'],'dependance'=>$p,'version'=>denormaliser_version($v))) : _T('svp:message_dependance_plugin',array('plugin'=>$info['n'],'dependance'=>$p))); } unset($new, $vieux); break; case 'erreur': break; // present, mais conflit de version // de deux choses l'une : // soit on trouve un paquet meilleur... // soit pas :) case 'conflit': $this->log(" conflit -> demande $v, present : " . $ninfo['v']); if (!$this->sera_off($p) and $new = $this->chercher_plugin_compatible($p, $v) and $this->verifier_dependances_plugin($new, ++$prof)) { // on connait le nouveau... $cache[] = $new; $this->remove($ninfo); $this->add($new); $this->change($ninfo,'up'); $this->log("-- update : $p"); } else { $this->log("-- !erreur : $p"); // on ne trouve pas la dependance ! $this->invalider($info); $this->erreur($id, $v ? _T('svp:message_dependance_plugin_version',array('plugin'=>$info['n'],'dependance'=>$p,'version'=>denormaliser_version($v))) : _T('svp:message_dependance_plugin',array('plugin'=>$info['n'],'dependance'=>$p))); } break; } } } if ($this->sera_invalide($info['p'])) { break; } } unset($n, $v, $p, $ninfo, $present, $conflit, $erreur, $err); // si le plugin est devenu invalide... // on invalide toutes les actions qu'on vient de faire ! if ($this->sera_invalide($info['p'])) { $this->log("> Purge du cache"); foreach ($cache as $i) { $this->invalider($i); } return false; } } return true; } /** * Retourne un tableau des différentes actions qui seront faites * * @param string $quoi * Type de demande * - ask : les actions demandées * - changes : les actions en plus par rapport à ce qui était demandé * - todo : toutes les actions * @return array * Liste des actions (joliement traduites et expliquées) **/ function presenter_actions($quoi) { $res = array(); foreach ($this->$quoi as $id=>$info) { $res[] = _T('svp:message_action_'.$info['todo'], array( 'plugin'=>$info['n'], 'version'=>denormaliser_version($info['v']), 'version_maj'=>denormaliser_version($info['maj']))); } return $res; } } /** * Gère la partie vérifier des formulaires utilisant le Décideur * * @param array $a_actionner * Tableau des actions par paquet (id_paquet => action) * @param array $erreurs * Tableau d'erreurs de verifier (CVT) * @return bool * true si tout va bien, false sinon (erreur pour trouver les dépendances, ...) **/ function svp_decider_verifier_actions_demandees($a_actionner, &$erreurs) { $decideur = new Decideur; $decideur->erreur_sur_maj_introuvable = false; $decideur->verifier_dependances($a_actionner); if (!$decideur->ok) { $erreurs['decideur_erreurs'] = array(); foreach ($decideur->err as $id=>$errs) { foreach($errs as $err) { $erreurs['decideur_erreurs'][] = $err; } } return false; } $erreurs['decideur_propositions'] = $decideur->presenter_actions('changes'); $erreurs['decideur_demandes'] = $decideur->presenter_actions('ask'); $erreurs['decideur_actions'] = $decideur->presenter_actions('todo'); // On construit la liste des actions pour la passer au formulaire en hidden $todo = array(); foreach ($decideur->todo as $_todo) { $todo[$_todo['i']] = $_todo['todo']; } set_request('_todo', serialize($todo)); return true; } ?> where_compatible_spip.php000066600000003530152503074170011632 0ustar00<?php /** * Gestion des recherches de plugins par version ou branche * * @plugin SVP pour SPIP * @license GPL * @package SPIP\SVP\Recherche **/ if (!defined("_ECRIRE_INC_VERSION")) return; /** * Construit le WHERE d'une requête SQL de selection des plugins ou paquets * compatibles avec une version ou une branche de spip. * * Cette fonction est appelée par le critère {compatible_spip} * * @used-by svp_compter() * @used-by critere_compatible_spip_dist() * * @param string $version * Numéro de version de SPIP, tel que 2.0.8 * @param string $table * Table d'application ou son alias SQL * @param string $op * Opérateur de comparaison, tel que '>' ou '=' * @return string * Expression where de la requête SQL */ function inc_where_compatible_spip($version='', $table, $op) { // le critere s'applique a une VERSION (1.9.2, 2.0.8, ...) if (count(explode('.', $version)) == 3) { $min = 'SUBSTRING_INDEX(' . $table . '.compatibilite_spip, \';\', 1)'; $max = 'SUBSTRING_INDEX(' . $table . '.compatibilite_spip, \';\', -1)'; $where = 'CASE WHEN '.$min.' = \'\' OR '.$min.' = \'[\' THEN \'1.9.0\' <= \''.$version.'\' ELSE TRIM(LEADING \'[\' FROM '.$min.') <= \''.$version.'\' END AND CASE WHEN '.$max.' = \'\' OR '.$max.' = \']\' THEN \'99.99.99\' >= \''.$version.'\' WHEN '.$max.' = \')\' OR '.$max.' = \'[\' THEN \'99.99.99\' > \''.$version.'\' WHEN RIGHT('.$max.', 1) = \')\' OR RIGHT('.$max.', 1) = \'[\' THEN LEFT('.$max.', LENGTH('.$max.') - 1) > \''.$version.'\' ELSE LEFT('.$max.', LENGTH('.$max.') - 1) >= \''.$version.'\' END'; } // le critere s'applique a une BRANCHE (1.9, 2.0, ...) elseif (count(explode('.', $version)) == 2) { $where = 'LOCATE(\''.$version.'\', '.$table.'.branches_spip) '.$op.' 0'; } // le critere est vide ou mal specifie else { $where = '1=1'; } return $where; } ?> pcltar.php000066600000374446152503074170006574 0ustar00<?php // -------------------------------------------------------------------------------- // PhpConcept Library - Tar Module 1.3.1 // -------------------------------------------------------------------------------- // License GNU/GPL - Vincent Blavet - January 2003 // http://www.phpconcept.net // -------------------------------------------------------------------------------- // // Presentation : // PclTar is a library that allow you to create a GNU TAR + GNU ZIP archive, // to add files or directories, to extract all the archive or a part of it. // So far tests show that the files generated by PclTar are readable by // gzip tools and WinZip application. // // Description : // See readme.txt (English & Fran�ais) and http://www.phpconcept.net // // Warning : // This library and the associated files are non commercial, non professional // work. // It should not have unexpected results. However if any damage is caused by // this software the author can not be responsible. // The use of this software is at the risk of the user. // // -------------------------------------------------------------------------------- // ----- Look for double include if (!defined("PCL_TAR")) { define( "PCL_TAR", 1 ); // ----- Configuration variable // Theses values may be changed by the user of PclTar library if (!isset($g_pcltar_lib_dir)) $g_pcltar_lib_dir = find_in_path("lib/pcltar"); // ----- Error codes // -1 : Unable to open file in binary write mode // -2 : Unable to open file in binary read mode // -3 : Invalid parameters // -4 : File does not exist // -5 : Filename is too long (max. 99) // -6 : Not a valid tar file // -7 : Invalid extracted file size // -8 : Unable to create directory // -9 : Invalid archive extension // -10 : Invalid archive format // -11 : Unable to delete file (unlink) // -12 : Unable to rename file (rename) // -13 : Invalid header checksum // -------------------------------------------------------------------------------- // ***** UNDER THIS LINE NOTHING NEEDS TO BE MODIFIED ***** // -------------------------------------------------------------------------------- // ----- Global variables $g_pcltar_version = "1.3.1"; // ----- Extract extension type (.php3/.php/...) $g_pcltar_extension = "php"; // ----- Include other libraries // This library should be called by each script before the include of PhpZip // Library in order to limit the potential 'lib' directory path problem. if (!defined("PCLERROR_LIB")) { include($g_pcltar_lib_dir."/pclerror.lib.".$g_pcltar_extension); } if (!defined("PCLTRACE_LIB")) { include($g_pcltar_lib_dir."/pcltrace.lib.".$g_pcltar_extension); } // -------------------------------------------------------------------------------- // Function : PclTarCreate() // Description : // Creates a new archive with name $p_tarname containing the files and/or // directories indicated in $p_list. If the tar filename extension is // ".tar", the file will not be compressed. If it is ".tar.gz" or ".tgz" // it will be a gzip compressed tar archive. // If you want to use an other extension, you must indicate the mode in // $p_mode ("tar" or "tgz"). // $p_add_dir and $p_remove_dir give you the ability to store a path // which is not the real path of the files. // Parameters : // $p_tarname : Name of an existing tar file // $p_filelist : An array containing file or directory names, or // a string containing one filename or directory name, or // a string containing a list of filenames and/or directory // names separated by spaces. // $p_mode : "tar" for normal tar archive, "tgz" for gzipped tar archive, // if $p_mode is not specified, it will be determined by the extension. // $p_add_dir : Path to add in the filename path archived // $p_remove_dir : Path to remove in the filename path archived // Return Values : // 1 on success, or an error code (see table at the beginning). // -------------------------------------------------------------------------------- function PclTarCreate($p_tarname, $p_filelist="", $p_mode="", $p_add_dir="", $p_remove_dir="") { TrFctStart(__FILE__, __LINE__, "PclTarCreate", "tar=$p_tarname, file='$p_filelist', mode=$p_mode, add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); $v_result=1; // ----- Look for default mode if (($p_mode == "") || (($p_mode!="tar") && ($p_mode!="tgz"))) { // ----- Extract the tar format from the extension if (($p_mode = PclTarHandleExtension($p_tarname)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Trace TrFctMessage(__FILE__, __LINE__, 1, "Auto mode selected : found $p_mode"); } // ----- Look if the $p_filelist is really an array if (is_array($p_filelist)) { // ----- Call the create fct $v_result = PclTarHandleCreate($p_tarname, $p_filelist, $p_mode, $p_add_dir, $p_remove_dir); } // ----- Look if the $p_filelist is a string else if (is_string($p_filelist)) { // ----- Create a list with the elements from the string $v_list = explode(" ", $p_filelist); // ----- Call the create fct $v_result = PclTarHandleCreate($p_tarname, $v_list, $p_mode, $p_add_dir, $p_remove_dir); } // ----- Invalid variable else { // ----- Error log PclErrorLog(-3, "Invalid variable type p_filelist"); $v_result = -3; } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarAdd() // Description : // PLEASE DO NOT USE ANY MORE THIS FUNCTION. Use PclTarAddList(). // // This function is maintained only for compatibility reason // // Parameters : // $p_tarname : Name of an existing tar file // $p_filelist : An array containing file or directory names, or // a string containing one filename or directory name, or // a string containing a list of filenames and/or directory // names separated by spaces. // Return Values : // 1 on success, // Or an error code (see list on top). // -------------------------------------------------------------------------------- function PclTarAdd($p_tarname, $p_filelist) { TrFctStart(__FILE__, __LINE__, "PclTarAdd", "tar=$p_tarname, file=$p_filelist"); $v_result=1; $v_list_detail = array(); // ----- Extract the tar format from the extension if (($p_mode = PclTarHandleExtension($p_tarname)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Look if the $p_filelist is really an array if (is_array($p_filelist)) { // ----- Call the add fct $v_result = PclTarHandleAppend($p_tarname, $p_filelist, $p_mode, $v_list_detail, "", ""); } // ----- Look if the $p_filelist is a string else if (is_string($p_filelist)) { // ----- Create a list with the elements from the string $v_list = explode(" ", $p_filelist); // ----- Call the add fct $v_result = PclTarHandleAppend($p_tarname, $v_list, $p_mode, $v_list_detail, "", ""); } // ----- Invalid variable else { // ----- Error log PclErrorLog(-3, "Invalid variable type p_filelist"); $v_result = -3; } // ----- Cleaning unset($v_list_detail); // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarAddList() // Description : // Add a list of files or directories ($p_filelist) in the tar archive $p_tarname. // The list can be an array of file/directory names or a string with names // separated by one space. // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is // different from the real path of the file. This is usefull if you want to have PclTar // running in any directory, and memorize relative path from an other directory. // If $p_mode is not set it will be automatically computed from the $p_tarname // extension (.tar, .tar.gz or .tgz). // Parameters : // $p_tarname : Name of an existing tar file // $p_filelist : An array containing file or directory names, or // a string containing one filename or directory name, or // a string containing a list of filenames and/or directory // names separated by spaces. // $p_add_dir : Path to add in the filename path archived // $p_remove_dir : Path to remove in the filename path archived // $p_mode : 'tar' or 'tgz', if not set, will be determined by $p_tarname extension // Return Values : // 1 on success, // Or an error code (see list on top). // -------------------------------------------------------------------------------- function PclTarAddList($p_tarname, $p_filelist, $p_add_dir="", $p_remove_dir="", $p_mode="") { TrFctStart(__FILE__, __LINE__, "PclTarAddList", "tar=$p_tarname, file=$p_filelist, p_add_dir='$p_add_dir', p_remove_dir='$p_remove_dir', mode=$p_mode"); $v_result=1; $p_list_detail = array(); // ----- Extract the tar format from the extension if (($p_mode == "") || (($p_mode!="tar") && ($p_mode!="tgz"))) { if (($p_mode = PclTarHandleExtension($p_tarname)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } } // ----- Look if the $p_filelist is really an array if (is_array($p_filelist)) { // ----- Call the add fct $v_result = PclTarHandleAppend($p_tarname, $p_filelist, $p_mode, $p_list_detail, $p_add_dir, $p_remove_dir); } // ----- Look if the $p_filelist is a string else if (is_string($p_filelist)) { // ----- Create a list with the elements from the string $v_list = explode(" ", $p_filelist); // ----- Call the add fct $v_result = PclTarHandleAppend($p_tarname, $v_list, $p_mode, $p_list_detail, $p_add_dir, $p_remove_dir); } // ----- Invalid variable else { // ----- Error log PclErrorLog(-3, "Invalid variable type p_filelist"); $v_result = -3; } // ----- Return if ($v_result != 1) { TrFctEnd(__FILE__, __LINE__, 0); return 0; } TrFctEnd(__FILE__, __LINE__, $p_list_detail); return $p_list_detail; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarList() // Description : // Gives the list of all the files present in the tar archive $p_tarname. // The list is the function result, it will be 0 on error. // Depending on the $p_tarname extension (.tar, .tar.gz or .tgz) the // function will determine the type of the archive. // Parameters : // $p_tarname : Name of an existing tar file // $p_mode : 'tar' or 'tgz', if not set, will be determined by $p_tarname extension // Return Values : // 0 on error (Use PclErrorCode() and PclErrorString() for more info) // or // An array containing file properties. Each file properties is an array of // properties. // The properties (array field names) are : // filename, size, mode, uid, gid, mtime, typeflag, status // Exemple : $v_list = PclTarList("my.tar"); // for ($i=0; $i<sizeof($v_list); $i++) // echo "Filename :'".$v_list[$i][filename]."'<br>"; // -------------------------------------------------------------------------------- function PclTarList($p_tarname, $p_mode="") { TrFctStart(__FILE__, __LINE__, "PclTarList", "tar=$p_tarname, mode='$p_mode'"); $v_result=1; // ----- Extract the tar format from the extension if (($p_mode == "") || (($p_mode!="tar") && ($p_mode!="tgz"))) { if (($p_mode = PclTarHandleExtension($p_tarname)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } } // ----- Call the extracting fct $p_list = array(); if (($v_result = PclTarHandleExtract($p_tarname, 0, $p_list, "list", "", $p_mode, "")) != 1) { unset($p_list); TrFctEnd(__FILE__, __LINE__, 0, PclErrorString()); return(0); } // ----- Return TrFctEnd(__FILE__, __LINE__, $p_list); return $p_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarExtract() // Description : // Extract all the files present in the archive $p_tarname, in the directory // $p_path. The relative path of the archived files are keep and become // relative to $p_path. // If a file with the same name already exists it will be replaced. // If the path to the file does not exist, it will be created. // Depending on the $p_tarname extension (.tar, .tar.gz or .tgz) the // function will determine the type of the archive. // Parameters : // $p_tarname : Name of an existing tar file. // $p_path : Path where the files will be extracted. The files will use // their memorized path from $p_path. // If $p_path is "", files will be extracted in "./". // $p_remove_path : Path to remove (from the file memorized path) while writing the // extracted files. If the path does not match the file path, // the file is extracted with its memorized path. // $p_path and $p_remove_path are commulative. // $p_mode : 'tar' or 'tgz', if not set, will be determined by $p_tarname extension // Return Values : // Same as PclTarList() // -------------------------------------------------------------------------------- function PclTarExtract($p_tarname, $p_path="./", $p_remove_path="", $p_mode="") { TrFctStart(__FILE__, __LINE__, "PclTarExtract", "tar='$p_tarname', path='$p_path', remove_path='$p_remove_path', mode='$p_mode'"); $v_result=1; // ----- Extract the tar format from the extension if (($p_mode == "") || (($p_mode!="tar") && ($p_mode!="tgz"))) { if (($p_mode = PclTarHandleExtension($p_tarname)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } } // ----- Call the extracting fct if (($v_result = PclTarHandleExtract($p_tarname, 0, $p_list, "complete", $p_path, $v_tar_mode, $p_remove_path)) != 1) { TrFctEnd(__FILE__, __LINE__, 0, PclErrorString()); return(0); } // ----- Return TrFctEnd(__FILE__, __LINE__, $p_list); return $p_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarExtractList() // Description : // Extract the files present in the archive $p_tarname and specified in // $p_filelist, in the directory // $p_path. The relative path of the archived files are keep and become // relative to $p_path. // If a directory is sp�cified in the list, all the files from this directory // will be extracted. // If a file with the same name already exists it will be replaced. // If the path to the file does not exist, it will be created. // Depending on the $p_tarname extension (.tar, .tar.gz or .tgz) the // function will determine the type of the archive. // Parameters : // $p_tarname : Name of an existing tar file // $p_filelist : An array containing file or directory names, or // a string containing one filename or directory name, or // a string containing a list of filenames and/or directory // names separated by spaces. // $p_path : Path where the files will be extracted. The files will use // their memorized path from $p_path. // If $p_path is "", files will be extracted in "./". // $p_remove_path : Path to remove (from the file memorized path) while writing the // extracted files. If the path does not match the file path, // the file is extracted with its memorized path. // $p_path and $p_remove_path are commulative. // $p_mode : 'tar' or 'tgz', if not set, will be determined by $p_tarname extension // Return Values : // Same as PclTarList() // -------------------------------------------------------------------------------- function PclTarExtractList($p_tarname, $p_filelist, $p_path="./", $p_remove_path="", $p_mode="") { TrFctStart(__FILE__, __LINE__, "PclTarExtractList", "tar=$p_tarname, list, path=$p_path, remove_path='$p_remove_path', mode='$p_mode'"); $v_result=1; // ----- Extract the tar format from the extension if (($p_mode == "") || (($p_mode!="tar") && ($p_mode!="tgz"))) { if (($p_mode = PclTarHandleExtension($p_tarname)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } } // ----- Look if the $p_filelist is really an array if (is_array($p_filelist)) { // ----- Call the extracting fct if (($v_result = PclTarHandleExtract($p_tarname, $p_filelist, $p_list, "partial", $p_path, $v_tar_mode, $p_remove_path)) != 1) { TrFctEnd(__FILE__, __LINE__, 0, PclErrorString()); return(0); } } // ----- Look if the $p_filelist is a string else if (is_string($p_filelist)) { // ----- Create a list with the elements from the string $v_list = explode(" ", $p_filelist); // ----- Call the extracting fct if (($v_result = PclTarHandleExtract($p_tarname, $v_list, $p_list, "partial", $p_path, $v_tar_mode, $p_remove_path)) != 1) { TrFctEnd(__FILE__, __LINE__, 0, PclErrorString()); return(0); } } // ----- Invalid variable else { // ----- Error log PclErrorLog(-3, "Invalid variable type p_filelist"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } // ----- Return TrFctEnd(__FILE__, __LINE__, $p_list); return $p_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarExtractIndex() // Description : // Extract the files present in the archive $p_tarname and specified at // the indexes in $p_index, in the directory // $p_path. The relative path of the archived files are keep and become // relative to $p_path. // If a directory is specified in the list, the directory only is created. All // the file stored in this archive for this directory // are not extracted. // If a file with the same name already exists it will be replaced. // If the path to the file does not exist, it will be created. // Depending on the $p_tarname extension (.tar, .tar.gz or .tgz) the // function will determine the type of the archive. // Parameters : // $p_tarname : Name of an existing tar file // $p_index : A single index (integer) or a string of indexes of files to // extract. The form of the string is "0,4-6,8-12" with only numbers // and '-' for range or ',' to separate ranges. No spaces or ';' // are allowed. // $p_path : Path where the files will be extracted. The files will use // their memorized path from $p_path. // If $p_path is "", files will be extracted in "./". // $p_remove_path : Path to remove (from the file memorized path) while writing the // extracted files. If the path does not match the file path, // the file is extracted with its memorized path. // $p_path and $p_remove_path are commulative. // $p_mode : 'tar' or 'tgz', if not set, will be determined by $p_tarname extension // Return Values : // Same as PclTarList() // -------------------------------------------------------------------------------- function PclTarExtractIndex($p_tarname, $p_index, $p_path="./", $p_remove_path="", $p_mode="") { TrFctStart(__FILE__, __LINE__, "PclTarExtractIndex", "tar=$p_tarname, index='$p_index', path=$p_path, remove_path='$p_remove_path', mode='$p_mode'"); $v_result=1; // ----- Extract the tar format from the extension if (($p_mode == "") || (($p_mode!="tar") && ($p_mode!="tgz"))) { if (($p_mode = PclTarHandleExtension($p_tarname)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } } // ----- Look if the $p_index is really an integer if (is_integer($p_index)) { // ----- Call the extracting fct if (($v_result = PclTarHandleExtractByIndexList($p_tarname, "$p_index", $p_list, $p_path, $p_remove_path, $v_tar_mode)) != 1) { TrFctEnd(__FILE__, __LINE__, 0, PclErrorString()); return(0); } } // ----- Look if the $p_filelist is a string else if (is_string($p_index)) { // ----- Call the extracting fct if (($v_result = PclTarHandleExtractByIndexList($p_tarname, $p_index, $p_list, $p_path, $p_remove_path, $v_tar_mode)) != 1) { TrFctEnd(__FILE__, __LINE__, 0, PclErrorString()); return(0); } } // ----- Invalid variable else { // ----- Error log PclErrorLog(-3, "Invalid variable type $p_index"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } // ----- Return TrFctEnd(__FILE__, __LINE__, $p_list); return $p_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarDelete() // Description : // This function deletes from the archive $p_tarname the files which are listed // in $p_filelist. $p_filelist can be a string with file names separated by // spaces, or an array containing the file names. // Parameters : // $p_tarname : Name of an existing tar file // $p_filelist : An array or a string containing file names to remove from the // archive. // $p_mode : 'tar' or 'tgz', if not set, will be determined by $p_tarname extension // Return Values : // List of the files which are kept in the archive (same format as PclTarList()) // -------------------------------------------------------------------------------- function PclTarDelete($p_tarname, $p_filelist, $p_mode="") { TrFctStart(__FILE__, __LINE__, "PclTarDelete", "tar='$p_tarname', list='$p_filelist', mode='$p_mode'"); $v_result=1; // ----- Extract the tar format from the extension if (($p_mode == "") || (($p_mode!="tar") && ($p_mode!="tgz"))) { if (($p_mode = PclTarHandleExtension($p_tarname)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } } // ----- Look if the $p_filelist is really an array if (is_array($p_filelist)) { // ----- Call the extracting fct if (($v_result = PclTarHandleDelete($p_tarname, $p_filelist, $p_list, $p_mode)) != 1) { TrFctEnd(__FILE__, __LINE__, 0, PclErrorString()); return(0); } } // ----- Look if the $p_filelist is a string else if (is_string($p_filelist)) { // ----- Create a list with the elements from the string $v_list = explode(" ", $p_filelist); // ----- Call the extracting fct if (($v_result = PclTarHandleDelete($p_tarname, $v_list, $p_list, $p_mode)) != 1) { TrFctEnd(__FILE__, __LINE__, 0, PclErrorString()); return(0); } } // ----- Invalid variable else { // ----- Error log PclErrorLog(-3, "Invalid variable type p_filelist"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } // ----- Return TrFctEnd(__FILE__, __LINE__, $p_list); return $p_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarUpdate() // Description : // This function updates the files in $p_filelist which are already in the // $p_tarname archive with an older last modified date. If the file does not // exist, it is added at the end of the archive. // Parameters : // $p_tarname : Name of an existing tar file // $p_filelist : An array or a string containing file names to update from the // archive. // $p_mode : 'tar' or 'tgz', if not set, will be determined by $p_tarname extension // Return Values : // List of the files contained in the archive. The field status contains // "updated", "not_updated", "added" or "ok" for the files not concerned. // -------------------------------------------------------------------------------- function PclTarUpdate($p_tarname, $p_filelist, $p_mode="", $p_add_dir="", $p_remove_dir="") { TrFctStart(__FILE__, __LINE__, "PclTarUpdate", "tar='$p_tarname', list='$p_filelist', mode='$p_mode'"); $v_result=1; // ----- Extract the tar format from the extension if (($p_mode == "") || (($p_mode!="tar") && ($p_mode!="tgz"))) { if (($p_mode = PclTarHandleExtension($p_tarname)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } } // ----- Look if the $p_filelist is really an array if (is_array($p_filelist)) { // ----- Call the extracting fct if (($v_result = PclTarHandleUpdate($p_tarname, $p_filelist, $p_list, $p_mode, $p_add_dir, $p_remove_dir)) != 1) { TrFctEnd(__FILE__, __LINE__, 0, PclErrorString()); return(0); } } // ----- Look if the $p_filelist is a string else if (is_string($p_filelist)) { // ----- Create a list with the elements from the string $v_list = explode(" ", $p_filelist); // ----- Call the extracting fct if (($v_result = PclTarHandleUpdate($p_tarname, $v_list, $p_list, $p_mode, $p_add_dir, $p_remove_dir)) != 1) { TrFctEnd(__FILE__, __LINE__, 0, PclErrorString()); return(0); } } // ----- Invalid variable else { // ----- Error log PclErrorLog(-3, "Invalid variable type p_filelist"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } // ----- Return TrFctEnd(__FILE__, __LINE__, $p_list); return $p_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarMerge() // Description : // This function add the content of $p_tarname_add at the end of $p_tarname. // Parameters : // $p_tarname : Name of an existing tar file // $p_tarname_add : Name of an existing tar file taht will be added at the end // of $p_tarname. // $p_mode : 'tar' or 'tgz', if not set, will be determined by $p_tarname extension // $p_mode_add : 'tar' or 'tgz', if not set, will be determined by $p_tarname_add // extension // Return Values : // List of the files contained in the archive. The field status contains // "updated", "not_updated", "added" or "ok" for the files not concerned. // -------------------------------------------------------------------------------- function PclTarMerge($p_tarname, $p_tarname_add, $p_mode="", $p_mode_add="") { TrFctStart(__FILE__, __LINE__, "PclTarMerge", "tar='$p_tarname', tar_add='$p_tarname_add', mode='$p_mode', mode_add='$p_mode_add'"); $v_result=1; // ----- Check the parameters if (($p_tarname == "") || ($p_tarname_add == "")) { // ----- Error log PclErrorLog(-3, "Invalid empty archive name"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Extract the tar format from the extension if (($p_mode == "") || (($p_mode!="tar") && ($p_mode!="tgz"))) { if (($p_mode = PclTarHandleExtension($p_tarname)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } } if (($p_mode_add == "") || (($p_mode_add!="tar") && ($p_mode_add!="tgz"))) { if (($p_mode_add = PclTarHandleExtension($p_tarname_add)) == "") { // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return 0; } } // ----- Clear filecache clearstatcache(); // ----- Check the file size if ((!is_file($p_tarname)) || (((($v_size = filesize($p_tarname)) % 512) != 0) && ($p_mode=="tar"))) { // ----- Error log if (!is_file($p_tarname)) PclErrorLog(-4, "Archive '$p_tarname' does not exist"); else PclErrorLog(-6, "Archive '$p_tarname' has invalid size ".filesize($p_tarname)."(not a 512 block multiple)"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } if ((!is_file($p_tarname_add)) || (((($v_size_add = filesize($p_tarname_add)) % 512) != 0) && ($p_mode_add=="tar"))) { // ----- Error log if (!is_file($p_tarname_add)) PclErrorLog(-4, "Archive '$p_tarname_add' does not exist"); else PclErrorLog(-6, "Archive '$p_tarname_add' has invalid size ".filesize($p_tarname_add)."(not a 512 block multiple)"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Look for compressed archive if ($p_mode == "tgz") { // ----- Open the file in read mode if (($p_tar = @gzopen($p_tarname, "rb")) == 0) { // ----- Error log PclErrorLog(-2, "Unable to open file '$p_tarname' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Open a temporary file in write mode $v_temp_tarname = uniqid("pcltar-").".tmp"; TrFctMessage(__FILE__, __LINE__, 2, "Creating temporary archive file $v_temp_tarname"); if (($v_temp_tar = @gzopen($v_temp_tarname, "wb")) == 0) { // ----- Close tar file gzclose($p_tar); // ----- Error log PclErrorLog(-1, "Unable to open file '$v_temp_tarname' in binary write mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Read the first 512 bytes block $v_buffer = gzread($p_tar, 512); // ----- Read the following blocks but not the last one if (!gzeof($p_tar)) { TrFctMessage(__FILE__, __LINE__, 3, "More than one 512 block file"); $i=1; // ----- Read new 512 block and write the already read do{ // ----- Write the already read block $v_binary_data = pack("a512", "$v_buffer"); gzputs($v_temp_tar, $v_binary_data); $i++; TrFctMessage(__FILE__, __LINE__, 3, "Reading block $i"); // ----- Read next block $v_buffer = gzread($p_tar, 512); } while (!gzeof($p_tar)); TrFctMessage(__FILE__, __LINE__, 3, "$i 512 bytes blocks"); } } // ----- Look for uncompressed tar file else if ($p_mode=="tar") { // ----- Open the tar file if (($p_tar = fopen($p_tarname, "r+b")) == 0) { // ----- Error log PclErrorLog(-1, "Unable to open file '$p_tarname' in binary write mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Go to the beginning of last block TrFctMessage(__FILE__, __LINE__, 4, "Position before :".($p_mode=="tar"?ftell($p_tar):gztell($p_tar))); fseek($p_tar, $v_size-512); TrFctMessage(__FILE__, __LINE__, 4, "Position after :".($p_mode=="tar"?ftell($p_tar):gztell($p_tar))); } // ----- Look for unknown type else { // ----- Error log PclErrorLog(-3, "Invalid tar mode $p_mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Look for type of archive to add if ($p_mode_add == "tgz") { TrFctMessage(__FILE__, __LINE__, 4, "Opening file $p_tarname_add"); // ----- Open the file in read mode if (($p_tar_add = @gzopen($p_tarname_add, "rb")) == 0) { // ----- Error log PclErrorLog(-2, "Unable to open file '$p_tarname_add' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Read the first 512 bytes block $v_buffer = gzread($p_tar_add, 512); // ----- Read the following blocks but not the last one if (!gzeof($p_tar_add)) { TrFctMessage(__FILE__, __LINE__, 3, "More than one 512 block file"); $i=1; // ----- Read new 512 block and write the already read do{ // ----- Write the already read block $v_binary_data = pack("a512", "$v_buffer"); if ($p_mode=="tar") fputs($p_tar, $v_binary_data); else gzputs($v_temp_tar, $v_binary_data); $i++; TrFctMessage(__FILE__, __LINE__, 3, "Reading block $i"); // ----- Read next block $v_buffer = gzread($p_tar_add, 512); } while (!gzeof($p_tar_add)); TrFctMessage(__FILE__, __LINE__, 3, "$i 512 bytes blocks"); } // ----- Close the files gzclose($p_tar_add); } // ----- Look for uncompressed tar file else if ($p_mode=="tar") { // ----- Open the file in read mode if (($p_tar_add = @fopen($p_tarname_add, "rb")) == 0) { // ----- Error log PclErrorLog(-2, "Unable to open file '$p_tarname_add' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Read the first 512 bytes block $v_buffer = fread($p_tar_add, 512); // ----- Read the following blocks but not the last one if (!feof($p_tar_add)) { TrFctMessage(__FILE__, __LINE__, 3, "More than one 512 block file"); $i=1; // ----- Read new 512 block and write the already read do{ // ----- Write the already read block $v_binary_data = pack("a512", "$v_buffer"); if ($p_mode=="tar") fputs($p_tar, $v_binary_data); else gzputs($v_temp_tar, $v_binary_data); $i++; TrFctMessage(__FILE__, __LINE__, 3, "Reading block $i"); // ----- Read next block $v_buffer = fread($p_tar_add, 512); } while (!feof($p_tar_add)); TrFctMessage(__FILE__, __LINE__, 3, "$i 512 bytes blocks"); } // ----- Close the files fclose($p_tar_add); } // ----- Call the footer of the tar archive $v_result = PclTarHandleFooter($p_tar, $p_mode); // ----- Look for closing compressed archive if ($p_mode == "tgz") { // ----- Close the files gzclose($p_tar); gzclose($v_temp_tar); // ----- Unlink tar file if (!@unlink($p_tarname)) { // ----- Error log PclErrorLog(-11, "Error while deleting archive name $p_tarname"); } // ----- Rename tar file if (!@rename($v_temp_tarname, $p_tarname)) { // ----- Error log PclErrorLog(-12, "Error while renaming temporary file $v_temp_tarname to archive name $p_tarname"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // ----- Look for closing uncompressed tar file else if ($p_mode=="tar") { // ----- Close the tarfile fclose($p_tar); } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** // ***** ***** // ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleCreate() // Description : // Parameters : // $p_tarname : Name of the tar file // $p_list : An array containing the file or directory names to add in the tar // $p_mode : "tar" for normal tar archive, "tgz" for gzipped tar archive // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleCreate($p_tarname, $p_list, $p_mode, $p_add_dir="", $p_remove_dir="") { TrFctStart(__FILE__, __LINE__, "PclTarHandleCreate", "tar=$p_tarname, list, mode=$p_mode, add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); $v_result=1; $v_list_detail = array(); // ----- Check the parameters if (($p_tarname == "") || (($p_mode != "tar") && ($p_mode != "tgz"))) { // ----- Error log if ($p_tarname == "") PclErrorLog(-3, "Invalid empty archive name"); else PclErrorLog(-3, "Unknown mode '$p_mode'"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Look for tar file if ($p_mode == "tar") { // ----- Open the tar file if (($p_tar = fopen($p_tarname, "wb")) == 0) { // ----- Error log PclErrorLog(-1, "Unable to open file [$p_tarname] in binary write mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Call the adding fct inside the tar if (($v_result = PclTarHandleAddList($p_tar, $p_list, $p_mode, $v_list_detail, $p_add_dir, $p_remove_dir)) == 1) { // ----- Call the footer of the tar archive $v_result = PclTarHandleFooter($p_tar, $p_mode); } // ----- Close the tarfile fclose($p_tar); } // ----- Look for tgz file else { // ----- Open the tar file if (($p_tar = @gzopen($p_tarname, "wb")) == 0) { // ----- Error log PclErrorLog(-1, "Unable to open file [$p_tarname] in binary write mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Call the adding fct inside the tar if (($v_result = PclTarHandleAddList($p_tar, $p_list, $p_mode, $v_list_detail, $p_add_dir, $p_remove_dir)) == 1) { // ----- Call the footer of the tar archive $v_result = PclTarHandleFooter($p_tar, $p_mode); } // ----- Close the tarfile gzclose($p_tar); } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleAppend() // Description : // Parameters : // $p_tarname : Name of the tar file // $p_list : An array containing the file or directory names to add in the tar // $p_mode : "tar" for normal tar archive, "tgz" for gzipped tar archive // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleAppend($p_tarname, $p_list, $p_mode, &$p_list_detail, $p_add_dir, $p_remove_dir) { TrFctStart(__FILE__, __LINE__, "PclTarHandleAppend", "tar=$p_tarname, list, mode=$p_mode"); $v_result=1; // ----- Check the parameters if ($p_tarname == "") { // ----- Error log PclErrorLog(-3, "Invalid empty archive name"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } clearstatcache(); // ----- Check the file size if ((!is_file($p_tarname)) || (((($v_size = filesize($p_tarname)) % 512) != 0) && ($p_mode=="tar"))) { // ----- Error log if (!is_file($p_tarname)) PclErrorLog(-4, "Archive '$p_tarname' does not exist"); else PclErrorLog(-6, "Archive '$p_tarname' has invalid size ".filesize($p_tarname)."(not a 512 block multiple)"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Look for compressed archive if ($p_mode == "tgz") { // ----- Open the file in read mode if (($p_tar = @gzopen($p_tarname, "rb")) == 0) { // ----- Error log PclErrorLog(-2, "Unable to open file '$p_tarname' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Open a temporary file in write mode $v_temp_tarname = uniqid("pcltar-").".tmp"; TrFctMessage(__FILE__, __LINE__, 2, "Creating temporary archive file $v_temp_tarname"); if (($v_temp_tar = @gzopen($v_temp_tarname, "wb")) == 0) { // ----- Close tar file gzclose($p_tar); // ----- Error log PclErrorLog(-1, "Unable to open file '$v_temp_tarname' in binary write mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Read the first 512 bytes block $v_buffer = gzread($p_tar, 512); // ----- Read the following blocks but not the last one if (!gzeof($p_tar)) { TrFctMessage(__FILE__, __LINE__, 3, "More than one 512 block file"); $i=1; // ----- Read new 512 block and write the already read do{ // ----- Write the already read block $v_binary_data = pack("a512", "$v_buffer"); gzputs($v_temp_tar, $v_binary_data); $i++; TrFctMessage(__FILE__, __LINE__, 3, "Reading block $i"); // ----- Read next block $v_buffer = gzread($p_tar, 512); } while (!gzeof($p_tar)); TrFctMessage(__FILE__, __LINE__, 3, "$i 512 bytes blocks"); } // ----- Call the adding fct inside the tar if (($v_result = PclTarHandleAddList($v_temp_tar, $p_list, $p_mode, $p_list_detail, $p_add_dir, $p_remove_dir)) == 1) { // ----- Call the footer of the tar archive $v_result = PclTarHandleFooter($v_temp_tar, $p_mode); } // ----- Close the files gzclose($p_tar); gzclose($v_temp_tar); // ----- Unlink tar file if (!@unlink($p_tarname)) { // ----- Error log PclErrorLog(-11, "Error while deleting archive name $p_tarname"); } // ----- Rename tar file if (!@rename($v_temp_tarname, $p_tarname)) { // ----- Error log PclErrorLog(-12, "Error while renaming temporary file $v_temp_tarname to archive name $p_tarname"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // ----- Look for uncompressed tar file else if ($p_mode=="tar") { // ----- Open the tar file if (($p_tar = fopen($p_tarname, "r+b")) == 0) { // ----- Error log PclErrorLog(-1, "Unable to open file '$p_tarname' in binary write mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Go to the beginning of last block TrFctMessage(__FILE__, __LINE__, 4, "Position before :".($p_mode=="tar"?ftell($p_tar):gztell($p_tar))); fseek($p_tar, $v_size-512); TrFctMessage(__FILE__, __LINE__, 4, "Position after :".($p_mode=="tar"?ftell($p_tar):gztell($p_tar))); // ----- Call the adding fct inside the tar if (($v_result = PclTarHandleAddList($p_tar, $p_list, $p_mode, $p_list_detail, $p_add_dir, $p_remove_dir)) == 1) { // ----- Call the footer of the tar archive $v_result = PclTarHandleFooter($p_tar, $p_mode); } // ----- Close the tarfile fclose($p_tar); } // ----- Look for unknown type else { // ----- Error log PclErrorLog(-3, "Invalid tar mode $p_mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleAddList() // Description : // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is // different from the real path of the file. This is usefull if you want to have PclTar // running in any directory, and memorize relative path from an other directory. // Parameters : // $p_tar : File descriptor of the tar archive // $p_list : An array containing the file or directory names to add in the tar // $p_mode : "tar" for normal tar archive, "tgz" for gzipped tar archive // $p_list_detail : list of added files with their properties (specially the status field) // $p_add_dir : Path to add in the filename path archived // $p_remove_dir : Path to remove in the filename path archived // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleAddList($p_tar, $p_list, $p_mode, &$p_list_detail, $p_add_dir, $p_remove_dir) { TrFctStart(__FILE__, __LINE__, "PclTarHandleAddList", "tar='$p_tar', list, mode='$p_mode', add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); $v_result=1; $v_header = array(); // ----- Recuperate the current number of elt in list $v_nb = sizeof($p_list_detail); // ----- Check the parameters if ($p_tar == 0) { // ----- Error log PclErrorLog(-3, "Invalid file descriptor in file ".__FILE__.", line ".__LINE__); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Check the arguments if (sizeof($p_list) == 0) { // ----- Error log PclErrorLog(-3, "Invalid file list parameter (invalid or empty list)"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Loop on the files for ($j=0; ($j<count($p_list)) && ($v_result==1); $j++) { // ----- Recuperate the filename $p_filename = $p_list[$j]; TrFctMessage(__FILE__, __LINE__, 2, "Looking for file [$p_filename]"); // ----- Skip empty file names if ($p_filename == "") { TrFctMessage(__FILE__, __LINE__, 2, "Skip empty filename"); continue; } // ----- Check the filename if (!file_exists($p_filename)) { // ----- Error log TrFctMessage(__FILE__, __LINE__, 2, "File '$p_filename' does not exists"); PclErrorLog(-4, "File '$p_filename' does not exists"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Check the path length if (strlen($p_filename) > 99) { // ----- Error log PclErrorLog(-5, "File name is too long (max. 99) : '$p_filename'"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } TrFctMessage(__FILE__, __LINE__, 4, "File position before header =".($p_mode=="tar"?ftell($p_tar):gztell($p_tar))); // ----- Add the file if (($v_result = PclTarHandleAddFile($p_tar, $p_filename, $p_mode, $v_header, $p_add_dir, $p_remove_dir)) != 1) { // ----- Return status TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // ----- Store the file infos $p_list_detail[$v_nb++] = $v_header; // ----- Look for directory if (is_dir($p_filename)) { TrFctMessage(__FILE__, __LINE__, 2, "$p_filename is a directory"); // ----- Look for path if ($p_filename != ".") $v_path = $p_filename."/"; else $v_path = ""; // ----- Read the directory for files and sub-directories $p_hdir = opendir($p_filename); $p_hitem = readdir($p_hdir); // '.' directory $p_hitem = readdir($p_hdir); // '..' directory while ($p_hitem = readdir($p_hdir)) { // ----- Look for a file if (is_file($v_path.$p_hitem)) { TrFctMessage(__FILE__, __LINE__, 4, "Add the file '".$v_path.$p_hitem."'"); // ----- Add the file if (($v_result = PclTarHandleAddFile($p_tar, $v_path.$p_hitem, $p_mode, $v_header, $p_add_dir, $p_remove_dir)) != 1) { // ----- Return status TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // ----- Store the file infos $p_list_detail[$v_nb++] = $v_header; } // ----- Recursive call to PclTarHandleAddFile() else { TrFctMessage(__FILE__, __LINE__, 4, "'".$v_path.$p_hitem."' is a directory"); // ----- Need an array as parameter $p_temp_list[0] = $v_path.$p_hitem; $v_result = PclTarHandleAddList($p_tar, $p_temp_list, $p_mode, $p_list_detail, $p_add_dir, $p_remove_dir); } } // ----- Free memory for the recursive loop unset($p_temp_list); unset($p_hdir); unset($p_hitem); } else { TrFctMessage(__FILE__, __LINE__, 4, "File position after blocks =".($p_mode=="tar"?ftell($p_tar):gztell($p_tar))); } } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleAddFile() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleAddFile($p_tar, $p_filename, $p_mode, &$p_header, $p_add_dir, $p_remove_dir) { TrFctStart(__FILE__, __LINE__, "PclTarHandleAddFile", "tar='$p_tar', filename='$p_filename', p_mode='$p_mode', add_dir='$p_add_dir', remove_dir='$p_remove_dir'"); $v_result=1; // ----- Check the parameters if ($p_tar == 0) { // ----- Error log PclErrorLog(-3, "Invalid file descriptor in file ".__FILE__.", line ".__LINE__); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Skip empty file names if ($p_filename == "") { // ----- Error log PclErrorLog(-3, "Invalid file list parameter (invalid or empty list)"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Calculate the stored filename $v_stored_filename = $p_filename; if ($p_remove_dir != "") { if (substr($p_remove_dir, -1) != '/') $p_remove_dir .= "/"; if ((substr($p_filename, 0, 2) == "./") || (substr($p_remove_dir, 0, 2) == "./")) { if ((substr($p_filename, 0, 2) == "./") && (substr($p_remove_dir, 0, 2) != "./")) $p_remove_dir = "./".$p_remove_dir; if ((substr($p_filename, 0, 2) != "./") && (substr($p_remove_dir, 0, 2) == "./")) $p_remove_dir = substr($p_remove_dir, 2); } if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) { $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); TrFctMessage(__FILE__, __LINE__, 3, "Remove path '$p_remove_dir' in file '$p_filename' = '$v_stored_filename'"); } } if ($p_add_dir != "") { if (substr($p_add_dir, -1) == "/") $v_stored_filename = $p_add_dir.$v_stored_filename; else $v_stored_filename = $p_add_dir."/".$v_stored_filename; TrFctMessage(__FILE__, __LINE__, 3, "Add path '$p_add_dir' in file '$p_filename' = '$v_stored_filename'"); } // ----- Check the path length if (strlen($v_stored_filename) > 99) { // ----- Error log PclErrorLog(-5, "Stored file name is too long (max. 99) : '$v_stored_filename'"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Look for a file if (is_file($p_filename)) { // ----- Open the source file if (($v_file = fopen($p_filename, "rb")) == 0) { // ----- Error log PclErrorLog(-2, "Unable to open file '$p_filename' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Call the header generation if (($v_result = PclTarHandleHeader($p_tar, $p_filename, $p_mode, $p_header, $v_stored_filename)) != 1) { // ----- Return status TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } TrFctMessage(__FILE__, __LINE__, 4, "File position after header =".($p_mode=="tar"?ftell($p_tar):gztell($p_tar))); // ----- Read the file by 512 octets blocks $i=0; while (($v_buffer = fread($v_file, 512)) != "") { $v_binary_data = pack("a512", "$v_buffer"); if ($p_mode == "tar") fputs($p_tar, $v_binary_data); else gzputs($p_tar, $v_binary_data); $i++; } TrFctMessage(__FILE__, __LINE__, 2, "$i 512 bytes blocks"); // ----- Close the file fclose($v_file); TrFctMessage(__FILE__, __LINE__, 4, "File position after blocks =".($p_mode=="tar"?ftell($p_tar):gztell($p_tar))); } // ----- Look for a directory else { // ----- Call the header generation if (($v_result = PclTarHandleHeader($p_tar, $p_filename, $p_mode, $p_header, $v_stored_filename)) != 1) { // ----- Return status TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } TrFctMessage(__FILE__, __LINE__, 4, "File position after header =".($p_mode=="tar"?ftell($p_tar):gztell($p_tar))); } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleHeader() // Description : // This function creates in the TAR $p_tar, the TAR header for the file // $p_filename. // // 1. The informations needed to compose the header are recuperated and formatted // 2. Two binary strings are composed for the first part of the header, before // and after checksum field. // 3. The checksum is calculated from the two binary strings // 4. The header is write in the tar file (first binary string, binary string // for checksum and last binary string). // Parameters : // $p_tar : a valid file descriptor, opened in write mode, // $p_filename : The name of the file the header is for, // $p_mode : The mode of the archive ("tar" or "tgz"). // $p_header : A pointer to a array where will be set the file properties // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleHeader($p_tar, $p_filename, $p_mode, &$p_header, $p_stored_filename) { TrFctStart(__FILE__, __LINE__, "PclTarHandleHeader", "tar=$p_tar, file='$p_filename', mode='$p_mode', stored_filename='$p_stored_filename'"); $v_result=1; // ----- Check the parameters if (($p_tar == 0) || ($p_filename == "")) { // ----- Error log PclErrorLog(-3, "Invalid file descriptor in file ".__FILE__.", line ".__LINE__); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Filename (reduce the path of stored name) if ($p_stored_filename == "") $p_stored_filename = $p_filename; $v_reduce_filename = PclTarHandlePathReduction($p_stored_filename); TrFctMessage(__FILE__, __LINE__, 2, "Filename (reduced) '$v_reduce_filename', strlen ".strlen($v_reduce_filename)); // ----- Get file info $v_info = stat($p_filename); $v_uid = sprintf("%6s ", DecOct($v_info[4])); $v_gid = sprintf("%6s ", DecOct($v_info[5])); TrFctMessage(__FILE__, __LINE__, 3, "uid=$v_uid, gid=$v_gid"); $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename))); TrFctMessage(__FILE__, __LINE__, 3, "file permissions $v_perms"); // ----- File mtime $v_mtime_data = filemtime($p_filename); TrFctMessage(__FILE__, __LINE__, 2, "File mtime : $v_mtime_data"); $v_mtime = sprintf("%11s", DecOct($v_mtime_data)); // ----- File typeflag // '0' or '\0' is the code for regular file // '5' is directory if (is_dir($p_filename)) { $v_typeflag = "5"; $v_size = 0; } else { $v_typeflag = ""; // ----- Get the file size clearstatcache(); $v_size = filesize($p_filename); } TrFctMessage(__FILE__, __LINE__, 2, "File size : $v_size"); $v_size = sprintf("%11s ", DecOct($v_size)); TrFctMessage(__FILE__, __LINE__, 2, "File typeflag : $v_typeflag"); // ----- Linkname $v_linkname = ""; // ----- Magic $v_magic = ""; // ----- Version $v_version = ""; // ----- uname $v_uname = ""; // ----- gname $v_gname = ""; // ----- devmajor $v_devmajor = ""; // ----- devminor $v_devminor = ""; // ----- prefix $v_prefix = ""; // ----- Compose the binary string of the header in two parts arround the checksum position $v_binary_data_first = pack("a100a8a8a8a12A12", $v_reduce_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime); $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, ""); // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header for ($i=0; $i<148; $i++) { $v_checksum += ord(substr($v_binary_data_first,$i,1)); } // ..... Ignore the checksum value and replace it by ' ' (space) for ($i=148; $i<156; $i++) { $v_checksum += ord(' '); } // ..... Last part of the header for ($i=156, $j=0; $i<512; $i++, $j++) { $v_checksum += ord(substr($v_binary_data_last,$j,1)); } TrFctMessage(__FILE__, __LINE__, 3, "Calculated checksum : $v_checksum"); // ----- Write the first 148 bytes of the header in the archive if ($p_mode == "tar") fputs($p_tar, $v_binary_data_first, 148); else gzputs($p_tar, $v_binary_data_first, 148); // ----- Write the calculated checksum $v_checksum = sprintf("%6s ", DecOct($v_checksum)); $v_binary_data = pack("a8", $v_checksum); if ($p_mode == "tar") fputs($p_tar, $v_binary_data, 8); else gzputs($p_tar, $v_binary_data, 8); // ----- Write the last 356 bytes of the header in the archive if ($p_mode == "tar") fputs($p_tar, $v_binary_data_last, 356); else gzputs($p_tar, $v_binary_data_last, 356); // ----- Set the properties in the header "structure" $p_header[filename] = $v_reduce_filename; $p_header[mode] = $v_perms; $p_header[uid] = $v_uid; $p_header[gid] = $v_gid; $p_header[size] = $v_size; $p_header[mtime] = $v_mtime; $p_header[typeflag] = $v_typeflag; $p_header[status] = "added"; // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleFooter() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleFooter($p_tar, $p_mode) { TrFctStart(__FILE__, __LINE__, "PclTarHandleFooter", "tar='$p_tar', p_mode=$p_mode"); $v_result=1; // ----- Write the last 0 filled block for end of archive $v_binary_data = pack("a512", ""); if ($p_mode == "tar") fputs($p_tar, $v_binary_data); else gzputs($p_tar, $v_binary_data); // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleExtract() // Description : // Parameters : // $p_tarname : Filename of the tar (or tgz) archive // $p_file_list : An array which contains the list of files to extract, this // array may be empty when $p_mode is 'complete' // $p_list_detail : An array where will be placed the properties of each extracted/listed file // $p_mode : 'complete' will extract all files from the archive, // 'partial' will look for files in $p_file_list // 'list' will only list the files from the archive without any extract // $p_path : Path to add while writing the extracted files // $p_tar_mode : 'tar' for GNU TAR archive, 'tgz' for compressed archive // $p_remove_path : Path to remove (from the file memorized path) while writing the // extracted files. If the path does not match the file path, // the file is extracted with its memorized path. // $p_remove_path does not apply to 'list' mode. // $p_path and $p_remove_path are commulative. // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleExtract($p_tarname, $p_file_list, &$p_list_detail, $p_mode, $p_path, $p_tar_mode, $p_remove_path) { TrFctStart(__FILE__, __LINE__, "PclTarHandleExtract", "archive='$p_tarname', list, mode=$p_mode, path=$p_path, tar_mode=$p_tar_mode, remove_path='$p_remove_path'"); $v_result=1; $v_nb = 0; $v_extract_all = TRUE; $v_listing = FALSE; // ----- Check the path if (($p_path == "") || ((substr($p_path, 0, 1) != "/") && (substr($p_path, 0, 3) != "../"))) $p_path = "./".$p_path; // ----- Look for path to remove format (should end by /) if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) { $p_remove_path .= '/'; } $p_remove_path_size = strlen($p_remove_path); // ----- Study the mode switch ($p_mode) { case "complete" : // ----- Flag extract of all files $v_extract_all = TRUE; $v_listing = FALSE; break; case "partial" : // ----- Flag extract of specific files $v_extract_all = FALSE; $v_listing = FALSE; break; case "list" : // ----- Flag list of all files $v_extract_all = FALSE; $v_listing = TRUE; break; default : // ----- Error log PclErrorLog(-3, "Invalid extract mode ($p_mode)"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Open the tar file if ($p_tar_mode == "tar") { TrFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); $v_tar = fopen($p_tarname, "rb"); } else { TrFctMessage(__FILE__, __LINE__, 3, "Open file in gzip binary read mode"); $v_tar = @gzopen($p_tarname, "rb"); } // ----- Check that the archive is open if ($v_tar == 0) { // ----- Error log PclErrorLog(-2, "Unable to open archive '$p_tarname' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Read the blocks While (!($v_end_of_file = ($p_tar_mode == "tar"?feof($v_tar):gzeof($v_tar)))) { TrFctMessage(__FILE__, __LINE__, 3, "Looking for next header ..."); // ----- Clear cache of file infos clearstatcache(); // ----- Reset extract tag $v_extract_file = FALSE; $v_extraction_stopped = 0; // ----- Read the 512 bytes header if ($p_tar_mode == "tar") $v_binary_data = fread($v_tar, 512); else $v_binary_data = gzread($v_tar, 512); // ----- Read the header properties if (($v_result = PclTarHandleReadHeader($v_binary_data, $v_header)) != 1) { // ----- Close the archive file if ($p_tar_mode == "tar") fclose($v_tar); else gzclose($v_tar); // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // ----- Look for empty blocks to skip if ($v_header[filename] == "") { TrFctMessage(__FILE__, __LINE__, 2, "Empty block found. End of archive ?"); continue; } TrFctMessage(__FILE__, __LINE__, 2, "Found file '$v_header[filename]', size '$v_header[size]'"); // ----- Look for partial extract if ((!$v_extract_all) && (is_array($p_file_list))) { TrFctMessage(__FILE__, __LINE__, 2, "Look if the file '$v_header[filename]' need to be extracted"); // ----- By default no unzip if the file is not found $v_extract_file = FALSE; // ----- Look into the file list for ($i=0; $i<sizeof($p_file_list); $i++) { TrFctMessage(__FILE__, __LINE__, 2, "Compare archived file '$v_header[filename]' from asked list file '".$p_file_list[$i]."'"); // ----- Look if it is a directory if (substr($p_file_list[$i], -1) == "/") { TrFctMessage(__FILE__, __LINE__, 3, "Compare file '$v_header[filename]' with directory '$p_file_list[$i]'"); // ----- Look if the directory is in the filename path if ((strlen($v_header[filename]) > strlen($p_file_list[$i])) && (substr($v_header[filename], 0, strlen($p_file_list[$i])) == $p_file_list[$i])) { // ----- The file is in the directory, so extract it TrFctMessage(__FILE__, __LINE__, 2, "File '$v_header[filename]' is in directory '$p_file_list[$i]' : extract it"); $v_extract_file = TRUE; // ----- End of loop break; } } // ----- It is a file, so compare the file names else if ($p_file_list[$i] == $v_header[filename]) { // ----- File found TrFctMessage(__FILE__, __LINE__, 2, "File '$v_header[filename]' should be extracted"); $v_extract_file = TRUE; // ----- End of loop break; } } // ----- Trace if (!$v_extract_file) { TrFctMessage(__FILE__, __LINE__, 2, "File '$v_header[filename]' should not be extracted"); } } else { // ----- All files need to be extracted $v_extract_file = TRUE; } // ----- Look if this file need to be extracted if (($v_extract_file) && (!$v_listing)) { // ----- Look for path to remove if (($p_remove_path != "") && (substr($v_header[filename], 0, $p_remove_path_size) == $p_remove_path)) { TrFctMessage(__FILE__, __LINE__, 3, "Found path '$p_remove_path' to remove in file '$v_header[filename]'"); // ----- Remove the path $v_header[filename] = substr($v_header[filename], $p_remove_path_size); TrFctMessage(__FILE__, __LINE__, 3, "Reslting file is '$v_header[filename]'"); } // ----- Add the path to the file if (($p_path != "./") && ($p_path != "/")) { // ----- Look for the path end '/' while (substr($p_path, -1) == "/") { TrFctMessage(__FILE__, __LINE__, 3, "Destination path [$p_path] ends by '/'"); $p_path = substr($p_path, 0, strlen($p_path)-1); TrFctMessage(__FILE__, __LINE__, 3, "Modified to [$p_path]"); } // ----- Add the path if (substr($v_header[filename], 0, 1) == "/") $v_header[filename] = $p_path.$v_header[filename]; else $v_header[filename] = $p_path."/".$v_header[filename]; } // ----- Trace TrFctMessage(__FILE__, __LINE__, 2, "Extracting file (with path) '$v_header[filename]', size '$v_header[size]'"); // ----- Check that the file does not exists if (file_exists($v_header[filename])) { TrFctMessage(__FILE__, __LINE__, 2, "File '$v_header[filename]' already exists"); // ----- Look if file is a directory if (is_dir($v_header[filename])) { TrFctMessage(__FILE__, __LINE__, 2, "Existing file '$v_header[filename]' is a directory"); // ----- Change the file status $v_header[status] = "already_a_directory"; // ----- Skip the extract $v_extraction_stopped = 1; $v_extract_file = 0; } // ----- Look if file is write protected else if (!is_writeable($v_header[filename])) { TrFctMessage(__FILE__, __LINE__, 2, "Existing file '$v_header[filename]' is write protected"); // ----- Change the file status $v_header[status] = "write_protected"; // ----- Skip the extract $v_extraction_stopped = 1; $v_extract_file = 0; } // ----- Look if the extracted file is older else if (filemtime($v_header[filename]) > $v_header[mtime]) { TrFctMessage(__FILE__, __LINE__, 2, "Existing file '$v_header[filename]' is newer (".date("l dS of F Y h:i:s A", filemtime($v_header[filename])).") than the extracted file (".date("l dS of F Y h:i:s A", $v_header[mtime]).")"); // ----- Change the file status $v_header[status] = "newer_exist"; // ----- Skip the extract $v_extraction_stopped = 1; $v_extract_file = 0; } } // ----- Check the directory availability and create it if necessary else { if ($v_header[typeflag]=="5") $v_dir_to_check = $v_header[filename]; else if (!strstr($v_header[filename], "/")) $v_dir_to_check = ""; else $v_dir_to_check = dirname($v_header[filename]); if (($v_result = PclTarHandlerDirCheck($v_dir_to_check)) != 1) { TrFctMessage(__FILE__, __LINE__, 2, "Unable to create path for '$v_header[filename]'"); // ----- Change the file status $v_header[status] = "path_creation_fail"; // ----- Skip the extract $v_extraction_stopped = 1; $v_extract_file = 0; } } // ----- Do the extraction if (($v_extract_file) && ($v_header[typeflag]!="5")) { // ----- Open the destination file in write mode if (($v_dest_file = @fopen($v_header[filename], "wb")) == 0) { TrFctMessage(__FILE__, __LINE__, 2, "Error while opening '$v_header[filename]' in write binary mode"); // ----- Change the file status $v_header[status] = "write_error"; // ----- Jump to next file TrFctMessage(__FILE__, __LINE__, 2, "Jump to next file"); if ($p_tar_mode == "tar") fseek($v_tar, ftell($v_tar)+(ceil(($v_header[size]/512))*512)); else gzseek($v_tar, gztell($v_tar)+(ceil(($v_header[size]/512))*512)); } else { TrFctMessage(__FILE__, __LINE__, 2, "Start extraction of '$v_header[filename]'"); // ----- Read data $n = floor($v_header[size]/512); for ($i=0; $i<$n; $i++) { TrFctMessage(__FILE__, __LINE__, 3, "Read complete 512 bytes block number ".($i+1)); if ($p_tar_mode == "tar") $v_content = fread($v_tar, 512); else $v_content = gzread($v_tar, 512); fwrite($v_dest_file, $v_content, 512); } if (($v_header[size] % 512) != 0) { TrFctMessage(__FILE__, __LINE__, 3, "Read last ".($v_header[size] % 512)." bytes in a 512 block"); if ($p_tar_mode == "tar") $v_content = fread($v_tar, 512); else $v_content = gzread($v_tar, 512); fwrite($v_dest_file, $v_content, ($v_header[size] % 512)); } // ----- Close the destination file fclose($v_dest_file); // ----- Change the file mode, mtime touch($v_header[filename], $v_header[mtime]); //chmod($v_header[filename], DecOct($v_header[mode])); } // ----- Check the file size clearstatcache(); if (filesize($v_header[filename]) != $v_header[size]) { // ----- Close the archive file if ($p_tar_mode == "tar") fclose($v_tar); else gzclose($v_tar); // ----- Error log PclErrorLog(-7, "Extracted file '$v_header[filename]' does not have the correct file size '".filesize($v_filename)."' ('$v_header[size]' expected). Archive may be corrupted."); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Trace TrFctMessage(__FILE__, __LINE__, 2, "Extraction done"); } else { TrFctMessage(__FILE__, __LINE__, 2, "Extraction of file '$v_header[filename]' skipped."); // ----- Jump to next file TrFctMessage(__FILE__, __LINE__, 2, "Jump to next file"); if ($p_tar_mode == "tar") fseek($v_tar, ftell($v_tar)+(ceil(($v_header[size]/512))*512)); else gzseek($v_tar, gztell($v_tar)+(ceil(($v_header[size]/512))*512)); } } // ----- Look for file that is not to be unzipped else { // ----- Trace TrFctMessage(__FILE__, __LINE__, 2, "Jump file '$v_header[filename]'"); TrFctMessage(__FILE__, __LINE__, 4, "Position avant jump [".($p_tar_mode=="tar"?ftell($v_tar):gztell($v_tar))."]"); // ----- Jump to next file if ($p_tar_mode == "tar") fseek($v_tar, ($p_tar_mode=="tar"?ftell($v_tar):gztell($v_tar))+(ceil(($v_header[size]/512))*512)); else gzseek($v_tar, gztell($v_tar)+(ceil(($v_header[size]/512))*512)); TrFctMessage(__FILE__, __LINE__, 4, "Position apr�s jump [".($p_tar_mode=="tar"?ftell($v_tar):gztell($v_tar))."]"); } if ($p_tar_mode == "tar") $v_end_of_file = feof($v_tar); else $v_end_of_file = gzeof($v_tar); // ----- File name and properties are logged if listing mode or file is extracted if ($v_listing || $v_extract_file || $v_extraction_stopped) { TrFctMessage(__FILE__, __LINE__, 2, "Memorize info about file '$v_header[filename]'"); // ----- Log extracted files if (($v_file_dir = dirname($v_header[filename])) == $v_header[filename]) $v_file_dir = ""; if ((substr($v_header[filename], 0, 1) == "/") && ($v_file_dir == "")) $v_file_dir = "/"; // ----- Add the array describing the file into the list $p_list_detail[$v_nb] = $v_header; // ----- Increment $v_nb++; } } // ----- Close the tarfile if ($p_tar_mode == "tar") fclose($v_tar); else gzclose($v_tar); // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleExtractByIndexList() // Description : // Extract the files which are at the indexes specified. If the 'file' at the // index is a directory, the directory only is created, not all the files stored // for that directory. // Parameters : // $p_index_string : String of indexes of files to extract. The form of the // string is "0,4-6,8-12" with only numbers and '-' for // for range, and ',' to separate ranges. No spaces or ';' // are allowed. // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleExtractByIndexList($p_tarname, $p_index_string, &$p_list_detail, $p_path, $p_remove_path, $p_tar_mode) { TrFctStart(__FILE__, __LINE__, "PclTarHandleExtractByIndexList", "archive='$p_tarname', index_string='$p_index_string', list, path=$p_path, remove_path='$p_remove_path', tar_mode=$p_tar_mode"); $v_result=1; $v_nb = 0; // ----- TBC : I should check the string by a regexp // ----- Check the path if (($p_path == "") || ((substr($p_path, 0, 1) != "/") && (substr($p_path, 0, 3) != "../") && (substr($p_path, 0, 2) != "./"))) $p_path = "./".$p_path; // ----- Look for path to remove format (should end by /) if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) { $p_remove_path .= '/'; } $p_remove_path_size = strlen($p_remove_path); // ----- Open the tar file if ($p_tar_mode == "tar") { TrFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); $v_tar = @fopen($p_tarname, "rb"); } else { TrFctMessage(__FILE__, __LINE__, 3, "Open file in gzip binary read mode"); $v_tar = @gzopen($p_tarname, "rb"); } // ----- Check that the archive is open if ($v_tar == 0) { // ----- Error log PclErrorLog(-2, "Unable to open archive '$p_tarname' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Manipulate the index list $v_list = explode(",", $p_index_string); sort($v_list); // ----- Loop on the index list $v_index=0; for ($i=0; ($i<sizeof($v_list)) && ($v_result); $i++) { TrFctMessage(__FILE__, __LINE__, 3, "Looking for index part '$v_list[$i]'"); // ----- Extract range $v_index_list = explode("-", $v_list[$i]); $v_size_index_list = sizeof($v_index_list); if ($v_size_index_list == 1) { TrFctMessage(__FILE__, __LINE__, 3, "Only one index '$v_index_list[0]'"); // ----- Do the extraction $v_result = PclTarHandleExtractByIndex($v_tar, $v_index, $v_index_list[0], $v_index_list[0], $p_list_detail, $p_path, $p_remove_path, $p_tar_mode); } else if ($v_size_index_list == 2) { TrFctMessage(__FILE__, __LINE__, 3, "Two indexes '$v_index_list[0]' and '$v_index_list[1]'"); // ----- Do the extraction $v_result = PclTarHandleExtractByIndex($v_tar, $v_index, $v_index_list[0], $v_index_list[1], $p_list_detail, $p_path, $p_remove_path, $p_tar_mode); } } // ----- Close the tarfile if ($p_tar_mode == "tar") fclose($v_tar); else gzclose($v_tar); // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleExtractByIndex() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleExtractByIndex($p_tar, &$p_index_current, $p_index_start, $p_index_stop, &$p_list_detail, $p_path, $p_remove_path, $p_tar_mode) { TrFctStart(__FILE__, __LINE__, "PclTarHandleExtractByIndex", "archive_descr='$p_tar', index_current=$p_index_current, index_start='$p_index_start', index_stop='$p_index_stop', list, path=$p_path, remove_path='$p_remove_path', tar_mode=$p_tar_mode"); $v_result=1; $v_nb = 0; // TBC : I should replace all $v_tar by $p_tar in this function .... $v_tar = $p_tar; // ----- Look the number of elements already in $p_list_detail $v_nb = sizeof($p_list_detail); // ----- Read the blocks While (!($v_end_of_file = ($p_tar_mode == "tar"?feof($v_tar):gzeof($v_tar)))) { TrFctMessage(__FILE__, __LINE__, 3, "Looking for next file ..."); TrFctMessage(__FILE__, __LINE__, 3, "Index current=$p_index_current, range=[$p_index_start, $p_index_stop])"); if ($p_index_current > $p_index_stop) { TrFctMessage(__FILE__, __LINE__, 2, "Stop extraction, past stop index"); break; } // ----- Clear cache of file infos clearstatcache(); // ----- Reset extract tag $v_extract_file = FALSE; $v_extraction_stopped = 0; // ----- Read the 512 bytes header if ($p_tar_mode == "tar") $v_binary_data = fread($v_tar, 512); else $v_binary_data = gzread($v_tar, 512); // ----- Read the header properties if (($v_result = PclTarHandleReadHeader($v_binary_data, $v_header)) != 1) { // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // ----- Look for empty blocks to skip if ($v_header[filename] == "") { TrFctMessage(__FILE__, __LINE__, 2, "Empty block found. End of archive ?"); continue; } TrFctMessage(__FILE__, __LINE__, 2, "Found file '$v_header[filename]', size '$v_header[size]'"); // ----- Look if file is in the range to be extracted if (($p_index_current >= $p_index_start) && ($p_index_current <= $p_index_stop)) { TrFctMessage(__FILE__, __LINE__, 2, "File '$v_header[filename]' is in the range to be extracted"); $v_extract_file = TRUE; } else { TrFctMessage(__FILE__, __LINE__, 2, "File '$v_header[filename]' is out of the range"); $v_extract_file = FALSE; } // ----- Look if this file need to be extracted if ($v_extract_file) { if (($v_result = PclTarHandleExtractFile($v_tar, $v_header, $p_path, $p_remove_path, $p_tar_mode)) != 1) { // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } } // ----- Look for file that is not to be extracted else { // ----- Trace TrFctMessage(__FILE__, __LINE__, 2, "Jump file '$v_header[filename]'"); TrFctMessage(__FILE__, __LINE__, 4, "Position avant jump [".($p_tar_mode=="tar"?ftell($v_tar):gztell($v_tar))."]"); // ----- Jump to next file if ($p_tar_mode == "tar") fseek($v_tar, ($p_tar_mode=="tar"?ftell($v_tar):gztell($v_tar))+(ceil(($v_header[size]/512))*512)); else gzseek($v_tar, gztell($v_tar)+(ceil(($v_header[size]/512))*512)); TrFctMessage(__FILE__, __LINE__, 4, "Position apr�s jump [".($p_tar_mode=="tar"?ftell($v_tar):gztell($v_tar))."]"); } if ($p_tar_mode == "tar") $v_end_of_file = feof($v_tar); else $v_end_of_file = gzeof($v_tar); // ----- File name and properties are logged if listing mode or file is extracted if ($v_extract_file) { TrFctMessage(__FILE__, __LINE__, 2, "Memorize info about file '$v_header[filename]'"); // ----- Log extracted files if (($v_file_dir = dirname($v_header[filename])) == $v_header[filename]) $v_file_dir = ""; if ((substr($v_header[filename], 0, 1) == "/") && ($v_file_dir == "")) $v_file_dir = "/"; // ----- Add the array describing the file into the list $p_list_detail[$v_nb] = $v_header; // ----- Increment $v_nb++; } // ----- Increment the current file index $p_index_current++; } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleExtractFile() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleExtractFile($p_tar, &$v_header, $p_path, $p_remove_path, $p_tar_mode) { TrFctStart(__FILE__, __LINE__, "PclTarHandleExtractFile", "archive_descr='$p_tar', path=$p_path, remove_path='$p_remove_path', tar_mode=$p_tar_mode"); $v_result=1; // TBC : I should replace all $v_tar by $p_tar in this function .... $v_tar = $p_tar; $v_extract_file = 1; $p_remove_path_size = strlen($p_remove_path); // ----- Look for path to remove if (($p_remove_path != "") && (substr($v_header[filename], 0, $p_remove_path_size) == $p_remove_path)) { TrFctMessage(__FILE__, __LINE__, 3, "Found path '$p_remove_path' to remove in file '$v_header[filename]'"); // ----- Remove the path $v_header[filename] = substr($v_header[filename], $p_remove_path_size); TrFctMessage(__FILE__, __LINE__, 3, "Resulting file is '$v_header[filename]'"); } // ----- Add the path to the file if (($p_path != "./") && ($p_path != "/")) { // ----- Look for the path end '/' while (substr($p_path, -1) == "/") { TrFctMessage(__FILE__, __LINE__, 3, "Destination path [$p_path] ends by '/'"); $p_path = substr($p_path, 0, strlen($p_path)-1); TrFctMessage(__FILE__, __LINE__, 3, "Modified to [$p_path]"); } // ----- Add the path if (substr($v_header[filename], 0, 1) == "/") $v_header[filename] = $p_path.$v_header[filename]; else $v_header[filename] = $p_path."/".$v_header[filename]; } // ----- Trace TrFctMessage(__FILE__, __LINE__, 2, "Extracting file (with path) '$v_header[filename]', size '$v_header[size]'"); // ----- Check that the file does not exists if (file_exists($v_header[filename])) { TrFctMessage(__FILE__, __LINE__, 2, "File '$v_header[filename]' already exists"); // ----- Look if file is a directory if (is_dir($v_header[filename])) { TrFctMessage(__FILE__, __LINE__, 2, "Existing file '$v_header[filename]' is a directory"); // ----- Change the file status $v_header[status] = "already_a_directory"; // ----- Skip the extract $v_extraction_stopped = 1; $v_extract_file = 0; } // ----- Look if file is write protected else if (!is_writeable($v_header[filename])) { TrFctMessage(__FILE__, __LINE__, 2, "Existing file '$v_header[filename]' is write protected"); // ----- Change the file status $v_header[status] = "write_protected"; // ----- Skip the extract $v_extraction_stopped = 1; $v_extract_file = 0; } // ----- Look if the extracted file is older else if (filemtime($v_header[filename]) > $v_header[mtime]) { TrFctMessage(__FILE__, __LINE__, 2, "Existing file '$v_header[filename]' is newer (".date("l dS of F Y h:i:s A", filemtime($v_header[filename])).") than the extracted file (".date("l dS of F Y h:i:s A", $v_header[mtime]).")"); // ----- Change the file status $v_header[status] = "newer_exist"; // ----- Skip the extract $v_extraction_stopped = 1; $v_extract_file = 0; } } // ----- Check the directory availability and create it if necessary else { if ($v_header[typeflag]=="5") $v_dir_to_check = $v_header[filename]; else if (!strstr($v_header[filename], "/")) $v_dir_to_check = ""; else $v_dir_to_check = dirname($v_header[filename]); if (($v_result = PclTarHandlerDirCheck($v_dir_to_check)) != 1) { TrFctMessage(__FILE__, __LINE__, 2, "Unable to create path for '$v_header[filename]'"); // ----- Change the file status $v_header[status] = "path_creation_fail"; // ----- Skip the extract $v_extraction_stopped = 1; $v_extract_file = 0; } } // ----- Do the real bytes extraction (if not a directory) if (($v_extract_file) && ($v_header[typeflag]!="5")) { // ----- Open the destination file in write mode if (($v_dest_file = @fopen($v_header[filename], "wb")) == 0) { TrFctMessage(__FILE__, __LINE__, 2, "Error while opening '$v_header[filename]' in write binary mode"); // ----- Change the file status $v_header[status] = "write_error"; // ----- Jump to next file TrFctMessage(__FILE__, __LINE__, 2, "Jump to next file"); if ($p_tar_mode == "tar") fseek($v_tar, ftell($v_tar)+(ceil(($v_header[size]/512))*512)); else gzseek($v_tar, gztell($v_tar)+(ceil(($v_header[size]/512))*512)); } else { TrFctMessage(__FILE__, __LINE__, 2, "Start extraction of '$v_header[filename]'"); // ----- Read data $n = floor($v_header[size]/512); for ($i=0; $i<$n; $i++) { TrFctMessage(__FILE__, __LINE__, 3, "Read complete 512 bytes block number ".($i+1)); if ($p_tar_mode == "tar") $v_content = fread($v_tar, 512); else $v_content = gzread($v_tar, 512); fwrite($v_dest_file, $v_content, 512); } if (($v_header[size] % 512) != 0) { TrFctMessage(__FILE__, __LINE__, 3, "Read last ".($v_header[size] % 512)." bytes in a 512 block"); if ($p_tar_mode == "tar") $v_content = fread($v_tar, 512); else $v_content = gzread($v_tar, 512); fwrite($v_dest_file, $v_content, ($v_header[size] % 512)); } // ----- Close the destination file fclose($v_dest_file); // ----- Change the file mode, mtime touch($v_header[filename], $v_header[mtime]); //chmod($v_header[filename], DecOct($v_header[mode])); } // ----- Check the file size clearstatcache(); if (filesize($v_header[filename]) != $v_header[size]) { // ----- Error log PclErrorLog(-7, "Extracted file '$v_header[filename]' does not have the correct file size '".filesize($v_filename)."' ('$v_header[size]' expected). Archive may be corrupted."); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Trace TrFctMessage(__FILE__, __LINE__, 2, "Extraction done"); } else { TrFctMessage(__FILE__, __LINE__, 2, "Extraction of file '$v_header[filename]' skipped."); // ----- Jump to next file TrFctMessage(__FILE__, __LINE__, 2, "Jump to next file"); if ($p_tar_mode == "tar") fseek($v_tar, ftell($v_tar)+(ceil(($v_header[size]/512))*512)); else gzseek($v_tar, gztell($v_tar)+(ceil(($v_header[size]/512))*512)); } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleDelete() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleDelete($p_tarname, $p_file_list, &$p_list_detail, $p_tar_mode) { TrFctStart(__FILE__, __LINE__, "PclTarHandleDelete", "archive='$p_tarname', list, tar_mode=$p_tar_mode"); $v_result=1; $v_nb=0; // ----- Look for regular tar file if ($p_tar_mode == "tar") { // ----- Open file TrFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); if (($v_tar = @fopen($p_tarname, "rb")) == 0) { // ----- Error log PclErrorLog(-2, "Unable to open file '$p_tarname' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Open a temporary file in write mode $v_temp_tarname = uniqid("pcltar-").".tmp"; TrFctMessage(__FILE__, __LINE__, 2, "Creating temporary archive file $v_temp_tarname"); if (($v_temp_tar = @fopen($v_temp_tarname, "wb")) == 0) { // ----- Close tar file fclose($v_tar); // ----- Error log PclErrorLog(-1, "Unable to open file '$v_temp_tarname' in binary write mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } } // ----- Look for compressed tar file else { // ----- Open the file in read mode TrFctMessage(__FILE__, __LINE__, 3, "Open file in gzip binary read mode"); if (($v_tar = @gzopen($p_tarname, "rb")) == 0) { // ----- Error log PclErrorLog(-2, "Unable to open file '$p_tarname' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Open a temporary file in write mode $v_temp_tarname = uniqid("pcltar-").".tmp"; TrFctMessage(__FILE__, __LINE__, 2, "Creating temporary archive file $v_temp_tarname"); if (($v_temp_tar = @gzopen($v_temp_tarname, "wb")) == 0) { // ----- Close tar file gzclose($v_tar); // ----- Error log PclErrorLog(-1, "Unable to open file '$v_temp_tarname' in binary write mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } } // ----- Read the blocks While (!($v_end_of_file = ($p_tar_mode == "tar"?feof($v_tar):gzeof($v_tar)))) { TrFctMessage(__FILE__, __LINE__, 3, "Looking for next header ..."); // ----- Clear cache of file infos clearstatcache(); // ----- Reset delete tag $v_delete_file = FALSE; // ----- Read the first 512 block header if ($p_tar_mode == "tar") $v_binary_data = fread($v_tar, 512); else $v_binary_data = gzread($v_tar, 512); // ----- Read the header properties if (($v_result = PclTarHandleReadHeader($v_binary_data, $v_header)) != 1) { // ----- Close the archive file if ($p_tar_mode == "tar") { fclose($v_tar); fclose($v_temp_tar); } else { gzclose($v_tar); gzclose($v_temp_tar); } @unlink($v_temp_tarname); // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // ----- Look for empty blocks to skip if ($v_header[filename] == "") { TrFctMessage(__FILE__, __LINE__, 2, "Empty block found. End of archive ?"); continue; } TrFctMessage(__FILE__, __LINE__, 2, "Found file '$v_header[filename]', size '$v_header[size]'"); // ----- Look for filenames to delete for ($i=0, $v_delete_file=FALSE; ($i<sizeof($p_file_list)) && (!$v_delete_file); $i++) { // ----- Compare the file names // if ($p_file_list[$i] == $v_header[filename]) if (($v_len = strcmp($p_file_list[$i], $v_header[filename])) <= 0) { if ($v_len==0) { TrFctMessage(__FILE__, __LINE__, 3, "Found that '$v_header[filename]' need to be deleted"); $v_delete_file = TRUE; } else { TrFctMessage(__FILE__, __LINE__, 3, "Look if '$v_header[filename]' is a file in $p_file_list[$i]"); if (substr($v_header[filename], strlen($p_file_list[$i]), 1) == "/") { TrFctMessage(__FILE__, __LINE__, 3, "'$v_header[filename]' is a file in $p_file_list[$i]"); $v_delete_file = TRUE; } } } } // ----- Copy files that do not need to be deleted if (!$v_delete_file) { TrFctMessage(__FILE__, __LINE__, 2, "Keep file '$v_header[filename]'"); // ----- Write the file header if ($p_tar_mode == "tar") { fputs($v_temp_tar, $v_binary_data, 512); } else { gzputs($v_temp_tar, $v_binary_data, 512); } // ----- Write the file data $n = ceil($v_header[size]/512); for ($i=0; $i<$n; $i++) { TrFctMessage(__FILE__, __LINE__, 3, "Read complete 512 bytes block number ".($i+1)); if ($p_tar_mode == "tar") { $v_content = fread($v_tar, 512); fwrite($v_temp_tar, $v_content, 512); } else { $v_content = gzread($v_tar, 512); gzwrite($v_temp_tar, $v_content, 512); } } // ----- File name and properties are logged if listing mode or file is extracted TrFctMessage(__FILE__, __LINE__, 2, "Memorize info about file '$v_header[filename]'"); // ----- Add the array describing the file into the list $p_list_detail[$v_nb] = $v_header; $p_list_detail[$v_nb][status] = "ok"; // ----- Increment $v_nb++; } // ----- Look for file that is to be deleted else { // ----- Trace TrFctMessage(__FILE__, __LINE__, 2, "Start deletion of '$v_header[filename]'"); TrFctMessage(__FILE__, __LINE__, 4, "Position avant jump [".($p_tar_mode=="tar"?ftell($v_tar):gztell($v_tar))."]"); // ----- Jump to next file if ($p_tar_mode == "tar") fseek($v_tar, ftell($v_tar)+(ceil(($v_header[size]/512))*512)); else gzseek($v_tar, gztell($v_tar)+(ceil(($v_header[size]/512))*512)); TrFctMessage(__FILE__, __LINE__, 4, "Position apr�s jump [".($p_tar_mode=="tar"?ftell($v_tar):gztell($v_tar))."]"); } // ----- Look for end of file if ($p_tar_mode == "tar") $v_end_of_file = feof($v_tar); else $v_end_of_file = gzeof($v_tar); } // ----- Write the last empty buffer PclTarHandleFooter($v_temp_tar, $p_tar_mode); // ----- Close the tarfile if ($p_tar_mode == "tar") { fclose($v_tar); fclose($v_temp_tar); } else { gzclose($v_tar); gzclose($v_temp_tar); } // ----- Unlink tar file if (!@unlink($p_tarname)) { // ----- Error log PclErrorLog(-11, "Error while deleting archive name $p_tarname"); } // ----- Rename tar file if (!@rename($v_temp_tarname, $p_tarname)) { // ----- Error log PclErrorLog(-12, "Error while renaming temporary file $v_temp_tarname to archive name $p_tarname"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleUpdate() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleUpdate($p_tarname, $p_file_list, &$p_list_detail, $p_tar_mode, $p_add_dir, $p_remove_dir) { TrFctStart(__FILE__, __LINE__, "PclTarHandleUpdate", "archive='$p_tarname', list, tar_mode=$p_tar_mode"); $v_result=1; $v_nb=0; $v_found_list = array(); // ----- Look for regular tar file if ($p_tar_mode == "tar") { // ----- Open file TrFctMessage(__FILE__, __LINE__, 3, "Open file in binary read mode"); if (($v_tar = @fopen($p_tarname, "rb")) == 0) { // ----- Error log PclErrorLog(-2, "Unable to open file '$p_tarname' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Open a temporary file in write mode $v_temp_tarname = uniqid("pcltar-").".tmp"; TrFctMessage(__FILE__, __LINE__, 2, "Creating temporary archive file $v_temp_tarname"); if (($v_temp_tar = @fopen($v_temp_tarname, "wb")) == 0) { // ----- Close tar file fclose($v_tar); // ----- Error log PclErrorLog(-1, "Unable to open file '$v_temp_tarname' in binary write mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } } // ----- Look for compressed tar file else { // ----- Open the file in read mode TrFctMessage(__FILE__, __LINE__, 3, "Open file in gzip binary read mode"); if (($v_tar = @gzopen($p_tarname, "rb")) == 0) { // ----- Error log PclErrorLog(-2, "Unable to open file '$p_tarname' in binary read mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Open a temporary file in write mode $v_temp_tarname = uniqid("pcltar-").".tmp"; TrFctMessage(__FILE__, __LINE__, 2, "Creating temporary archive file $v_temp_tarname"); if (($v_temp_tar = @gzopen($v_temp_tarname, "wb")) == 0) { // ----- Close tar file gzclose($v_tar); // ----- Error log PclErrorLog(-1, "Unable to open file '$v_temp_tarname' in binary write mode"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } } // ----- Prepare the list of files for ($i=0; $i<sizeof($p_file_list); $i++) { // ----- Reset the found list $v_found_list[$i] = 0; // ----- Calculate the stored filename $v_stored_list[$i] = $p_file_list[$i]; if ($p_remove_dir != "") { if (substr($p_file_list[$i], -1) != '/') $p_remove_dir .= "/"; if (substr($p_file_list[$i], 0, strlen($p_remove_dir)) == $p_remove_dir) { $v_stored_list[$i] = substr($p_file_list[$i], strlen($p_remove_dir)); TrFctMessage(__FILE__, __LINE__, 3, "Remove path '$p_remove_dir' in file '$p_file_list[$i]' = '$v_stored_list[$i]'"); } } if ($p_add_dir != "") { if (substr($p_add_dir, -1) == "/") $v_stored_list[$i] = $p_add_dir.$v_stored_list[$i]; else $v_stored_list[$i] = $p_add_dir."/".$v_stored_list[$i]; TrFctMessage(__FILE__, __LINE__, 3, "Add path '$p_add_dir' in file '$p_file_list[$i]' = '$v_stored_list[$i]'"); } $v_stored_list[$i] = PclTarHandlePathReduction($v_stored_list[$i]); TrFctMessage(__FILE__, __LINE__, 3, "After reduction '$v_stored_list[$i]'"); } // ----- Update file cache clearstatcache(); // ----- Read the blocks While (!($v_end_of_file = ($p_tar_mode == "tar"?feof($v_tar):gzeof($v_tar)))) { TrFctMessage(__FILE__, __LINE__, 3, "Looking for next header ..."); // ----- Clear cache of file infos clearstatcache(); // ----- Reset current found filename $v_current_filename = ""; // ----- Reset delete tag $v_delete_file = FALSE; // ----- Read the first 512 block header if ($p_tar_mode == "tar") $v_binary_data = fread($v_tar, 512); else $v_binary_data = gzread($v_tar, 512); // ----- Read the header properties if (($v_result = PclTarHandleReadHeader($v_binary_data, $v_header)) != 1) { // ----- Close the archive file if ($p_tar_mode == "tar") { fclose($v_tar); fclose($v_temp_tar); } else { gzclose($v_tar); gzclose($v_temp_tar); } @unlink($v_temp_tarname); // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // ----- Look for empty blocks to skip if ($v_header[filename] == "") { TrFctMessage(__FILE__, __LINE__, 2, "Empty block found. End of archive ?"); continue; } TrFctMessage(__FILE__, __LINE__, 2, "Found file '$v_header[filename]', size '$v_header[size]'"); // ----- Look for filenames to update for ($i=0, $v_update_file=FALSE, $v_found_file=FALSE; ($i<sizeof($v_stored_list)) && (!$v_update_file); $i++) { TrFctMessage(__FILE__, __LINE__, 4, "Compare with file '$v_stored_list[$i]'"); // ----- Compare the file names if ($v_stored_list[$i] == $v_header[filename]) { TrFctMessage(__FILE__, __LINE__, 3, "File '$v_stored_list[$i]' is present in archive"); TrFctMessage(__FILE__, __LINE__, 3, "File '$v_stored_list[$i]' mtime=".filemtime($p_file_list[$i])." ".date("l dS of F Y h:i:s A", filemtime($p_file_list[$i]))); TrFctMessage(__FILE__, __LINE__, 3, "Archived mtime=".$v_header[mtime]." ".date("l dS of F Y h:i:s A", $v_header[mtime])); // ----- Store found informations $v_found_file = TRUE; $v_current_filename = $p_file_list[$i]; // ----- Look if the file need to be updated if (filemtime($p_file_list[$i]) > $v_header[mtime]) { TrFctMessage(__FILE__, __LINE__, 3, "File '$p_file_list[$i]' need to be updated"); $v_update_file = TRUE; } else { TrFctMessage(__FILE__, __LINE__, 3, "File '$p_file_list[$i]' does not need to be updated"); $v_update_file = FALSE; } // ----- Flag the name in order not to add the file at the end $v_found_list[$i] = 1; } else { TrFctMessage(__FILE__, __LINE__, 4, "File '$p_file_list[$i]' is not '$v_header[filename]'"); } } // ----- Copy files that do not need to be updated if (!$v_update_file) { TrFctMessage(__FILE__, __LINE__, 2, "Keep file '$v_header[filename]'"); // ----- Write the file header if ($p_tar_mode == "tar") { fputs($v_temp_tar, $v_binary_data, 512); } else { gzputs($v_temp_tar, $v_binary_data, 512); } // ----- Write the file data $n = ceil($v_header[size]/512); for ($j=0; $j<$n; $j++) { TrFctMessage(__FILE__, __LINE__, 3, "Read complete 512 bytes block number ".($j+1)); if ($p_tar_mode == "tar") { $v_content = fread($v_tar, 512); fwrite($v_temp_tar, $v_content, 512); } else { $v_content = gzread($v_tar, 512); gzwrite($v_temp_tar, $v_content, 512); } } // ----- File name and properties are logged if listing mode or file is extracted TrFctMessage(__FILE__, __LINE__, 2, "Memorize info about file '$v_header[filename]'"); // ----- Add the array describing the file into the list $p_list_detail[$v_nb] = $v_header; $p_list_detail[$v_nb][status] = ($v_found_file?"not_updated":"ok"); // ----- Increment $v_nb++; } // ----- Look for file that need to be updated else { // ----- Trace TrFctMessage(__FILE__, __LINE__, 2, "Start update of file '$v_current_filename'"); // ----- Store the old file size $v_old_size = $v_header[size]; // ----- Add the file if (($v_result = PclTarHandleAddFile($v_temp_tar, $v_current_filename, $p_tar_mode, $v_header, $p_add_dir, $p_remove_dir)) != 1) { // ----- Close the tarfile if ($p_tar_mode == "tar") { fclose($v_tar); fclose($v_temp_tar); } else { gzclose($v_tar); gzclose($v_temp_tar); } @unlink($p_temp_tarname); // ----- Return status TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // ----- Trace TrFctMessage(__FILE__, __LINE__, 2, "Skip old file '$v_header[filename]'"); // ----- Jump to next file if ($p_tar_mode == "tar") fseek($v_tar, ftell($v_tar)+(ceil(($v_old_size/512))*512)); else gzseek($v_tar, gztell($v_tar)+(ceil(($v_old_size/512))*512)); // ----- Add the array describing the file into the list $p_list_detail[$v_nb] = $v_header; $p_list_detail[$v_nb][status] = "updated"; // ----- Increment $v_nb++; } // ----- Look for end of file if ($p_tar_mode == "tar") $v_end_of_file = feof($v_tar); else $v_end_of_file = gzeof($v_tar); } // ----- Look for files that does not exists in the archive and need to be added for ($i=0; $i<sizeof($p_file_list); $i++) { // ----- Look if file not found in the archive if (!$v_found_list[$i]) { TrFctMessage(__FILE__, __LINE__, 3, "File '$p_file_list[$i]' need to be added"); // ----- Add the file if (($v_result = PclTarHandleAddFile($v_temp_tar, $p_file_list[$i], $p_tar_mode, $v_header, $p_add_dir, $p_remove_dir)) != 1) { // ----- Close the tarfile if ($p_tar_mode == "tar") { fclose($v_tar); fclose($v_temp_tar); } else { gzclose($v_tar); gzclose($v_temp_tar); } @unlink($p_temp_tarname); // ----- Return status TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // ----- Add the array describing the file into the list $p_list_detail[$v_nb] = $v_header; $p_list_detail[$v_nb][status] = "added"; // ----- Increment $v_nb++; } else { TrFctMessage(__FILE__, __LINE__, 3, "File '$p_file_list[$i]' was already updated if needed"); } } // ----- Write the last empty buffer PclTarHandleFooter($v_temp_tar, $p_tar_mode); // ----- Close the tarfile if ($p_tar_mode == "tar") { fclose($v_tar); fclose($v_temp_tar); } else { gzclose($v_tar); gzclose($v_temp_tar); } // ----- Unlink tar file if (!@unlink($p_tarname)) { // ----- Error log PclErrorLog(-11, "Error while deleting archive name $p_tarname"); } // ----- Rename tar file if (!@rename($v_temp_tarname, $p_tarname)) { // ----- Error log PclErrorLog(-12, "Error while renaming temporary file $v_temp_tarname to archive name $p_tarname"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleReadHeader() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleReadHeader($v_binary_data, &$v_header) { TrFctStart(__FILE__, __LINE__, "PclTarHandleReadHeader", ""); $v_result=1; // ----- Read the 512 bytes header /* if ($p_tar_mode == "tar") $v_binary_data = fread($p_tar, 512); else $v_binary_data = gzread($p_tar, 512); */ // ----- Look for no more block if (strlen($v_binary_data)==0) { $v_header[filename] = ""; $v_header[status] = "empty"; // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result, "End of archive found"); return $v_result; } // ----- Look for invalid block size if (strlen($v_binary_data) != 512) { $v_header[filename] = ""; $v_header[status] = "invalid_header"; TrFctMessage(__FILE__, __LINE__, 2, "Invalid block size : ".strlen($v_binary_data)); // ----- Error log PclErrorLog(-10, "Invalid block size : ".strlen($v_binary_data)); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Calculate the checksum $v_checksum = 0; // ..... First part of the header for ($i=0; $i<148; $i++) { $v_checksum+=ord(substr($v_binary_data,$i,1)); } // ..... Ignore the checksum value and replace it by ' ' (space) for ($i=148; $i<156; $i++) { $v_checksum += ord(' '); } // ..... Last part of the header for ($i=156; $i<512; $i++) { $v_checksum+=ord(substr($v_binary_data,$i,1)); } TrFctMessage(__FILE__, __LINE__, 3, "Calculated checksum : $v_checksum"); // ----- Extract the values TrFctMessage(__FILE__, __LINE__, 2, "Header : '$v_binary_data'"); $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $v_binary_data); // ----- Extract the checksum for check $v_header[checksum] = OctDec(trim($v_data[checksum])); TrFctMessage(__FILE__, __LINE__, 3, "File checksum : $v_header[checksum]"); if ($v_header[checksum] != $v_checksum) { TrFctMessage(__FILE__, __LINE__, 2, "File checksum is invalid : $v_checksum calculated, $v_header[checksum] expected"); $v_header[filename] = ""; $v_header[status] = "invalid_header"; // ----- Look for last block (empty block) if (($v_checksum == 256) && ($v_header[checksum] == 0)) { $v_header[status] = "empty"; // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result, "End of archive found"); return $v_result; } // ----- Error log PclErrorLog(-13, "Invalid checksum : $v_checksum calculated, $v_header[checksum] expected"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } TrFctMessage(__FILE__, __LINE__, 2, "File checksum is valid ($v_checksum)"); // ----- Extract the properties $v_header[filename] = trim($v_data[filename]); TrFctMessage(__FILE__, __LINE__, 2, "Name : '$v_header[filename]'"); $v_header[mode] = OctDec(trim($v_data[mode])); TrFctMessage(__FILE__, __LINE__, 2, "Mode : '".DecOct($v_header[mode])."'"); $v_header[uid] = OctDec(trim($v_data[uid])); TrFctMessage(__FILE__, __LINE__, 2, "Uid : '$v_header[uid]'"); $v_header[gid] = OctDec(trim($v_data[gid])); TrFctMessage(__FILE__, __LINE__, 2, "Gid : '$v_header[gid]'"); $v_header[size] = OctDec(trim($v_data[size])); TrFctMessage(__FILE__, __LINE__, 2, "Size : '$v_header[size]'"); $v_header[mtime] = OctDec(trim($v_data[mtime])); TrFctMessage(__FILE__, __LINE__, 2, "Date : ".date("l dS of F Y h:i:s A", $v_header[mtime])); if (($v_header[typeflag] = $v_data[typeflag]) == "5") { $v_header[size] = 0; TrFctMessage(__FILE__, __LINE__, 2, "Size (folder) : '$v_header[size]'"); } TrFctMessage(__FILE__, __LINE__, 2, "File typeflag : $v_header[typeflag]"); /* ----- All these fields are removed form the header because they do not carry interesting info $v_header[link] = trim($v_data[link]); TrFctMessage(__FILE__, __LINE__, 2, "Linkname : $v_header[linkname]"); $v_header[magic] = trim($v_data[magic]); TrFctMessage(__FILE__, __LINE__, 2, "Magic : $v_header[magic]"); $v_header[version] = trim($v_data[version]); TrFctMessage(__FILE__, __LINE__, 2, "Version : $v_header[version]"); $v_header[uname] = trim($v_data[uname]); TrFctMessage(__FILE__, __LINE__, 2, "Uname : $v_header[uname]"); $v_header[gname] = trim($v_data[gname]); TrFctMessage(__FILE__, __LINE__, 2, "Gname : $v_header[gname]"); $v_header[devmajor] = trim($v_data[devmajor]); TrFctMessage(__FILE__, __LINE__, 2, "Devmajor : $v_header[devmajor]"); $v_header[devminor] = trim($v_data[devminor]); TrFctMessage(__FILE__, __LINE__, 2, "Devminor : $v_header[devminor]"); */ // ----- Set the status field $v_header[status] = "ok"; // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandlerDirCheck() // Description : // Check if a directory exists, if not it creates it and all the parents directory // which may be useful. // Parameters : // $p_dir : Directory path to check (without / at the end). // Return Values : // 1 : OK // -1 : Unable to create directory // -------------------------------------------------------------------------------- function PclTarHandlerDirCheck($p_dir) { $v_result = 1; TrFctStart(__FILE__, __LINE__, "PclTarHandlerDirCheck", "$p_dir"); // ----- Check the directory availability if ((is_dir($p_dir)) || ($p_dir == "")) { TrFctEnd(__FILE__, __LINE__, "'$p_dir' is a directory"); return 1; } // ----- Look for file alone /* if (!strstr("$p_dir", "/")) { TrFctEnd(__FILE__, __LINE__, "'$p_dir' is a file with no directory"); return 1; } */ // ----- Extract parent directory $p_parent_dir = dirname($p_dir); TrFctMessage(__FILE__, __LINE__, 3, "Parent directory is '$p_parent_dir'"); // ----- Just a check if ($p_parent_dir != $p_dir) { // ----- Look for parent directory if ($p_parent_dir != "") { if (($v_result = PclTarHandlerDirCheck($p_parent_dir)) != 1) { TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } } } // ----- Create the directory TrFctMessage(__FILE__, __LINE__, 3, "Create directory '$p_dir'"); if (!@mkdir($p_dir, 0777)) { // ----- Error log PclErrorLog(-8, "Unable to create directory '$p_dir'"); // ----- Return TrFctEnd(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); return PclErrorCode(); } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result, "Directory '$p_dir' created"); return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandleExtension() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function PclTarHandleExtension($p_tarname) { TrFctStart(__FILE__, __LINE__, "PclTarHandleExtension", "tar=$p_tarname"); // ----- Look for file extension if ((substr($p_tarname, -7) == ".tar.gz") || (substr($p_tarname, -4) == ".tgz")) { TrFctMessage(__FILE__, __LINE__, 2, "Archive is a gzip tar"); $v_tar_mode = "tgz"; } else if (substr($p_tarname, -4) == ".tar") { TrFctMessage(__FILE__, __LINE__, 2, "Archive is a tar"); $v_tar_mode = "tar"; } else { // ----- Error log PclErrorLog(-9, "Invalid archive extension"); TrFctMessage(__FILE__, __LINE__, PclErrorCode(), PclErrorString()); $v_tar_mode = ""; } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_tar_mode); return $v_tar_mode; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclTarHandlePathReduction() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function PclTarHandlePathReduction($p_dir) { TrFctStart(__FILE__, __LINE__, "PclTarHandlePathReduction", "dir='$p_dir'"); $v_result = ""; // ----- Look for not empty path if ($p_dir != "") { // ----- Explode path by directory names $v_list = explode("/", $p_dir); // ----- Study directories from last to first for ($i=sizeof($v_list)-1; $i>=0; $i--) { // ----- Look for current path if ($v_list[$i] == ".") { // ----- Ignore this directory // Should be the first $i=0, but no check is done } else if ($v_list[$i] == "..") { // ----- Ignore it and ignore the $i-1 $i--; } else if (($v_list[$i] == "") && ($i!=(sizeof($v_list)-1)) && ($i!=0)) { // ----- Ignore only the double '//' in path, // but not the first and last '/' } else { $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:""); } } } // ----- Return TrFctEnd(__FILE__, __LINE__, $v_result); return $v_result; } // -------------------------------------------------------------------------------- // ----- End of double include look } ?>svp_rechercher.php000066600000023347152503074170010300 0ustar00<?php /** * Gestion des recherches de plugins * * @plugin SVP pour SPIP * @license GPL * @package SPIP\SVP\Recherche **/ if (!defined("_ECRIRE_INC_VERSION")) return; include_spip('inc/plugin'); // ----------------------- Recherches de plugins --------------------------------- /** * Calcule une liste des paquets en fonctions de critères de recherche * * Cette liste : * - est sans doublons, ie on ne garde que la version la plus récente * - correspond aux critères * - est compatible avec la version SPIP installée sur le site * - ne liste pas ceux étant déjà installés (ces paquets peuvent toutefois être affichés) * - est triée par nom ou score * * @param string $phrase * Texte de la recherche * @param string $categorie * Type de catégorie de plugin (auteur, date...) * @param string $etat * État de plugin (stable, test...) * @param string|int $depot * Identifiant de dépot * @param string $version_spip * Version de SPIP dont le paquet doit être compatible * @param array $exclusions * Liste d'identifiants de plugin à ne pas intégrer dans la liste * @param bool $afficher_exclusions * Afficher aussi les paquets déjà installés (true) * ou ceux qui ne le sont pas (false) ? * @param bool $doublon * Afficher toutes les versions de paquet (true) * ou seulement la plus récente (false) ? * @param string $tri * Ordre du tri : nom | score * * @return array * Tableau classé par pertinence de résultat * - 'prefixe' => tableau de description du paquet (si pas de doublons demandé) * - n => tableau de descriptions du paquet (si doublons autorisés) **/ function svp_rechercher_plugins_spip($phrase, $categorie, $etat, $depot, $version_spip='', $exclusions=array(), $afficher_exclusions=false, $doublon=false, $tri='nom') { include_spip('inc/rechercher'); $plugins = array(); $scores = array(); $ids_paquets = array(); // On prepare l'utilisation de la recherche en base SPIP en la limitant aux tables spip_plugins // et spip_paquets si elle n'est pas vide if ($phrase) { $liste = liste_des_champs(); $tables = array('plugin' => $liste['plugin']); $options = array('jointures' => true, 'score' => true); // On cherche dans tous les enregistrements de ces tables des correspondances les plugins qui // correspondent a la phrase recherchee // -- On obtient une liste d'id de plugins et d'id de paquets $resultats = array('plugin' => array(), 'paquet' => array()); $resultats = recherche_en_base($phrase, $tables, $options); // -- On prepare le tableau des scores avec les paquets trouves par la recherche if ($resultats) { // -- On convertit les id de plugins en id de paquets $ids = array(); if (isset($resultats['plugin']) and $resultats['plugin']) { $ids_plugin = array_keys($resultats['plugin']); $where[] = sql_in('id_plugin', $ids_plugin); $ids = sql_allfetsel('id_paquet, id_plugin', 'spip_paquets', $where); } // -- On prepare les listes des id de paquet et des scores de ces memes paquets if (isset($resultats['paquet']) and $resultats['paquet']) { $ids_paquets = array_keys($resultats['paquet']); foreach ($resultats['paquet'] as $_id => $_score) { $scores[$_id] = intval($resultats['paquet'][$_id]['score']); } } // -- On merge les deux tableaux de paquets sans doublon en mettant a jour un tableau des scores foreach ($ids as $_ids) { $id_paquet = intval($_ids['id_paquet']); $id_plugin = intval($_ids['id_plugin']); if (array_search($id_paquet, $ids_paquets) === false) { $ids_paquets[] = $id_paquet; $scores[$id_paquet] = intval($resultats['plugin'][$id_plugin]['score']); } else { $scores[$id_paquet] = intval($resultats['paquet'][$id_paquet]['score']) + intval($resultats['plugin'][$id_plugin]['score']); } } } } else { if ($ids_paquets = sql_allfetsel('id_paquet', 'spip_paquets')) { $ids_paquets = array_map('reset', $ids_paquets); foreach ($ids_paquets as $_id) $scores[$_id] = 0; } } // Maintenant, on continue la recherche en appliquant, sur la liste des id de paquets, // les filtres complementaires : categorie, etat, exclusions et compatibilite spip // si on a bien trouve des resultats precedemment ou si aucune phrase n'a ete saisie // -- Preparation de la requete if ($ids_paquets) { $from = array('spip_plugins AS t1', 'spip_paquets AS t2', 'spip_depots AS t3'); $select = array('t1.nom AS nom', 't1.slogan AS slogan', 't1.prefixe AS prefixe', 't1.id_plugin AS id_plugin', 't2.id_paquet AS id_paquet', 't2.description AS description', 't2.compatibilite_spip AS compatibilite_spip', 't2.lien_doc AS lien_doc', 't2.auteur AS auteur', 't2.licence AS licence', 't2.etat AS etat', 't2.logo AS logo', 't2.version AS version', 't2.nom_archive AS nom_archive', 't3.url_archives AS url_archives', ); $where = array('t1.id_plugin=t2.id_plugin', 't2.id_depot=t3.id_depot'); if ($ids_paquets) $where[] = sql_in('t2.id_paquet', $ids_paquets); if (($categorie) AND ($categorie != 'toute_categorie')) $where[] = 't1.categorie=' . sql_quote($categorie); if (($etat) AND ($etat != 'tout_etat')) $where[] = 't2.etat=' . sql_quote($etat); if (($depot) AND ($depot != 'tout_depot')) $where[] = 't2.id_depot=' . sql_quote($depot); if ($exclusions AND !$afficher_exclusions) $where[] = sql_in('t2.id_plugin', $exclusions, 'NOT'); if ($resultats = sql_select($select, $from, $where)) { while ($paquets = sql_fetch($resultats)) { $prefixe = $paquets['prefixe']; $version = $paquets['version']; $nom = extraire_multi($paquets['nom']); $slogan = extraire_multi($paquets['slogan']); $description = extraire_multi($paquets['description']); if (svp_verifier_compatibilite_spip($paquets['compatibilite_spip'], $version_spip)) { // Le paquet remplit tous les criteres, on peut le selectionner // -- on utilise uniquement la langue du site $paquets['nom'] = $nom; $paquets['slogan'] = $slogan; $paquets['description'] = $description; // -- on ajoute le score si on a bien saisi une phrase if ($phrase) $paquets['score'] = $scores[intval($paquets['id_paquet'])]; else $paquets['score'] = 0; // -- on construit l'url de l'archive $paquets['url_archive'] = $paquets['url_archives'] . '/' . $paquets['nom_archive']; // -- on gere les exclusions si elle doivent etre affichees if ($afficher_exclusions AND in_array($paquets['id_plugin'], $exclusions)) $paquets['installe'] = true; else $paquets['installe'] = false; // -- On traite les doublons (meme plugin, versions differentes) if ($doublon) // ajout systematique du paquet $plugins[] = $paquets; else { // ajout // - si pas encore trouve // - ou si sa version est inferieure (on garde que la derniere version) if (!isset($plugins[$prefixe]) OR !$plugins[$prefixe] OR ($plugins[$prefixe] AND spip_version_compare($plugins[$prefixe]['version'], $version, '<'))) { $plugins[$prefixe] = $paquets; } } } } } // On trie le tableau par score décroissant ou nom croissant $fonction = 'svp_trier_par_' . $tri; if ($doublon) usort($plugins, $fonction); else uasort($plugins, $fonction); } return $plugins; } /** * Récupère les identifiants des plugins déjà installés * * @return int[] * Liste d'identifiants de plugins */ function svp_lister_plugins_installes(){ $ids = array(); // On recupere la liste des plugins installes physiquement sur le site // Pour l'instant ce n'est pas possible avec les fonctions natives de SPIP // donc on se contente des plugins actifs // - liste des prefixes en lettres majuscules des plugins actifs $plugins = liste_plugin_actifs(); // - liste des id de plugin correspondants // Il se peut que certains plugins ne soient pas trouves dans la bdd car aucun zip n'est disponible // (donc pas inclus dans archives.xml). C'est le cas des plugins_dist du core $ids = sql_allfetsel('id_plugin', 'spip_plugins', sql_in('prefixe', array_keys($plugins))); $ids = array_map('reset', $ids); $ids = array_map('intval', $ids); return $ids; } /** * Teste la compatibilité d'un intervalle de compatibilité avec une version * donnée de SPIP * * @param string $intervalle * Intervalle de compatibilité, tel que [2.1;3.0] * @param string $version_spip * Version de SPIP, tel que 3.0.4 (par défaut la version de SPIP en cours) * @return bool * true si l'intervalle est compatible, false sinon */ function svp_verifier_compatibilite_spip($intervalle, $version_spip = '') { if (!$version_spip) $version_spip = $GLOBALS['spip_version_branche'].".".$GLOBALS['spip_version_code']; return plugin_version_compatible($intervalle, $version_spip,'spip'); } /** * Callback de tri pour trier les résultats de plugin par score décroissant. * * Cette fonction est appelée par un usort ou uasort * * @param array $p1 * Plugin à comparer * @param array $p2 * Plugin à comparer * @return int */ function svp_trier_par_score($p1, $p2){ if ($p1['score'] == $p2['score']) $retour = 0; else $retour = ($p1['score'] < $p2['score']) ? 1 : -1; return $retour; } /** * Callback de tri pour trier les résultats de plugin par nom (alphabétique). * * Si le nom est identique on classe par version decroissante * Cette fonction est appelée par un usort ou uasort * * @param array $p1 * Plugin à comparer * @param array $p2 * Plugin à comparer * @return int */ function svp_trier_par_nom($p1, $p2){ $c1 = strcasecmp($p1['nom'], $p2['nom']); if ($c1 == 0) { $c2 = spip_version_compare($p1['version'], $p1['version'], '<'); $retour = ($c2) ? 1 : -1; } else $retour = ($c1 < 0) ? -1 : 1; return $retour; } ?> svp_phraser.php000066600000036035152503074170007630 0ustar00<?php /** * Fichier permettant de phraser les XML des fichiers paquet.xml et plugin.xml * ainsi que des fichiers décrivant le contenu d'un dépot de paquets. * * @plugin SVP pour SPIP * @license GPL * @package SPIP\SVP\Plugins **/ if (!defined("_ECRIRE_INC_VERSION")) return; include_spip('inc/xml'); include_spip('inc/config'); if (!defined('_SVP_MODE_RUNTIME')) { /** * Mode d'utilisation de SVP runtime ou pas : * - En mode runtime (true), on ne charge que les plugins compatibles avec la version courante * - En mode non runtime (false) on charge tous les plugins : cas du site Plugins SPIP * Runtime est le mode par defaut * @var bool */ define('_SVP_MODE_RUNTIME', (lire_config('svp/mode_runtime', 'oui') == 'oui' ? true : false)); } // Type parseur XML à appliquer pour récupérer les infos du plugin /** @var string Phraseur à utiliser pour un XML de plugin.xml */ define('_SVP_DTD_PLUGIN', 'plugin'); /** @var string Phraseur à utiliser pour un XML de paquet.xml */ define('_SVP_DTD_PAQUET', 'paquet'); // Regexp de recherche des balises principales de archives.xml define('_SVP_REGEXP_BALISE_DEPOT', '#<depot[^>]*>(.*)</depot>#Uims'); define('_SVP_REGEXP_BALISE_ARCHIVES', '#<archives[^>]*>(.*)</archives>#Uims'); define('_SVP_REGEXP_BALISE_ARCHIVE', '#<archive[^s][^>]*>(.*)</archive>#Uims'); define('_SVP_REGEXP_BALISE_ZIP', '#<zip[^>]*>(.*)</zip>#Uims'); define('_SVP_REGEXP_BALISE_TRADUCTIONS', '#<traductions[^>]*>(.*)</traductions>#Uims'); define('_SVP_REGEXP_BALISE_PLUGIN', '#<plugin[^>]*>(.*)</plugin>#Uims'); define('_SVP_REGEXP_BALISE_PAQUET', '#<paquet[^>]*>(.*)</paquet>#Uims'); define('_SVP_REGEXP_BALISE_MULTIS', '#<multis[^>]*>(.*)</multis>#Uims'); // Liste des categories de plugin # define('_CATEGORIES_PLUGIN', serialize($categories_plugin)); $GLOBALS['categories_plugin'] = array( 'communication', 'edition', 'multimedia', 'navigation', 'date', 'divers', 'auteur', 'statistique', 'performance', 'maintenance', 'outil', 'theme', 'squelette', 'aucune' ); /** Liste des balises techniques autorisées dans la balise <spip> */ $GLOBALS['balises_techniques'] = array( 'menu', 'chemin', 'lib', 'necessite', 'onglet', 'procure', 'pipeline', 'utilise', 'options', 'fonctions', 'install'); # define('_BALISES_TECHNIQUES', serialize($balises_techniques)); /** Liste des balises autorisant une traduction */ $GLOBALS['balises_multis'] = array( 'nom', 'slogan', 'description'); # define('_BALISES_MULTIS', serialize($balises_multis)); /** * Phrase un fichier décrivant un dépot, dont le chemin local est donné * * Le fichier est au format XML et contient deux balises principales : * - <depot>...</depot> : informations de description du depot (facultatif) * - <archives>...</archives> : liste des informations sur chaque archive (obligatoire) * * La fonction met en cache le résultat du phrasage de chaque archive et ne * rephrase que les archives ayant changées. * * @param string $fichier_xml * Chemin local du fichier XML de description du dépot * @return array|bool * false si erreur, * Tableau de 2 index sinon : * - depot : description du dépot * - paquets : */ function svp_phraser_depot($fichier_xml) { // le fichier xml fournit sous forme de fichier lire_fichier($fichier_xml,$xml); // Initialisation du tableau des informations // -- Si aucun bloc depot n'est trouve le titre et le type prennent une valeur par defaut $infos = array( 'depot' => array( 'titre' => _T('svp:titre_nouveau_depot'), 'type' => 'manuel'), 'paquets' => array()); // Extraction et phrasage du bloc depot si il existe // -- Si le bloc <depot> n'est pas renseigne on ne considere pas cela comme une erreur $balises_depot = array('titre', 'descriptif', 'type', 'url_serveur', 'url_brouteur', 'url_archives', 'url_commits'); if (preg_match(_SVP_REGEXP_BALISE_DEPOT, $xml, $matches)) { if (is_array($arbre_depot = spip_xml_parse($matches[1]))) { $infos['depot'] = svp_aplatir_balises($balises_depot, $arbre_depot, 'nonvide', $infos['depot']); } } // Extraction et phrasage du bloc des archives si il existe // -- Le bloc <archives> peut etre une chaine de grande taille et provoquer une erreur // sur une recherche de regexp. On ne teste donc pas l'existence de cette balise // -- Si aucun bloc <archive> c'est aussi une erreur if (!preg_match_all(_SVP_REGEXP_BALISE_ARCHIVE, $xml, $matches)) return false; // lire le cache des md5 pour ne parser que ce qui a change $fichier_xml_md5 = $fichier_xml . ".md5.txt"; lire_fichier($fichier_xml_md5,$cache_md5); if (!$cache_md5 OR !$cache_md5 = unserialize($cache_md5)) $cache_md5 = array(); $infos['paquets'] = svp_phraser_archives($matches[0], $cache_md5); ecrire_fichier($fichier_xml_md5,serialize($cache_md5)); // -- Si aucun paquet extrait c'est aussi une erreur if (!$infos['paquets']) return false; return $infos; } /** * Phrase la liste des balises <archive> * * Chaque bloc XML est constitue de 3 sous-blocs principaux : * - <zip> : contient les balises d'information sur le zip (obligatoire) * - <traductions> : contient la compilation des informations de traduction (facultatif) * - <plugin> ou <paquet> suivant la DTD : le contenu du fichier plugin.xml ou paquet.xml (facultatif) * * @param array $archives * Tableau de la liste des archives trouvées dans la description d'un dépot * @param array $md5_cache * Tableau des descriptions d'archives déjà connues : on supprime * à la fin celles qui ne font plus parties du dépot. * @return array * Tableau décrivant chaque archive, avec en index l'url de l'archive. * Tableau (url => Tableau de description de l'archive) */ function svp_phraser_archives($archives,&$md5_cache=array()) { include_spip('inc/plugin'); $seen = array(); $paquets = array(); $version_spip = $GLOBALS['spip_version_branche'].".".$GLOBALS['spip_version_code']; // On verifie qu'il existe au moins une archive if (!$archives) return $paquets; // On phrase chacune des archives // Seul le bloc <zip> est obligatoire foreach ($archives as $_cle => $_archive){ // quand version spip ou mode runtime changent, // il faut mettre le xml a jour pour voir les plugins compatibles ou non $md5 = md5($_archive.":$version_spip:"._SVP_MODE_RUNTIME); if (isset($md5_cache[$md5])){ if (is_array($p=$md5_cache[$md5])) $paquets[$p['file']] = $p; // ce paquet est connu $seen[] = $md5; } elseif (preg_match(_SVP_REGEXP_BALISE_ZIP, $_archive, $matches)) { // Extraction de la balise <zip> $zip = svp_phraser_zip($matches[1]); if ($zip) { // Extraction de la balise traductions $traductions = array(); if (preg_match(_SVP_REGEXP_BALISE_TRADUCTIONS, $_archive, $matches)) $traductions = svp_phraser_traductions($matches[1]); // La balise <archive> peut posseder un attribut qui precise la DTD utilisee pour les plugins (plugin ou paquet) // Sinon, c'est la DTD plugin qui est utilisee list($tag, $attributs) = spip_xml_decompose_tag($_archive); // -- On stocke la DTD d'extraction des infos du plugin $dtd = (isset($attributs['dtd']) AND $attributs['dtd']) ? $attributs['dtd'] : _SVP_DTD_PLUGIN; // Extraction *des balises* plugin ou *de la balise* paquet suivant la DTD et la version SPIP // -- DTD : si on utilise plugin.xml on extrait la balise <plugin> sinon la balise <paquet> $xml = svp_phraser_plugin($dtd, $_archive); // Si on est en mode runtime, on est seulement interesse par les plugins compatibles avec // la version courant de SPIP. On ne stocke donc pas les autres plugins. // Si on est pas en mode runtime on prend tout ! if (!_SVP_MODE_RUNTIME OR (_SVP_MODE_RUNTIME AND isset($xml['compatibilite']) AND plugin_version_compatible($xml['compatibilite'], $version_spip, 'spip'))) { $paquets[$zip['file']] = $zip; $paquets[$zip['file']]['traductions'] = $traductions; $paquets[$zip['file']]['dtd'] = $dtd; $paquets[$zip['file']]['plugin'] = $xml; $paquets[$zip['file']]['md5'] = $md5; $md5_cache[$md5] = $paquets[$zip['file']]; $seen[] = $md5; } else{ $md5_cache[$md5] = $zip['file']; $seen[] = $md5; } } } } // supprimer du cache les zip qui ne sont pas dans le nouveau $archives $oldies = array_diff(array_keys($md5_cache),$seen); foreach ($oldies as $old_md5){ unset($md5_cache[$old_md5]); } return $paquets; } /** * Phrase le contenu du XML décrivant une archive suivant une DTD * de plugin.xml ou de paquet.xml donnée * * La fonction peut-être appelée via archives.xml ou via un xml de plugin. * Elle phrase la balise <multi> dans le cas d'une DTD paquet qui contient * les traductions du nom, slogan et description * * @global $balises_multis * * @param string $dtd * Nom du type de dtd : plugin ou paquet (pour phraser un plugin.xml ou un paquet.xml) * @param string $contenu * Contenu XML à phraser * @return array * Description du plugin **/ function svp_phraser_plugin($dtd, $contenu) { global $balises_multis; static $informer = array(); $plugin = array(); // On initialise les informations du plugin avec le contenu du plugin.xml ou paquet.xml $regexp = ($dtd == 'plugin') ? _SVP_REGEXP_BALISE_PLUGIN : _SVP_REGEXP_BALISE_PAQUET; if ($nb_balises = preg_match_all($regexp, $contenu, $matches)) { $plugins = array(); // Pour chacune des occurences de la balise on extrait les infos foreach ($matches[0] as $_balise_plugin) { // Extraction des informations du plugin suivant le standard SPIP if (!isset($informer[$dtd])) { $informer[$dtd] = charger_fonction('infos_' . $dtd, 'plugins'); } $plugins[] = $informer[$dtd]($_balise_plugin); } // On appelle systematiquement une fonction de mise a jour de la structure de donnees du plugin : // -- Si DTD plugin et que le nombre de balises plugin > 1 ou si DTD paquet avec une presence de balise spip // alors on fusionne donc les informations recoltees // -- sinon on arrange la structure pour deplacer le contenu des balises dites techniques dans un sous tableau // d'index 0 par similitude avec la structure fusionnee $fusionner = charger_fonction('fusion_' . $dtd, 'plugins'); if ($dtd == 'plugin') $plugin = $fusionner($plugins); else $plugin = $fusionner($plugins[0]); // Pour la DTD paquet, les traductions du nom, slogan et description sont compilees dans une balise // du fichier archives.xml. Il faut donc completer les informations precedentes avec cette balise if (($dtd == _SVP_DTD_PAQUET) AND (preg_match(_SVP_REGEXP_BALISE_MULTIS, $contenu, $matches))) { $multis = array(); if (is_array($arbre = spip_xml_parse($matches[1]))) $multis = svp_aplatir_balises($balises_multis, $arbre); // Le nom peut etre traduit ou pas, il faut donc le tester if ($multis['nom']) $plugin['nom'] = $multis['nom']; // Slogan et description sont forcement des items de langue $plugin['slogan'] = $multis['slogan']; $plugin['description'] = $multis['description']; } } return $plugin; } /** * Phrase le contenu de la balise <zip> * * Extrait du XML les informations du zip * * @param string $contenu * Description XML de l'archive * @return array * Description du zip. * - Index 'file' : nom du zip * - Index 'size' : taille * - Index 'date' : date de création * - Index 'last_commit' : date du dernier commit * - Index 'source' : arborescence relative des sources */ function svp_phraser_zip($contenu) { static $balises_zip = array('file', 'size', 'date', 'source', 'last_commit'); $zip = array(); if (is_array($arbre = spip_xml_parse($contenu))) $zip = svp_aplatir_balises($balises_zip, $arbre); return $zip; } /** * Phrase le contenu d'une balise <traductions> en un tableau plus * facilement utilisable * * @param string $contenu * Contenu XML de la balise <traductions> * @return array * Tableau complexe avec pour index les noms des modules de langue et pour * valeur leur description. Chaque description contient dedans 3 index : * - reference : la langue de référence * - gestionnaire : quel logiciel à servi à gérer les traductions * - langues : tableau classé par langue puis par traducteurs, qui indique * l'ensemble des traducteurs pour chacune des langues présentes */ function svp_phraser_traductions($contenu) { $traductions = array(); if (is_array($arbre = spip_xml_parse($contenu))) { foreach ($arbre as $_tag => $_langues) { // On commence par les balises <traduction> et leurs attributs list($tag, $attributs_traduction) = spip_xml_decompose_tag($_tag); $traductions[$attributs_traduction['module']]['reference'] = $attributs_traduction['reference']; $traductions[$attributs_traduction['module']]['gestionnaire'] = isset($attributs_traduction['gestionnaire']) ? $attributs_traduction['gestionnaire'] : '' ; // On continue par les balises <langue> qui donnent le code en attribut // et les balises <traducteur> qui donnent uniquement le nom en attribut if (is_array($_langues[0])) { foreach ($_langues[0] as $_tag => $_traducteurs) { list($tag, $attributs_langue) = spip_xml_decompose_tag($_tag); $traducteurs = array(); if (is_array($_traducteurs[0])) { foreach ($_traducteurs[0] as $_tag => $_vide) { list($tag, $attributs_traducteur) = spip_xml_decompose_tag($_tag); $traducteurs[] = $attributs_traducteur; } } $traductions[$attributs_traduction['module']]['langues'][$attributs_langue['code']] = $traducteurs; } } } } return $traductions; } /** * Aplatit plusieurs clés d'un arbre xml dans un tableau * * Effectue un trim() de la valeur trouvée dans l'arbre * * @param array $balises * Liste de noms de balises XML. * Peut aussi être un tableau indiquant un renommage d'une balise * au passage tel que 'x' => 'y' qui cherchera x dans l'arbre XML et * l'applatira dans y. * @param array $arbre_xml * Un arbre issu de spip_xml_parse() * @param string $mode * Mode d'affectation des valeurs trouvées * - 'vide_et_nonvide' : Affecte une chaine vide si la balise n'est * pas trouvée dans l'arbre et affecte la valeur de la balise sinon. * - 'nonvide' : Si la balise n'est pas trouvée dans l'arbre ou si son * contenu est vide, affecte la valeur du tableau initial concernant * cette balise si elle est connue. * @param array * Tableau initial pouvant contenir des valeurs par défaut à affecter * à chaque balise avec 'x' => 'valeur' */ function svp_aplatir_balises($balises, $arbre_xml, $mode='vide_et_nonvide', $tableau_initial=array()) { $tableau_aplati = array(); if (!$balises) return $tableau_initial; foreach ($balises as $_cle => $_valeur){ $tag = (is_string($_cle)) ? $_cle : $_valeur; $valeur_aplatie = ''; if (isset($arbre_xml[$tag])) { $valeur_aplatie = trim(spip_xml_aplatit($arbre_xml[$tag])); } if (($mode == 'vide_et_nonvide') OR (($mode == 'nonvide') AND $valeur_aplatie)) $tableau_aplati[$_valeur] = $valeur_aplatie; else $tableau_aplati[$_valeur] = isset($tableau_initial[$_valeur]) ? $tableau_initial[$_valeur] : ''; } return $tableau_aplati; } ?> svp_depoter_local.php000066600000056767152503074170011016 0ustar00<?php /** * Traitement du dépot local * * Le dépot local est la liste de paquets qui sont présents sur l'hébergement * dans un des répertoires de plugins, actifs ou non actifs. * * Dans la base, un dépot local est représenté avec un id_dépot nul. * Il n'y a cependant pas de ligne spécifique décrivant le dépot local * dans la table SQL spip_depots, mais juste des valeurs id_depot=0 dans * la table spip_paquets. * * @plugin SVP pour SPIP * @license GPL * @package SPIP\SVP\Depots */ /** * Met à jour les tables SQL paquets et plugins pour qui concerne * les paquets locaux (ceux présents sur le site). * * On ne met à jour que ce qui a changé, sauf si on force le recalcule * de toutes les informations locales avec var_mode=vider_paquets_locaux * dans l'URL ou en mettant le paramètre $force à true. * * @param bool $force * - false : n'actualise que les paquets modifiés * - true : efface et recrée la liste de tous les paquets locaux * @param array $erreurs_xml * Si des erreurs XML sont présentes, elles se retrouvent dans ce tableau * @return string * Temps d'exécution **/ function svp_actualiser_paquets_locaux($force = false, &$erreurs_xml = array()) { spip_timer('paquets_locaux'); $paquets = svp_descriptions_paquets_locaux($erreurs_xml); // un mode pour tout recalculer sans désinstaller le plugin... ! if ($force OR _request('var_mode') == 'vider_paquets_locaux') { svp_base_supprimer_paquets_locaux(); svp_base_inserer_paquets_locaux($paquets); } else { svp_base_modifier_paquets_locaux($paquets); } svp_base_actualiser_paquets_actifs(); $temps = spip_timer('paquets_locaux'); #spip_log('svp_actualiser_paquets_locaux', 'SVP'); #spip_log($temps, 'SVP'); return "Éxécuté en : " . $temps; } /** * Calcule la description de chaque paquet local * * Les paquets peuvent être stockés à 3 endroits : * plugins, plugins-dist, plugins-supp définis par les constantes respectives * _DIR_PLUGINS, _DIR_PLUGINS_DIST, _DIR_PLUGINS_SUPP * * @param array $erreurs_xml * Les erreurs XML éventuelles des paquet.xml se retrouvent dedans s'il y en a * @return array * Descriptions des paquets (intégrant un hash), stockés par * constante, puis par chemin. * array[_DIR_PLUGIN*][$chemin] = description **/ function svp_descriptions_paquets_locaux(&$erreurs_xml = array()) { include_spip('inc/plugin'); liste_plugin_files(_DIR_PLUGINS); liste_plugin_files(_DIR_PLUGINS_DIST); $get_infos = charger_fonction('get_infos', 'plugins'); $paquets_locaux = array( '_DIR_PLUGINS' => $get_infos(array(), false, _DIR_PLUGINS), '_DIR_PLUGINS_DIST' => $get_infos(array(), false, _DIR_PLUGINS_DIST), ); if (defined('_DIR_PLUGINS_SUPPL') and _DIR_PLUGINS_SUPPL) { liste_plugin_files(_DIR_PLUGINS_SUPPL); $paquets_locaux['_DIR_PLUGINS_SUPPL'] = $get_infos(array(), false, _DIR_PLUGINS_SUPPL); } // creer la liste des signatures foreach($paquets_locaux as $const_dir => $paquets) { foreach ($paquets as $chemin => $paquet) { // on propose le paquet uniquement s'il n'y a pas eu d'erreur de lecture XML bloquante if (!isset($paquet['erreur'])) { $paquets_locaux[$const_dir][$chemin]['signature'] = md5($const_dir . $chemin . serialize($paquet)); } else { // Erreur XML ! unset($paquets_locaux[$const_dir][$chemin]); spip_log("Impossible de lire la description XML de $chemin . Erreurs :", 'svp.' . _LOG_ERREUR); spip_log($paquet['erreur'], 'svp.' . _LOG_ERREUR); $erreurs_xml[] = $paquet['erreur'][0]; } } } return $paquets_locaux; } /** * Supprime tous les paquets et plugins locaux. **/ function svp_base_supprimer_paquets_locaux() { sql_delete('spip_paquets', 'id_depot = ' . 0); //_paquets locaux en 0 sql_delete('spip_plugins', sql_in('id_plugin', sql_get_select('DISTINCT(id_plugin)', 'spip_paquets'), 'NOT')); } /** * Actualise les informations en base sur les paquets locaux * en ne modifiant que ce qui a changé. * * @param array $paquets_locaux * Descriptions des paquets (intégrant un hash), stockés par * constante, puis par chemin. * array[_DIR_PLUGIN*][$chemin] = description **/ function svp_base_modifier_paquets_locaux($paquets_locaux) { include_spip('inc/svp_depoter_distant'); // On ne va modifier QUE les paquets locaux qui ont change // Et cela en comparant les md5 des informations fouries. $signatures = array(); // recuperer toutes les signatures foreach($paquets_locaux as $const_dir => $paquets) { foreach ($paquets as $chemin => $paquet) { $signatures[$paquet['signature']] = array( 'constante' => $const_dir, 'chemin' => $chemin, 'paquet' => $paquet, ); } } // tous les paquets du depot qui ne font pas parti des signatures $anciens_paquets = sql_allfetsel('id_paquet', 'spip_paquets', array('id_depot=' . sql_quote(0), sql_in('signature', array_keys($signatures), 'NOT'))); $anciens_paquets = array_map('array_shift', $anciens_paquets); // tous les plugins correspondants aux anciens paquets $anciens_plugins = sql_allfetsel('p.id_plugin', array('spip_plugins AS p', 'spip_paquets AS pa'), array('p.id_plugin=pa.id_plugin', sql_in('pa.id_paquet', $anciens_paquets))); $anciens_plugins = array_map('array_shift', $anciens_plugins); // suppression des anciens paquets sql_delete('spip_paquets', sql_in('id_paquet', $anciens_paquets)); // supprimer les plugins orphelins svp_supprimer_plugins_orphelins($anciens_plugins); // on ne garde que les paquets qui ne sont pas presents dans la base $signatures_base = sql_allfetsel('signature', 'spip_paquets', 'id_depot='.sql_quote(0)); $signatures_base = array_map('array_shift', $signatures_base); $signatures = array_diff_key($signatures, array_flip($signatures_base)); // on recree la liste des paquets locaux a inserer $paquets_locaux = array(); foreach ($signatures as $s => $infos) { if (!isset($paquets_locaux[$infos['constante']])) { $paquets_locaux[$infos['constante']] = array(); } $paquets_locaux[$infos['constante']][$infos['chemin']] = $infos['paquet']; } svp_base_inserer_paquets_locaux($paquets_locaux); } /** * Insère en base tous les paquets locaux transmis * * De chaque description est extrait la partie plugin (1 seul plugin * par préfixe de plugin connu) et la partie paquet (il peut y avoir plusieurs * paquets pour un même préfixe de plugin). * * @note * On essaie au mieux de faire des requêtes d'insertions multiples, * mieux gérées par les moteurs SQL (particulièrement pour SQLite) * * @param array $paquets_locaux * Descriptions des paquets (intégrant un hash), stockés par * constante, puis par chemin. * array[_DIR_PLUGIN*][$chemin] = description **/ function svp_base_inserer_paquets_locaux($paquets_locaux) { include_spip('inc/svp_depoter_distant'); // On initialise les informations specifiques au paquet : // l'id du depot et les infos de l'archive $paquet_base = array( 'id_depot' => 0, 'nom_archive' => '', 'nbo_archive' => '', 'maj_archive' => '', 'src_archive' => '', 'date_modif' => '', 'maj_version' => '', 'signature' => '', ); $preparer_sql_paquet = charger_fonction('preparer_sql_paquet', 'plugins'); // pour chaque decouverte, on insere les paquets en base. // on evite des requetes individuelles, tres couteuses en sqlite... $cle_plugins = array(); // prefixe => id $insert_plugins = array(); // insertion prefixe... $insert_plugins_vmax = array(); // vmax des nouveaux plugins... $insert_paquets = array(); // insertion de paquet... include_spip('inc/config'); $recents = lire_config('plugins_interessants'); $installes = lire_config('plugin_installes'); $actifs = lire_config('plugin'); $attentes = lire_config('plugin_attente'); foreach($paquets_locaux as $const_dir => $paquets) { foreach ($paquets as $chemin => $paquet) { // Si on est en presence d'un plugin dont la dtd est "paquet" on compile en multi // les nom, slogan et description a partir des fichiers de langue. // De cette façon, les informations des plugins locaux et distants seront identiques // => On evite l'utilisation de _T() dans les squelettes if ($paquet['dtd'] == 'paquet') { $multis = svp_compiler_multis($paquet['prefix'], constant($const_dir) . '/' . $chemin); if (isset($multis['nom'])) $paquet['nom'] = $multis['nom']; $paquet['slogan'] = (isset($multis['slogan'])) ? $multis['slogan'] : ''; $paquet['description'] = (isset($multis['description'])) ? $multis['description'] : ''; } $le_paquet = $paquet_base; #$le_paquet['traductions'] = serialize($paquet['traductions']); if ($champs = $preparer_sql_paquet($paquet)) { // Eclater les champs recuperes en deux sous tableaux, un par table (plugin, paquet) $champs = eclater_plugin_paquet($champs); $paquet_plugin = true; // On complete les informations du paquet et du plugin $le_paquet = array_merge($le_paquet, $champs['paquet']); $le_plugin = $champs['plugin']; // On loge l'absence de categorie ou une categorie erronee et on positionne la categorie par defaut "aucune" if (!$le_plugin['categorie']) { $le_plugin['categorie'] = 'aucune'; } else { if (!in_array($le_plugin['categorie'], $GLOBALS['categories_plugin'])) { $le_plugin['categorie'] = 'aucune'; } } // creation du plugin... $prefixe = strtoupper( $le_plugin['prefixe'] ); // on fait attention lorqu'on cherche ou ajoute un plugin // le nom et slogan est TOUJOURS celui de la plus haute version // et il faut donc possiblement mettre a jour la base... // // + on est tolerant avec les versions identiques de plugin deja presentes // on permet le recalculer le titre... if (!isset($cle_plugins[$prefixe])) { if (!$res = sql_fetsel('id_plugin, vmax', 'spip_plugins', 'prefixe = '.sql_quote($prefixe))) { // on ne stocke pas de vmax pour les plugins locaux dans la bdd... (parait il) if (!isset($insert_plugins[$prefixe])) { $insert_plugins[$prefixe] = $le_plugin; $insert_plugins_vmax[$prefixe] = $le_paquet['version']; } elseif (spip_version_compare($le_paquet['version'], $insert_plugins_vmax[$prefixe], '>')) { $insert_plugins[$prefixe] = $le_plugin; $insert_plugins_vmax[$prefixe] = $le_paquet['version']; } } else { $id_plugin = $res['id_plugin']; $cle_plugins[$prefixe] = $id_plugin; // comme justement on ne stocke pas de vmax pour les plugins locaux... // il est possible que ce test soit faux. pff. if (spip_version_compare($le_paquet['version'], $res['vmax'], '>=')) { sql_updateq('spip_plugins', $le_plugin, 'id_plugin='.sql_quote($id_plugin)); } } } // ajout du prefixe dans le paquet $le_paquet['prefixe'] = $prefixe; $le_paquet['constante'] = $const_dir; $le_paquet['src_archive'] = $chemin; $le_paquet['recent'] = isset($recents[$chemin]) ? $recents[$chemin] : 0; $le_paquet['installe'] = in_array($chemin, $installes) ? 'oui': 'non'; // est desinstallable ? $le_paquet['obsolete'] = 'non'; $le_paquet['signature'] = $paquet['signature']; // le plugin est il actuellement actif ? $actif = "non"; if (isset($actifs[$prefixe]) and ($actifs[$prefixe]['dir_type'] == $const_dir) and ($actifs[$prefixe]['dir'] == $chemin)) { $actif = "oui"; } $le_paquet['actif'] = $actif; // le plugin etait il actif mais temporairement desactive // parce qu'une dependence a disparue ? $attente = "non"; if (isset($attentes[$prefixe]) and ($attentes[$prefixe]['dir_type'] == $const_dir) and ($attentes[$prefixe]['dir'] == $chemin)) { $attente = "oui"; $le_paquet['actif'] = "oui"; // il est presenté dans la liste des actifs (en erreur). } $le_paquet['attente'] = $attente; // on recherche d'eventuelle mises a jour existantes if ($maj_version = svp_rechercher_maj_version($prefixe, $le_paquet['version'], $le_paquet['etatnum'])) { $le_paquet['maj_version'] = $maj_version; } $insert_paquets[] = $le_paquet; } } } if ($insert_plugins) { sql_insertq_multi('spip_plugins', $insert_plugins); $pls = sql_allfetsel(array('id_plugin', 'prefixe'), 'spip_plugins', sql_in('prefixe', array_keys($insert_plugins))); foreach ($pls as $p) { $cle_plugins[$p['prefixe']] = $p['id_plugin']; } } if ($insert_paquets) { // sert pour le calcul d'obsolescence $id_plugin_concernes = array(); foreach ($insert_paquets as $c => $p) { $insert_paquets[$c]['id_plugin'] = $cle_plugins[$p['prefixe']]; $id_plugin_concernes[ $insert_paquets[$c]['id_plugin'] ] = true; // remettre les necessite, utilise, librairie dans la cle 0 // comme SVP if ($dep = unserialize($insert_paquets[$c]['dependances']) and is_array($dep)) { foreach ($dep as $d => $contenu) { if ($contenu) { $new = array(); foreach($contenu as $n) { unset($n['id']); $new[ strtolower($n['nom']) ] = $n; } $dep[$d] = array($new); } } $insert_paquets[$c]['dependances'] = serialize($dep); } } sql_insertq_multi('spip_paquets', $insert_paquets); svp_corriger_obsolete_paquets( array_keys($id_plugin_concernes) ); } } /** * Fait correspondre l'état des métas des plugins actifs & installés * avec ceux en base de données dans spip_paquets pour le dépot local **/ function svp_base_actualiser_paquets_actifs() { $installes = lire_config('plugin_installes'); $actifs = lire_config('plugin'); $attentes = lire_config('plugin_attente'); $locaux = sql_allfetsel( array('id_paquet', 'prefixe', 'actif', 'installe', 'attente', 'constante', 'src_archive'), 'spip_paquets', 'id_depot='.sql_quote(0)); $changements = array(); foreach ($locaux as $l) { $copie = $l; $prefixe = strtoupper($l['prefixe']); // actif ? if (isset($actifs[$prefixe]) and ($actifs[$prefixe]['dir_type'] == $l['constante']) and ($actifs[$prefixe]['dir'] == $l['src_archive'])) { $copie['actif'] = "oui"; } else { $copie['actif'] = "non"; } // attente ? if (isset($attentes[$prefixe]) and ($attentes[$prefixe]['dir_type'] == $l['constante']) and ($attentes[$prefixe]['dir'] == $l['src_archive'])) { $copie['attente'] = "oui"; $copie['actif'] = "oui"; // il est presente dans la liste des actifs (en erreur). } else { $copie['attente'] = "non"; } // installe ? if (in_array($l['src_archive'], $installes)) { $copie['installe'] = "oui"; } else { $copie['installe'] = "non"; } if ($copie != $l) { $changements[ $l['id_paquet'] ] = array( 'actif' => $copie['actif'], 'installe' => $copie['installe'], 'attente' => $copie['attente'] ); } } if (count($changements)) { // On insere, en encapsulant pour sqlite... if (sql_preferer_transaction()) { sql_demarrer_transaction(); } foreach ($changements as $id_paquet => $data) { sql_updateq('spip_paquets', $data, 'id_paquet=' . intval($id_paquet)); } if (sql_preferer_transaction()) { sql_terminer_transaction(); } } } /** * Construit le contenu multilangue (tag <multi>) des balises nom, slogan * et description à partir des items de langue contenus dans le fichier * paquet-prefixe_langue.php * * @param string $prefixe Préfixe du plugin * @param string $dir_source Chemin d'accès du plugin * @return array * Tableau clé => texte multilangue entre <multi> et </multi> * Les clés peuvent être 'nom', 'slogan' et 'description', mais * seules les clés ayant une explication dans la chaine de langue * sont retournées. */ function svp_compiler_multis($prefixe, $dir_source) { $multis =array(); // ici on cherche le fichier et les cles avec un prefixe en minuscule systematiquement... $prefixe = strtolower($prefixe); $module = "paquet-$prefixe"; $item_nom = $prefixe . "_nom"; $item_slogan = $prefixe . "_slogan"; $item_description = $prefixe . "_description"; // On cherche tous les fichiers de langue destines a la traduction du paquet.xml if ($fichiers_langue = glob($dir_source . "/lang/{$module}_*.php")) { include_spip('inc/lang_liste'); $nom = $slogan = $description = ''; foreach ($fichiers_langue as $_fichier_langue) { $nom_fichier = basename($_fichier_langue, '.php'); $langue = substr($nom_fichier, strlen($module) + 1 - strlen($nom_fichier)); // Si la langue est reconnue, on traite la liste des items de langue if (isset($GLOBALS['codes_langues'][$langue])) { $GLOBALS['idx_lang'] = $langue; include($_fichier_langue); foreach ($GLOBALS[$langue] as $_item => $_traduction) { if ($_traduction = trim($_traduction)) { if ($_item == $item_nom) $nom .= "[$langue]$_traduction"; if ($_item == $item_slogan) $slogan .= "[$langue]$_traduction"; if ($_item == $item_description) $description .= "[$langue]$_traduction"; } } } } // Finaliser la construction des balises multi if ($nom) $multis['nom'] = "<multi>$nom</multi>"; if ($slogan) $multis['slogan'] = "<multi>$slogan</multi>"; if ($description) $multis['description'] = "<multi>$description</multi>"; } return $multis; } /** * Met à jour les informations d'obsolescence des paquets locaux. * * L'obsolescence indique qu'un paquet est plus ancien (de version ou état * moins avancé) qu'un autre également présent localement. * * @param array $ids_plugin * Liste d'identifiants de plugins * En cas d'absence, passera sur tous les paquets locaux **/ function svp_corriger_obsolete_paquets($ids_plugin = array()) { // on minimise au maximum le nombre de requetes. // 1 pour lister les paquets // 1 pour mettre à jour les obsoletes à oui // 1 pour mettre à jour les obsoletes à non $where = array('pa.id_plugin = pl.id_plugin', 'id_depot='.sql_quote(0)); if ($ids_plugin) { $where[] = sql_in('pl.id_plugin', $ids_plugin); } // comme l'on a de nouveaux paquets locaux... // certains sont peut etre devenus obsoletes // parmis tous les plugins locaux presents // concernes par les memes prefixes que les plugins ajoutes. $obsoletes = array(); $changements = array(); $paquets = sql_allfetsel( array('pa.id_paquet', 'pl.prefixe', 'pa.version', 'pa.etatnum', 'pa.obsolete', 'pa.compatibilite_spip'), array('spip_paquets AS pa', 'spip_plugins AS pl'), $where); // L'obsolescence doit tenir compte de la compatibilité avec notre version de SPIP en cours foreach ($paquets as $c => $p) { $paquets[$c]['compatible'] = plugin_version_compatible($p['compatibilite_spip'], $GLOBALS['spip_version_branche'], 'spip'); } foreach ($paquets as $c => $p) { $obsoletes[$p['prefixe']][] = $c; // si 2 paquet locaux ont le meme prefixe, // sont compatibles avec notre SPIP, // mais pas la meme version, // l'un est obsolete : la version la plus ancienne // Si version et etat sont egaux, on ne decide pas d'obsolescence. if (count($obsoletes[$p['prefixe']]) > 1) { foreach ($obsoletes[$p['prefixe']] as $cle) { if ($cle == $c) continue; if (!$paquets[$c]['compatible']) continue; // je suis plus petit qu'un autre if (spip_version_compare($paquets[$c]['version'], $paquets[$cle]['version'], '<')) { if ($paquets[$c]['etatnum'] <= $paquets[$cle]['etatnum']) { if ($paquets[$c]['obsolete'] != 'oui') { $paquets[$c]['obsolete'] = 'oui'; $changements[$c] = true; } } } // je suis plus grand ou egal a un autre... else { // je suis strictement plus grand qu'un autre... if (spip_version_compare($paquets[$c]['version'], $paquets[$cle]['version'], '>')) { // si mon etat est meilleur, rendre obsolete les autres if ($paquets[$c]['etatnum'] >= $paquets[$cle]['etatnum']) { if ($paquets[$cle]['obsolete'] != 'oui') { $paquets[$cle]['obsolete'] = 'oui'; $changements[$cle] = true; } } } // je suis egal a un autre // si mon etat est strictement meilleur, rendre obsolete les autres elseif ($paquets[$c]['etatnum'] > $paquets[$cle]['etatnum']) { if ($paquets[$cle]['obsolete'] != 'oui') { $paquets[$cle]['obsolete'] = 'oui'; $changements[$cle] = true; } } } } } else { if ($paquets[$c]['obsolete'] != 'non') { $paquets[$c]['obsolete'] = 'non'; $changements[$c] = true; } } } if (count($changements)) { $oui = $non = array(); foreach ($changements as $c => $null) { if ($paquets[$c]['obsolete'] == 'oui') { $oui[] = $paquets[$c]['id_paquet']; } else { $non[] = $paquets[$c]['id_paquet']; } } if ($oui) { sql_updateq('spip_paquets', array('obsolete'=>'oui'), sql_in('id_paquet', $oui)); } if ($non) { sql_updateq('spip_paquets', array('obsolete'=>'non'), sql_in('id_paquet', $non)); } } } /** * Supprime les plugins devenus orphelins dans cette liste. * * @param array $ids_plugin * Liste d'identifiants de plugins * @return array * Liste de plugins non orphelins **/ function svp_supprimer_plugins_orphelins($ids_plugin) { // tous les plugins encore lies a des depots... if ($ids_plugin) { $p = sql_allfetsel('DISTINCT(p.id_plugin)', array('spip_plugins AS p', 'spip_paquets AS pa'), array(sql_in('p.id_plugin', $ids_plugin), 'p.id_plugin=pa.id_plugin')); $p = array_map('array_shift', $p); $diff = array_diff($ids_plugin, $p); // pour chaque plugin non encore utilise, on les vire ! sql_delete('spip_plugins', sql_in('id_plugin', $diff)); return $p; // les plugins encore en vie ! } return array(); } /** * Cherche dans les dépots distants un plugin qui serait plus à jour * que le prefixe, version et état que l'on transmet * * @param string $prefixe * Préfixe du plugin * @param string $version * Version du paquet à comparer * @param int $etatnum * État du paquet numérique * @return string * Version plus à jour, sinon rien **/ function svp_rechercher_maj_version($prefixe, $version, $etatnum) { $maj_version = ""; if ($res = sql_allfetsel( array('pl.id_plugin', 'pa.version'), array('spip_plugins AS pl', 'spip_paquets AS pa'), array( 'pl.id_plugin = pa.id_plugin', 'pa.id_depot>' . sql_quote(0), 'pl.prefixe=' . sql_quote($prefixe), 'pa.etatnum>=' . sql_quote($etatnum)))) { foreach ($res as $paquet_distant) { // si version superieure et etat identique ou meilleur, // c'est que c'est une mise a jour possible ! if (spip_version_compare($paquet_distant['version'],$version,'>')) { if (!strlen($maj_version) or spip_version_compare($paquet_distant['version'], $maj_version, '>')) { $maj_version = $paquet_distant['version']; } # a voir si on utilisera... # "superieur" => "varchar(3) DEFAULT 'non' NOT NULL", # // superieur : version plus recente disponible (distant) d'un plugin (actif?) existant } } } return $maj_version; } /** * Actualise l'information 'maj_version' pour tous les paquets locaux **/ function svp_actualiser_maj_version() { $update = array(); // tous les paquets locaux if ($locaux = sql_allfetsel( array('id_paquet', 'prefixe', 'version', 'maj_version', 'etatnum'), array('spip_paquets'), array('id_depot=' . sql_quote(0)))) { foreach ($locaux as $paquet) { $new_maj_version = svp_rechercher_maj_version($paquet['prefixe'], $paquet['version'], $paquet['etatnum']); if ($new_maj_version != $paquet['maj_version']) { $update[$paquet['id_paquet']] = array('maj_version' => $new_maj_version); } } } if ($update) { // On insere, en encapsulant pour sqlite... if (sql_preferer_transaction()) { sql_demarrer_transaction(); } foreach ($update as $id_paquet => $data) { sql_updateq('spip_paquets', $data, 'id_paquet=' . intval($id_paquet)); } if (sql_preferer_transaction()) { sql_terminer_transaction(); } } } ?> svp_outiller.php000066600000037244152503074170010026 0ustar00<?php /** * Fichier de fonctions * * @plugin SVP pour SPIP * @license GPL * @package SPIP\SVP\Fonctions **/ if (!defined('_ECRIRE_INC_VERSION')) return; if (!defined('_SVP_VERSION_SPIP_MIN')) { /** * Version SPIP minimale quand un plugin ne le precise pas * * Version SPIP correspondant à l'apparition des plugins */ define('_SVP_VERSION_SPIP_MIN', '1.9.0'); } if (!defined('_SVP_VERSION_SPIP_MAX')) { /** * Version SPIP maximale * * Pour l'instant on ne connait pas la borne sup exacte */ define('_SVP_VERSION_SPIP_MAX', '3.1.99'); } /** * Liste des branches significatives de SPIP et de leurs bornes (versions min et max) * * À mettre a jour en fonction des sorties * @global array $GLOBALS['infos_branches_spip'] */ $GLOBALS['infos_branches_spip'] = array( '1.9' => array(_SVP_VERSION_SPIP_MIN,'1.9.2'), '2.0' => array('2.0.0','2.0.99'), '2.1' => array('2.1.0','2.1.99'), '3.0' => array('3.0.0','3.0.99'), '3.1' => array('3.1.0',_SVP_VERSION_SPIP_MAX) ); # define('_INFOS_BRANCHES_SPIP', serialize($infos_branches_spip)); /** * Liste des licences de plugin * * @global array $GLOBALS['licences_plugin'] */ $GLOBALS['licences_plugin'] = array( 'apache' => array( 'versions' => array('2.0', '1.1', '1.0'), 'nom' => 'Apache licence, version @version@', 'url' => 'http://www.apache.org/licenses/LICENSE-@version@'), 'art' => array( 'versions' => array('1.3'), 'nom' => 'Art libre @version@', 'url' => 'http://artlibre.org/licence/lal'), 'mit' => array( 'versions' => array(), 'nom' => 'MIT', 'url' => 'http://opensource.org/licenses/mit-license.php'), 'bsd' => array( 'versions' => array(), 'nom' => 'BSD', 'url' => 'http://www.freebsd.org/copyright/license.html'), 'agpl' => array( 'versions' => array('3'), 'nom' => 'AGPL @version@', 'url' => 'http://www.gnu.org/licenses/agpl.html'), 'fdl' => array( 'versions' => array('1.3', '1.2', '1.1'), 'nom' => 'FDL @version@', 'url' => 'http://www.gnu.org/licenses/fdl-@version@.html'), 'lgpl' => array( 'versions' => array('3.0', '2.1'), 'nom' => array('3.0' => 'LGPL 3', '2.1' => 'LGPL 2.1'), 'url' => 'http://www.gnu.org/licenses/lgpl-@version@.html'), 'gpl' => array( 'versions' => array('3', '2', '1'), 'nom' => 'GPL @version@', 'url' => 'http://www.gnu.org/licenses/gpl-@version@.0.html'), 'ccby' => array( 'versions' => array('2.0', '2.5', '3.0'), 'suffixes' => array('-sa', '-nc', '-nd', '-nc-nd', '-nc-sa'), 'nom' => 'CC BY@suffixe@ @version@', 'url' => 'http://creativecommons.org/licenses/by@suffixe@/@version@/') ); # define('_LICENCES_PLUGIN', serialize($licences_plugin)); /** * Fusionne 2 intervalles de compatibilité * * Soit '[1.9;2.1]' et '[2.1;3.0.*]', la fonction retourne '[1.9;3.0.*]' * * En gros la fonction est utilisé pour calculer l'intervalle de validité * d'un plugin ayant plusieurs paquets avec des compatibilités différentes. * La compatibilité du plugin est le total de toutes les compatibilités. * * @param string $intervalle_a * Intervalle de compatibilité * @param string $intervalle_b * Intervalle de compatibilité * @return string * Intervalle de compatibilité **/ function fusionner_intervalles($intervalle_a, $intervalle_b) { // On recupere les bornes de chaque intervalle $borne_a = extraire_bornes($intervalle_a); $borne_b = extraire_bornes($intervalle_b); // On initialise la borne min de chaque intervalle a 1.9.0 inclus si vide if (!$borne_a['min']['valeur']) { $borne_a['min']['valeur'] = _SVP_VERSION_SPIP_MIN; $borne_a['min']['incluse'] = true; } if (!$borne_b['min']['valeur']) { $borne_b['min']['valeur'] = _SVP_VERSION_SPIP_MIN; $borne_b['min']['incluse'] = true; } // On initialise la borne max de chaque intervalle a la version SPIP max incluse si vide if (!$borne_a['max']['valeur']) { $borne_a['max']['valeur'] = _SVP_VERSION_SPIP_MAX; $borne_a['max']['incluse'] = true; } if (!$borne_b['max']['valeur']) { $borne_b['max']['valeur'] = _SVP_VERSION_SPIP_MAX; $borne_b['max']['incluse'] = true; } // On calcul maintenant : // -- la borne min de l'intervalle fusionne = min(min_a, min_b) if (spip_version_compare($borne_a['min']['valeur'], $borne_b['min']['valeur'], '<=')) $bornes_fusionnees['min'] = $borne_a['min']; else $bornes_fusionnees['min'] = $borne_b['min']; // -- la borne max de l'intervalle fusionne = max(max_a, max_b) if (spip_version_compare($borne_a['max']['valeur'], $borne_b['max']['valeur'], '<=')) $bornes_fusionnees['max'] = $borne_b['max']; else $bornes_fusionnees['max'] = $borne_a['max']; return construire_intervalle($bornes_fusionnees); } /** * Extrait les valeurs d'un intervalle de compatibilité. * * Calcule les valeurs min, max et si ces valeurs sont intégrées ou non * à l'intervalle. * * @param string $intervalle * Intervalle de compatibilité, tel que '[2.1;3.0]' * @param bool $initialiser * - True pour mettre les valeurs connues mini et maxi de SPIP lorsque * les bornes ne sont pas renseignées dans l'intervalle. * - False pour ne rien mettre sinon. * @return array * Tableau avec les index : * - min : la borne inférieure, qui contient les index 'valeur' et 'incluse' * - max : la borne supérieure, qui contient les index 'valeur' et 'incluse' * Le sous index 'incluse' vaut true si cette borne est incluse dans l'intervalle. **/ function extraire_bornes($intervalle, $initialiser=false) { static $borne_vide = array('valeur' => '', 'incluse' => false); static $borne_inf_init = array('valeur' => _SVP_VERSION_SPIP_MIN, 'incluse' => true); static $borne_sup_init = array('valeur' => _SVP_VERSION_SPIP_MAX, 'incluse' => true); if ($initialiser) $bornes = array('min' => $borne_inf_init, 'max' => $borne_sup_init); else $bornes = array('min' => $borne_vide, 'max' => $borne_vide); if ($intervalle AND preg_match(',^[\[\(\]]([0-9.a-zRC\s\-]*)[;]([0-9.a-zRC\s\-\*]*)[\]\)\[]$,Uis', $intervalle, $matches)) { if ($matches[1]) { $bornes['min']['valeur'] = trim($matches[1]); $bornes['min']['incluse'] = ($intervalle{0} == "["); } if ($matches[2]) { $bornes['max']['valeur'] = trim($matches[2]); $bornes['max']['incluse'] = (substr($intervalle,-1) == "]"); } } return $bornes; } /** * Contruit un intervalle de compatibilité * * @param array $bornes * L'intervalle décrit sous forme de tableau avec pour index : * - min : la borne inférieure, qui contient les index 'valeur' et 'incluse' * - max : la borne supérieure, qui contient les index 'valeur' et 'incluse' * Le sous index 'incluse' vaut true si cette borne est incluse dans l'intervalle. * @param string $dtd * DTD de destination (paquet ou plugin) qui influera sur l'écriture à faire * en utilisant des parenthèses ou des crochets pour définir l'exclusion d'une intervalle * tel que ']2.1.2,3.0.1[' (paquet) ou '(2.1.2,3.0.1)' (plugin) * @return string * Intervalle de compatibilité tel que '[2.1;3.0]' **/ function construire_intervalle($bornes, $dtd='paquet') { return ($bornes['min']['incluse'] ? '[' : ($dtd=='paquet' ? ']' : '(')) . $bornes['min']['valeur'] . ';' . $bornes['max']['valeur'] . ($bornes['max']['incluse'] ? ']' : ($dtd=='paquet' ? '[' : ')')); } /** * Retourne la liste des branches de SPIP comprises dans un intervalle * de compatibilité donné. * * @param string $intervalle * Intervalle de compatibilité, tel que [2.0.0;3.0.0] * @return string * Branches de SPIP séparées par des virgules, tel que 2.0,2.1,3.0 **/ function compiler_branches_spip($intervalle) { include_spip('plugins/installer'); global $infos_branches_spip; $liste_branches_spip = array_keys($GLOBALS['infos_branches_spip']); $bornes = extraire_bornes($intervalle, false); // On traite d'abord les cas ou l'intervalle est : // - vide // - non vide mais avec les deux bornes vides // Dans ces cas la compatibilite est totale, on renvoie toutes les branches if (!$intervalle OR (!$bornes['min']['valeur'] AND !$bornes['max']['valeur'])) return implode(',', $liste_branches_spip); // On force l'initialisation des bornes et on les nettoie des suffixes d'etat $bornes = extraire_bornes($intervalle, true); // Si les bornes sont en dehors de l'intervalle [_SVP_VERSION_SPIP_MIN;_SVP_VERSION_SPIP_MAX] on le reduit if (spip_version_compare($bornes['min']['valeur'], _SVP_VERSION_SPIP_MIN, '<')) { $bornes['min']['valeur'] = _SVP_VERSION_SPIP_MIN; $bornes['min']['incluse'] = true; } if (spip_version_compare(_SVP_VERSION_SPIP_MAX, $bornes['max']['valeur'], '<=')) { $bornes['max']['valeur'] = _SVP_VERSION_SPIP_MAX; $bornes['max']['incluse'] = true; } // On les nettoie des suffixes d'etat $borne_inf = strtolower(preg_replace(',([0-9])[\s-.]?(dev|alpha|a|beta|b|rc|pl|p),i','\\1',$bornes['min']['valeur'])); $borne_sup = strtolower(preg_replace(',([0-9])[\s-.]?(dev|alpha|a|beta|b|rc|pl|p),i','\\1',$bornes['max']['valeur'])); // On determine les branches inf et sup issues du phrasage de l'intervalle // -- on initialise la branche inf de l'intervalle que l'on va preciser ensuite $t = explode('.', $borne_inf); $branche_inf = $t[0] . '.' . $t[1]; // -- pour eviter toutes erreur fatale on verifie que la branche est bien dans la liste des possibles // -- -> si non, on renvoie vide if (!in_array($branche_inf, $liste_branches_spip)) return ''; // -- on complete la borne inf de l'intervalle de x.y en x.y.z et on determine la vraie branche if (!isset($t[2]) or !$t[2]) { if ($bornes['min']['incluse']) $borne_inf = $infos_branches_spip[$branche_inf][0]; else { $branche_inf = $liste_branches_spip[array_search($branche_inf, $liste_branches_spip)+1]; $borne_inf = $infos_branches_spip[$branche_inf][0]; } } // -- on initialise la branche sup de l'intervalle que l'on va preciser ensuite $t = explode('.', $borne_sup); // des gens mettent juste * (pas glop) $branche_sup = $t[0] . (isset($t[1]) ? '.' . $t[1] : ''); // -- pour eviter toutes erreur fatale on verifie que la branche est bien dans la liste des possibles // -- -> si non, on renvoie vide if (!in_array($branche_sup, $liste_branches_spip)) return ''; // -- on complete la borne sup de l'intervalle de x.y en x.y.z et on determine la vraie branche if (!isset($t[2]) or !$t[2]) { if ($bornes['max']['incluse']) $borne_sup = $infos_branches_spip[$branche_sup][1]; else { $branche_sup = $liste_branches_spip[array_search($branche_sup, $liste_branches_spip)-1]; $borne_sup = $infos_branches_spip[$branche_sup][1]; } } // -- on verifie que les bornes sont bien dans l'ordre : // -> sinon on retourne la branche sup uniquement if (spip_version_compare($borne_inf, $borne_sup, '>=')) return $branche_sup; // A ce stade, on a un intervalle ferme en bornes ou en branches // Il suffit de trouver les branches qui y sont incluses, sachant que les branches inf et sup // le sont a coup sur maintenant $index_inf = array_search($branche_inf, $liste_branches_spip); $index_sup = array_search($branche_sup, $liste_branches_spip); $liste = array(); for ($i = $index_inf; $i <= $index_sup; $i++) { $liste[] = $liste_branches_spip[$i]; } return implode(',', $liste); } /** * Transforme un texte écrit en entités HTML, dans le charset du site * * @param string $texte * Texte avec des entités HTML * @return string $texte * Texte dans le charset du site **/ function entite2charset($texte) { if (!strlen($texte)) return ''; include_spip('inc/charsets'); return unicode2charset(html_entity_decode(preg_replace('/&([lg]t;)/S', '&\1', $texte), ENT_NOQUOTES, $GLOBALS['meta']['charset'])); } /** * Teste si 2 balises XML sont identiques * * @param array|string $balise1 * Balise à comparer * @param array|string $balise2 * Balise à comparer * @return bool * True si elles sont identiques, false sinon. **/ function balise_identique($balise1, $balise2) { if (is_array($balise1)) { foreach ($balise1 as $_attribut1 => $_valeur1){ if (!array_key_exists($_attribut1, $balise2)) return false; else if ($_valeur1 != $balise2[$_attribut1]) return false; } return true; } else return ($balise1 == $balise2); } /** * Déterminer la licence exacte avec un nom et un lien de doc standardisé * * @param string $prefixe * Préfixe de la licence tel que gnu, free, cc, creative common * @param string $nom * Nom de la licence tel que gpl, lgpl, agpl, fdl, mit, bsd... * @param string $suffixe * Suffixe de la licence tel que licence, -sharealike, -nc-nd ... * @param string $version * Version de la licence tel que 3.0 * @return array * Si la licence est connu, retourne 2 index : * - nom : le nom le la licence * - url : lien vers la licence */ function definir_licence($prefixe, $nom, $suffixe, $version) { global $licences_plugin; $licence = array(); $prefixe = strtolower($prefixe); $nom = strtolower($nom); $suffixe = strtolower($suffixe); if (((trim($prefixe) == 'creative common') AND ($nom == 'attribution')) OR (($prefixe == 'cc') AND ($nom == 'by'))) $nom = 'ccby'; if (array_key_exists($nom, $licences_plugin)) { if (!$licences_plugin[$nom]['versions']) { // La licence n'est pas versionnee : on affecte donc directement le nom et l'url $licence['nom'] = $licences_plugin[$nom]['nom']; $licence['url'] = $licences_plugin[$nom]['url']; } else { // Si la version est pas bonne on prend la plus recente if (!$version OR !in_array($version, $licences_plugin[$nom]['versions'], true)) $version = $licences_plugin[$nom]['versions'][0]; if (is_array($licences_plugin[$nom]['nom'])) $licence['nom'] = $licences_plugin[$nom]['nom'][$version]; else $licence['nom'] = str_replace('@version@', $version, $licences_plugin[$nom]['nom']); $licence['url'] = str_replace('@version@', $version, $licences_plugin[$nom]['url']); if ($nom == 'ccby') { if ($suffixe == '-sharealike') $suffixe = '-sa'; if (!$suffixe OR !in_array($suffixe, $licences_plugin[$nom]['suffixes'], true)) $suffixe = ''; $licence['nom'] = str_replace('@suffixe@', strtoupper($suffixe), $licence['nom']); $licence['url'] = str_replace('@suffixe@', $suffixe, $licence['url']); } } } return $licence; } /** * Liste les librairies présentes * * Cherche des librairie dans tous les dossiers 'lib' présents dans chaque * chemin déclaré (plugins, squelettes, SPIP). Un répertoire dans un dossier * 'lib' est considéré comme une librairie, et le nom de ce répertoire est * utilisé comme nom de la librairie. * * @return array * Tableau de couples (nom de la librairie => répertoire de la librairie) **/ function svp_lister_librairies() { $libs = array(); foreach (array_reverse(creer_chemin()) as $d) { if (is_dir($dir = $d.'lib/') AND $t = @opendir($dir)) { while (($f = readdir($t)) !== false) { if ($f[0] != '.' AND is_dir("$dir/$f")) $libs[$f] = $dir; } } } return $libs; } /** * Retourne '00x.00y.00z' à partir de 'x.y.z' * * Retourne la chaine de la version x.y.z sous une forme normalisée * permettant le tri naturel. On complète à gauche d'un nombre de zéro * manquant pour aller à 3 caractères entre chaque point. * * @see denormaliser_version() * @param string $version * Numéro de version dénormalisée * @return string * Numéro de version normalisée **/ function normaliser_version($version='') { $version_normalisee = ''; if (preg_match(',([0-9.]+)[\s-.]?(dev|alpha|a|beta|b|rc|pl|p)?,i', $version, $matches)) { if (isset($matches[1]) and $matches[1]) { $v = explode('.', $matches[1]); foreach($v as $_nombre) { $vn[] = str_pad($_nombre, 3, '0', STR_PAD_LEFT); } $version_normalisee = implode('.', $vn); if (isset($matches[2]) and $matches[2]) $version_normalisee = $version_normalisee . '-' . $matches[2]; } } return $version_normalisee; } ?> svp_depoter_distant.php000066600000102534152503074170011352 0ustar00<?php /** * Traitement des dépots distants * * Un dépot distant est une liste de paquets que l'on peut télécharger. * Cette liste est donnée par un fichier XML que l'on peut relire * régulièrement pour actualiser nos informations. Effectivement, chaque * paquet (et plugin) décrit est inséré en base de données pour nous * faciliter les recherches. * * @plugin SVP pour SPIP * @license GPL * @package SPIP\SVP\Depots */ if (!defined("_ECRIRE_INC_VERSION")) return; include_spip('inc/plugin'); include_spip('inc/svp_phraser'); /** * Ajout d'un dépot et de son contenu (paquets, plugins) dans la base de données * * Si une erreur survient (syntaxe XML incorrecte, pas de plugin dans le dépot), * son texte est placé dans le paramètre $erreur * * @param string $url * URL du fichier XML de description du dépot * @param string $erreur * Texte d'un éventuel message d'erreur * @return bool * true si le dépot est ajouté correctement, false sinon */ function svp_ajouter_depot($url, &$erreur='') { include_spip('inc/distant'); // On considere que l'url a deja ete validee (correcte et nouveau depot) $url = trim($url); // Ajout du depot dans la table spip_depots. Les compteurs de paquets et de plugins // sont mis a jour apres le traitement des paquets // on recupère le XML $fichier_xml = copie_locale($url, 'modif'); if (!$fichier_xml) { $erreur = _T('svp:message_nok_xml_non_recupere', array('fichier' => $url)); return false; } $fichier_xml = _DIR_RACINE . $fichier_xml; // Lire les donnees d'un depot de paquets $infos = svp_phraser_depot($fichier_xml); if (!$infos) { $erreur = _T('svp:message_nok_xml_non_conforme', array('fichier' => $url)); return false; } $titre = filtrer_entites($infos['depot']['titre']); $champs = array('titre' => $titre, 'descriptif' => filtrer_entites($infos['depot']['descriptif']), 'type' => $infos['depot']['type'], 'url_serveur' => $infos['depot']['url_serveur'], 'url_brouteur' => $infos['depot']['url_brouteur'], 'url_archives' => $infos['depot']['url_archives'], 'url_commits' => $infos['depot']['url_commits'], 'xml_paquets'=> $url, 'sha_paquets'=> sha1_file($fichier_xml), 'nbr_paquets' => 0, 'nbr_plugins' => 0, 'nbr_autres' => 0); // verifier avant l'insertion que le depot n'existe pas deja // car la recuperation pouvant etre longue on risque le probleme en cas de concurrence if (sql_countsel('spip_depots','xml_paquets='.sql_quote($url))){ $erreur = _T('svp:message_nok_depot_deja_ajoute', array('url' => $url)); return false; } elseif (!$id_depot = sql_insertq('spip_depots', $champs)) { $erreur = _T('svp:message_nok_sql_insert_depot', array('objet' => "$titre ($url)")); return false; } // Ajout des paquets dans spip_paquets et actualisation des plugins dans spip_plugins $ok = svp_actualiser_paquets($id_depot, $infos['paquets'], $nb_paquets, $nb_plugins, $nb_autres); if (!$ok OR ($nb_paquets == 0)) { // Si une erreur s'est produite, on supprime le depot deja insere sql_delete('spip_depots','id_depot='.sql_quote($id_depot)); if (!$ok) $erreur = _T('svp:message_nok_xml_non_conforme', array('fichier' => $url)); else $erreur = _T('svp:message_nok_aucun_paquet_ajoute', array('url' => $url)); return false; } // On met à jour le nombre de paquets et de plugins du depot maintenant ! sql_updateq('spip_depots', array('nbr_paquets'=> $nb_paquets, 'nbr_plugins'=> $nb_plugins, 'nbr_autres'=> $nb_autres), 'id_depot=' . sql_quote($id_depot)); // On vide les paquets locaux pour mettre a jour leurs donnees relatives au depot // comme les mises a jour disponibles include_spip('inc/svp_depoter_local'); svp_base_supprimer_paquets_locaux(); return true; } /** * Suppression d'un dépot et de son contenu (paquets, plugins) dans la base de données * * Cette suppression entraîne des recalcul comme les versions maximales * des plugins téléchargeables qui peuvent changer. * * @param int $id * Identifiant du dépot * @return bool * false si le dépot n'est pas trouvé, true sinon */ function svp_supprimer_depot($id){ $id = intval($id); // Pas de depot a cet id ? if (!$id_depot = sql_getfetsel('id_depot', 'spip_depots', 'id_depot='. sql_quote($id)) ){ return false; } // on calcule les versions max des plugins heberges par le depot $vmax =array(); if ($resultats = sql_allfetsel('id_plugin, version', 'spip_paquets', 'id_depot='. sql_quote($id))) { foreach ($resultats as $paquet) { $id_plugin = $paquet['id_plugin']; if (!isset($vmax[$id_plugin]) OR (spip_version_compare($vmax[$id_plugin], $paquet['version'], '<'))) $vmax[$id_plugin] = $paquet['version']; } } // On supprime les paquets heberges par le depot sql_delete('spip_paquets','id_depot='.sql_quote($id_depot)); // Si on est pas en mode runtime, on utilise surement l'espace public pour afficher les plugins. // Il faut donc verifier que les urls suivent bien la mise à jour // Donc avant de nettoyer la base des plugins du depot ayant disparus on supprime toutes les urls // associees a ce depot : on les recreera apres le nettoyage if (!_SVP_MODE_RUNTIME) svp_actualiser_url_plugins($id_depot); // Nettoyer les autres relations à ce dépot svp_nettoyer_apres_suppression($id_depot, $vmax); // Si on est pas en mode runtime, on utilise surement l'espace public pour afficher les plugins. // Il faut donc s'assurer que les urls suivent bien la mise à jour // - on supprime toutes les urls plugin // - on les regenere pour la liste des plugins mise a jour if (!_SVP_MODE_RUNTIME) svp_actualiser_url_plugins($id_depot); // On supprime le depot lui-meme sql_delete('spip_depots','id_depot='.sql_quote($id_depot)); // on supprime les paquets locaux pour reactualisation include_spip('inc/svp_depoter_local'); svp_base_supprimer_paquets_locaux(); return true; } /** * Nettoyer la base de données après la suppression d'un dépot * * Supprime * - les liens des plugins avec le dépot (table spip_depots_plugins) * - les plugins dont aucun paquet n'est encore hébergé par un dépot restant (table spip_plugins) * Remet à zéro la version maximale des plugins ayant vu leur paquet en version maximale supprimée * * @param int $id_depot * Identifiant du dépot * @param array $vmax * Tableau de la version maximale des plugins du dépot supprimé * Tableau (id_plugin => version maximale) * @return bool * true toujours. **/ function svp_nettoyer_apres_suppression($id_depot, $vmax) { // On rapatrie la liste des plugins du depot qui servira apres qu'on ait supprime les liens // de la table spip_depots_plugins $liens = sql_allfetsel('id_plugin', 'spip_depots_plugins', 'id_depot='.sql_quote($id_depot)); $plugins_depot = array_map('reset', $liens); // On peut donc supprimer tous ces liens *plugins-depots* du depot sql_delete('spip_depots_plugins', 'id_depot='.sql_quote($id_depot)); // On verifie pour chaque plugin concerne par la disparition de paquets si c'est la version // la plus elevee qui a ete supprimee. // Si oui, on positionne le vmax a 0, ce qui permettra de remettre a jour le plugin systematiquement // a la prochaine actualisation. // Cette operation est necessaire car on n'impose pas que les informations du plugin soient identiques // pour chaque paquet !!! // On insere, en encapsulant pour sqlite... if (sql_preferer_transaction()) { sql_demarrer_transaction(); } if ($resultats = sql_allfetsel('id_plugin, vmax', 'spip_plugins', sql_in('id_plugin', $plugins_depot))) { foreach ($resultats as $plugin) { if (spip_version_compare($plugin['vmax'], $vmax[$plugin['id_plugin']], '=')) sql_updateq('spip_plugins', array('vmax' => ''), 'id_plugin=' . sql_quote($plugin['id_plugin'])); } } if (sql_preferer_transaction()) { sql_terminer_transaction(); } // Maintenant on calcule la liste des plugins du depot qui ne sont pas heberges // par un autre depot => donc a supprimer // - Liste de tous les plugins encore lies a un autre depot // tous les plugins correspondants aux anciens paquets $plugins_restants = sql_allfetsel('DISTINCT(id_plugin)', 'spip_paquets', sql_in('id_plugin', $plugins_depot)); $plugins_restants = array_map('array_shift', $plugins_restants); // - L'intersection des deux tableaux renvoie les plugins a supprimer $plugins_a_supprimer = array_diff($plugins_depot, $plugins_restants); // On supprimer les plugins identifies sql_delete('spip_plugins', sql_in('id_plugin', $plugins_a_supprimer)); return true; } /** * Actualisation des plugins d'un dépot déjà crée * * Actualise les informations uniquement si la signature du fichier * XML de description du dépot a changé * * @param int $id * Identifiant du dépot * @return bool * false si erreur, true sinon */ function svp_actualiser_depot($id){ include_spip('inc/distant'); $id = intval($id); // pas de depot a cet id ? if (!$depot = sql_fetsel('*', 'spip_depots', 'id_depot='. sql_quote($id)) ){ return false; } $fichier_xml = _DIR_RACINE . copie_locale($depot['xml_paquets'], 'modif'); $sha = sha1_file($fichier_xml); if ($depot['sha_paquets'] == $sha) { // Le fichier n'a pas change (meme sha1) alors on ne fait qu'actualiser la date // de mise a jour du depot en mettant a jour *inutilement* le sha1 spip_log('Aucune modification du fichier XML, actualisation non declenchee - id_depot = ' . $depot['id_depot'], 'svp_actions.' . _LOG_INFO); sql_replace('spip_depots', array_diff_key($depot, array('maj' => ''))); } else { // Le fichier a bien change il faut actualiser tout le depot $infos = svp_phraser_depot($fichier_xml); if (!$infos) return false; // On actualise les paquets dans spip_paquets en premier lieu. // Lors de la mise a jour des paquets, les plugins aussi sont actualises $ok = svp_actualiser_paquets($depot['id_depot'], $infos['paquets'], $nb_paquets, $nb_plugins, $nb_autres); // apres la mise a jour des paquets d'un depot, on actualise les informations des paquets locaux // principalement l'info "maj_version" indiquant s'il existe un paquet plus recent include_spip('inc/svp_depoter_local'); svp_actualiser_maj_version(); if ($ok) { // On met à jour : // -- les infos ne pouvant pas etre editees par le formulaire d'edition // d'un depot et extraites du xml // -- le nombre de paquets et de plugins du depot ainsi que le nouveau sha1 // ce qui aura pour effet d'actualiser la date de mise a jour $champs = array('url_serveur' => $infos['depot']['url_serveur'], 'url_brouteur' => $infos['depot']['url_brouteur'], 'url_archives' => $infos['depot']['url_archives'], 'url_commits' => $infos['depot']['url_commits'], 'nbr_paquets' => $nb_paquets, 'nbr_plugins' => $nb_plugins, 'nbr_autres' => $nb_autres, 'sha_paquets' => $sha); sql_updateq('spip_depots', $champs, 'id_depot=' . sql_quote($depot['id_depot'])); } } return true; } /** * Actualisation de la table des paquets pour le dépot choisi * * Enlève de la base les paquets du dépots qui ne sont plus présents * dans la description du XML. Ajoute ou met à jour les autres. * * @param int $id_depot * Identifiant du dépot * @param array $paquets * Tableau des paquets extraits du fichier XML * L'index est le nom de l'archive (xxxx.zip) et le contenu est * un tableau à deux entrées : * - Index 'plugin' : le tableau des infos du plugin * - Index 'file' : le nom de l'archive .zip * @param int $nb_paquets * Nombre de paquets réellement inserés dans la base * @param int $nb_plugins * Nombre de plugins parmi les paquets inserés * @param int &$nb_autres * Nombre de contributions non issues de plugin parmi les paquets inserés * @return bool * false si aucun dépot ou paquets, true sinon */ function svp_actualiser_paquets($id_depot, $paquets, &$nb_paquets, &$nb_plugins, &$nb_autres) { // Initialisation des compteurs $nb_paquets = 0; $nb_plugins = 0; $nb_autres = 0; // Si aucun depot ou aucun paquet on renvoie une erreur if ((!$id_depot) OR (!is_array($paquets))) return false; // On initialise l'url de base des logos du depot et son type afin de // calculer l'url complete de chaque logo $select = array('url_archives', 'type'); $depot = sql_fetsel($select, 'spip_depots', 'id_depot=' . sql_quote($id_depot)); // On supprime tous les paquets du depot // qui ont ete evacues, c'est a dire ceux dont les signatures // ne correspondent pas aux nouveaux... // et on retablit les vmax des plugins restants... $signatures = array(); foreach ($paquets as $_paquet) { $signatures[] = $_paquet['md5']; } // tous les paquets du depot qui ne font pas parti des signatures $anciens_paquets = sql_allfetsel('id_paquet', 'spip_paquets', array('id_depot=' . sql_quote($id_depot), sql_in('signature', $signatures, 'NOT'))); $anciens_paquets = array_map('array_shift', $anciens_paquets); // pour ces vieux paquets, on les nettoie de la base if ($anciens_paquets) { // tous les plugins correspondants aux anciens paquets $anciens_plugins = sql_allfetsel('pl.id_plugin', array('spip_plugins AS pl', 'spip_paquets AS pa'), array('pl.id_plugin=pa.id_plugin', sql_in('pa.id_paquet', $anciens_paquets))); $anciens_plugins = array_map('array_shift', $anciens_plugins); // suppression des anciens paquets sql_delete('spip_paquets', sql_in('id_paquet', $anciens_paquets)); // suppressions des liaisons depots / anciens plugins // on enlève la liaison lorsqu'il n'y a plus aucun paquet lie a un des plugins qui ont vu un paquet enlevé // liste des plugins qui ont encore des paquets dans ce depot $plugins_restants = sql_allfetsel('pl.id_plugin', array('spip_plugins AS pl', 'spip_paquets AS pa'), array(sql_in('pl.id_plugin', $anciens_plugins), 'pl.id_plugin=pa.id_plugin', 'pa.id_depot=' . sql_quote($id_depot))); $plugins_restants = array_map('array_shift', $plugins_restants); // par opposition, on retrouve ceux qui n'en ont plus... $plugins_supprimes = array_diff($anciens_plugins, $plugins_restants); sql_delete('spip_depots_plugins', array('id_depot='. sql_quote($id_depot), sql_in('id_plugin', $plugins_supprimes))); unset($plugins_restants, $plugins_supprimes); // supprimer les plugins orphelins include_spip('inc/svp_depoter_local'); svp_supprimer_plugins_orphelins($anciens_plugins); // corriger les vmax des plugins svp_corriger_vmax_plugins($anciens_plugins); // corriger les compats, branches aussi svp_completer_plugins($anciens_plugins); } // on ne garde que les paquets qui ne sont pas presents dans la base $signatures = sql_allfetsel('signature', 'spip_paquets', 'id_depot='.sql_quote($id_depot)); $signatures = array_map('array_shift', $signatures); foreach ($paquets as $cle => $_infos) { if (in_array($_infos['md5'], $signatures)) { unset($paquets[$cle]); } } // tableaux d'actions $insert_paquets = array(); $insert_plugins = array(); $insert_contribs = array(); $prefixes = array(); // prefixe => id_plugin // On met a jour ou on cree chaque paquet a partir du contenu du fichier xml // On ne fait pas cas de la compatibilite avec la version de SPIP installee // car l'operation doit permettre de collecter tous les paquets foreach ($paquets as $_archive => $_infos) { $insert_paquet = array(); // On initialise les informations specifiques au paquet : // l'id du depot et les infos de l'archive $insert_paquet['id_depot'] = $id_depot; $insert_paquet['nom_archive'] = $_archive; $insert_paquet['nbo_archive'] = $_infos['size']; $insert_paquet['maj_archive'] = date('Y-m-d H:i:s', $_infos['date']); $insert_paquet['src_archive'] = $_infos['source']; $insert_paquet['date_modif'] = $_infos['last_commit']; // On serialise le tableau des traductions par module $insert_paquet['traductions'] = serialize($_infos['traductions']); // On ajoute la signature $insert_paquet['signature'] = $_infos['md5']; // On verifie si le paquet est celui d'un plugin ou pas // -- Les traitements du XML dependent de la DTD utilisee // Formatage des informations extraites du plugin pour insertion dans la base SVP $formater = charger_fonction('preparer_sql_' . $_infos['dtd'], 'plugins'); if ($champs_aplat = $formater($_infos['plugin'])) { // Eclater les champs recuperes en deux sous tableaux, un par table (plugin, paquet) $champs = eclater_plugin_paquet($champs_aplat); $paquet_plugin = true; // On complete les informations du paquet et du plugin $insert_paquet = array_merge($insert_paquet, $champs['paquet']); $insert_plugin = $champs['plugin']; // On construit l'url complete du logo // Le logo est maintenant disponible a la meme adresse que le zip et porte le nom du zip. // Son extension originale est conservee if ($insert_paquet['logo']) $insert_paquet['logo'] = $depot['url_archives'] . '/' . basename($insert_paquet['nom_archive'], '.zip') . '.' . pathinfo($insert_paquet['logo'], PATHINFO_EXTENSION); // On loge l'absence de categorie ou une categorie erronee et on positionne la categorie // par defaut "aucune" // Provisoire tant que la DTD n'est pas en fonction if (!$insert_plugin['categorie']) { spip_log("Categorie absente dans le paquet issu de <". $insert_paquet['src_archive'] . "> du depot <" . $insert_paquet['id_depot'] . ">\n", 'svp_paquets.' . _LOG_INFO_IMPORTANTE); $insert_plugin['categorie'] = 'aucune'; } else { $svp_categories = $GLOBALS['categories_plugin']; if (!in_array($insert_plugin['categorie'], $svp_categories)) { spip_log("Categorie k" . $insert_plugin['categorie'] . "l incorrecte dans le paquet issu de <". $insert_paquet['src_archive'] . "> du depot <" . $insert_paquet['id_depot'] . ">\n", 'svp_paquets.' . _LOG_INFO_IMPORTANTE); $insert_plugin['categorie'] = 'aucune'; } } } else { $paquet_plugin = false; } // On teste l'existence du paquet dans la base avec les champs // id_depot, nom_archive et src_archive pour être sur de l'unicité. // - si le paquet n'existe pas, on le crée, // - sinon (et ça ne devrait pas arriver), on ne fait qu'un update if (!$paquet = sql_fetsel('*', 'spip_paquets', array('id_depot='. sql_quote($insert_paquet['id_depot']), 'nom_archive='. sql_quote($insert_paquet['nom_archive']), 'src_archive='. sql_quote($insert_paquet['src_archive'])))) { // Le paquet n'existe pas encore en base de donnees // ------------------------------------------------ // On positionne la date de creation a celle du dernier commit ce qui est bien le cas $insert_paquet['date_crea'] = $insert_paquet['date_modif']; // Les collisions ne sont possibles que si on ajoute un nouveau paquet $collision = false; if ($paquet_plugin) { // On est en presence d'un PLUGIN // ------------------------------ // On evite les doublons de paquet // Pour determiner un doublon on verifie actuellement : // - le prefixe // - la version du paquet et de la base // - l'etat $where = array('t1.id_plugin=t2.id_plugin', 't1.version=' . sql_quote($insert_paquet['version']), 't1.version_base=' . sql_quote($insert_paquet['version_base']), 't1.etatnum=' . sql_quote($insert_paquet['etatnum']), 't1.id_depot>' . intval(0), 't2.prefixe=' . sql_quote($insert_plugin['prefixe'])); if (!$id_paquet = sql_getfetsel('t1.id_paquet', 'spip_paquets AS t1, spip_plugins AS t2', $where)) { // On traite d'abord le plugin du paquet pour recuperer l'id_plugin // On rajoute le plugin dans la table spip_plugins si celui-ci n'y est pas encore ou on recupere // l'id si il existe deja et on le met a jour si la version du paquet est plus elevee $plugin = sql_fetsel('id_plugin, vmax', 'spip_plugins', array('prefixe=' . sql_quote($insert_plugin['prefixe']))); if (!$plugin AND !array_key_exists($insert_plugin['prefixe'], $insert_plugins)) { $insert_plugins[ $insert_plugin['prefixe'] ] = array_merge($insert_plugin, array('vmax' => $insert_paquet['version'])); } else { if ($plugin) { $id_plugin = $plugin['id_plugin']; $prefixes[$insert_plugin['prefixe']] = $id_plugin; } if (array_key_exists($insert_plugin['prefixe'], $insert_plugins) AND (spip_version_compare($insert_plugins[ $insert_plugin['prefixe'] ]['vmax'], $insert_paquet['version'], '<='))) { // attribuer au plugin le nom et le slogan du paquet le plus à jour $insert_plugins[ $insert_plugin['prefixe'] ]['nom'] = $insert_plugin['nom']; $insert_plugins[ $insert_plugin['prefixe'] ]['slogan'] = $insert_plugin['slogan']; $insert_plugins[ $insert_plugin['prefixe'] ]['vmax'] = $insert_paquet['version']; } } // On traite maintenant le paquet connaissant l'id du plugin // temporaire qui sera supprime lors de la connaissance de l'id_paquet $insert_paquet['prefixe'] = $insert_plugin['prefixe']; $insert_paquets[] = $insert_paquet; } else $collision = true; } else { // On est en presence d'une CONTRIBUTION NON PLUGIN // ------------------------------------------------ $where = array( 'id_depot=' . sql_quote($insert_paquet['id_depot']), 'nom_archive=' . sql_quote($insert_paquet['nom_archive'])); if (!$id_paquet = sql_getfetsel('id_paquet', 'spip_paquets', $where)) { // Ce n'est pas un plugin, donc id_plugin=0 et toutes les infos plugin sont nulles $insert_paquet['id_plugin'] = 0; $insert_contribs[] = $insert_paquet; } else $collision = true; } // On loge le paquet ayant ete refuse dans un fichier a part afin de les verifier // apres coup if ($collision) { spip_log("Collision avec le paquet <". $insert_paquet['nom_archive'] . " / " . $insert_paquet['src_archive'] . "> du depot <" . $insert_paquet['id_depot'] . ">\n", 'svp_paquets.' . _LOG_INFO_IMPORTANTE); } } else { // Le paquet existe deja en base de donnees // ---------------------------------------- // On ne devrait plus arriver ICI... // Code obsolete ? spip_log('!!!!!! Passage dans code obsolete (svp/svp_depoter_distant)', 'depoter'); // on effectue les traitements en attente // pour que les updates soient corrects svp_inserer_multi($insert_plugins, $insert_paquets, $insert_contribs, $prefixes); // On met a jour le paquet en premier lieu qu'il soit un plugin ou une contribution sql_updateq('spip_paquets', $insert_paquet, 'id_paquet=' . sql_quote($paquet['id_paquet'])); } } // on effectue les traitements en attente // pour que les updates soient corrects svp_inserer_multi($insert_plugins, $insert_paquets, $insert_contribs, $prefixes); // On rajoute le plugin comme heberge par le depot si celui-ci n'est pas encore enregistre comme tel $ids = sql_allfetsel('p.id_plugin', array('spip_plugins AS p', 'spip_depots_plugins AS dp'), array('p.id_plugin=dp.id_plugin', 'dp.id_depot='.sql_quote($id_depot))); $ids = array_map('array_shift', $ids); // inserer les liens avec le depots $insert_dp = array(); $news_id = array_diff(array_values($prefixes), $ids); foreach($news_id as $id) { $insert_dp[] = array('id_depot'=>$id_depot, 'id_plugin'=>$id); } if ($insert_dp) { sql_insertq_multi('spip_depots_plugins', $insert_dp); } // on recalcul les vmax des plugins de ce depot. svp_corriger_vmax_plugins(array_values($prefixes)); // On compile maintenant certaines informations des paquets mis a jour dans les plugins // (date de creation, date de modif, version spip...) svp_completer_plugins_depot($id_depot); // Si on est pas en mode runtime, on utilise surement l'espace public pour afficher les plugins. // Il faut donc s'assurer que les urls suivent bien la mise à jour // - on supprime toutes les urls plugin // - on les regenere pour la liste des plugins mise a jour if (!_SVP_MODE_RUNTIME) svp_actualiser_url_plugins($id_depot); // Calcul des compteurs de paquets, plugins et contributions $nb_paquets = sql_countsel('spip_paquets', 'id_depot=' . sql_quote($id_depot)); $nb_plugins = sql_countsel('spip_depots_plugins', 'id_depot=' . sql_quote($id_depot)); $nb_autres = sql_countsel('spip_paquets', array('id_depot=' . sql_quote($id_depot), 'id_plugin=0')); return true; } /** * Insertion en masse de plugins ou de paquets. * * Les paquets peuvent de pas avoir d'info "prefixe" (à transformer en id_plugin) * lorsqu'ils ne proviennent pas de plugin (squelettes...) * * @param array $insert_plugins * Tableau de description de plugins. * Une description est un tableau de couples (colonne sql => valeur) * pour l'insertion en base de données. * @param array $insert_paquets * Tableau de description de paquets. * Une description est un tableau de couples (colonne sql => valeur) * pour l'insertion en base de données. * @param array $insert_contribs * Tableau de description de paquets (contributions non plugins). * Une description est un tableau de couples (colonne sql => valeur) * pour l'insertion en base de données. * @param array $prefixes * Couples de relation (préfixe de plugin => identifiant de plugin) connues, * pour limiter les accès SQL. * @return void **/ function svp_inserer_multi(&$insert_plugins, &$insert_paquets, &$insert_contribs, &$prefixes) { if (count($insert_plugins)) { sql_insertq_multi('spip_plugins', $insert_plugins); $insert_plugins = array(); } if (count($insert_paquets)) { // on cherche tous les id_plugin/prefixe que l'on a à récuperer // en une seule requete $prefixes_manquants = array(); foreach ($insert_paquets as $p) { // on ne connait que le prefixe if (isset($p['prefixe']) and !isset($prefixes[ $p['prefixe'] ])) { $prefixes_manquants[] = $p['prefixe']; } } // recuperer les nouveaux prefixes : $new = sql_allfetsel(array('prefixe', 'id_plugin'), 'spip_plugins', sql_in('prefixe', $prefixes_manquants)); foreach ($new as $p) { $prefixes[ $p['prefixe'] ] = $p['id_plugin']; } // inserer les id_plugin dans les paquets a inserer // inserer le prefixe dans le paquet (pour raccourcis de jointures) foreach ($insert_paquets as $c=>$p) { if (isset($p['prefixe'])) { $insert_paquets[$c]['id_plugin'] = $prefixes[ $insert_paquets[$c]['prefixe'] ]; } else { $insert_paquets[$c]['prefixe'] = array_search($p['id_plugin'], $prefixes); } } // on insere tout ! sql_insertq_multi('spip_paquets', $insert_paquets); $insert_paquets = array(); } // les contribs n'ont pas le même nombre de champs dans les insertions // et n'ont pas de plugin rattachés. if (count($insert_contribs)) { sql_insertq_multi('spip_paquets', $insert_contribs); $insert_contribs = array(); } } /** * Complète les informations des plugins contenus dans un depot * en compilant certaines informations (compatibilités, dates, branches) * * @param int $id_depot * Identifiant du depot à actualiser **/ function svp_completer_plugins_depot($id_depot) { // On limite la revue des paquets a ceux des plugins heberges par le depot en cours d'actualisation $ids_plugins = sql_allfetsel('id_plugin', 'spip_depots_plugins', array('id_depot=' . sql_quote($id_depot))); $ids_plugins = array_map('reset', $ids_plugins); if ($ids_plugins) { svp_completer_plugins($ids_plugins); } } /** * Complète les informations des plugins, d'une liste de plugins donnés, * en compilant certaines informations (compatibilités, dates, branches) * * @param array $ids_plugin * Liste d'identifiants de plugins * @return bool * false si rien à faire, true sinon **/ function svp_completer_plugins($ids_plugin) { if (!$ids_plugin) { return false; } include_spip('inc/svp_outiller'); // -- on recupere tous les paquets associes aux plugins indiques et on compile les infos if ($resultats = sql_allfetsel('id_plugin, compatibilite_spip, date_crea, date_modif', 'spip_paquets', array(sql_in('id_plugin', $ids_plugin), 'id_depot>'.intval(0)), '', 'id_plugin')) { $plugin_en_cours = 0; $inserts = array(); foreach($resultats as $paquet) { // On finalise le plugin en cours et on passe au suivant if ($plugin_en_cours != $paquet['id_plugin']) { // On met a jour le plugin en cours if ($plugin_en_cours) { // On deduit maintenant les branches de la compatibilite globale $complements['branches_spip'] = compiler_branches_spip($complements['compatibilite_spip']); $inserts[$plugin_en_cours] = $complements; } // On passe au plugin suivant $plugin_en_cours = $paquet['id_plugin']; $complements = array('compatibilite_spip' => '', 'branches_spip' => '', 'date_crea' => 0, 'date_modif' => 0); } // On compile les compléments du plugin avec le paquet courant sauf les branches // qui sont deduites en fin de compilation de la compatibilite if ($paquet['date_modif'] > $complements['date_modif']) $complements['date_modif'] = $paquet['date_modif']; if (($complements['date_crea'] === 0) OR ($paquet['date_crea'] < $complements['date_crea'])) $complements['date_crea'] = $paquet['date_crea']; if ($paquet['compatibilite_spip']) if (!$complements['compatibilite_spip']) $complements['compatibilite_spip'] = $paquet['compatibilite_spip']; else $complements['compatibilite_spip'] = fusionner_intervalles($paquet['compatibilite_spip'], $complements['compatibilite_spip']); } // On finalise le dernier plugin en cours $complements['branches_spip'] = compiler_branches_spip($complements['compatibilite_spip']); $inserts[$plugin_en_cours] = $complements; // On insere, en encapsulant pour sqlite... if (sql_preferer_transaction()) { sql_demarrer_transaction(); } foreach ($inserts as $id_plugin => $complements) { sql_updateq('spip_plugins', $complements, 'id_plugin=' . intval($id_plugin)); } if (sql_preferer_transaction()) { sql_terminer_transaction(); } } return true; } /** * Recrée toutes les URLs propres de plugin * * Supprime toutes les urls de plugin de la table spip_urls puis les régénère. * * @return int * Nombre d'URLs de plugin régénérées **/ function svp_actualiser_url_plugins () { $nb_plugins = 0; // On supprime toutes les urls de plugin sql_delete('spip_urls', array('type=\'plugin\'')); // On recupere les ids des plugins et on regenere les urls if ($ids_plugin = sql_allfetsel('id_plugin', 'spip_plugins')) { $ids_plugin = array_map('reset', $ids_plugin); $nb_plugins = count($ids_plugin); foreach ($ids_plugin as $_id) { generer_url_entite($_id, 'plugin', '', '', true); } } return $nb_plugins; } /** * Éclate une description de paquet issu du XML du dépot en deux parties, * une pour le plugin, l'autre pour le paquet * * Sépare en deux une description de champs désignant un paquet, en extrayant : * - la partie plugin, soit ce qui peut être propre à plusieurs paquets. * On trouve dedans le prefixe, nom, slogan, catégorie, tags * - la partie paquet, soit ce qui est propre à ce conteneur là. On trouve * dedans entre autres la description, la version, la compatibilité * à SPIP, les dépendances, etc... * * @param array $champs_aplat * Couples (clé => valeur) d'un paquet issu de l'analyse XML du dépot * @return array * Tableau de 2 index : * - Index 'plugin' : couples (clé=>valeur) relatives au plugin * - Index 'paquet' : couples (clé=>valeur) spécifiques au paquet **/ function eclater_plugin_paquet($champs_aplat) { return array( 'plugin' => array( 'prefixe' => $champs_aplat['prefixe'], 'nom' => $champs_aplat['nom'], 'slogan' => $champs_aplat['slogan'], 'categorie' => $champs_aplat['categorie'], 'tags' => $champs_aplat['tags']), 'paquet' => array( 'logo' => $champs_aplat['logo'], 'description' => $champs_aplat['description'], 'auteur' => $champs_aplat['auteur'], 'credit' => $champs_aplat['credit'], 'version' => $champs_aplat['version'], 'version_base' => $champs_aplat['version_base'], 'compatibilite_spip' => $champs_aplat['compatibilite_spip'], 'branches_spip' => $champs_aplat['branches_spip'], 'etat' => $champs_aplat['etat'], 'etatnum' => $champs_aplat['etatnum'], 'licence' => $champs_aplat['licence'], 'copyright' => $champs_aplat['copyright'], 'lien_doc' => $champs_aplat['lien_doc'], 'lien_demo' => $champs_aplat['lien_demo'], 'lien_dev' => $champs_aplat['lien_dev'], 'dependances' => $champs_aplat['dependances']) ); } /** * Détermine la version max de chaque plugin, c'est à dire * la version maxi d'un des paquets qui lui est lié. * * @param array $plugins Liste d'identifiant de plugins **/ function svp_corriger_vmax_plugins($plugins) { // tous les plugins encore lies a des depots (hors local)... // la vmax est a retablir... if ($plugins) { $p = sql_allfetsel('DISTINCT(p.id_plugin)', array('spip_plugins AS p', 'spip_paquets AS pa'), array(sql_in('p.id_plugin', $plugins), 'p.id_plugin=pa.id_plugin', 'pa.id_depot>'.intval(0))); $p = array_map('array_shift', $p); // pour les autres, on la fixe correctement // On insere, en encapsulant pour sqlite... if (sql_preferer_transaction()) { sql_demarrer_transaction(); } foreach ($p as $id_plugin) { $vmax = ''; if ($pa = sql_allfetsel('version', 'spip_paquets', array('id_plugin='.$id_plugin, 'id_depot>'.intval(0)))) { foreach ($pa as $v) { if (spip_version_compare($v['version'], $vmax, '>')) { $vmax = $v['version']; } } } sql_updateq('spip_plugins', array('vmax'=>$vmax), 'id_plugin=' . intval($id_plugin)); } if (sql_preferer_transaction()) { sql_terminer_transaction(); } } } ?> svp_actionner.php000066600000120676152503074170010153 0ustar00<?php /** * Gestion de l'actionneur : il effectue les actions sur les plugins * * @plugin SVP pour SPIP * @license GPL * @package SPIP\SVP\Actionneur */ if (!defined("_ECRIRE_INC_VERSION")) return; /** * L'actionneur calcule l'ordre des actions, permet de les stocker * dans un fichier cache et de les effectuer. * * @package SPIP\SVP\Actionner **/ class Actionneur { /** * Instance du décideur * @var Decideur */ var $decideur; /** * Loguer les différents éléments * * Sa valeur sera initialisée par la configuration 'mode_log_verbeux' de SVP * * @var bool */ var $log = false; /** * Liste des actions à faire * @var array * Tableau identifiant du paquet => type d'action */ var $start = array(); /** * Actions en cours d'analyse * * Lorsqu'on ajoute les actions à faire, elles sont réordonnées * et classées dans ces quatre sous-tableaux * * Chaque sous-tableau est composé d'une description courte du paquet * auquel est ajouté dans l'index 'todo' le type d'action à faire. * * @var array * Index 'off' : les paquets à désactiver (ordre inverse des dépendances) * Index 'lib' : les librairies à installer * Index 'on' : les paquets à activer (ordre des dépendances) * Index 'neutre' : autres actions dont l'ordre a peu d'importance. */ var $middle = array( 'off' => array(), 'lib' => array(), 'on' => array(), 'neutre' => array(), ); // actions à la fin (apres analyse, et dans l'ordre) /** * Liste des actions à faire * * Liste de description courtes des paquets + index 'todo' indiquant l'action * @var array */ var $end = array(); /** * Liste des actions faites * Liste de description courtes des paquets + index 'todo' indiquant l'action * @var array */ var $done = array(); // faites /** * Actions en cours * Description courte du paquet + index 'todo' indiquant l'action * @var array */ var $work = array(); /** * Liste des erreurs * * @var array Liste des erreurs */ var $err = array(); /** * Verrou. * Le verrou est posé au moment de passer à l'action. * @var array * Index 'id_auteur' : Identifiant de l'auteur ayant déclenché des actions * Indix 'time' : timestamp de l'heure de déclenchement de l'action */ var $lock = array('id_auteur'=>0, 'time'=>''); /** * SVP (ce plugin) est-il à désactiver dans une des actions ? * * Dans ce cas, on tente de le désactiver après d'autres plugins à désactiver * sinon l'ensemble des actions suivantes échoueraient. * * @var bool * false si SVP n'est pas à désactiver, true sinon */ var $svp_off = false; /** * Constructeur * * Détermine si les logs sont activés et instancie un décideur. */ function Actionneur(){ include_spip('inc/config'); $this->log = (lire_config('svp/mode_log_verbeux') == 'oui'); include_spip('inc/svp_decider'); $this->decideur = new Decideur(); #$this->decideur->start(); // pour denormaliser_version() include_spip('svp_fonctions'); } /** * Ajoute un log * * Ajoute un log si la propriété $log l'autorise; * * @param mixed $quoi * La chose à logguer (souvent un texte) **/ function log($quoi) { if ($this->log) { spip_log($quoi,'actionneur'); } } /** * Ajoute une erreur * * Ajoute une erreur à la liste des erreurs présentées au moment * de traiter les actions. * * @param string $erreur * Le texte de l'erreur **/ function err($erreur) { if ($erreur) { $this->err[] = $erreur; } } /** * Remet à zéro les tableaux d'actions */ function clear() { $this->middle = array( 'off' => array(), 'lib' => array(), 'on' => array(), 'neutre' => array(), ); $this->end = array(); $this->done = array(); $this->work = array(); } /** * Ajoute les actions à faire dans l'actionneur * * @param array $todo * Tableau des actions à faire (identifiant de paquet => type d'action) **/ function ajouter_actions($todo) { foreach ($todo as $id => $action) { $this->start[$id] = $action; } $this->ordonner_actions(); } /** * Ajoute une librairie à installer * * Ajoute l'action de télécharger une librairie, si la libraire * n'est pas déjà présente et si le répertoire de librairie est * écrivable. * * @param string $nom Nom de la librairie * @param string $source URL pour obtenir la librairie */ function add_lib($nom, $source) { if (!$this->decideur->est_presente_lib($nom)) { if (is_writable(_DIR_LIB)) { $this->middle['lib'][$nom] = array( 'todo'=>'getlib', 'n'=>$nom, 'p'=>$nom, 'v'=>$source, 's'=>$source, ); } else { // erreur : impossible d'ecrire dans _DIR_LIB ! // TODO : message et retour d'erreur a gerer... return false; } } return true; } /** * Ordonne les actions demandées * * La fonction définie quelles sont les actions graduellement réalisables. * Si un plugin A dépend de B qui dépend de C * - pour tout ce qui est à installer : ordre des dependances (d'abord C, puis B, puis A) * - pour tout ce qui est à désinstaller : ordre inverse des dependances. (d'abord A, puis B, puis C) * * On commence donc par séparer * - ce qui est à désinstaller, * - ce qui est à installer, * - les actions neutres (get, up sur non actif, kill) * * Dans les traitements, on commencera par faire * - ce qui est à désinstaller (il est possible que certains plugins * nécessitent la désinstallation d'autres présents - tel que : 1 seul * service d'envoi de mail) * - puis ce qui est a installer (à commencer par les librairies, puis paquets), * - puis les actions neutres */ function ordonner_actions() { // nettoyer le terrain $this->clear(); foreach ($this->start as $id=>$action) { $i = $this->decideur->infos_courtes_id($id); $i = $i['i'][$id]; switch ($action) { case 'getlib': // le plugin en ayant besoin le fera // comme un grand... break; case 'geton': case 'on': $this->on($i, $action); break; case 'up': // si le plugin est actif if ($i['a'] == 'oui') { $this->on($i, $action); } else { $this->neutre($i, $action); } break; case 'upon': $this->on($i, $action); break; case 'off': case 'stop': $this->off($i, $action); break; case 'get': case 'kill': $this->neutre($i, $action); break; } } // c'est termine, on passe tout dans la fin... foreach ($this->middle as $acts) { $this->end = array_merge($this->end, $acts); } // si on a vu une desactivation de SVP // on le met comme derniere action... // sinon on ne pourrait pas faire les suivantes ! if ($this->svp_off) { $this->log("SVP a desactiver a la fin."); foreach ($this->end as $c => $info) { if ($info['p'] == 'SVP') { unset($this->end[$c]); $this->end[] = $info; break; } } } $this->log("------------"); #$this->log("Fin du tri :"); #$this->log($this->end); } /** * Ajoute un paquet à activer * * À chaque fois qu'un nouveau paquet arrive ici, on le compare * avec ceux déjà présents pour savoir si on doit le traiter avant * ou après un des paquets à activer déjà présent. * * Si le paquet est une dépendance d'un autre plugin, il faut le mettre * avant (pour l'activer avant celui qui en dépend). * * Si le paquet demande une librairie, celle-ci est ajoutée (les * librairies seront téléchargées avant l'activation des plugins, * le plugin aura donc sa librairie lorsqu'il sera activé) * * * @param array $info * Description du paquet * @param string $action * Action à réaliser (on, upon) * @return void **/ function on($info, $action) { $info['todo'] = $action; $p = $info['p']; $this->log("ON: $p $action"); // si dependance, il faut le mettre avant ! $in = $out = $deps = $deps_all = array(); // raz des cles pour avoir les memes que $out (utile reellement ?) $this->middle['on'] = array_values($this->middle['on']); // ajout des dependance foreach ($info['dn'] as $dep) { $in[] = $dep['nom']; } // ajout des librairies foreach ($info['dl'] as $lib) { // il faudrait gerer un retour d'erreur eventuel ! $this->add_lib($lib['nom'], $lib['lien']); } // on recupere : tous les prefix de plugin a activer (out) // ie. ce plugin peut dependre d'un de ceux la // // ainsi que les dependences de ces plugins (deps) // ie. ces plugins peuvent dependre de ce nouveau a activer. foreach ($this->middle['on'] as $inf) { $out[] = $inf['p']; foreach ($inf['dn'] as $dep) { $deps[$inf['p']][] = $dep['nom']; $deps_all[] = $dep['nom']; } } if (!$in) { // pas de dependance, on le met en premier ! $this->log("- placer $p tout en haut"); array_unshift($this->middle['on'], $info); } else { // intersection = dependance presente aussi // on place notre action juste apres la derniere dependance if ($diff = array_intersect($in, $out)) { $key = array(); foreach($diff as $d) {$key[] = array_search($d, $out);} $key = max($key); $this->log("- placer $p apres " . $this->middle['on'][$key]['p']); if ($key == count($this->middle['on'])) { $this->middle['on'][] = $info; } else { array_splice($this->middle['on'], $key+1, 0, array($info)); } // intersection = plugin dependant de celui-ci // on place notre plugin juste avant la premiere dependance a lui trouvee } elseif (in_array($p, $deps_all)) { foreach ($deps as $prefix=>$dep) { if (in_array($p, $dep)) { $key = array_search($prefix, $out); $this->log("- placer $p avant $prefix qui en depend ($key)"); if ($key == 0) { array_unshift($this->middle['on'], $info); } else { array_splice($this->middle['on'], $key, 0, array($info)); } break; } } // rien de particulier, il a des dependances mais les plugins // ne sont pas encore la ou les dependances sont deja actives // donc on le place tout en bas } else { $this->log("- placer $p tout en bas"); $this->middle['on'][] = $info; } } unset($diff, $in, $out); } /** * Ajoute un paquet avec une action neutre * * Ces actions seront traitées en dernier, et peu importe leur * ordre car elles n'entrent pas en conflit avec des dépendances. * * @param array $info * Description du paquet * @param string $action * Action à réaliser (kill, get, up (sur plugin inactif)) * @return void **/ function neutre($info, $action) { $info['todo'] = $action; $this->log("NEUTRE: $info[p] $action"); $this->middle['neutre'][] = $info; } /** * Ajoute un paquet à désactiver * * Ces actions seront traitées en premier. * * À chaque fois qu'un nouveau paquet arrive ici, on le compare * avec ceux déjà présents pour savoir si on doit le traiter avant * ou après un des paquets à désactiver déjà présent. * * Si le paquet est une dépendance d'un autre plugin, il faut le mettre * après (pour désactiver avant celui qui en dépend). * * @param array $info * Description du paquet * @param string $action * Action à réaliser (kill, get, up (sur plugin inactif)) * @return void **/ function off($info, $action) { $info['todo'] = $action; $p = $info['p']; $this->log("OFF: $p $action"); // signaler la desactivation de SVP if ($p == 'SVP') { $this->svp_off = true; } // si dependance, il faut le mettre avant ! $in = $out = array(); // raz des cles pour avoir les memes que $out (utile reellement ?) $this->middle['off'] = array_values($this->middle['off']); foreach ($info['dn'] as $dep) { $in[] = $dep['nom']; } foreach ($this->middle['off'] as $inf) { $out[] = $inf['p']; } if (!$in) { // ce plugin n'a pas de dependance, on le met en dernier ! $this->log("- placer $p tout en bas"); $this->middle['off'][] = $info; } else { // ce plugin a des dependances, // on le desactive juste avant elles. // intersection = dependance presente aussi // on place notre action juste avant la premiere dependance if ($diff = array_intersect($in, $out)) { $key = array(); foreach($diff as $d) {$key[] = array_search($d, $out);} $key = min($key); $this->log("- placer $p avant " . $this->middle['off'][$key]['p']); array_splice($this->middle['off'], $key, 0, array($info)); } else { // aucune des dependances n'est a desactiver // (du moins à ce tour ci), // on le met en premier ! $this->log("- placer $p tout en haut"); array_unshift($this->middle['off'], $info); // etait ->middle['on'] ?? ... } } unset($diff, $in, $out); } /** * Retourne un bilan, texte HTML, des actions qui ont été faites * * Si c'est un affichage du bilan de fin, et qu'il reste des actions * à faire, un lien est proposé pour faire supprimer ces actions restantes * et le verrou qui va avec. * * @param bool $fin * Est-ce un affichage intermédiaire (false) ou le tout dernier (true). * @return string * Bilan des actions au format HTML **/ function presenter_actions($fin = false) { $affiche = ""; include_spip('inc/filtres_boites'); if (count($this->err)) { $erreurs = "<ul>"; foreach ($this->err as $i) { $erreurs .= "\t<li class='erreur'>" . $i . "</li>\n"; } $erreurs .= "</ul>"; $affiche .= boite_ouvrir(_T('svp:actions_en_erreur'), 'error') . $erreurs . boite_fermer(); } if (count($this->done)) { $oks = true; $done = "<ul>"; foreach ($this->done as $i) { $ok = ($i['done'] ? true : false); $oks = &$ok; $ok_texte = $ok ? 'ok' : 'fail'; $cle_t = 'svp:message_action_finale_' . $i['todo'] . '_' . $ok_texte; $texte = _T($cle_t, array( 'plugin' => $i['n'], 'version' => denormaliser_version($i['v']), 'version_maj' => denormaliser_version($i['maj']))); if (is_string($i['done'])) { $texte .= " <span class='$ok_texte'>$i[done]</span>"; } $done .= "\t<li class='$ok_texte'>$texte</li>\n"; } $done .= "</ul>"; $affiche .= boite_ouvrir(_T('svp:actions_realises'), ($oks ? 'success' : 'notice')) . $done . boite_fermer(); } if (count($this->end)) { $todo = "<ul>"; foreach ($this->end as $i) { $todo .= "\t<li>"._T('svp:message_action_'.$i['todo'],array( 'plugin'=>$i['n'], 'version'=>denormaliser_version($i['v']), 'version_maj'=>denormaliser_version($i['maj'])))."</li>\n"; } $todo .= "</ul>\n"; $titre = ($fin ? _T('svp:actions_non_traitees') : _T('svp:actions_a_faire')); // s'il reste des actions à faire alors que c'est la fin qui est affichée, // on met un lien pour vider. C'est un cas anormal qui peut surgir : // - en cas d'erreur sur une des actions bloquant l'espace privé // - en cas d'appel d'admin_plugins concurrent par le même admin ou 2 admins... if ($fin) { include_spip('inc/filtres'); if ($this->lock['time']) { $time = $this->lock['time']; } else { $time = time(); } $date = date('Y-m-d H:i:s', $time); $todo .= "<br />\n"; $todo .= "<p class='error'>" . _T('svp:erreur_actions_non_traitees', array( 'auteur' => sql_getfetsel('nom', 'spip_auteurs', 'id_auteur=' . sql_quote($this->lock['id_auteur'])), 'date' => affdate_heure($date) )) . "</p>\n"; $todo .= "<a href='" . parametre_url(self(), 'nettoyer_actions', '1'). "'>" . _T('svp:nettoyer_actions') . "</a>\n"; } $affiche .= boite_ouvrir($titre, 'notice') . $todo . boite_fermer(); } if ($affiche) { include_spip('inc/filtres'); $affiche = wrap($affiche, "<div class='svp_retour'>"); } return $affiche; } /** * Teste l'existance d'un verrou par un auteur ? * * Si un id_auteur est transmis, teste que c'est cet auteur * précis qui a posé le verrou. * * @see Actionneur::verouiller() * * @param int|string $id_auteur * Identifiant de l'auteur, ou vide * @return bool * true si un verrou est là, false sinon **/ function est_verrouille($id_auteur = '') { if ($id_auteur == '') { return ($this->lock['id_auteur'] ? true : false); } return ($this->lock['id_auteur'] == $id_auteur); } /** * Pose un verrou * * Un verrou permet de garentir qu'une seule exécution d'actions * est lancé à la fois, ce qui évite que deux administrateurs * puissent demander en même temps des actions qui pourraient * s'entrechoquer. * * Le verrou est signé par l'id_auteur de l'auteur actuellement identifié. * * Le verrou sera sauvegardé en fichier avec la liste des actions * * @see Actionneur::sauver_actions() **/ function verrouiller() { $this->lock = array( 'id_auteur' => $GLOBALS['visiteur_session']['id_auteur'], 'time' => time(), ); } /** * Enlève le verrou **/ function deverrouiller() { $this->lock = array( 'id_auteur' => 0, 'time' => '', ); } /** * Sauvegarde en fichier cache la liste des actions et le verrou * * Crée un tableau contenant les informations principales qui permettront * de retrouver ce qui est à faire comme action, ce qui a été fait, * les erreurs générées, et le verrouillage. * * Le cache peut être lu avec la méthode get_actions() * * @see Actionneur::get_actions() **/ function sauver_actions() { $contenu = serialize(array( 'todo' => $this->end, 'done' => $this->done, 'work' => $this->work, 'err' => $this->err, 'lock' => $this->lock, )); ecrire_fichier(_DIR_TMP . 'stp_actions.txt', $contenu); } /** * Lit le fichier cache de la liste des actions et verrou * * Restaure les informations contenues dans le fichier de cache * et écrites avec la méthode sauver_actions(). * * @see Actionneur::sauver_actions() **/ function get_actions() { lire_fichier(_DIR_TMP . 'stp_actions.txt', $contenu); $infos = unserialize($contenu); $this->end = $infos['todo']; $this->work = $infos['work']; $this->done = $infos['done']; $this->err = $infos['err']; $this->lock = $infos['lock']; } /** * Nettoyage des actions et verrou * * Remet tout à zéro pour pouvoir repartir d'un bon pied. **/ function nettoyer_actions() { $this->todo = array(); $this->done = array(); $this->work = array(); $this->err = array(); $this->deverrouiller(); $this->sauver_actions(); } /** * Effectue une des actions qui reste à faire. * * Dépile une des actions à faire s'il n'y en a pas en cours * au moment de l'appel et traite cette action * * @see Actionneur::do_action() * @return bool|array * False si aucune action à faire, * sinon tableau de description courte du paquet + index 'todo' indiquant l'action **/ function one_action() { // s'il reste des actions, on en prend une, et on la fait // de meme si une action est en cours mais pas terminee (timeout) // on tente de la refaire... if (count($this->end) OR $this->work) { // on verrouille avec l'auteur en cours pour // que seul lui puisse effectuer des actions a ce moment la if (!$this->est_verrouille()) { $this->verrouiller(); } // si ce n'est pas verrouille par l'auteur en cours... // ce n'est pas normal, donc on quitte sans rien faire. elseif (!$this->est_verrouille($GLOBALS['visiteur_session']['id_auteur'])) { return false; } // si pas d'action en cours if (!$this->work) { // on prend une des actions en attente $this->work = array_shift($this->end); } $action = $this->work; $this->sauver_actions(); // effectue l'action dans work $this->do_action(); // si la liste des actions en attente est maintenant vide // on deverrouille aussitot. if (!count($this->end)) { $this->deverrouiller(); $this->sauver_actions(); } return $action; } else { // on ne devrait normalement plus tomber sur un cas de verrouillage ici // mais sait-on jamais. Tester ne couter rien :) if ($this->est_verrouille()) { $this->deverrouiller(); $this->sauver_actions(); } } return false; } /** * Effectue l'action en attente. * * Appelle une methode do_{todo} de l'Actionneur où todo * est le type d'action à faire. * * Place dans la clé 'done' de description courte du paquet * le résultat de l'action (un booléen indiquant si elle s'est bien * déroulée). **/ function do_action() { if ($do = $this->work) { $todo = 'do_' . $do['todo']; lire_metas(); // avoir les metas a jour $this->log("Faire $todo avec $do[n]"); $do['done'] = $this->$todo($do); $this->done[] = $do; $this->work = array(); $this->sauver_actions(); } } /** * Attraper et activer un paquet * * @param array $info * Description courte du paquet * @return bool * false si erreur, true sinon. */ function do_geton($info) { if (!$this->tester_repertoire_plugins_auto()) { return false; } $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($info['i'])); if ($dirs = $this->get_paquet_id($i)) { $this->activer_plugin_dossier($dirs['dossier'], $i); return true; } $this->log("GetOn : Erreur de chargement du paquet " .$info['n']); return false; } /** * Activer un paquet * * Soit il est là... soit il est à télécharger... * * @param array $info * Description courte du paquet * @return bool * false si erreur, true sinon. */ function do_on($info) { $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($info['i'])); // à télécharger ? if ($i['id_zone'] > 0) { return $this->do_geton($info); } // a activer uniquement // il faudra prendre en compte les autres _DIR_xx if (in_array($i['constante'], array('_DIR_PLUGINS','_DIR_PLUGINS_SUPPL'))) { $dossier = rtrim($i['src_archive'], '/'); $this->activer_plugin_dossier($dossier, $i, $i['constante']); return true; } return false; } /** * Mettre à jour un paquet * * @param array $info * Description courte du paquet * @return bool|array * false si erreur, * description courte du nouveau plugin sinon. */ function do_up($info) { // ecriture du nouveau // suppression de l'ancien (si dans auto, et pas au meme endroit) // OU suppression des anciens fichiers if (!$this->tester_repertoire_plugins_auto()) { return false; } // $i est le paquet a mettre à jour (donc present) // $maj est le paquet a telecharger qui est a jour (donc distant) $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($info['i'])); // on cherche la mise a jour... // c'est a dire le paquet source que l'on met a jour. if ($maj = sql_fetsel('pa.*', array('spip_paquets AS pa', 'spip_plugins AS pl'), array( 'pl.prefixe='.sql_quote($info['p']), 'pa.version='.sql_quote($info['maj']), 'pa.id_plugin = pl.id_plugin', 'pa.id_depot>'.sql_quote(0)), '', 'pa.etatnum DESC', '0,1')) { if ($dirs = $this->get_paquet_id($maj)) { // Si le plugin a jour n'est pas dans le meme dossier que l'ancien... // il faut : // - activer le plugin sur son nouvel emplacement (uniquement si l'ancien est actif)... // - supprimer l'ancien (si faisable) if (($dirs['dossier'] . '/') != $i['src_archive']) { if ($i['actif'] == 'oui') { $this->activer_plugin_dossier($dirs['dossier'], $maj); } // l'ancien repertoire a supprimer pouvait etre auto/X // alors que le nouveau est auto/X/Y ... // il faut prendre en compte ce cas particulier et ne pas ecraser auto/X ! if (substr($i['src_archive'], 0, 5) == 'auto/' and (false === strpos($dirs['dossier'], $i['src_archive']))) { if (supprimer_repertoire( constant($i['constante']) . $i['src_archive']) ) { sql_delete('spip_paquets', 'id_paquet=' . sql_quote($info['i'])); } } } $this->ajouter_plugin_interessants_meta($dirs['dossier']); return $dirs; } } return false; } /** * Mettre à jour et activer un paquet * * @param array $info * Description courte du paquet * @return bool * false si erreur, true sinon */ function do_upon($info) { $i = sql_fetsel('*', 'spip_paquets', 'id_paquet='.sql_quote($info['i'])); if ($dirs = $this->do_up($info)) { $this->activer_plugin_dossier($dirs['dossier'], $i, $i['constante']); return true; } return false; } /** * Désactiver un paquet * * @param array $info * Description courte du paquet * @return bool * false si erreur, true sinon */ function do_off($info) { $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($info['i'])); // il faudra prendre en compte les autres _DIR_xx if (in_array($i['constante'], array('_DIR_PLUGINS','_DIR_PLUGINS_SUPPL'))) { include_spip('inc/plugin'); $dossier = rtrim($i['src_archive'], '/'); ecrire_plugin_actifs(array(rtrim($dossier,'/')), false, 'enleve'); sql_updateq('spip_paquets', array('actif'=>'non', 'installe'=>'non'), 'id_paquet='.sql_quote($info['i'])); $this->actualiser_plugin_interessants(); // ce retour est un rien faux... // il faudrait que la fonction ecrire_plugin_actifs() // retourne au moins d'eventuels message d'erreur ! return true; } return false; } /** * Désinstaller un paquet * * @param array $info * Description courte du paquet * @return bool * false si erreur, true sinon */ function do_stop($info) { $i = sql_fetsel('*','spip_paquets','id_paquet=' . sql_quote($info['i'])); // il faudra prendre en compte les autres _DIR_xx if (in_array($i['constante'], array('_DIR_PLUGINS','_DIR_PLUGINS_SUPPL'))) { include_spip('inc/plugin'); $dossier = rtrim($i['src_archive'],'/'); $installer_plugins = charger_fonction('installer', 'plugins'); // retourne : // - false : pas de procedure d'install/desinstalle // - true : operation deja faite // - tableau : operation faite ce tour ci. $infos = $installer_plugins($dossier, 'uninstall'); if (is_bool($infos) OR !$infos['install_test'][0]) { include_spip('inc/plugin'); ecrire_plugin_actifs(array($dossier), false, 'enleve'); sql_updateq('spip_paquets', array('actif'=>'non', 'installe'=>'non'), 'id_paquet='.sql_quote($info['i'])); return true; } else { // echec $this->log("Échec de la désinstallation de " . $i['src_archive']); } } $this->actualiser_plugin_interessants(); return false; } /** * Effacer les fichiers d'un paquet * * @param array $info * Description courte du paquet * @return bool * false si erreur, true sinon */ function do_kill($info) { // on reverifie que c'est bien un plugin auto ! // il faudrait aussi faire tres attention sur un site mutualise // cette option est encore plus delicate que les autres... $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($info['i'])); if (in_array($i['constante'], array('_DIR_PLUGINS','_DIR_PLUGINS_SUPPL')) and substr($i['src_archive'], 0, 5) == 'auto/') { $dir = constant($i['constante']) . $i['src_archive']; if (supprimer_repertoire($dir)) { $id_plugin = sql_getfetsel('id_plugin', 'spip_paquets', 'id_paquet=' . sql_quote($info['i'])); // on supprime le paquet sql_delete('spip_paquets', 'id_paquet=' . sql_quote($info['i'])); // ainsi que le plugin s'il n'est plus utilise $utilise = sql_allfetsel( 'pl.id_plugin', array('spip_paquets AS pa', 'spip_plugins AS pl'), array('pa.id_plugin = pl.id_plugin', 'pa.id_plugin=' . sql_quote($id_plugin))); if (!$utilise) { sql_delete('spip_plugins', 'id_plugin=' . sql_quote($id_plugin)); } else { // on met a jour d'eventuels obsoletes qui ne le sont plus maintenant // ie si on supprime une version superieure à une autre qui existe en local... include_spip('inc/svp_depoter_local'); svp_corriger_obsolete_paquets(array($id_plugin)); } // on tente un nettoyage jusqu'a la racine de auto/ // si la suppression concerne une profondeur d'au moins 2 // et que les repertoires sont vides $chemins = explode('/', $i['src_archive']); // auto / prefixe / version // le premier c'est auto array_shift($chemins); // le dernier est deja fait... array_pop($chemins); // entre les deux... while (count($chemins)) { $vide = true; $dir = constant($i['constante']) . 'auto/' . implode('/', $chemins); $fichiers = scandir($dir); if ($fichiers) { foreach ($fichiers as $f) { if ($f[0] != '.') { $vide = false; break; } } } // on tente de supprimer si c'est effectivement vide. if ($vide and !supprimer_repertoire($dir)) { break; } array_pop($chemins); } return true; } } return false; } /** * Installer une librairie * * @param array $info * Description courte du paquet (une librairie ici) * @return bool * false si erreur, true sinon */ function do_getlib($info) { if (!defined('_DIR_LIB') or !_DIR_LIB) { $this->err(_T('svp:erreur_dir_dib_indefini')); $this->log("/!\ Pas de _DIR_LIB defini !"); return false; } if (!is_writable(_DIR_LIB)) { $this->err(_T('svp:erreur_dir_dib_ecriture', array('dir' => _DIR_LIB ))); $this->log("/!\ Ne peut pas écrire dans _DIR_LIB !"); return false; } if(!autoriser('plugins_ajouter')){ $this->err(_T('svp:erreur_auth_plugins_ajouter_lib')); $this->log("/!\ Pas autorisé à ajouter des libs !"); return false; } $this->log("Recuperer la librairie : " . $info['n'] ); // on recupere la mise a jour... include_spip('action/teleporter'); $teleporter_composant = charger_fonction('teleporter_composant', 'action'); $ok = $teleporter_composant('http', $info['v'], _DIR_LIB . $info['n']); if ($ok === true) { return true; } $this->err($ok); $this->log("Téléporteur en erreur : " . $ok); return false; } /** * Télécharger un paquet * * @param array $info * Description courte du paquet * @return bool * false si erreur, true sinon */ function do_get($info) { if (!$this->tester_repertoire_plugins_auto()) { return false; } $i = sql_fetsel('*', 'spip_paquets', 'id_paquet=' . sql_quote($info['i'])); if ($dirs = $this->get_paquet_id($info['i'])) { $this->ajouter_plugin_interessants_meta($dirs['dossier']); return true; } return false; } /** * Lancer l'installation d'un paquet * * @param array $info * Description courte du paquet * @return bool * false si erreur, true sinon */ function do_install($info) { return $this->installer_plugin($info); } /** * Activer un plugin * * @param string $dossier * Chemin du répertoire du plugin * @param array $i * Description en BDD du paquet - row SQL (tableau clé => valeur) * @param string $constante * Constante indiquant le chemin de base du plugin (_DIR_PLUGINS, _DIR_PLUGINS_SUPPL, _DIR_PLUGINS_DIST) * @return void **/ function activer_plugin_dossier($dossier, $i, $constante='_DIR_PLUGINS') { include_spip('inc/plugin'); $this->log("Demande d'activation de : " . $dossier); //il faut absolument que tous les fichiers de cache // soient inclus avant modification, sinon un appel ulterieur risquerait // de charger des fichiers deja charges par un autre ! // C'est surtout le ficher de fonction le probleme (options et pipelines // sont normalement deja charges). if (@is_readable(_CACHE_PLUGINS_OPT)) {include_once(_CACHE_PLUGINS_OPT);} if (@is_readable(_CACHE_PLUGINS_FCT)) {include_once(_CACHE_PLUGINS_FCT);} if (@is_readable(_CACHE_PIPELINES)) {include_once(_CACHE_PIPELINES);} include_spip('inc/plugin'); ecrire_plugin_actifs(array($dossier), false, 'ajoute'); $installe = $i['version_base'] ? 'oui' : 'non'; if ($installe == 'oui') { if(!$i['constante']) $i['constante'] = '_DIR_PLUGINS'; // installer le plugin au prochain tour $new_action = array_merge($this->work, array( 'todo'=>'install', 'dossier'=>rtrim($dossier,'/'), 'constante'=>$i['constante'], 'v'=>$i['version'], // pas forcement la meme version qu'avant lors d'une mise a jour. )); array_unshift($this->end, $new_action); $this->log("Demande d'installation de $dossier"); #$this->installer_plugin($dossier); } $this->ajouter_plugin_interessants_meta($dossier); $this->actualiser_plugin_interessants(); } /** * Actualiser les plugins intéressants * * Décrémente chaque score de plugin présent dans la méta * 'plugins_interessants' et signifiant que ces plugins * ont été utilisés récemment. * * Les plugins atteignant un score de zéro sont évacués ce la liste. */ function actualiser_plugin_interessants() { // Chaque fois que l'on valide des plugins, // on memorise la liste de ces plugins comme etant "interessants", // avec un score initial, qui sera decremente a chaque tour : // ainsi un plugin active pourra reter visible a l'ecran, // jusqu'a ce qu'il tombe dans l'oubli. $plugins_interessants = @unserialize($GLOBALS['meta']['plugins_interessants']); if (!is_array($plugins_interessants)) { $plugins_interessants = array(); } $dossiers = array(); $dossiers_old = array(); foreach($plugins_interessants as $p => $score) { if (--$score > 0) { $plugins_interessants[$p] = $score; $dossiers[$p.'/'] = true; } else { unset($plugins_interessants[$p]); $dossiers_old[$p.'/'] = true; } } // enlever les anciens if ($dossiers_old) { // ATTENTION, il faudra prendre en compte les _DIR_xx sql_updateq('spip_paquets', array('recent'=>0), sql_in('src_archive', array_keys($dossiers_old))); } $plugs = sql_allfetsel('src_archive','spip_paquets', 'actif='.sql_quote('oui')); $plugs = array_map('array_shift', $plugs); foreach ($plugs as $dossier) { $dossiers[$dossier] = true; $plugins_interessants[ rtrim($dossier, '/') ] = 30; // score initial } $plugs = sql_updateq('spip_paquets', array('recent'=>1), sql_in('src_archive', array_keys($dossiers))); ecrire_meta('plugins_interessants', serialize($plugins_interessants)); } /** * Ajoute un plugin dans les plugins intéressants * * Initialise à 30 le score du plugin indiqué par le chemin transmis, * dans la liste des plugins intéressants. * * @param string $dir * Chemin du répertoire du plugin */ function ajouter_plugin_interessants_meta($dir) { $plugins_interessants = @unserialize($GLOBALS['meta']['plugins_interessants']); if (!is_array($plugins_interessants)) { $plugins_interessants = array(); } $plugins_interessants[$dir] = 30; ecrire_meta('plugins_interessants', serialize($plugins_interessants)); } /** * Lancer l'installation d'un plugin * * @param array $info * Description courte du paquet * @return bool * false si erreur, true sinon */ function installer_plugin($info){ // il faut info['dossier'] et info['constante'] pour installer if ($plug = $info['dossier']) { $installer_plugins = charger_fonction('installer', 'plugins'); $infos = $installer_plugins($plug, 'install', $info['constante']); if ($infos) { // en absence d'erreur, on met a jour la liste des plugins installes... if (!is_array($infos) OR $infos['install_test'][0]) { $meta_plug_installes = @unserialize($GLOBALS['meta']['plugin_installes']); if (!$meta_plug_installes) { $meta_plug_installes=array(); } $meta_plug_installes[] = $plug; ecrire_meta('plugin_installes',serialize($meta_plug_installes),'non'); } if (!is_array($infos)) { // l'installation avait deja ete faite un autre jour return true; } else { // l'installation est neuve list($ok, $trace) = $infos['install_test']; if ($ok) { return true; } // l'installation est en erreur $this->err(_T('svp:message_action_finale_install_fail', array('plugin' => $info['n'], 'version'=>denormaliser_version($info['v']))) . "<br />" . $trace); } } } return false; } /** * Télécharge un paquet * * Supprime les fichiers obsolètes (si présents) * * @param int|array $id_or_row * Identifiant du paquet ou description ligne SQL du paquet * @return bool|array * False si erreur. * Tableau de 2 index sinon : * - dir : Chemin du paquet téléchargé depuis la racine * - dossier : Chemin du paquet téléchargé, depuis _DIR_PLUGINS */ function get_paquet_id($id_or_row) { // on peut passer direct le row sql... if (!is_array($id_or_row)) { $i = sql_fetsel('*','spip_paquets','id_paquet='.sql_quote($id_or_row)); } else { $i = $id_or_row; } unset($id_or_row); if ($i['nom_archive'] and $i['id_depot']) { $this->log("Recuperer l'archive : " . $i['nom_archive'] ); if ($adresse = sql_getfetsel('url_archives', 'spip_depots', 'id_depot='.sql_quote($i['id_depot']))) { $zip = $adresse . '/' . $i['nom_archive']; // destination : auto/prefixe/version (sinon auto/nom_archive/version) $prefixe = sql_getfetsel('pl.prefixe', array('spip_paquets AS pa', 'spip_plugins AS pl'), array('pa.id_plugin = pl.id_plugin', 'pa.id_paquet=' . sql_quote($i['id_paquet']))); // prefixe $base = ($prefixe ? strtolower($prefixe) : substr($i['nom_archive'], 0, -4) ); // enlever .zip ... // prefixe/version $dest = $base . '/v' . denormaliser_version($i['version']); // si on tombe sur un auto/X ayant des fichiers (et pas uniquement des dossiers) // ou un dossier qui ne commence pas par 'v' // c'est que auto/X n'était pas chargé avec SVP // ce qui peut arriver lorsqu'on migre de SPIP 2.1 à 3.0 // dans ce cas, on supprime auto X pour mettre notre nouveau paquet. $ecraser_base = false; if (is_dir(_DIR_PLUGINS_AUTO . $base)) { $base_files = scandir(_DIR_PLUGINS_AUTO . $base); if (is_array($base_files)) { $base_files = array_diff($base_files, array('.', '..')); foreach ($base_files as $f) { if (($f[0] != '.' and $f[0] != 'v') // commence pas par v OR ($f[0] != '.' and !is_dir(_DIR_PLUGINS_AUTO . $base . '/' . $f))) { // commence par v mais pas repertoire $ecraser_base = true; break; } } } } if ($ecraser_base) { supprimer_repertoire(_DIR_PLUGINS_AUTO . $base); } // on recupere la mise a jour... include_spip('action/teleporter'); $teleporter_composant = charger_fonction('teleporter_composant', 'action'); $ok = $teleporter_composant('http', $zip, _DIR_PLUGINS_AUTO . $dest); if ($ok === true) { return array( 'dir'=> _DIR_PLUGINS_AUTO . $dest, 'dossier' => 'auto/' . $dest, // c'est depuis _DIR_PLUGINS ... pas bien en dur... ); } $this->err($ok); $this->log("Téléporteur en erreur : " . $ok); } else { $this->log("Aucune adresse pour le dépot " . $i['id_depot'] ); } } return false; } /** * Teste que le répertoire plugins auto existe et * que l'on peut ecrire dedans ! * * @return bool * True si on peut écrire dedans, false sinon **/ function tester_repertoire_plugins_auto() { include_spip('inc/plugin'); // pour _DIR_PLUGINS_AUTO if (!defined('_DIR_PLUGINS_AUTO') or !_DIR_PLUGINS_AUTO) { $this->err(_T('svp:erreur_dir_plugins_auto_indefini')); $this->log("/!\ Pas de _DIR_PLUGINS_AUTO defini !"); return false; } if (!is_writable(_DIR_PLUGINS_AUTO)) { $this->err(_T('svp:erreur_dir_plugins_auto_ecriture', array('dir'=>_DIR_PLUGINS_AUTO))); $this->log("/!\ Ne peut pas écrire dans _DIR_PLUGINS_AUTO !"); return false; } return true; } /** * Teste si le plugin SVP (celui-ci donc) a * été désinstallé / désactivé dans les actions réalisées * * @note * On ne peut tester sa désactivation que dans le hit où la désinstallation * est réalisée, puisque après, s'il a été désactivé, au prochain hit * on ne connaîtra plus ce fichier ! * * @return bool * true si SVP a été désactivé, false sinon **/ function tester_si_svp_desactive() { foreach ($this->done as $d) { if ($d['p'] == 'SVP' AND $d['done'] == true AND in_array($d['todo'], array('off', 'stop'))) { return true; } } return false; } } /** * Gère le traitement des actions des formulaires utilisant l'Actionneur * * @param array $actions * Liste des actions a faire (id_paquet => action) * @param array $retour * Tableau de retour du CVT dans la partie traiter * @param string $redirect * URL de retour * @return void **/ function svp_actionner_traiter_actions_demandees($actions, &$retour,$redirect=null) { $actionneur = new Actionneur(); $actionneur->ajouter_actions($actions); $actionneur->verrouiller(); $actionneur->sauver_actions(); $redirect = $redirect ? $redirect : generer_url_ecrire('admin_plugin'); $retour['redirect'] = generer_url_action('actionner', 'redirect='.urlencode($redirect)); set_request('_todo', ''); $retour['message_ok'] = _T("svp:action_patienter"); } ?> messages.php000066600000014062152503076610007100 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2014 * * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ if (!defined('_ECRIRE_INC_VERSION')) return; include_spip('inc/filtres'); include_spip('base/abstract_sql'); if (!defined('_EMAIL_GENERAL')) define('_EMAIL_GENERAL','general'); // permet aux admin d'envoyer un email a tout le monde /** * Lister les statuts des auteurs pouvant recevoir un message * c'est tous les auteurs au moins redacteur * * @return array */ function messagerie_statuts_destinataires_possibles(){ include_spip('inc/filtres_ecrire'); return pipeline('messagerie_statuts_destinataires_possibles', auteurs_lister_statuts('redacteurs',false)); } /** * Nettoyer une liste de destinataires * @param $destinataires * @return array */ function messagerie_nettoyer_destinataires($destinataires){ foreach ($destinataires as $k=>$id){ // il se peut que l'id recupere l'ancre qui suit avec certains ie ... :( if (preg_match(',^[0-9]+#[a-z_0-9]+,',$id)) $destinataires[$k] = intval($id); } return $destinataires; } /** * Fonction generique de verification des destinataires * lors de l'envoi d'un message ou de recommander * un destinataire peut etre un id_auteur numerique * ou une adresse mail valide, si l'options accepter_email est true * * @param array $destinataires * @param array $options * @return array */ function messagerie_verifier_destinataires($destinataires,$options=array('accepter_email'=>true)){ $erreurs = array(); $destinataires = messagerie_nettoyer_destinataires($destinataires); foreach ($destinataires as $id){ if (is_numeric($id)){ if (!$id) $erreurs[] = _T('organiseur:erreur_destinataire_invalide',array('dest'=>$id)); } else { if (!$options['accepter_email'] OR !email_valide($id)) $erreurs[] = _T('organiseur:erreur_destinataire_invalide',array('dest'=>$id)); } } return $erreurs; } /** * Selectionner les destinataires en distinguant emails et id_auteur * * @param array $dests * @return array */ function messagerie_destiner($dests){ // separer les destinataires auteur des destinataires email $auteurs_dest = array(); $email_dests = array(); $dests = messagerie_nettoyer_destinataires($dests); foreach ($dests as $id){ if (is_numeric($id)) $auteurs_dest[] = $id; elseif (defined('_MESSAGERIE_EMAIL_GENERAL') AND $id!=_MESSAGERIE_EMAIL_GENERAL) $email_dests[] = $id; } if (count($email_dests)) { // retrouver les id des emails pour ceux qui sont en base $res = sql_select('id_auteur,email','spip_auteurs',sql_in('email', $email_dests)); $auteurs_dest_found = array(); while ($row = sql_fetch($res)){ $auteurs_dest_found[] = $row['id_auteur']; } $auteurs_dest = array_merge($auteurs_dest,$auteurs_dest_found); } return array($auteurs_dest,$email_dests); } /** * Diffuser un message par la messagerie interne * * @param int $id_message * @param array $auteurs_dest * @return bool|int */ function messagerie_diffuser_message($id_message, $auteurs_dest=array()){ $out = false; if ($id_message=intval($id_message) AND count($auteurs_dest)){ include_spip('action/editer_liens'); $out = objet_associer(array('auteur'=>$auteurs_dest),array('message'=>$id_message),array('vu'=>'non')); } return $out; } /** * Envoyer un message par mail pour les destinataires externes * * @param int $id_message * @param array $emails_dest * @return bool */ function messagerie_mailer_message($id_message, $emails_dest=array()){ if ($id_message=intval($id_message) AND count($emails_dest)) { if ($row = sql_fetsel('titre,texte,id_auteur','spip_messages','id_message='.intval($id_message))){ $from = sql_getfetsel('email','spip_auteurs','id_auteur='.$row['id_auteur']); foreach($emails_dest as $email) job_queue_add( 'envoyer_mail', 'messagerie mail', array($email,$row['titre'],array('texte'=>$row['texte'],'from'=>$from)), 'inc/' ); return true; } } return false; } /** * Marquer un message dans l'etat indique par $vu * * @param int $id_auteur * @param array $liste * @param string $vu * @return void */ function messagerie_marquer_message($id_auteur,$liste,$vu){ include_spip('action/editer_liens'); if (!is_array($liste)) $liste = array($liste); // completer les liens qui n'existent pas encore // ex : pour marquer lue une annonce, on ajoute le lien d'abord (n'existe pas) // puis on le marque 'oui' $liens = objet_trouver_liens(array('auteur'=>$id_auteur),array('message'=>$liste)); $l = array(); foreach($liens as $lien) $l[] = $lien['message']; objet_associer(array('auteur'=>$id_auteur),array('message'=>array_diff($liste,$l)),array('vu'=>$vu)); // puis les marquer tous lus objet_qualifier_liens(array('auteur'=>$id_auteur),array('message'=>$liste),array('vu'=>$vu)); include_spip('inc/invalideur'); suivre_invalideur("message/".implode(',',$liste)); } /** * Marquer un message comme lu * * @param int $id_auteur * @param array $liste_id_message */ function messagerie_marquer_lus($id_auteur,$liste_id_message){messagerie_marquer_message($id_auteur,$liste_id_message,'oui');} /** * Marquer un message comme non lu * * @param int $id_auteur * @param array $liste_id_message */ function messagerie_marquer_non_lus($id_auteur,$liste_id_message){messagerie_marquer_message($id_auteur,$liste_id_message,'non');} /** * Effacer un message recu * * @param int $id_auteur * @param array $liste_id_message */ function messagerie_effacer_message_recu($id_auteur,$liste_id_message){messagerie_marquer_message($id_auteur,$liste_id_message,'poub');} ?> quete_calendrier.php000066600000021733152503076610010607 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2014 * * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ if (!defined('_ECRIRE_INC_VERSION')) return; include_spip('inc/filtres'); // http://code.spip.net/@calendrier_categories function calendrier_categories($table, $num, $objet) { if (function_exists('generer_calendrier_class')) return generer_calendrier_class($table, $num, $objet); else { // cf agenda.css $num= sql_getfetsel((($objet != 'id_breve') ? 'id_secteur' : 'id_rubrique') . " AS id", $table, "$objet=$num"); return 'calendrier-couleur' . (($num%14)+1); } } // ici on prend en fait le jour, la veille et le lendemain // http://code.spip.net/@quete_calendrier_jour function quete_calendrier_jour($annee,$mois,$jour) { $avant = "'" . date("Y-m-d", mktime(0,0,0,$mois,$jour-1,$annee)) . "'"; $apres = "'" . date("Y-m-d", mktime(1,1,1,$mois,$jour+1,$annee)) . " 23:59:59'"; return array($avant, $apres); } // retourne un tableau de 2 tableaux indexes par des dates // - le premier indique les evenements du jour, sans indication de duree // - le deuxime indique les evenements commencant ce jour, avec indication de duree // http://code.spip.net/@quete_calendrier_interval function quete_calendrier_interval($limites) { include_spip('inc/urls'); list($avant, $apres) = $limites; $evt = array(); quete_calendrier_interval_articles($avant, $apres, $evt); quete_calendrier_interval_breves($avant, $apres, $evt); quete_calendrier_interval_rubriques($avant, $apres, $evt); return array($evt, quete_calendrier_interval_rv($avant, $apres)); } // http://code.spip.net/@quete_calendrier_interval_forums function quete_calendrier_interval_forums($limites, &$evenements) { list($avant, $apres) = $limites; $result=sql_select("DISTINCT titre, date_heure, id_forum", "spip_forum", "date_heure >= $avant AND date_heure < $apres", '', "date_heure"); while($row=sql_fetch($result)){ $amj = date_anneemoisjour($row['date_heure']); $id = $row['id_forum']; if (autoriser('voir','forum',$id)) $evenements[$amj][]= array( 'URL' => generer_url_entite($id, 'forum'), 'CATEGORIES' => 'calendrier-couleur7', 'SUMMARY' => $row['titre'], 'DTSTART' => date_ical($row['date_heure'])); } } # 3 fonctions retournant les evenements d'une periode # le tableau retourne est indexe par les balises du format ics # afin qu'il soit facile de produire de tels documents. # L'URL de chacun de ces evenements est celle de l'espace prive # pour faciliter la navigation, ce qu'on obtient utilisant # le 4e argument des fonctions generer_url_ecrire_$table // http://code.spip.net/@quete_calendrier_interval_articles function quete_calendrier_interval_articles($avant, $apres, &$evenements) { $result=sql_select('id_article, titre, date, descriptif, chapo, lang', 'spip_articles', "statut='publie' AND date >= $avant AND date < $apres", '', "date"); // tables traduites $objets = explode(',', $GLOBALS['meta']['multi_objets']); if (in_array('spip_articles', $objets)) { include_spip('inc/lang_liste'); $langues = $GLOBALS['codes_langues']; } else $langues = array(); while($row=sql_fetch($result)){ $amj = date_anneemoisjour($row['date']); $id = $row['id_article']; if (autoriser('voir','article',$id)) $evenements[$amj][]= array( 'CATEGORIES' => calendrier_categories('spip_articles', $id, 'id_article'), 'DESCRIPTION' => $row['descriptif'] ? $row['descriptif'] : $langues[$row['lang']], 'SUMMARY' => $row['titre'], 'URL' => generer_url_ecrire_objet('article',$id, '','','prop')); } } // http://code.spip.net/@quete_calendrier_interval_rubriques function quete_calendrier_interval_rubriques($avant, $apres, &$evenements) { $result=sql_select('DISTINCT R.id_rubrique, titre, descriptif, date', 'spip_rubriques AS R, spip_documents_liens AS L', "statut='publie' AND date >= $avant AND date < $apres AND R.id_rubrique = L.id_objet AND L.objet='rubrique'",'', "date"); while($row=sql_fetch($result)){ $amj = date_anneemoisjour($row['date']); $id = $row['id_rubrique']; if (autoriser('voir','rubrique',$id)) $evenements[$amj][]= array( 'CATEGORIES' => calendrier_categories('spip_rubriques', $id, 'id_rubrique'), 'DESCRIPTION' => $row['descriptif'], 'SUMMARY' => $row['titre'], 'URL' => generer_url_ecrire_objet('rubrique',$id, '','', 'prop')); } } // http://code.spip.net/@quete_calendrier_interval_breves function quete_calendrier_interval_breves($avant, $apres, &$evenements) { $result=sql_select("id_breve, titre, date_heure, id_rubrique", 'spip_breves', "statut='publie' AND date_heure >= $avant AND date_heure < $apres", '', "date_heure"); while($row=sql_fetch($result)){ $amj = date_anneemoisjour($row['date_heure']); $id = $row['id_breve']; $ir = $row['id_rubrique']; if (autoriser('voir','breve',$id)) $evenements[$amj][]= array( 'URL' => generer_url_ecrire_objet('breve',$id, '','', 'prop'), 'CATEGORIES' => calendrier_categories('spip_breves', $ir, 'id_breve'), 'SUMMARY' => $row['titre']); } } // http://code.spip.net/@quete_calendrier_interval_rv function quete_calendrier_interval_rv($avant, $apres) { include_spip('inc/session'); $connect_id_auteur = session_get('id_auteur'); $evenements= array(); if (!$connect_id_auteur) return $evenements; $result=sql_select("M.id_message, M.titre, M.texte, M.date_heure, M.date_fin, M.type", "spip_messages AS M LEFT JOIN spip_auteurs_liens AS L ON (L.id_objet=M.id_message)", "((L.objet='message' AND (L.id_auteur=$connect_id_auteur OR M.type='affich')) OR (L.objet IS NULL AND M.id_auteur=$connect_id_auteur AND ".sql_in('M.type',array('pb','affich'))."))" ." AND M.rv='oui' AND ((M.date_fin >= $avant OR M.date_heure >= $avant) AND M.date_heure <= $apres) AND M.statut='publie'", "M.id_message", "M.date_heure"); while($row=sql_fetch($result)){ $date_heure=$row["date_heure"]; $date_fin=$row["date_fin"]; $type=$row["type"]; $id_message=$row['id_message']; if ($type=="pb") $cat = 'calendrier-couleur2'; else { if ($type=="affich") $cat = 'calendrier-couleur4'; else { if ($type!="normal") $cat = 'calendrier-couleur12'; else { $cat = 'calendrier-couleur9'; $auteurs = array_map('array_shift', sql_allfetsel("nom", "spip_auteurs AS A LEFT JOIN spip_auteurs_liens AS L ON L.id_auteur=A.id_auteur", "(L.objet='message' AND L.id_objet=$id_message AND (A.id_auteur!=$connect_id_auteur))")); } } } $jour_avant = substr($avant, 9,2); $mois_avant = substr($avant, 6,2); $annee_avant = substr($avant, 1,4); $jour_apres = substr($apres, 9,2); $mois_apres = substr($apres, 6,2); $annee_apres = substr($apres, 1,4); $ical_apres = date_anneemoisjour("$annee_apres-$mois_apres-".sprintf("%02d",$jour_apres)); // Calcul pour les semaines a cheval sur deux mois $j = 0; $amj = date_anneemoisjour("$annee_avant-$mois_avant-".sprintf("%02d", $j+($jour_avant))); while ($amj <= $ical_apres) { if (!($amj == date_anneemoisjour($date_fin) AND preg_match(",00:00:00,", $date_fin))) // Ne pas prendre la fin a minuit sur jour precedent $evenements[$amj][$id_message]= array( 'URL' => generer_url_ecrire("message","id_message=$id_message"), 'DTSTART' => date_ical($date_heure), 'DTEND' => date_ical($date_fin), 'DESCRIPTION' => $row['texte'], 'SUMMARY' => $row['titre'], 'CATEGORIES' => $cat, 'ATTENDEE' => (count($auteurs) == 0) ? '' : join($auteurs,", ")); $j ++; $ladate = date("Y-m-d",mktime (1,1,1,$mois_avant, ($j + $jour_avant), $annee_avant)); $amj = date_anneemoisjour($ladate); } } return $evenements; } // http://code.spip.net/@quete_calendrier_agenda function quete_calendrier_agenda ($annee, $mois) { include_spip('inc/session'); $connect_id_auteur = session_get('id_auteur'); $rv = array(); if (!$connect_id_auteur) return $rv; $date = date("Y-m-d", mktime(0,0,0,$mois, 1, $annee)); $mois = mois($date); $annee = annee($date); // rendez-vous personnels dans le mois $result_messages = sql_select("M.titre AS summary, M.texte AS description, M.id_message AS uid, M.date_heure", "spip_messages AS M, spip_auteurs_liens AS L", "((L.id_auteur=$connect_id_auteur AND L.id_objet=M.id_message AND L.objet='message') OR M.type='affich') AND M.rv='oui' AND M.date_heure >='$annee-$mois-1' AND date_heure < DATE_ADD('$annee-$mois-1', INTERVAL 1 MONTH) AND M.statut='publie'"); while($row=sql_fetch($result_messages)) { $rv[journum($row['date_heure'])] = $row; } return $rv; } ?> date_gestion.php000066600000003472152503076610007741 0ustar00<?php /***************************************************************************\ * SPIP, Systeme de publication pour l'internet * * * * Copyright (c) 2001-2014 * * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James * * * * Ce programme est un logiciel libre distribue sous licence GNU/GPL. * * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. * \***************************************************************************/ if (!defined('_ECRIRE_INC_VERSION')) return; /** * Recuperer les champs date_xx et heure_xx, verifier leur coherence et les reformater * * @param string $suffixe * @param bool $horaire * @param array $erreurs * @return int */ function verifier_corriger_date_saisie($suffixe,$horaire,&$erreurs){ include_spip('inc/filtres'); $date = _request("date_$suffixe").($horaire?' '.trim(_request("heure_$suffixe")).':00':''); $date = recup_date($date); if (!$date) return ''; $ret = null; if (!$ret=mktime(0,0,0,$date[1],$date[2],$date[0])) $erreurs["date_$suffixe"] = _T('organiseur:erreur_date'); elseif (!$ret=mktime($date[3],$date[4],$date[5],$date[1],$date[2],$date[0])) $erreurs["date_$suffixe"] = _T('organiseur:erreur_heure'); if ($ret){ if (trim(_request("date_$suffixe")!==($d=date('d/m/Y',$ret)))){ $erreurs["date_$suffixe"] = _T('organiseur:erreur_date_corrigee'); set_request("date_$suffixe",$d); } if ($horaire AND trim(_request("heure_$suffixe")!==($h=date('H:i',$ret)))){ $erreurs["heure_$suffixe"] = _T('organiseur:erreur_heure_corrigee'); set_request("heure_$suffixe",$h); } } return $ret; } ?>
/home/etiennec/www/b9da6/../e6335/../b594c/../plugins/../f1f54/inc.tar