<?php
/**
 * @version $Id$
 * @author Thomas Crespin <thomas.crespin@sesamath.net>
 * @copyright Thomas Crespin 2009-2022
 * 
 * ****************************************************************************************************
 * SACoche <https://sacoche.sesamath.net> - Suivi d’Acquisitions de Compétences
 * © Thomas Crespin pour Sésamath <https://www.sesamath.net> - Tous droits réservés.
 * Logiciel placé sous la licence libre Affero GPL 3 <https://www.gnu.org/licenses/agpl-3.0.html>.
 * ****************************************************************************************************
 * 
 * Ce fichier est une partie de SACoche.
 * 
 * SACoche est un logiciel libre ; vous pouvez le redistribuer ou le modifier suivant les termes 
 * de la “GNU Affero General Public License” telle que publiée par la Free Software Foundation :
 * soit la version 3 de cette licence, soit (à votre gré) toute version ultérieure.
 * 
 * SACoche est distribué dans l’espoir qu’il vous sera utile, mais SANS AUCUNE GARANTIE :
 * sans même la garantie implicite de COMMERCIALISABILITÉ ni d’ADÉQUATION À UN OBJECTIF PARTICULIER.
 * Consultez la Licence Publique Générale GNU Affero pour plus de détails.
 * 
 * Vous devriez avoir reçu une copie de la Licence Publique Générale GNU Affero avec SACoche ;
 * si ce n’est pas le cas, consultez : <http://www.gnu.org/licenses/>.
 * 
 */

// Liens de téléchargement.

define( 'FIREFOX_URL_DOWNLOAD', 'https://www.mozilla.org/fr/firefox/');
define(  'CHROME_URL_DOWNLOAD', 'https://www.google.fr/chrome');
define(   'OPERA_URL_DOWNLOAD', 'http://www.opera.com/fr/download');
define(    'EDGE_URL_DOWNLOAD', 'https://www.microsoft.com/fr-fr/edge');
define(  'SAFARI_URL_DOWNLOAD', 'http://www.apple.com/fr/safari/'); // plus téléchargeable ? Pour Windows, devel stoppé version 5.1.7.
define('EXPLORER_URL_DOWNLOAD', 'http://windows.microsoft.com/fr-fr/internet-explorer/download-ie');

// Versions des navigateurs.
// https://caniuse.com/#search=use%20strict
// https://caniuse.com/#search=transform
// https://caniuse.com/#search=writing-mode
// https://caniuse.com/#search=grid
// https://caniuse.com/#search=grid-template-columns
// https://caniuse.com/#feat=stream

define( 'FIREFOX_VERSION_MINI_REQUISE'   , 4); define( 'FIREFOX_DATE_MINI_REQUISE'   , '(mars 2011)');
define( 'FIREFOX_VERSION_MINI_CONSEILLEE',52); define( 'FIREFOX_DATE_MINI_CONSEILLEE', '(mars 2017)');

define(  'CHROME_VERSION_MINI_REQUISE'   ,13); define(  'CHROME_DATE_MINI_REQUISE'   , '(août 2011)');
define(  'CHROME_VERSION_MINI_CONSEILLEE',57); define(  'CHROME_DATE_MINI_CONSEILLEE', '(mars 2017)');

define(   'OPERA_VERSION_MINI_REQUISE'   ,15); define(   'OPERA_DATE_MINI_REQUISE'   , '(juillet 2013)');
define(   'OPERA_VERSION_MINI_CONSEILLEE',44); define(   'OPERA_DATE_MINI_CONSEILLEE', '(mars 2017)');

define(    'EDGE_VERSION_MINI_REQUISE'   ,12); define(    'EDGE_DATE_MINI_REQUISE'   , '(juillet 2015)');
define(    'EDGE_VERSION_MINI_CONSEILLEE',16); define(    'EDGE_DATE_MINI_CONSEILLEE', '(octobre 2017)');

define(  'SAFARI_VERSION_MINI_REQUISE'   , 6); define(  'SAFARI_DATE_MINI_REQUISE'   , '(juillet 2012)');
define(  'SAFARI_VERSION_MINI_CONSEILLEE',11); define(  'SAFARI_DATE_MINI_CONSEILLEE', '(septembre 2017)');

define('EXPLORER_VERSION_MINI_REQUISE'   ,10  ); define('EXPLORER_DATE_MINI_REQUISE'   , '(septembre 2012)');
define('EXPLORER_VERSION_MINI_CONSEILLEE',NULL); define('EXPLORER_DATE_MINI_CONSEILLEE', NULL);

class Browser
{

  public static $tab_navigo = array(
    'firefox'  => 'Firefox' ,
    'chrome'   => 'Chrome' ,
    'opera'    => 'Opéra' ,
    'edge'     => 'Edge' ,
    'safari'   => 'Safari' ,
    'explorer' => 'Internet Explorer'
  );

  // //////////////////////////////////////////////////
  // Méthode privée (interne)
  // //////////////////////////////////////////////////

  /**
   * Parses a user agent string into its important parts
   * @author Jesse G. Donat <donatj@gmail.com>
   * @link https://github.com/donatj/PhpUserAgent
   * @link http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT
   * @param string|null $u_agent User agent string to parse or null. Uses $_SERVER['HTTP_USER_AGENT'] on NULL
   * @throws \InvalidArgumentException on not having a proper user agent to parse.
   * @return string[] an array with browser, version and platform keys
   * 
   * AJOUT DE COMMENTAIRES PERSOS
   * 
   * Cette fonction a l’avantage d'être légère et mise à jour.
   * Version 1.7.0 récupérée en mars 2022.
   * Réécriture du 1er test / retrait de "throw new \InvalidArgumentException('parse_user_agent requires a user agent')" qui sinon déclenche une sortie sur une erreur fatale.
   * Remplacement des définitions simplifiées de tableaux [] par array() pour compatibilité PHP 5.3.
   * Le tableau retourné est de la forme array( [platform] => ... [browser] => ... [version] => ... )
   */
  private static function parse_user_agent( $u_agent = null ) {
    $platform = null;
    $browser  = null;
    $version  = null;
    $return = array( 'platform' => &$platform, 'browser' => &$browser, 'version' => &$version );
    if( empty($u_agent) ) {
      if( isset($_SERVER['HTTP_USER_AGENT']) ) {
        $u_agent = $_SERVER['HTTP_USER_AGENT'];
      } else {
        return $return;
      }
    }
    if( preg_match('/\((.*?)\)/m', $u_agent, $parent_matches) ) {
      preg_match_all(<<<'REGEX'
/(?P<platform>BB\d+;|Android|Adr|Symbian|Sailfish|CrOS|Tizen|iPhone|iPad|iPod|Linux|(?:Open|Net|Free)BSD|Macintosh|
Windows(?:\ Phone)?|Silk|linux-gnu|BlackBerry|PlayBook|X11|(?:New\ )?Nintendo\ (?:WiiU?|3?DS|Switch)|Xbox(?:\ One)?)
(?:\ [^;]*)?
(?:;|$)/imx
REGEX
        , $parent_matches[1], $result);
      $priority = array( 'Xbox One', 'Xbox', 'Windows Phone', 'Tizen', 'Android', 'FreeBSD', 'NetBSD', 'OpenBSD', 'CrOS', 'X11', 'Sailfish' );
      $result['platform'] = array_unique($result['platform']);
      if( count($result['platform']) > 1 ) {
        if( $keys = array_intersect($priority, $result['platform']) ) {
          $platform = reset($keys);
        } else {
          $platform = $result['platform'][0];
        }
      } elseif( isset($result['platform'][0]) ) {
        $platform = $result['platform'][0];
      }
    }
    if( $platform == 'linux-gnu' || $platform == 'X11' ) {
      $platform = 'Linux';
    } elseif( $platform == 'CrOS' ) {
      $platform = 'Chrome OS';
    } elseif( $platform == 'Adr' ) {
      $platform = 'Android';
    } elseif( $platform === null ) {
      if(preg_match_all('%(?P<platform>Android)[:/ ]%ix', $u_agent, $result)) {
        $platform = $result['platform'][0];
      }
    }
    preg_match_all(<<<'REGEX'
%(?P<browser>Camino|Kindle(\ Fire)?|Firefox|Iceweasel|IceCat|Safari|MSIE|Trident|AppleWebKit|
TizenBrowser|(?:Headless)?Chrome|YaBrowser|Vivaldi|IEMobile|Opera|OPR|Silk|Midori|(?-i:Edge)|EdgA?|CriOS|UCBrowser|Puffin|
OculusBrowser|SamsungBrowser|SailfishBrowser|XiaoMi/MiuiBrowser|
Baiduspider|Applebot|Facebot|Googlebot|YandexBot|bingbot|Lynx|Version|Wget|curl|
Valve\ Steam\ Tenfoot|
NintendoBrowser|PLAYSTATION\ (?:\d|Vita)+)
\)?;?
(?:[:/ ](?P<version>[0-9A-Z.]+)|/[A-Z]*)%ix
REGEX
      , $u_agent, $result);
    // If nothing matched, return null (to avoid undefined index errors)
    if( !isset($result['browser'][0], $result['version'][0]) ) {
      if( preg_match('%^(?!Mozilla)(?P<browser>[A-Z0-9\-]+)(/(?P<version>[0-9A-Z.]+))?%ix', $u_agent, $result) ) {
        return array(
          'platform' => $platform ? $platform : null,
          'browser' => $result['browser'],
          'version' => empty($result['version']) ? null : $result['version'],
        );
      }

      return $return;
    }
    if( preg_match('/rv:(?P<version>[0-9A-Z.]+)/i', $u_agent, $rv_result) ) {
      $rv_result = $rv_result['version'];
    }
    $browser = $result['browser'][0];
    $version = $result['version'][0];
    $lowerBrowser = array_map('strtolower', $result['browser']);
    $find = function ( $search, &$key = null, &$value = null ) use ( $lowerBrowser ) {
      $search = (array)$search;
      foreach( $search as $val ) {
        $xkey = array_search(strtolower($val), $lowerBrowser);
        if( $xkey !== false ) {
          $value = $val;
          $key   = $xkey;
          return true;
        }
      }
      return false;
    };
    $findT = function ( array $search, &$key = null, &$value = null ) use ( $find ) {
      $value2 = null;
      if( $find(array_keys($search), $key, $value2) ) {
        $value = $search[$value2];
        return true;
      }
      return false;
    };
    $key = 0;
    $val = '';
    if( $findT( array( 'OPR' => 'Opera', 'Facebot' => 'iMessageBot', 'UCBrowser' => 'UC Browser', 'YaBrowser' => 'Yandex', 'Iceweasel' => 'Firefox', 'Icecat' => 'Firefox', 'CriOS' => 'Chrome', 'Edg' => 'Edge', 'EdgA' => 'Edge', 'XiaoMi/MiuiBrowser' => 'MiuiBrowser' ), $key, $browser) ) {
      $version = is_numeric(substr($result['version'][$key], 0, 1)) ? $result['version'][$key] : null;
    } elseif( $find('Playstation Vita', $key, $platform) ) {
      $platform = 'PlayStation Vita';
      $browser  = 'Browser';
    } elseif( $find( array( 'Kindle Fire', 'Silk' ), $key, $val) ) {
      $browser  = $val == 'Silk' ? 'Silk' : 'Kindle';
      $platform = 'Kindle Fire';
      if( !($version = $result['version'][$key]) || !is_numeric($version[0]) ) {
        $version = $result['version'][array_search('Version', $result['browser'])];
      }
    } elseif( $find('NintendoBrowser', $key) || $platform == 'Nintendo 3DS' ) {
      $browser = 'NintendoBrowser';
      $version = $result['version'][$key];
    } elseif( $find('Kindle', $key, $platform) ) {
      $browser = $result['browser'][$key];
      $version = $result['version'][$key];
    } elseif( $find('Opera', $key, $browser) ) {
      $find('Version', $key);
      $version = $result['version'][$key];
    } elseif( $find('Puffin', $key, $browser) ) {
      $version = $result['version'][$key];
      if( strlen($version) > 3 ) {
        $part = substr($version, -2);
        if( ctype_upper($part) ) {
          $version = substr($version, 0, -2);
          $flags = array( 'IP' => 'iPhone', 'IT' => 'iPad', 'AP' => 'Android', 'AT' => 'Android', 'WP' => 'Windows Phone', 'WT' => 'Windows' );
          if( isset($flags[$part]) ) {
            $platform = $flags[$part];
          }
        }
      }
    } elseif( $find( array( 'Applebot', 'IEMobile', 'Edge', 'Midori', 'Vivaldi', 'OculusBrowser', 'SamsungBrowser', 'Valve Steam Tenfoot', 'Chrome', 'HeadlessChrome', 'SailfishBrowser' ), $key, $browser) ) {
      $version = $result['version'][$key];
    } elseif( $rv_result && $find('Trident') ) {
      $browser = 'MSIE';
      $version = $rv_result;
    } elseif( $browser == 'AppleWebKit' ) {
      if( $platform == 'Android' ) {
        $browser = 'Android Browser';
      } elseif( strpos((string)$platform, 'BB') === 0 ) {
        $browser  = 'BlackBerry Browser';
        $platform = 'BlackBerry';
      } elseif( $platform == 'BlackBerry' || $platform == 'PlayBook' ) {
        $browser = 'BlackBerry Browser';
      } else {
        $find('Safari', $key, $browser) || $find('TizenBrowser', $key, $browser);
      }

      $find('Version', $key);
      $version = $result['version'][$key];
    } elseif( $pKey = preg_grep('/playstation \d/i', $result['browser']) ) {
      $pKey = reset($pKey);

      $platform = 'PlayStation ' . preg_replace('/\D/', '', $pKey);
      $browser  = 'NetFront';
    }
    return $return;
  }


  // //////////////////////////////////////////////////
  // Méthode publique
  // //////////////////////////////////////////////////

  /*
   * Méthode pour renvoyer les infos concernant le navigateur utilisé.
   * 
   * @return array   array( 'modele'=>... , 'version'=>... , 'alerte'=>... )
   */
  public static function caracteristiques_navigateur()
  {
    $tab_return = Browser::parse_user_agent(); // array( [platform] => ... [browser] => ... [version] => ... )
    if( in_array( $tab_return['browser'] , array('Firefox') ) )
    {
      $tab_return['modele'] = 'firefox';
    }
    elseif( in_array( $tab_return['browser'] , array('Chrome') ) )
    {
      $tab_return['modele'] = 'chrome';
    }
    elseif( in_array( $tab_return['browser'] , array('Opera','Opera Next') ) )
    {
      $tab_return['modele'] = 'opera';
    }
    elseif( in_array( $tab_return['browser'] , array('Edge') ) )
    {
      $tab_return['modele'] = 'edge';
    }
    elseif( in_array( $tab_return['browser'] , array('Safari') ) )
    {
      $tab_return['modele'] = 'safari';
    }
    elseif( in_array( $tab_return['browser'] , array('MSIE','IEMobile') ) )
    {
      $tab_return['modele'] = 'explorer';
    }
    else
    {
      $tab_return['modele'] = strtolower((string)$tab_return['browser']); // Pas Clean::lower() car appelé depuis ./_js/video.js.php
    }
    $alerte = '';
    foreach(Browser::$tab_navigo as $navigo_ref => $navigo_name)
    {
      if($tab_return['modele']==$navigo_ref)
      {
        $navigo_upper = strtoupper($navigo_ref); // Pas Clean::upper() car appelé depuis ./_js/video.js.php
        $version_mini_requise    = constant($navigo_upper.'_VERSION_MINI_REQUISE');
        $version_mini_conseillee = constant($navigo_upper.'_VERSION_MINI_CONSEILLEE');
        if( is_null($version_mini_conseillee) )
        {
          $alerte = 'Votre navigateur est obsolète, il n’est plus développé ! Changez-en pour votre sécurité et un bon fonctionnement.' ;
        }
        elseif( version_compare( $tab_return['version'] , $version_mini_requise , '<' ) )
        {
          $alerte = 'Votre navigateur est trop ancien ! '.$navigo_name.' est utilisable pour <em>SACoche</em> à partir de sa version '.$version_mini_requise.'.';
        }
        elseif( version_compare( $tab_return['version'] , $version_mini_conseillee , '<' ) )
        {
          $alerte = 'La version de votre navigateur est très ancienne ! Mettez-le à jour pour une navigation plus sure, rapide et efficace.' ;
        }
      }
    }
    if(defined('APPEL_SITE_PROJET'))
    {
      // On est déjà sur la page de téléchargement
      $lien = '';
    }
    else if(defined('SERVEUR_BROWSERS'))
    {
      $lien = '<br><a target="_blank" rel="noopener noreferrer" href="'.SERVEUR_BROWSERS.'">Informations et liens de téléchargement.</a>';
    }
    else
    {
      // Appel depuis ./_js/video.js.php
      $lien = '<br><a target="_blank" rel="noopener noreferrer" href="https://sacoche.sesamath.net/?page=browsers">Informations et liens de téléchargement.</a>';
    }
    $tab_return['alerte'] = ($alerte) ? $alerte.$lien : NULL ;
    return $tab_return;
  }

}
?>