<?php
// script pour PHP4 ou PHP5, on suppose que magic_quotes est on
// encodage : UTF-8 (PHP est incapable de gérer un autre encodage...)

/*
XPAGES pour WebJaxe

Copyright (C) 2007 Observatoire de Paris-Meudon

Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier conformément aux dispositions de la Licence Publique Générale GNU, telle que publiée par la Free Software Foundation ; version 2 de la licence, ou encore (à votre choix) toute version ultérieure.

Ce programme est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE ; sans même la garantie implicite de COMMERCIALISATION ou D'ADAPTATION A UN OBJET PARTICULIER. Pour plus de détail, voir la Licence Publique Générale GNU .

Vous devez avoir reçu un exemplaire de la Licence Publique Générale GNU en même temps que ce programme ; si ce n'est pas le cas, écrivez à la Free Software Foundation Inc., 675 Mass Ave, Cambridge, MA 02139, Etats-Unis.
*/


// lance une erreur avec un message d'erreur
function erreur_xpages($message) {
    trigger_error($message, E_USER_ERROR);
}


// Transformation XSLT
// Paramètres:
//     nom_fichier_entree: string
//     nom_fichier_xslt: string
//     nom_fichier_sortie: string
//     parametres: array (paramètres passés à la feuille de style)
function transfo_XSLT($nom_fichier_entree, $nom_fichier_xslt, $nom_fichier_sortie, $parametres) {
    if (version_compare(PHP_VERSION, '5') >= 0) {
        if (version_compare(PHP_VERSION, '5.1') && function_exists('libxml_use_internal_errors'))
            libxml_use_internal_errors(true);
        $xsltproc = new XSLTProcessor();
        
        // cf PHP Bug #54446 et #61233
        if (version_compare(PHP_VERSION, '5.4', '<')) {
            $oldval = ini_get('xsl.security_prefs');
            if ($oldval != '0') {
                $oldval = ini_set('xsl.security_prefs', '0');
                if ($oldval === false)
                    erreur_xpages("Impossible de changer xsl.security_prefs avec ini_set: ajoutez xsl.security_prefs=0 dans le php.ini");
            }
        } else
            $oldval = $xsltproc->setSecurityPrefs(0);
        
        $domdoc_xslt = new DOMDocument();
        $domdoc_xslt->load($nom_fichier_xslt);
        $xsltproc->importStyleSheet($domdoc_xslt);
        if (version_compare(PHP_VERSION, '5.1') && function_exists('libxml_get_last_error')) {
            $lxerr = libxml_get_last_error();
            if ($lxerr != false) {
                $details = ': '.$lxerr->message.' ligne '.$lxerr->line;
                libxml_clear_errors();
                erreur_xpages('Erreur dans la transformation XSLT (importStyleSheet '.$nom_fichier_xslt.')'. $details);
            }
        }
        if ($parametres) {
            foreach ($parametres as $param => $value) {
                $xsltproc->setParameter('', $param, $value);
            }
        }
        
        $domdoc_entree = new DOMDocument();
        $domdoc_entree->load($nom_fichier_entree);
        $result = $xsltproc->transformToURI($domdoc_entree, $nom_fichier_sortie);
        
        if ($oldval != 0 && $oldval != '0') {
            if (version_compare(PHP_VERSION, '5.4', '<'))
                ini_set('xsl.security_prefs', $oldval);
            else
                $xsltproc->setSecurityPrefs($oldval);
        }
        
        if ($result > 0)
            return($result);
        else {
            $details = '';
            if (version_compare(PHP_VERSION, '5.1') && function_exists('libxml_get_errors')) {
                // remarque: pour debugger il peut être utile de désactiver tous les error handlers
                $erreurs = libxml_get_errors();
                if (count($erreurs) > 0) {
                    $details = ': ';
                    foreach ($erreurs as $lxerr)
                        $details .= $lxerr->message.' ligne '.$lxerr->line.'; ';
                }
                libxml_clear_errors();
            }
            erreur_xpages('Erreur dans la transformation XSLT (transformToURI '.$nom_fichier_xslt.')'. $details);
        }
        
    } else {
        $xsltproc = xslt_create(); // nécessite l'extension XSLT avec Sablotron et iconv
        //$result = xslt_process($xsltproc, $nom_fichier_entree, $nom_fichier_xslt, $nom_fichier_sortie, NULL, $parametres);
        // sur Windows la ligne précédente donne : XML parser error 4: not well-formed
        // -> on lit le XML indépendamment
        xslt_set_encoding($xsltproc, 'ISO-8859-1');
        // pb : parfois, xslt_set_encoding est utilisé non seulement en entrée mais aussi en sortie,
        // même si on spécifie un autre encodage dans le XSLT
        $xsl_string = join('', file($nom_fichier_xslt));
        $xml_string = join('', file($nom_fichier_entree));
        $arg_buffer = array('/xml' => $xml_string, '/xsl' => $xsl_string);
        $result = xslt_process($xsltproc, 'arg:/xml', 'arg:/xsl', NULL, $arg_buffer, $parametres);
        if ($result) {
            xslt_free($xsltproc);
            $fp = fopen($nom_fichier_sortie, "w+");
            fwrite($fp, $result);
            fclose($fp);
            return($result);
        } else {
            $errstr = xslt_error($xsltproc);
            $errno = xslt_errno($xsltproc);
            xslt_free($xsltproc);
            erreur_xpages("Erreur dans la transformation XSLT: ".$errstr." code d'erreur: ".$errno);
        }
    }
}


// Copie d'un dossier source vers un dossier destination (attention, le dossier source
//     est copié *vers* le dossier destination, pas *à l'intérieur*)
function copier_dossier($source, $dest) {
    // Simple copy for a file
    if (is_file($source)) {
        $c = copy($source, $dest);
        //chmod($dest, 0777); // est-ce que ça va planter chez free ?
        return $c;
    }
     
    // Make destination directory
    //if (!is_dir($dest)) {
    //    $oldumask = umask(0);
    //    mkdir($dest, 0777);
    //    umask($oldumask); // est-ce que ça va planter chez free ?
    //}
    if (!is_dir($dest))
        mkdir($dest);
     
    // Loop through the folder
    $dir = dir($source);
    while (false !== $entry = $dir->read()) {
        // Skip pointers
        if ($entry == '.' || $entry == '..')
            continue;
         
        // Deep copy directories
        if ($dest !== "$source/$entry")
            copier_dossier("$source/$entry", "$dest/$entry");
    }
     
    // Clean up
    $dir->close();
    return true;
}


// Lecture d'une image dans un format donné
// types des paramètres:
//     chemin_image: string
//     format: int (constante IMAGETYPE_XXX)
// Renvoit l'image (de type resource), ou false en cas de problème
function lire_image($chemin_image, $format) {
    if ($format == IMAGETYPE_PNG)
        $img = imagecreatefrompng($chemin_image); // nécessite l'extension GD
    else if ($format == IMAGETYPE_JPEG)
        $img = imagecreatefromjpeg($chemin_image);
    else if ($format == IMAGETYPE_GIF)
        $img = imagecreatefromgif($chemin_image);
    else
        return(false);
    return($img);
}


// renvoie true si le fichier GIF est animé
function est_anime($chemin_image) {
    if (!($fh = @fopen($chemin_image, 'rb')))
        return(false);
    $count = 0;
    while (!feof($fh) && $count < 2) {
        $chunk = fread($fh, 1024 * 100);
        $count += preg_match_all('#\x00\x21\xF9\x04.{4}\x00\x2C#s', $chunk, $matches);
    }
    fclose($fh);
    return($count > 1);
}


// Redimensionnement d'une image pour qu'elle rentre dans un rectangle de taille largeur_max x hauteur_max
// Renvoit l'image (de type resource), ou false en cas de problème
function redimensionner($img, $largeur_max, $hauteur_max) {
    $largeur1 = imagesx($img);
    $hauteur1 = imagesy($img);
    if ($largeur1 / $largeur_max > $hauteur1 / $hauteur_max)
        $ratio = $largeur_max / $largeur1;
    else
        $ratio = $hauteur_max / $hauteur1;
    $largeur2 = round($largeur1 * $ratio);
    $hauteur2 = round($hauteur1 * $ratio);
    
    $img2 = imagecreatetruecolor($largeur2, $hauteur2);
    if (! $img2)
        return(false);
    
    // pour les GIFs, recopie de la couleur transparente (sinon ça fait un fond noir)
    $itrans = imagecolortransparent($img);
    if ($itrans >= 0 && $itrans < imagecolorstotal($img)) {
        $trans = imagecolorsforindex($img, $itrans);
        $trans2 = imagecolorallocate($img2, $trans['red'], $trans['green'], $trans['blue']);
        imagefill($img2, 0, 0, $trans2);
        imagecolortransparent($img2, $trans2);
    } else
        imagealphablending($img2, false); // pour les PNG, on essaye de préserver la couche alpha
    
    if (!imagecopyresampled($img2, $img, 0, 0, 0, 0, $largeur2, $hauteur2, $largeur1, $hauteur1))
        return(false);
    return $img2;
}


// Enregistrement d'une image dans un format particulier
// types des paramètres:
//     img: resource
//     chemin_image: string
//     format: int (constante IMAGETYPE_XXX)
// renvoit true (si ça c'est bien passé) ou false
function enregistrer_image($img, $chemin_image, $format) {
    mkdir_recursif(dirname($chemin_image));
    if ($format == IMAGETYPE_PNG) {
        imagesavealpha($img, true);
        return(imagepng($img, $chemin_image));
    } else if ($format == IMAGETYPE_JPEG)
        return(imagejpeg($img, $chemin_image));
    else if ($format == IMAGETYPE_GIF)
        return(imagegif($img, $chemin_image));
    else
        return(false);
}


// Renvoit les dimensions d'un fichier MPEG dans un tableau
//     [0] : largeur
//     [1] : hauteur
// ou false si ça n'a pas marché
function dimensions_mpeg($f) {
    $signature = 0x000001B3;
    $handle = fopen($f, "rb");
    if (!$handle)
        erreur_xpages("Erreur à l'ouverture du fichier ".$f);
    $s = fread($handle, 4);
    $b1 = ord($s{0});
    $b2 = ord($s{1});
    $b3 = ord($s{2});
    $b4 = ord($s{3});
    $v = (($b1 & 0xFF) << 24) | (($b2 & 0xFF) << 16) | (($b3 & 0xFF) << 8) | ($b4 & 0xFF);
   while ($v != $signature && !feof($handle)) {
        $s = fread($handle, 1);
        $bi = ord($s{0});
        $b1 = $b2;
        $b2 = $b3;
        $b3 = $b4;
        $b4 = $bi;
        $v = (($b1 & 0xFF) << 24) | (($b2 & 0xFF) << 16) | (($b3 & 0xFF) << 8) | ($b4 & 0xFF);
    }
    if ($v == $signature) {
        $s = fread($handle, 3);
        $b1 = ord($s{0});
        $b2 = ord($s{1});
        $b3 = ord($s{2});
        $largeur = (($b1 & 0xFF) << 4) | (($b2 & 0xF0) >> 4);
        $hauteur = (($b2 & 0x0F) << 8) | ($b3 & 0xFF);
    }
    fclose($handle);
    if ($v != $signature)
        return(false);
    return(array($largeur, $hauteur));
}

// Renvoit les dimensions d'un fichier MP4 dans un tableau
//     [0] : largeur
//     [1] : hauteur
// ou false si ça n'a pas marché
function dimensions_mp4($f) {
    $handle = fopen($f, 'rb');
    if (!$handle)
        erreur_xpages("Erreur à l'ouverture du fichier ".$f);
    $size = findBox($handle, 'moov');
    if ($size == false) {
        erreur_xpages('Erreur: Dimensions non trouvées pour '.$f);
        fclose($handle);
        return(false);
    }
    $largeur = 0;
    $hauteur = 0;
    while (!feof($handle) && ($largeur == 0 || $hauteur == 0)) {
        $size = findBox($handle, 'trak');
        if ($size != false)
            $size = findBox($handle, 'tkhd');
        if ($size == false) {
            erreur_xpages('Erreur: Dimensions non trouvées pour '.$f);
            fclose($handle);
            return(false);
        }
        $s = fread($handle, 1);
        $v = ord($s{0});
        if ($v == 0)
            fseek($handle, 75, SEEK_CUR);
        else
            fseek($handle, 87, SEEK_CUR);
        $s = fread($handle, 4);
        // SI32 ou 16.16 ?
        $largeur = ((ord($s{0}) & 0xFF) << 8) | (ord($s{1}) & 0xFF);
        $s = fread($handle, 4);
        $hauteur = ((ord($s{0}) & 0xFF) << 8) | (ord($s{1}) & 0xFF);
    }
    fclose($handle);
    if ($largeur == 0 || $hauteur == 0)
        return(false);
    else
        return(array($largeur, $hauteur));
}
function findBox($handle, $t) {
    while (!feof($handle)) {
        $s = fread($handle, 4);
        $size = ((ord($s{0}) & 0xFF) << 24) | ((ord($s{1}) & 0xFF) << 16) | ((ord($s{2}) & 0xFF) << 8) | (ord($s{3}) & 0xFF);
        // (on ne gère pas le cas où size=1 et qu'il faut la lire en 64bits)
        if ($size < 2)
            return(false);
        $type = fread($handle, 4);
        if ($t == $type)
            return($size);
        else
            fseek($handle, $size - 8, SEEK_CUR);
    }
    return(false);
}

// Renvoit les dimensions d'un fichier OGG Theora dans un tableau
//     [0] : largeur
//     [1] : hauteur
// ou false si ça n'a pas marché
function dimensions_ogg($f) {
    $signature = 0x4F676753; // OggS
    $handle = fopen($f, "rb");
    if (!$handle)
        erreur_xpages("Erreur à l'ouverture du fichier ".$f);
    $s = fread($handle, 4);
    $b1 = ord($s{0});
    $b2 = ord($s{1});
    $b3 = ord($s{2});
    $b4 = ord($s{3});
    $bi = 0;
    $v = (($b1 & 0xFF) << 24) | (($b2 & 0xFF) << 16) | (($b3 & 0xFF) << 8) | ($b4 & 0xFF);
    if ($v != $signature) {
        fclose($handle);
        return(false);
    }
    // la signature 0x80746865 (identification header) dépasse un entier avec PHP, et il n'existe pas d'entier long...
    while (!($b1 == 0x80 && $b2 == 0x74 && $b3 == 0x68 && $b4 == 0x65) && !feof($handle)) {
        $s = fread($handle, 1);
        $bi = ord($s{0});
        $b1 = $b2;
        $b2 = $b3;
        $b3 = $b4;
        $b4 = $bi;
    }
    if ($v != $signature) {
        fclose($handle);
        return(false);
    }
    $s = fread($handle, 10); // on saute 10 octets après 'the'
    $s = fread($handle, 3);
    $b1 = ord($s{0});
    $b2 = ord($s{1});
    $b3 = ord($s{2});
    $largeur = (($b1 & 0xFF) << 16) | (($b2 & 0xFF) << 8) | ($b3 & 0xFF); // PICW
    $s = fread($handle, 3);
    $b1 = ord($s{0});
    $b2 = ord($s{1});
    $b3 = ord($s{2});
    $hauteur = (($b1 & 0xFF) << 16) | (($b2 & 0xFF) << 8) | ($b3 & 0xFF); // PICH
    fclose($handle);
    return(array($largeur, $hauteur));
}

// Renvoit les dimensions d'un fichier WebM dans un tableau
//     [0] : largeur
//     [1] : hauteur
// ou false si ça n'a pas marché
function dimensions_webm($f) {
    $handle = fopen($f, "rb");
    if (!$handle)
        erreur_xpages("Erreur à l'ouverture du fichier ".$f);
    // chemin pour PixelWidth : Segment > Tracks > TrackEntry > Video > PixelWidth
    $chemin = array_reverse(array(0x18538067, 0x1654AE6B, 0xAE, 0xE0, 0xB0));
    $data = rechercherElement($handle, $chemin);
    if ($data === false) {
        fclose($handle);
        return(false);
    }
    if (strlen($data) == 1)
        $largeur = ord($data{0}) & 0xFF;
    else if (strlen($data) == 2)
        $largeur = ((ord($data{0}) & 0xFF) << 8) | (ord($data{1}) & 0xFF);
    else
        erreur_xpages("Erreur à l'obtention des dimensions de la vidéo ".$f);
    // on suppose que la hauteur est après la largeur
    $chemin = array(0xBA);
    $data = rechercherElement($handle, $chemin);
    if ($data === false) {
        fclose($handle);
        return(false);
    }
    if (strlen($data) == 1)
        $hauteur = ord($data{0}) & 0xFF;
    else if (strlen($data) == 2)
        $hauteur = ((ord($data{0}) & 0xFF) << 8) | (ord($data{1}) & 0xFF);
    else
        erreur_xpages("Erreur à l'obtention des dimensions de la vidéo ".$f);
    fclose($handle);
    return(array($largeur, $hauteur));
}

function rechercherElement($handle, $chemin) {
    $size = 0;
    while (count($chemin) > 0) {
        $dernier = end($chemin);
        $id = lireID($handle);
        if ($id === false)
            return(false);
        $size = lireSize($handle);
        if ($size === false)
            return(false);
        if ($dernier == $id)
            array_pop($chemin);
        else
            fseek($handle, $size, SEEK_CUR);
    }
    $data = fread($handle, $size);
    return($data);
}

function lireID($handle) {
    $s = fread($handle, 1);
    if ($s === false || $s === '')
        return(false);
    $octet = ord($s{0});
    $pos = 7;
    $bit = ($octet >> $pos) & 0x7F;
    while ($bit == 0 && $pos > 4) {
        $pos--;
        $bit = ($octet >> $pos) & 0x7F;
    }
    $nb = 8 - $pos; // nb d'octets total de l'ID
    $id = $octet;
    for ($i=0; $i<$nb-1; $i++) {
        $s = fread($handle, 1);
        if ($s === false || $s === '')
            return(false);
        $octet = ord($s{0});
        $id = ($id << 8) | ($octet & 0xFF);
    }
    return($id);
}

function lireSize($handle) {
    $s = fread($handle, 1);
    if ($s === false || $s === '')
        return(false);
    $octet = ord($s{0});
    $pos = 7;
    $bit = ($octet >> $pos) & 0x7F;
    while ($bit == 0 && $pos > 0) {
        $pos--;
        $bit = ($octet >> $pos) & 0x7F;
    }
    $reste = $octet & ((0xFF >> (8 - $pos)) & 0x7F);
    $nb = 8 - $pos;
    $size = $reste;
    for ($i=0; $i<$nb-1; $i++) {
        $s = fread($handle, 1);
        if ($s === false || $s === '')
            return(false);
        $octet = ord($s{0});
        $size = ($size << 8) | ($octet & 0xFF);
    }
    return($size);
}


// Création d'un répertoire, et de tous les répertoires parents si nécessaire
// renvoit true (si ça c'est bien passé) ou false
function mkdir_recursif($dossier) {
    if (strrpos($dossier, DIRECTORY_SEPARATOR) == 0) // racine ?
        return(true);
    if (is_dir($dossier))
        return(true);
    if (mkdir_recursif(dirname($dossier)))
        return(mkdir($dossier));
    else
        return(false);
}

?>
