- SPIP-Core v3.2.8 -> v3.2.9 (12 février 2021)
- ---------------------------------------------
-
- c965889bc | cedric | 2021-02-10 | Fix #4316 : la PR a ete fermee sur github quand on est passe a git.spip.net je suppose
- c76c39a6e | cedric | 2021-02-10 | nonsense code, copie colle surement, fix #4315
- 93a917a4d | cedric | 2021-02-10 | Fix #4140 (deja fixe en 3.3)
- 7246f1966 | cedric | 2021-02-08 | Fix #3869 (ou du moins on espere, sinon c'est pas si grave)
- 55bc9fd5d | erational | 2021-02-08 | [bugfix] empecher warning sur le count de l'iterateur devrait resoudre le ticket https://core.spip.net/issues/4450 et ..
- 42898eb47 | rastapopoulos | 2021-02-05 | Corrige 4401 en améliorant la rustine déjà en place
- 24eda3678 | cedric | 2021-02-05 | nettoyer l'adresse site qu'on enregistre fix #4629
- bb69f3466 | bruno | 2021-02-05 | ne pas stocker le champ ldap_password dans les fichiers de session
- 2269d0a6a | cedric | 2021-02-01 | appels a autoriser sur les formulaires editer, a minima quand on les utilise en modification d'un objet existant depui..
- 610c24f06 | cedric | 2021-02-02 | Eviter d'utiliser une globale, gerer le flag en static avec operations de lecture+reset
- a8493a3ce | cedric | 2021-02-01 | Eviter de donner sa langue au chat
- f9d9e5b1a | cedric | 2021-02-01 | on accepte pas une fonction de config inconnue si elle vient d'un modele
- 962a95f44 | cedric | 2021-02-01 | Utiliser contexte_compil pour reperer les appels a executer_balise_dynamique() venant d'un modele, et lever un flag le..
- c35edb769 | cedric | 2021-02-01 | une fonction pour detecter qu'un formulaire a ete inclu via un modele et non directement via une balide #FORMULAIRE_xxx
- 577ad8d21 | bruno | 2021-01-21 | éviter d'afficher n'importe quoi dans le message d'édition concurrente
- f1f27e9f7 | bruno | 2021-02-01 | attribut_html() sur les attributs renvoyés par env_to_params() & env_to_attributs()
- 6c6b6c6d4 | nicod | 2021-01-06 | Stocker les contextes dans des fichiers en cache si la longueur de l’argument géneré est plus long que ce qui est tolé..
- 2be11db95 | bruno | 2020-10-25 | fix undefined index sur l'action réparation de la base
-
-
-
- SPIP-plugins-dist v3.2.8 -> v3.2.9 (12 février 2021)
- -----------------------------------------------------
-
- breves | ba1e040 | cedric | 2021-02-04 | verifier l'autorisation de modifier la breve passe en argument
- mediabox | 9020d16 | cedric | 2021-02-08 | #4625 : echapper les valeurs injectees dans le js via les parametres mediabox, c'est plus propre
- medias | 96bf7b2 | cedric | 2021-02-04 | Verifier l'autorisation de modifier les documents passes en argument du formulaire
- medias | bfc3319 | cedric | 2021-01-20 | Report de 049dee8 : Extraire la fonction de determination automatique du statut d'un document dans une fonction inc_de..
- medias | 9764525 | cedric | 2021-01-19 | Normaliser le comportement de document_instituer() en appelant les pipelines pre_editon et post_edition avec action=in..
- medias | 2293b84 | tcharlss | 2021-01-19 | Report de 890506eb99 : Pour le JS des modes d'affichages, cibler plus précisément l'élément, sinon les boutons peuvent..
- mots | 0955e0d | cedric | 2021-02-04 | Verifier les autorisations de modifier les mots/groupes de mots passes en argument du formulaire d'edition
- revisions | e573977 | bruno | 2020-10-23 | éviter un warning dans les révisions lors de la suppression d'un lien par une personne non identifiée
- sites | 28bdb59 | cedric | 2021-02-10 | Fix #4296 : l'url peut etre dans le href meme si c'est pas un link autofermant...
- sites | 92994f3 | cedric | 2021-02-04 | verifier l'autorisation de modifier le site passe en argument du formulaire d'edition
- sites | 638ceef | cedric | 2020-12-18 | Fix le id_mot manquant sur les syndic_articles. Ce n'est pas un usage frequent, mais il existe et soyons homogene avec..
- textwheel | 317b045 | rastapopoulos | 2021-02-05 | Correction de 4508 : on ne gère le caption/summary *réellement* qu'en première ligne, seulement si on n'a pas déjà gén..
+SPIP-Core v3.2.10 -> v3.2.11 (26 March 2021)
+--------------------------------------------
+
+b52a4a5b3 | cedric | 2021-03-12 | twitterbot est aussi notre ami pour le laisser scraper l'url qu'on veut touitter (fil)
+58d5d6190 | cedric | 2021-02-15 | Report de https://git.spip.net/spip-contrib-outils/securite/commit/e7b571681a92eb40eddabbbb24b45dc472e113c1 qui fix #4..
+6611fd50b | cedric | 2021-02-15 | Report de https://git.spip.net/spip-contrib-outils/securite/commit/3eccaf41426d4f3c8f28b50d81e12fbe5f8af4c2
+62d33c975 | marcimat | 2021-03-26 | Notice-- : Attribut sans ses quotes... (realet)
+
+
+
+SPIP-Core v3.2.9 -> v3.2.10 (26 mars 2021)
+-------------------------------------------
+
+0b1bd0542 | marcimat | 2018-09-05 | Compat PHP 7.x : Scorie résiduelle du passage à mysqli. Mais ces fonctions ne semblent plus utilisées.
+7621a660a | marcimat | 2021-03-19 | Retour partiel sur 31df72005 pour compat PHP 5.4 ...
+4de4b3c34 | marcimat | 2021-03-19 | Correction deprecated php 7.4 : ordre de join inversé.
+0ea620c9a | marcimat | 2018-09-05 | Tickets #4059 et #4138 : meilleure compat PHP 7.2
+f69b39c9e | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+a54ab9a89 | rastapopoulos | 2021-03-14 | Backport de 2e55e3a60e à la main car plus dans le même fichier en 3.3.
+bdc53dcc9 | marcimat | 2021-03-11 | Lorsqu'on déclare un traitement à un champ de rubrique, tel que `$table_des_traitements['DEMO']['rubriques'] = ...`, c..
+510983b09 | cedric | 2021-03-09 | Fix https://core.spip.net/issues/4442 : le vieux parseur xml a la main (qu'il faudrait virer) ne tolerait pas l'utilis..
+31df72005 | marcimat | 2021-03-05 | Suite de e11b28be4 : plus éviter une fatale en PHP 8 si unicode2charset cherche à utiliser un charset inexistant
+00c2038da | marcimat | 2021-03-05 | Correction d'une Fatale Suite à 27e4f1bcc. C'est sport mais le commit ajoute des accents dans le squelettes prive/sque..
+e380b0afd | cy.altern | 2021-03-04 | report a4cdf3b633
+916b67198 | marcimat | 2021-03-04 | Ticket #4348 : Compat PHP 7.4 (deprecated curly braces array)
+910c245ea | marcimat | 2020-03-26 | Compat PHP 7.4 : éviter une notice lorsque la pagination ne trouve aucune entrée.
+1b5549e51 | marcimat | 2019-08-26 | Ticket #4348 : Compat PHP 7.4 (notice).
+c5492ea3e | marcimat | 2019-08-26 | Ticket #4348 : Compat PHP 7.4 (deprecated curly braces array)
+da6dfc068 | marcimat | 2019-08-26 | Ticket #4348 : Compat PHP 7.4, Trying to access array offset on value of type null.
+db1814dc5 | marcimat | 2019-08-25 | Compat PHP 7.4, Deprecated: Array and string offset access syntax with curly braces (Francky)
+330eb930f | marcimat | 2019-06-17 | Ticket #4348 : Correction pour PHP 7.4 (Left-associative ternary operator deprecation)
+130ada180 | marcimat | 2018-02-09 | Compatibilité PHP 7.2 : create_function => function xxx each => key, current, next
+8075d79f2 | marcimat | 2017-12-11 | Ticket #4059 : Compat PHP 7.2, remplacer un create_function.
+061107f80 | marcimat | 2017-12-11 | Ticket #4059 : Compat PHP 7.2, remplacer des create_function.
+af94fa5d9 | marcimat | 2017-12-11 | Ticket #4059 : Compat PHP 7.2, remplacer des create_function. (encore un eval du coup).
+e7fe0d5aa | marcimat | 2017-12-11 | Ticket #4059 : Compat PHP 7.2, remplacer des create_function.
+49f24e83b | marcimat | 2017-12-11 | Ticket #4059 : Compat PHP 7.2. Un create_function de moins. Pour le coup, ça semble obligé ici de conserver une évalua..
+6555cb7b4 | marcimat | 2021-03-04 | Report adapté de 5647069fb (Meilleure compat PHP 7.3)
+fcb3e1f5e | marcimat | 2019-08-21 | Ticket #4348 : Notices/Warning en PHP 7.3+ : éviter un appel à time() avec un argument.
+31c614782 | bruno | 2020-03-13 | report adapté de a0c24ecb6f8c1d70dce86b859eb448fb0415d869
+60d4d3d1f | cedric | 2021-02-17 | Fix vraiment #4167 : _deja_loge peut contenir un js de redirection et doit donc etre non filtre par interdire_scripts()
+7046e391d | cedric | 2021-02-17 | Fix #4387 : Revert "Fix #4167 : éviter d'afficher le script de redirection js quand le form de login est utilisé en aj..
+03cc99fce | cedric | 2021-02-17 | Fix l'utilisation du niveau de log 0 (maieul) cf https://git.spip.net/spip/spip/pulls/106
+62e4284fd | bruno | 2021-02-10 | éviter un plantage avec url_page quand on utilise une fonction perso pour generer_generer_url_XXX
+0281d90f2 | maieul | 2021-02-10 | fix #4633, ne pas passer une date quotée au pipeline optimiser_base_disparus
+
+
+SPIP-plugins-dist v3.2.9 -> v3.2.10 (26 mars 2021)
+---------------------------------------------------
+
+aide | 329cfce | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+archiviste | de3283f | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+breves | 668efb9 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+breves | 658f025 | maieul | 2021-02-10 | Adaptation du code au fix du core https://core.spip.net/issues/4633
+compagnon | d8aaa96 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+compresseur | 08b96ff | marcimat | 2021-03-24 | Mise à jour de CSSTidy en version 1.7.3
+compresseur | 30d1b1f | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+compresseur | 827ba88 | marcimat | 2018-09-05 | Tickets #4059 et #4138 : meilleure compat PHP 7.2
+dump | d767e27 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+filtres_images | eb9ace8 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+forum | cb90b1a | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+jquery_ui | b04572e | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+mediabox | bcadf3d | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+medias | d096d93 | marcimat | 2021-03-26 | Report de d1ca0c8c : Suite de 890506eb99 : Il faut ajouteur la classe .portfolios__titre aux autres blocs documents du..
+medias | 4e13bea | cedric | 2021-03-25 | Fix regression (Maieul via https://git.spip.net/spip/medias/pulls/17)
+medias | 1d917cd | spip.franck | 2020-07-12 | - Mise à jour de la lib getid3 en 1.9.20, nous étions en 1.9.18 - Le changelog est dispo ici: https://github.com/James..
+medias | e019d3e | spip.franck | 2019-09-20 | Mise à jour de la lib getid3 en 1.9.18, nous étions en 1.9.16. Le changelog est dispo ici: https://github.com/JamesHei..
+medias | 2dc03d5 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+mots | 6a346ba | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+organiseur | a7177ba | marcimat | 2021-03-19 | Correction join à l'envers.
+organiseur | 7c665fc | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+petitions | 2909a25 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+plan | e722c87 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+porte_plume | 0003d09 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+revisions | 1d88a68 | bruno | 2018-09-15 | retour sur r111482 : éviter une erreur Maximum execution time
+revisions | 2f2d47c | marcimat | 2018-09-05 | Tickets #4059 et #4138 : meilleure compat PHP 7.2
+revisions | 535cb2f | marcimat | 2018-09-15 | Correction sur r111486 (compat PHP 7.2, suppression each()) qui était erronné.
+revisions | aa56bf3 | marcimat | 2018-09-05 | Ticket #4138 : Compatibilité PHP 7.2
+revisions | df4b821 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+revisions | 19c0b6c | marcimat | 2019-08-21 | Ticket #4348 : Compat PHP 7.4+ Deprecated: The behavior of unparenthesized expressions containing both '.' and '+'/'-'
+safehtml | b2853a1 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+safehtml | 6e81e13 | marcimat | 2019-08-26 | Tiket #4348 / Compat PHP 7.4 : Array and string offset access syntax with curly braces is deprecated
+sites | 622387c | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+squelettes_par_rubrique | 357b283 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+statistiques | 291386b | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+svp | a3c6abf | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+svp | 9fd9349 | marcimat | 2019-08-25 | Compat PHP 7.4, Deprecated: Array and string offset access syntax with curly braces (Francky)
+textwheel | 8f70594 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+textwheel | 84d32c5 | cedric | 2018-09-06 | create_function est deprecie en PHP 7.2, on reecrit donc l'optimisation des subwheels via une fonction anonyme de type..
+textwheel | 1bf8732 | marcimat | 2018-02-09 | Compat PHP 7.2 : create_function => function (les plus simples, mais il en reste !…)
+urls_etendues | be9066b | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+vertebres | a381bc9 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+squelettes-dist | 6323be9 | marcimat | 2021-03-18 | Suppression du fichier .gitattributes inutile.
+
+
+
+SPIP-Core v3.2.8 -> v3.2.9 (12 février 2021)
+---------------------------------------------
+
+c965889bc | cedric | 2021-02-10 | Fix #4316 : la PR a ete fermee sur github quand on est passe a git.spip.net je suppose
+c76c39a6e | cedric | 2021-02-10 | nonsense code, copie colle surement, fix #4315
+93a917a4d | cedric | 2021-02-10 | Fix #4140 (deja fixe en 3.3)
+7246f1966 | cedric | 2021-02-08 | Fix #3869 (ou du moins on espere, sinon c'est pas si grave)
+55bc9fd5d | erational | 2021-02-08 | [bugfix] empecher warning sur le count de l'iterateur devrait resoudre le ticket https://core.spip.net/issues/4450 et ..
+42898eb47 | rastapopoulos | 2021-02-05 | Corrige 4401 en améliorant la rustine déjà en place
+24eda3678 | cedric | 2021-02-05 | nettoyer l'adresse site qu'on enregistre fix #4629
+bb69f3466 | bruno | 2021-02-05 | ne pas stocker le champ ldap_password dans les fichiers de session
+2269d0a6a | cedric | 2021-02-01 | appels a autoriser sur les formulaires editer, a minima quand on les utilise en modification d'un objet existant depui..
+610c24f06 | cedric | 2021-02-02 | Eviter d'utiliser une globale, gerer le flag en static avec operations de lecture+reset
+a8493a3ce | cedric | 2021-02-01 | Eviter de donner sa langue au chat
+f9d9e5b1a | cedric | 2021-02-01 | on accepte pas une fonction de config inconnue si elle vient d'un modele
+962a95f44 | cedric | 2021-02-01 | Utiliser contexte_compil pour reperer les appels a executer_balise_dynamique() venant d'un modele, et lever un flag le..
+c35edb769 | cedric | 2021-02-01 | une fonction pour detecter qu'un formulaire a ete inclu via un modele et non directement via une balide #FORMULAIRE_xxx
+577ad8d21 | bruno | 2021-01-21 | éviter d'afficher n'importe quoi dans le message d'édition concurrente
+f1f27e9f7 | bruno | 2021-02-01 | attribut_html() sur les attributs renvoyés par env_to_params() & env_to_attributs()
+6c6b6c6d4 | nicod | 2021-01-06 | Stocker les contextes dans des fichiers en cache si la longueur de l’argument géneré est plus long que ce qui est tolé..
+2be11db95 | bruno | 2020-10-25 | fix undefined index sur l'action réparation de la base
+
+
+
+SPIP-plugins-dist v3.2.8 -> v3.2.9 (12 février 2021)
+-----------------------------------------------------
+
+breves | ba1e040 | cedric | 2021-02-04 | verifier l'autorisation de modifier la breve passe en argument
+mediabox | 9020d16 | cedric | 2021-02-08 | #4625 : echapper les valeurs injectees dans le js via les parametres mediabox, c'est plus propre
+medias | 96bf7b2 | cedric | 2021-02-04 | Verifier l'autorisation de modifier les documents passes en argument du formulaire
+medias | bfc3319 | cedric | 2021-01-20 | Report de 049dee8 : Extraire la fonction de determination automatique du statut d'un document dans une fonction inc_de..
+medias | 9764525 | cedric | 2021-01-19 | Normaliser le comportement de document_instituer() en appelant les pipelines pre_editon et post_edition avec action=in..
+medias | 2293b84 | tcharlss | 2021-01-19 | Report de 890506eb99 : Pour le JS des modes d'affichages, cibler plus précisément l'élément, sinon les boutons peuvent..
+mots | 0955e0d | cedric | 2021-02-04 | Verifier les autorisations de modifier les mots/groupes de mots passes en argument du formulaire d'edition
+revisions | e573977 | bruno | 2020-10-23 | éviter un warning dans les révisions lors de la suppression d'un lien par une personne non identifiée
+sites | 28bdb59 | cedric | 2021-02-10 | Fix #4296 : l'url peut etre dans le href meme si c'est pas un link autofermant...
+sites | 92994f3 | cedric | 2021-02-04 | verifier l'autorisation de modifier le site passe en argument du formulaire d'edition
+sites | 638ceef | cedric | 2020-12-18 | Fix le id_mot manquant sur les syndic_articles. Ce n'est pas un usage frequent, mais il existe et soyons homogene avec..
+textwheel | 317b045 | rastapopoulos | 2021-02-05 | Correction de 4508 : on ne gère le caption/summary *réellement* qu'en première ligne, seulement si on n'a pas déjà gén..
SPIP-Core v3.2.7 -> v3.2.8 (29 septembre 2020)
* ------------------
*/
-define('_ECRAN_SECURITE', '1.3.13'); // 2019-12-04
+define('_ECRAN_SECURITE', '1.4.1'); // 2021-03-12
/*
* Documentation : http://www.spip.net/fr_article4200.html
if (isset($_GET['test_ecran_securite']))
$ecran_securite_raison = 'test '._ECRAN_SECURITE;
+if (file_exists($f = __DIR__ . DIRECTORY_SEPARATOR . 'ecran_securite_options.php')) {
+ include ($f);
+}
+
/*
* Monitoring
* var_isbot=0 peut etre utilise par un bot de monitoring pour surveiller la disponibilite d'un site vu par les users
if (!defined('_IS_BOT_FRIEND')){
define('_IS_BOT_FRIEND',
isset($_SERVER['HTTP_USER_AGENT'])
- and preg_match(',' . implode ('|', array(
- 'facebookexternalhit',
- 'flipboardproxy',
- 'wordpress'
- )) . ',i',
- (string)$_SERVER['HTTP_USER_AGENT'])
+ and preg_match(
+ ',' . implode('|', array(
+ 'facebookexternalhit',
+ 'twitterbot',
+ 'flipboardproxy',
+ 'wordpress'
+ )) . ',i',
+ (string)$_SERVER['HTTP_USER_AGENT']
+ )
);
}
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Content-Type: text/html");
+ header("Connection: close");
die("<html><title>Error 403: Forbidden</title><body><h1>Error 403</h1><p>You are not authorized to view this page ($ecran_securite_raison)</p></body></html>");
}
header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");
header("Content-Type: text/html");
+ header("Connection: close");
die("<html><title>Status 429: Too Many Requests</title><body><h1>Status 429</h1><p>Too Many Requests (try again soon)</p></body></html>");
}
return false;
}
- $h = ord($c{$index});
+ $h = ord($c[$index]);
if ($h <= 0x7F) {
$bytes = 1;
if ($h <= 0xDF && $index < $len-1) {
$bytes = 2;
- return ($h & 0x1F) << 6 | (ord($c{$index+1}) & 0x3F);
+ return ($h & 0x1F) << 6 | (ord($c[$index+1]) & 0x3F);
} else {
if ($h <= 0xEF && $index < $len-2) {
$bytes = 3;
- return ($h & 0x0F) << 12 | (ord($c{$index+1}) & 0x3F) << 6
- | (ord($c{$index+2}) & 0x3F);
+ return ($h & 0x0F) << 12 | (ord($c[$index+1]) & 0x3F) << 6
+ | (ord($c[$index+2]) & 0x3F);
} else {
if ($h <= 0xF4 && $index < $len-3) {
$bytes = 4;
- return ($h & 0x0F) << 18 | (ord($c{$index+1}) & 0x3F) << 12
- | (ord($c{$index+2}) & 0x3F) << 6
- | (ord($c{$index+3}) & 0x3F);
+ return ($h & 0x0F) << 18 | (ord($c[$index+1]) & 0x3F) << 12
+ | (ord($c[$index+2]) & 0x3F) << 6
+ | (ord($c[$index+3]) & 0x3F);
} else {
// pas utf mais renvoyer quand meme ce qu'on a
$bytes = 1;
$code .= ", $args";
}
$code = $f('page', $code, $s);
-
+ $p->code = $code;
return $p;
}
$s = 'connect=' . addslashes($s);
* - indéfini sinon.
**/
function spip_query($query, $serveur = '') {
+
$f = spip_connect_sql($GLOBALS['spip_sql_version'], 'query', $serveur, true);
return function_exists($f) ? $f($query, $serveur) : false;
* @param string $redirect
* @return array|bool
*/
-function maj_base($version_cible = 0, $redirect = '') {
+function maj_base($version_cible = 0, $redirect = '', $debut_page = true) {
$version_installee = @$GLOBALS['meta']['version_installee'];
//
include_spip('maj/svn10000');
ksort($GLOBALS['maj']);
- $res = maj_while($version_installee, $cible, $GLOBALS['maj'], 'version_installee', 'meta', $redirect, true);
+ $res = maj_while($version_installee, $cible, $GLOBALS['maj'], 'version_installee', 'meta', $redirect, $debut_page);
if ($res) {
if (!is_array($res)) {
spip_log("Pb d'acces SQL a la mise a jour", 'maj.' . _LOG_INFO_ERREUR);
define('_TIME_OUT', $time + _UPGRADE_TIME_OUT);
}
- reset($maj);
- while (list($v, ) = each($maj)) {
+ foreach ($maj as $v => $operations) {
// si une maj pour cette version
if ($v == 'init' or
(spip_version_compare($v, $installee, '>')
maj_debut_page($v, $meta, $table);
}
echo "MAJ $v";
- $etape = serie_alter($v, $maj[$v], $meta, $table, $redirect);
+ $etape = serie_alter($v, $operations, $meta, $table, $redirect);
$trouver_table(''); // vider le cache des descriptions de table
# echec sur une etape en cours ?
# on sort
function optimiser_base_disparus($attente = 86400) {
# format = 20060610110141, si on veut forcer une optimisation tout de suite
- $mydate = sql_quote(date("Y-m-d H:i:s", time() - $attente));
+ $mydate = date("Y-m-d H:i:s", time() - $attente);
+ $mydate_quote = sql_quote($mydate);
$n = 0;
//
- // Rubriques
+ // Rubriques
//
# les articles qui sont dans une id_rubrique inexistante
ON A.id_rubrique=R.id_rubrique",
"A.id_rubrique > 0
AND R.id_rubrique IS NULL
- AND A.maj < $mydate");
+ AND A.maj < $mydate_quote");
$n += optimiser_sansref('spip_articles', 'id_article', $res);
// les articles a la poubelle
- sql_delete("spip_articles", "statut='poubelle' AND maj < $mydate");
+ sql_delete("spip_articles", "statut='poubelle' AND maj < $mydate_quote");
//
// Auteurs
LEFT JOIN spip_auteurs_liens AS L
ON L.id_auteur=A.id_auteur",
"L.id_auteur IS NULL
- AND A.statut='5poubelle' AND A.maj < $mydate");
+ AND A.statut='5poubelle' AND A.maj < $mydate_quote");
$n += optimiser_sansref('spip_auteurs', 'id_auteur', $res);
default:
// mbstring presente ?
if (init_mb_string()) {
- if ($order = mb_detect_order() # mb_string connait-il $charset?
- and mb_detect_order($charset)
- ) {
- $s = mb_convert_encoding($texte, 'utf-8', $charset);
- if ($s && $s != $texte) {
- return utf_8_to_unicode($s);
+ $order = mb_detect_order();
+ try {
+ # mb_string connait-il $charset?
+ if ($order and mb_detect_order($charset)) {
+ $s = mb_convert_encoding($texte, 'utf-8', $charset);
+ if ($s && $s != $texte) {
+ return utf_8_to_unicode($s);
+ }
}
- }
+
+ } catch (\Exception $e) {
+ // Le charset n'existe probablement pas
+ }
mb_detect_order($order); # remettre comme precedemment
}
// Sinon, peut-etre connaissons-nous ce charset ?
if (!isset($trans[$charset])) {
- if ($cset = load_charset($charset)
+ if (
+ $cset = load_charset($charset)
and is_array($GLOBALS['CHARSET'][$cset])
) {
foreach ($GLOBALS['CHARSET'][$cset] as $key => $val) {
}
}
}
- if (count($trans[$charset])) {
+ if (isset($trans[$charset]) and count($trans[$charset])) {
return str_replace(array_keys($trans[$charset]), array_values($trans[$charset]), $texte);
}
* @return string
*/
function translitteration_rapide($texte, $charset = 'AUTO', $complexe = '') {
- static $trans;
+ static $trans = [];
if ($charset == 'AUTO') {
$charset = $GLOBALS['meta']['charset'];
}
$table_translit = 'translit' . $complexe;
// 2. Translitterer grace a la table predefinie
- if (!$trans[$complexe]) {
+ if (!isset($trans[$complexe])) {
+ $trans[$complexe] = [];
load_charset($table_translit);
foreach ($GLOBALS['CHARSET'][$table_translit] as $key => $val) {
$trans[$complexe][caractere_utf_8($key)] = $val;
$texte = translitteration($texte, 'AUTO', 'complexe');
if ($chiffres) {
- $texte = preg_replace("/[aeiuoyd]['`?~.^+(-]{1,2}/eS",
- "translitteration_chiffree('\\0')", $texte);
+ $texte = preg_replace_callback(
+ "/[aeiuoyd]['`?~.^+(-]{1,2}/S",
+ function($m) { return translitteration_chiffree($m[0]); },
+ $texte
+ );
}
return $texte;
return $texte;
}
- // Reconnaitre le BOM utf-8 (0xEFBBBF)
if (bom_utf8($texte)) {
+ // Reconnaitre le BOM utf-8 (0xEFBBBF)
$charset = 'utf-8';
$texte = substr($texte, 3);
- } // charset precise par le contenu (xml)
- else {
- if (preg_match(
- ',<[?]xml[^>]*encoding[^>]*=[^>]*([-_a-z0-9]+?),UimsS', $texte, $regs)) {
- $charset = trim(strtolower($regs[1]));
- } // charset precise par le contenu (html)
- else {
- if (preg_match(
- ',<(meta|html|body)[^>]*charset[^>]*=[^>]*([-_a-z0-9]+?),UimsS',
- $texte, $regs)
- # eviter #CHARSET des squelettes
- and (($tmp = trim(strtolower($regs[2]))) != 'charset')
- ) {
- $charset = $tmp;
- } // charset de la reponse http
- else {
- if (preg_match(',charset=([-_a-z0-9]+),i', $headers, $regs)) {
- $charset = trim(strtolower($regs[1]));
- } else {
- $charset = '';
- }
- }
- }
+ } elseif (preg_match(',<[?]xml[^>]*encoding[^>]*=[^>]*([-_a-z0-9]+?),UimsS', $texte, $regs)) {
+ // charset precise par le contenu (xml)
+ $charset = trim(strtolower($regs[1]));
+ } elseif (
+ // charset precise par le contenu (html)
+ preg_match(',<(meta|html|body)[^>]*charset[^>]*=[^>]*([#-_a-z0-9]+?),UimsS', $texte, $regs)
+ # eviter toute balise SPIP tel que #CHARSET ou #CONFIG d'un squelette
+ and false === strpos($regs[2], '#')
+ and $tmp = trim(strtolower($regs[2]))
+ ) {
+ $charset = $tmp;
+ } elseif (preg_match(',charset=([-_a-z0-9]+),i', $headers, $regs)) {
+ // charset de la reponse http
+ $charset = trim(strtolower($regs[1]));
+ } else {
+ $charset = '';
}
+
+
// normaliser les noms du shif-jis japonais
if (preg_match(',^(x|shift)[_-]s?jis$,i', $charset)) {
$charset = 'shift-jis';
// verifier le impt=non
sql_updateq('spip_meta', array('impt' => 'non'), sql_in('nom', $meta_serveur));
- while (list($nom, $valeur) = each($liste_meta)) {
- if (!isset($GLOBALS['meta'][$nom]) or !$GLOBALS['meta'][$nom]) {
+ foreach ($liste_meta as $nom => $valeur) {
+ if (empty($GLOBALS['meta'][$nom])) {
ecrire_meta($nom, $valeur);
}
}
**/
function extraire_attribut($balise, $attribut, $complet = false) {
if (is_array($balise)) {
- array_walk($balise,
- create_function('&$a,$key,$t',
- '$a = extraire_attribut($a,$t);'
- ),
- $attribut);
+ array_walk(
+ $balise,
+ function(&$a, $key, $t){
+ $a = extraire_attribut($a, $t);
+ },
+ $attribut
+ );
return $balise;
}
function tags2dcsubject($tags) {
$subjects = '';
foreach (extraire_balises($tags, 'a') as $e) {
- if (extraire_attribut($e, rel) == 'tag') {
+ if (extraire_attribut($e, 'rel') == 'tag') {
$subjects .= '<dc:subject>'
. texte_backend(textebrut($e))
. '</dc:subject>' . "\n";
if (is_array($texte)) {
array_walk(
$texte,
- create_function('&$a,$key,$t', '$a = extraire_balise($a,$t);'),
+ function(&$a, $key, $t){
+ $a = extraire_balise($a, $t);
+ },
$tag
);
if (is_array($texte)) {
array_walk(
$texte,
- create_function('&$a,$key,$t', '$a = extraire_balises($a,$t);'),
+ function(&$a, $key, $t){
+ $a = extraire_balises($a, $t);
+ },
$tag
);
return preg_replace_callback(
",url\s*\(\s*['\"]?([^'\"/#\s][^:]*)['\"]?\s*\),Uims",
- create_function('$x',
- 'return "url(\'".suivre_lien(\'' . $path . '\',$x[1])."\')";'
- ), $contenu);
+ function($x) use ($path) {
+ return "url('" . suivre_lien($path, $x[1]) . "')";
+ },
+ $contenu
+ );
}
/**
* Proteger les champs passes dans l'url et utiliser dans {tri ...}
* preserver l'espace pour interpreter ensuite num xxx et multi xxx
+ * on permet d'utiliser les noms de champ prefixes
+ * articles.titre
+ * et les propriete json
+ * properties.gis[0].ville
*
* @param string $t
* @return string
*/
function tri_protege_champ($t) {
- return preg_replace(',[^\s\w.+],', '', $t);
+ return preg_replace(',[^\s\w.+\[\]],', '', $t);
}
/**
// enlever le width et height du style
$style = preg_replace(",(^|;)\s*(width|height)\s*:\s*[^;]+,ims", "", $style);
- if ($style and $style{0} == ';') {
+ if ($style and $style[0] == ';') {
$style = substr($style, 1);
}
$delim_pos = strrpos($encoded, '-');
if ($delim_pos > self::byteLength($this->_punycode_prefix)) {
for ($k = self::byteLength($this->_punycode_prefix); $k < $delim_pos; ++$k) {
- $decoded[] = ord($encoded{$k});
+ $decoded[] = ord($encoded[$k]);
}
}
$deco_len = count($decoded);
for ($enco_idx = ($delim_pos) ? ($delim_pos + 1) : 0; $enco_idx < $enco_len; ++$deco_len) {
for ($old_idx = $idx, $w = 1, $k = $this->_base; 1; $k += $this->_base) {
- $digit = $this->_decode_digit($encoded{$enco_idx++});
+ $digit = $this->_decode_digit($encoded[$enco_idx++]);
$idx += $digit * $w;
$t = ($k <= $bias) ? $this->_tmin :
(($k >= $bias + $this->_tmax) ? $this->_tmax : ($k - $bias));
$mode = 'next';
$test = 'none';
for ($k = 0; $k < $inp_len; ++$k) {
- $v = ord($input{$k}); // Extract byte from input string
+ $v = ord($input[$k]); // Extract byte from input string
if ($v < 128) { // We found an ASCII char - put into stirng as is
$output[$out_len] = $v;
++$out_len;
$out_len++;
$output[$out_len] = 0;
}
- $output[$out_len] += ord($input{$i}) << (8 * (3 - ($i % 4) ) );
+ $output[$out_len] += ord($input[$i]) << (8 * (3 - ($i % 4) ) );
}
return $output;
}
// MathML
if ($GLOBALS['traiter_math'] == 'mathml') {
- return join(file("$fichier"), "");
+ return implode("", file($fichier));
} // TeX
else {
list(, , , $size) = @getimagesize($fichier);
$rec = generer_url_ecrire('plonger', "rac=$idom&exclus=$exclu&do=$do&col=" . ($col + 1));
$info = generer_url_ecrire('informer', "type=rubrique&rac=$idom&do=$do&id=");
$args = "'$idom',this,$col,'" . $GLOBALS['spip_lang_left'] . "','$info',event";
- while (list($id, $titrebrut) = each($ordre)) {
+ foreach ($ordre as $id => $titrebrut) {
$titre = supprimer_numero($titrebrut);
$classe1 = $id_rubrique ? 'petite-rubrique' : "petit-secteur";
// cas du plugin qui n'est compatible qu'avec cette nouvelle version
}
- $minimum_inc = $intervalle{0} == "[";
+ $minimum_inc = $intervalle[0] == "[";
$maximum_inc = substr($intervalle, -1) == "]";
if (strlen($minimum)) {
$minimum = $regs[1];
$maximum = $regs[2];
- $minimum_inclus = $intervalle{0} == "[";
+ $minimum_inclus = $intervalle[0] == "[";
$maximum_inclus = substr($intervalle, -1) == "]";
if (strlen($minimum)) {
$GLOBALS['spip_version_branche'], 'spip')
) {
$dir = $chemin['path'];
- if (strlen($dir) and $dir{0} == "/") {
+ if (strlen($dir) and $dir[0] == "/") {
$dir = substr($dir, 1);
}
if (strlen($dir) and $dir == "./") {
*/
function supprimer_sessions($id_auteur, $toutes = true, $actives = true) {
+ $nb_files = 0;
+ $nb_max_files = (defined('_MAX_NB_SESSIONS_OUVERTES') ? _MAX_NB_SESSIONS_OUVERTES : 1000);
spip_log("supprimer sessions auteur $id_auteur", "session");
if ($toutes or $id_auteur !== $GLOBALS['visiteur_session']['id_auteur']) {
if ($dir = opendir(_DIR_SESSIONS)) {
+ $t = $_SERVER['REQUEST_TIME'] - (4*_RENOUVELLE_ALEA); // 48h par defaut
+ $t_short = $_SERVER['REQUEST_TIME'] - max(_RENOUVELLE_ALEA/4,3*3600); // 3h par defaut
$t = time() - (4 * _RENOUVELLE_ALEA);
while (($f = readdir($dir)) !== false) {
+ $nb_files++;
if (preg_match(",^[^\d-]*(-?\d+)_\w{32}\.php[3]?$,", $f, $regs)) {
$f = _DIR_SESSIONS . $f;
if (($actives and $regs[1] == $id_auteur) or ($t > filemtime($f))) {
spip_unlink($f);
}
+ // si il y a trop de sessions ouvertes, on purge les sessions anonymes de plus de 3H
+ // cf http://core.spip.org/issues/3276
+ elseif ($nb_files>$nb_max_files and !intval($regs[1]) and ($t_short > filemtime($f))) {
+ spip_unlink($f);
+ }
}
}
}
if (!isset($regs[1]) or !$logname = $regs[1]) {
$logname = null;
}
- if (!isset($regs[2]) or !$niveau = $regs[2]) {
+ if (!isset($regs[2])) {
$niveau = _LOG_INFO;
}
+ else {
+ $niveau = intval($regs[2]);
+ }
if ($niveau <= (defined('_LOG_FILTRE_GRAVITE') ? _LOG_FILTRE_GRAVITE : _LOG_INFO_IMPORTANTE)) {
if (!$pre) {
// les anciens IIS n'acceptent pas les POST sur ecrire/ (#419)
// meme pb sur thttpd cf. http://forum.spip.net/fr_184153.html
if (!defined('_SPIP_ECRIRE_SCRIPT')) {
- define('_SPIP_ECRIRE_SCRIPT', (empty($_SERVER['SERVER_SOFTWARE']) ? '' :
- preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE']) ?
- 'index.php' : ''));
+ if (!empty($_SERVER['SERVER_SOFTWARE']) and preg_match(',IIS|thttpd,', $_SERVER['SERVER_SOFTWARE'])) {
+ define('_SPIP_ECRIRE_SCRIPT', 'index.php');
+ } else {
+ define('_SPIP_ECRIRE_SCRIPT', '');
+ }
}
function spip_xml_decompose_tag($tag) {
$tagname = spip_xml_tagname($tag);
$liste = array();
- $p = strpos($tag, ' ');
- $tag = substr($tag, $p);
+ $tag = ltrim(strpbrk($tag, " \n\t"));
$p = strpos($tag, '=');
while ($p !== false) {
$attr = trim(substr($tag, 0, $p));
$tag = ltrim(substr($tag, $p + 1));
- $quote = $tag{0};
+ $quote = $tag[0];
$p = strpos($tag, $quote, 1);
$cont = substr($tag, 1, $p - 1);
$liste[$attr] = $cont;
// ex : 2.0.0, 2.0.0-dev, 2.0.0-beta, 2.0.0-beta2
// le _SPIP_VERSION_ID est un nombre entier représentant le numéro de version (2 chiffres pour chaque 03 + 02 + 06 = 30206
// le _SPIP_EXTRA_VERSION sert à repérer les version dev, beta etc. Pour une version stable il est vide.
-$spip_version_branche = "3.2.9";
-define('_SPIP_VERSION_ID', 30209);
+$spip_version_branche = "3.2.11";
+define('_SPIP_VERSION_ID', 30211);
define('_SPIP_EXTRA_VERSION', '');
// cette version dev accepte tous les plugins compatible avec la version ci-dessous
$bad_dirs = array();
$absent_dirs = array();
- while (list(, $my_dir) = each($GLOBALS['test_dirs'])) {
+ foreach ($GLOBALS['test_dirs'] as $i => $my_dir) {
$test = test_ecrire($my_dir);
if (!$test) {
$m = preg_replace(',^' . _DIR_RACINE . ',', '', $my_dir);
*/
public function rewind() {
reset($this->tableau);
- list($this->cle, $this->valeur) = each($this->tableau);
+ $this->cle = key($this->tableau);
+ $this->valeur = current($this->tableau);
+ next($this->tableau);
}
/**
*
**/
protected function select_datapath() {
- list(, $base) = each($this->command['datapath']);
+ $base = reset($this->command['datapath']);
if (strlen($base = ltrim(trim($base), "/"))) {
$this->tableau = table_valeur($this->tableau, $base);
if (!is_array($this->tableau)) {
$a = ' . sprintf($tv, '$aa') . ';
$b = ' . sprintf($tv, '$bb') . ';
if ($a <> $b)
- return ($a ' . ((isset($r[2]) and $r[2]) ? '>' : '<') . ' $b) ? -1 : 1;';
+ return ($a ' . (!empty($r[2]) ? '>' : '<') . ' $b) ? -1 : 1;';
}
}
}
}
if ($sortfunc) {
- uasort($this->tableau, create_function('$aa,$bb',
- $sortfunc . '
- return 0;'
- ));
+ $sortfunc .= "\n return 0;";
+ uasort($this->tableau, function($aa, $bb) use ($sortfunc) {
+ return eval($sortfunc);
+ });
}
}
*/
public function next() {
if ($this->valid()) {
- list($this->cle, $this->valeur) = each($this->tableau);
+ $this->cle = key($this->tableau);
+ $this->valeur = current($this->tableau);
+ next($this->tableau);
}
}
<paquet
prefix="spip"
categorie="outil"
- version="3.2.9"
+ version="3.2.11"
etat="stable"
compatibilite="];["
schema="23375"
$params = array_filter(explode('|', $params));
if ($params) {
- list(, $soustype) = each($params);
+ $soustype = current($params);
$soustype = strtolower(trim($soustype));
- if (in_array($soustype,
- array('left', 'right', 'center', 'ajax'))) {
- list(, $soustype) = each($params);
+ if (in_array($soustype, array('left', 'right', 'center', 'ajax'))) {
+ $soustype = next($params);
$soustype = strtolower($soustype);
}
// si true, les arguments simples (sans truc=chose) vont degager
$_contexte = argumenter_inclure($p->param, true, $p, $p->boucles, $p->id_boucle, false, false);
if (count($_contexte)) {
- list($key, $val) = each($_contexte);
+ $key = key($_contexte);
if (is_numeric($key)) {
array_shift($_contexte);
$__modele = interprete_argument_balise(1, $p);
$i++;
}
$squelette = preg_replace_callback(',\\\\([#[()\]{}<>]),',
- create_function('$a', "return '$inerte-'.ord(\$a[1]).'-';"), $squelette, -1, $esc);
+ function($a) use ($inerte) {
+ return "$inerte-" . ord($a[1]) . '-';
+ },
+ $squelette,
+ -1,
+ $esc
+ );
$descr = array(
'nom' => $nom,
// restituer les echappements
if ($esc) {
foreach ($boucles as $i => $boucle) {
- $boucles[$i]->return = preg_replace_callback(",$inerte-(\d+)-,", create_function('$a', 'return chr($a[1]);'),
- $boucle->return);
- $boucles[$i]->descr['squelette'] = preg_replace_callback(",$inerte-(\d+)-,",
- create_function('$a', 'return "\\\\".chr($a[1]);'),
- $boucle->descr['squelette']);
+ $boucles[$i]->return = preg_replace_callback(
+ ",$inerte-(\d+)-,",
+ function($a) {
+ return chr($a[1]);
+ },
+ $boucle->return
+ );
+ $boucles[$i]->descr['squelette'] = preg_replace_callback(
+ ",$inerte-(\d+)-,",
+ function($a) {
+ return "\\\\" . chr($a[1]);
+ },
+ $boucle->descr['squelette']
+ );
}
}
// penser a regarder aussi la clause groubpy pour ne pas simplifier abusivement
// <BOUCLE10(EVENEMENTS){id_rubrique} />#TOTAL_BOUCLE<//B10>
- list($t, $c) = each($from);
+ $t = key($from);
+ $c = current($from);
reset($from);
$e = '/\b(' . "$t\\." . join("|" . $t . '\.', $equiv) . ')\b/';
if (!(strpos($t, ' ') or // jointure des le depart cf boucle_doc
calculer_jointnul($t, $having, $e))
&& count($afrom[$t])
) {
- reset($afrom[$t]);
- list($nt, $nfrom) = each($afrom[$t]);
+ $nfrom = reset($afrom[$t]);
+ $nt = key($afrom[$t]);
unset($from[$t]);
$from[$nt] = $nfrom[1];
unset($afrom[$t][$nt]);
// Creer la fonction de filtrage sur $this
if ($this->filtre) {
- $this->func_filtre = create_function('$me', $b = 'return (' . join(') AND (', $this->filtre) . ');');
+ $filtres = 'return (' . join(') AND (', $this->filtre) . ');';
+ $this->func_filtre = function () use ($filtres) {
+ return eval($filtres);
+ };
}
}
# if (!in_array($cle, array('cle', 'valeur')))
# return;
- $a = '$me->get_select(\'' . $cle . '\')';
+ $a = '$this->get_select(\'' . $cle . '\')';
$filtre = '';
**/
public function accept() {
if ($f = $this->func_filtre) {
- return $f($this);
+ return $f();
}
return true;
$pos++;
}
// si on a pas trouve
- if ($row[$primary] != $valeur) {
+ if (!$row or $row[$primary] != $valeur) {
return 0;
}
$type_requete = isset($p->boucles[$idb]->type_requete) ? $p->boucles[$idb]->type_requete : false;
$table_sql = isset($p->boucles[$idb]->show['table_sql']) ? $p->boucles[$idb]->show['table_sql'] : false;
+ // bien prendre en compte les alias de boucles (hierarchie => rubrique, syndication => syncdic, etc.)
+ if ($type_requete and isset($GLOBALS['table_des_tables'][$type_requete])) {
+ $type_requete = $GLOBALS['table_des_tables'][$type_requete];
+ }
+
// le traitement peut n'etre defini que pour une table en particulier "spip_articles"
if ($table_sql and isset($ps[$table_sql])) {
$ps = $ps[$table_sql];
} // ou pour une boucle en particulier "DATA","articles"
elseif ($type_requete and isset($ps[$type_requete])) {
$ps = $ps[$type_requete];
- } // ou pour indiferrement quelle que soit la boucle
+ } // ou pour indifféremment quelle que soit la boucle
elseif (isset($ps[0])) {
$ps = $ps[0];
} else {
if (defined('_DEBUG_SLOW_QUERIES') and _DEBUG_SLOW_QUERIES) {
if (isset($GLOBALS['debug']['aucasou'])) {
list(, $id, , $infos) = $GLOBALS['debug']['aucasou'];
- $debug .= "BOUCLE$id @ " . $infos[0] . " | ";
+ $debug .= "BOUCLE$id @ " . (isset($infos[0]) ? $infos[0] : '') . " | ";
}
$debug .= $_SERVER['REQUEST_URI'] . ' + ' . $GLOBALS['ip'];
$debug = ' /* ' . mysqli_real_escape_string($link, str_replace('*/', '@/', $debug)) . ' */';
$nom = "$bd:$prefixe:$nom" . _LOCK_TIME;
$connexion['last'] = $q = "SELECT GET_LOCK(" . _q($nom) . ", $timeout) AS n";
- $q = @sql_fetch(mysql_query($q));
+
+ $q = @sql_fetch(mysqli_query(_mysql_link(), $q));
if (!$q) {
spip_log("pas de lock sql pour $nom", _LOG_ERREUR);
}
function _sqlite_modifier_table($table, $colonne, $opt = array(), $serveur = '') {
if (is_array($table)) {
- reset($table);
- list($table_origine, $table_destination) = each($table);
+ list($table_origine, $table_destination) = reset($table);
} else {
$table_origine = $table_destination = $table;
}
// ne prend actuellement qu'un changement
// mais pourra etre adapte pour changer plus qu'une colonne a la fois
if (is_array($colonne)) {
- reset($colonne);
- list($colonne_origine, $colonne_destination) = each($colonne);
+ list($colonne_origine, $colonne_destination) = reset($colonne);
} else {
$colonne_origine = $colonne_destination = $colonne;
}
*/
function breves_optimiser_base_disparus($flux) {
$n = &$flux['data'];
- $mydate = $flux['args']['date'];
-
+ $mydate = sql_quote($flux['args']['date']);
# les breves qui sont dans une id_rubrique inexistante
$res = sql_select(
LEFT JOIN spip_rubriques AS R
ON B.id_rubrique=R.id_rubrique',
'R.id_rubrique IS NULL
- AND B.maj < ' . sql_quote($mydate)
+ AND B.maj < ' . $mydate
);
$n += optimiser_sansref('spip_breves', 'id_breve', $res);
return preg_replace_callback(
",url\s*\(\s*['\"]?([^'\"/][^:]*[.](png|gif|jpg))['\"]?\s*\),Uims",
- create_function(
- '$x',
- 'return "url(\"".' . $filtre_embarque_fichier . '($x[1],"' . $base . '",_CSS_EMBARQUE_FICHIER_MAX_SIZE)."\")";'
- ),
+ function($x) use ($filtre_embarque_fichier, $base) {
+ return "url(\"" . $filtre_embarque_fichier($x[1], $base, _CSS_EMBARQUE_FICHIER_MAX_SIZE) . "\");";
+ },
$contenu
);
}
+++ /dev/null
-Changelog
-
-Key: # = backwards incompatible change, ! = new feature, - = bugfix.
-
-1.4, unknown release date
-# CSSTidy is now licensed under LGPL!
-! csstidy->set_cfg now accept a single parameter, an associative array, to
- set all configuration options
-! Templates can now be loaded via set_cfg using the 'template' config
- parameter
-! csstidy_print->formatted_page added, returns a full XHTML page based
- off of csstidy_print->formatted
-! cssparsed.css created, contains just the CSS for formatted CSS output
-! New CSS 3 units "ch" and "turn" added
-! Unit tests added, requires Text_Diff (PEAR) and SimpleTest
-! Some invalid selectors are now removed during optimization, this can
- be turned off by setting 'discard_invalid_selectors' to false
-- Added localizations for css_optimiser.php
-- Fixed bug with cookie setting for custom templates
-- Minor security problem in css_optimiser.php fixed
-- Fixed bug with float handling in exotic locales
-- Fixed bug with non-functioning getenv in ASAPI
-- Fixed bug with bad hexadecimal syntax recovery
-- At-selectors optimized by removing unnecessary url()
-- Fixed optimisation: 1.0 was not optimised to 1
-- Fixed incorrect parsing of !imporant close to numbers
-- Allowed Copy to Clipboard for Firefox if preference set (if not, gives
- instructions on how to add and warning re: security)
\ No newline at end of file
CSSTidy is a CSS minifier
+* css_optimiser.php is the web-interface
+* class.csstidy.php is the parser
+* bin/pcsstidy is the standalone command line executable
+
+This class represents a CSS parser which reads CSS code and saves it in an array.
+In opposite to most other CSS parsers, it does not use regular expressions and
+thus has full CSS3 support and a higher reliability. The downside of not using regular expressions
+is a lower speed though.
+Additional to that it applies some optimisations and fixes to the CSS code.
+
+
+## Usage
+
+```
+include('class.csstidy.php');
+$csstidy = new csstidy();
+
+// Set some options :
+$csstidy->set_cfg('optimise_shorthands', 2);
+$csstidy->set_cfg('template', 'high');
+
+// Parse the CSS
+$csstidy->parse($css_code);
+
+// Get back the optimized CSS Code
+$css_code_opt = $csstidy->print->plain();
+```
+
+
+## Changelog
+* v1.7.3 :
+ - fix bug and notice on reverse_left_and_right option
+* v1.7.1 :
+ - fix deprecated with PHP 7.4
+* v1.7.0 :
+ - provide bin/pcsstidy for command line usage
+ - support nested @media and @supports rules
* v1.6.5 :
- fix warnings with PHP 7.3
+ - fix warnings with PHP 7.3
* v1.6.4 :
- preserve important comments (starting with !) in the minification /*! Credits/Licence */
+ - preserve important comments (starting with !) in the minification /*! Credits/Licence */
* v1.6.3 :
- border-radius shorthands optimisation, reverse_left_and_right option
+ - border-radius shorthands optimisation, reverse_left_and_right option
* v1.5.7 :
- PHP 7 compatibility, composer update, Travis CI integration
+ - PHP 7 compatibility, composer update, Travis CI integration
* v1.5.6 :
- fixes minor bugs, mainly on CSS3 properties/units
+ - fixes minor bugs, mainly on CSS3 properties/units
* v1.5.2 :
- is PHP 5.4+ compliant, removes use of GLOBALS, fixes some bugs, integrates CSS3 units
- and now available on https://packagist.org/packages/cerdic/css-tidy
-* v1.4 : is the new version coming from master branch (corresponds to the initial trunk of svn repository) after beeing stabilized
+ - is PHP 5.4+ compliant, removes use of GLOBALS, fixes some bugs, integrates CSS3 units
+ - and now available on https://packagist.org/packages/cerdic/css-tidy
+* v1.4 :<br/>
+Is the new version coming from master branch (corresponds to the initial trunk of svn repository) after beeing stabilized
* v1.3 branch corresponds to the last stable relase published by the author.<br/>
It integrates some bugfixes and a 1.3.1 version has been taged
Since the original project (http://csstidy.sourceforge.net/index.php) has been suspended
Only PHP version is here maintained
----
+## Licence
-## CSSTidy
+ Copyright 2005-2007 Florian Schmitz
+ Copyright 2010-2019 Cedric Morin
-Original Tracker :
-http://sourceforge.net/tracker/?group_id=148404&atid=771415
-
-css_optimiser.php is the web-interface, css_parser.php contains the PHP class (CSSTidy).
-
-This class represents a CSS parser which reads CSS code and saves it in an array.
-In opposite to most other CSS parsers, it does not use regular expressions and
-thus has full CSS2 support and a higher reliability. The downside of not using regular expressions
-is a lower speed though.
-Additional to that it applies some optimisations and fixes to the CSS code.
-An online version should be available here: http://cdburnerxp.se/cssparse/css_optimiser.php
+ CSSTidy is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ CSSTidy is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
- Copyright 2005, 2006, 2007 Florian Schmitz
- CSSTidy is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- CSSTidy is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
+## History
- You should have received a copy of the GNU Lesser General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
+Original Tracker :
+http://sourceforge.net/tracker/?group_id=148404&atid=771415
* @author Mark Scherer (remove $GLOBALS once and for all + PHP5.4 comp) 2012
*/
-/**
- * Defines ctype functions if required.
- *
- * @TODO: Make these methods of CSSTidy.
- * @since 1.0.0
- */
-if (!function_exists('ctype_space')){
- /* ctype_space Check for whitespace character(s) */
- function ctype_space($text){
- return (1===preg_match("/^[ \r\n\t\f]+$/", $text));
- }
-}
-if (!function_exists('ctype_alpha')){
- /* ctype_alpha Check for alphabetic character(s) */
- function ctype_alpha($text){
- return (1===preg_match('/^[a-zA-Z]+$/', $text));
- }
-}
-if (!function_exists('ctype_xdigit')){
- /* ctype_xdigit Check for HEX character(s) */
- function ctype_xdigit($text){
- return (1===preg_match('/^[a-fA-F0-9]+$/', $text));
- }
-}
-
/**
* Defines constants
* @todo //TODO: make them class constants of csstidy
* An online version should be available here: http://cdburnerxp.se/cssparse/css_optimiser.php
* @package csstidy
* @author Florian Schmitz (floele at gmail dot com) 2005-2006
- * @version 1.6.5
+ * @version 1.7.3
*/
class csstidy {
* @var string
* @access private
*/
- public $version = '1.6.5';
+ public $version = '1.7.3';
/**
* Stores the settings
* @var array
*/
public function _add_token($type, $data, $do = false) {
if ($this->get_cfg('preserve_css') || $do) {
- $this->tokens[] = array($type, ($type == COMMENT or $type == IMPORTANT_COMMENT) ? $data : trim($data));
+ // nested @... : if opening a new part we just closed, remove the previous closing instead of adding opening
+ if ($type === AT_START
+ and count($this->tokens)
+ and $last = end($this->tokens)
+ and $last[0] === AT_END
+ and $last[1] === trim($data)) {
+ array_pop($this->tokens);
+ }
+ else {
+ $this->tokens[] = array($type, ($type == COMMENT or $type == IMPORTANT_COMMENT) ? $data : trim($data));
+ }
}
}
$add = '';
$replaced = false;
- while ($i < strlen($string) && (ctype_xdigit($string{$i}) || ctype_space($string{$i})) && strlen($add) < 6) {
- $add .= $string{$i};
+ while ($i < strlen($string) && (ctype_xdigit($string[$i]) || ctype_space($string[$i])) && strlen($add) < 6) {
+ $add .= $string[$i];
- if (ctype_space($string{$i})) {
+ if (ctype_space($string[$i])) {
break;
}
$i++;
$add = trim('\\' . $add);
}
- if (@ctype_xdigit($string{$i + 1}) && ctype_space($string{$i})
- && !$replaced || !ctype_space($string{$i})) {
+ if (@ctype_xdigit($string[$i + 1]) && ctype_space($string[$i])
+ && !$replaced || !ctype_space($string[$i])) {
$i--;
}
- if ($add !== '\\' || !$this->get_cfg('remove_bslash') || strpos($this->tokens_list, $string{$i + 1}) !== false) {
+ if ($add !== '\\' || !$this->get_cfg('remove_bslash') || strpos($this->tokens_list, $string[$i + 1]) !== false) {
return $add;
}
* @version 1.11
*/
public function is_token(&$string, $i) {
- return (strpos($this->tokens_list, $string{$i}) !== false && !$this->escaped($string, $i));
+ return (strpos($this->tokens_list, $string[$i]) !== false && !$this->escaped($string, $i));
}
/**
$this->print->input_css = $string;
$string = str_replace("\r\n", "\n", $string) . ' ';
$cur_comment = '';
+ $cur_at = '';
for ($i = 0, $size = strlen($string); $i < $size; $i++) {
- if ($string{$i} === "\n" || $string{$i} === "\r") {
+ if ($string[$i] === "\n" || $string[$i] === "\r") {
++$this->line;
}
/* Case in at-block */
case 'at':
if ($this->is_token($string, $i)) {
- if ($string{$i} === '/' && @$string{$i + 1} === '*') {
+ if ($string[$i] === '/' && @$string[$i + 1] === '*') {
$this->status = 'ic';
++$i;
$this->from[] = 'at';
- } elseif ($string{$i} === '{') {
+ } elseif ($string[$i] === '{') {
$this->status = 'is';
- $this->at = $this->css_new_media_section($this->at);
+ $this->at = $this->css_new_media_section($this->at, $cur_at);
$this->_add_token(AT_START, $this->at);
- } elseif ($string{$i} === ',') {
- $this->at = trim($this->at) . ',';
- } elseif ($string{$i} === '\\') {
- $this->at .= $this->_unicode($string, $i);
+ } elseif ($string[$i] === ',') {
+ $cur_at = trim($cur_at) . ',';
+ } elseif ($string[$i] === '\\') {
+ $cur_at .= $this->_unicode($string, $i);
}
// fix for complicated media, i.e @media screen and (-webkit-min-device-pixel-ratio:1.5)
- elseif (in_array($string{$i}, array('(', ')', ':', '.', '/'))) {
- $this->at .= $string{$i};
+ elseif (in_array($string[$i], array('(', ')', ':', '.', '/'))) {
+ $cur_at .= $string[$i];
}
} else {
- $lastpos = strlen($this->at) - 1;
- if (!( (ctype_space($this->at{$lastpos}) || $this->is_token($this->at, $lastpos) && $this->at{$lastpos} === ',') && ctype_space($string{$i}))) {
- $this->at .= $string{$i};
+ $lastpos = strlen($cur_at) - 1;
+ if (!( (ctype_space($cur_at[$lastpos]) || $this->is_token($cur_at, $lastpos) && $cur_at[$lastpos] === ',') && ctype_space($string[$i]))) {
+ $cur_at .= $string[$i];
}
}
break;
/* Case in-selector */
case 'is':
if ($this->is_token($string, $i)) {
- if ($string{$i} === '/' && @$string{$i + 1} === '*' && trim($this->selector) == '') {
+ if ($string[$i] === '/' && @$string[$i + 1] === '*' && trim($this->selector) == '') {
$this->status = 'ic';
++$i;
$this->from[] = 'is';
- } elseif ($string{$i} === '@' && trim($this->selector) == '') {
+ } elseif ($string[$i] === '@' && trim($this->selector) == '') {
// Check for at-rule
$this->invalid_at = true;
foreach ($at_rules as $name => $type) {
if (!strcasecmp(substr($string, $i + 1, strlen($name)), $name)) {
- ($type === 'at') ? $this->at = '@' . $name : $this->selector = '@' . $name;
+ ($type === 'at') ? $cur_at = '@' . $name : $this->selector = '@' . $name;
if ($type === 'atis') {
$this->next_selector_at = ($this->next_selector_at?$this->next_selector_at:($this->at?$this->at:DEFAULT_AT));
- $this->at = $this->css_new_media_section(' ');
+ $this->at = $this->css_new_media_section($this->at, ' ', true);
$type = 'is';
}
$this->status = $type;
$i += strlen($name);
$this->invalid_at = false;
+ break;
}
}
$this->selector = '@';
$invalid_at_name = '';
for ($j = $i + 1; $j < $size; ++$j) {
- if (!ctype_alpha($string{$j})) {
+ if (!ctype_alpha($string[$j])) {
break;
}
- $invalid_at_name .= $string{$j};
+ $invalid_at_name .= $string[$j];
}
$this->log('Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning');
}
- } elseif (($string{$i} === '"' || $string{$i} === "'")) {
- $this->cur_string[] = $string{$i};
+ } elseif (($string[$i] === '"' || $string[$i] === "'")) {
+ $this->cur_string[] = $string[$i];
$this->status = 'instr';
- $this->str_char[] = $string{$i};
+ $this->str_char[] = $string[$i];
$this->from[] = 'is';
/* fixing CSS3 attribute selectors, i.e. a[href$=".mp3" */
- $this->quoted_string[] = ($string{$i - 1} === '=' );
- } elseif ($this->invalid_at && $string{$i} === ';') {
+ $this->quoted_string[] = ($string[$i - 1] === '=' );
+ } elseif ($this->invalid_at && $string[$i] === ';') {
$this->invalid_at = false;
$this->status = 'is';
if ($this->next_selector_at) {
- $this->at = $this->css_new_media_section($this->next_selector_at);
+ $this->at = $this->css_close_media_section($this->at);
+ $this->at = $this->css_new_media_section($this->at, $this->next_selector_at);
$this->next_selector_at = '';
}
- } elseif ($string{$i} === '{') {
+ } elseif ($string[$i] === '{') {
$this->status = 'ip';
if ($this->at == '') {
- $this->at = $this->css_new_media_section(DEFAULT_AT);
+ $this->at = $this->css_new_media_section($this->at, DEFAULT_AT);
}
$this->selector = $this->css_new_selector($this->at,$this->selector);
$this->_add_token(SEL_START, $this->selector);
$this->added = false;
- } elseif ($string{$i} === '}') {
+ } elseif ($string[$i] === '}') {
$this->_add_token(AT_END, $this->at);
- $this->at = '';
+ $this->at = $this->css_close_media_section($this->at);
$this->selector = '';
$this->sel_separate = array();
- } elseif ($string{$i} === ',') {
+ } elseif ($string[$i] === ',') {
$this->selector = trim($this->selector) . ',';
$this->sel_separate[] = strlen($this->selector);
- } elseif ($string{$i} === '\\') {
+ } elseif ($string[$i] === '\\') {
$this->selector .= $this->_unicode($string, $i);
- } elseif ($string{$i} === '*' && @in_array($string{$i + 1}, array('.', '#', '[', ':')) && ($i==0 OR $string{$i - 1}!=='/')) {
+ } elseif ($string[$i] === '*' && @in_array($string[$i + 1], array('.', '#', '[', ':')) && ($i==0 OR $string[$i - 1]!=='/')) {
// remove unnecessary universal selector, FS#147, but not comment in selector
} else {
- $this->selector .= $string{$i};
+ $this->selector .= $string[$i];
}
} else {
$lastpos = strlen($this->selector) - 1;
- if ($lastpos == -1 || !( (ctype_space($this->selector{$lastpos}) || $this->is_token($this->selector, $lastpos) && $this->selector{$lastpos} === ',') && ctype_space($string{$i}))) {
- $this->selector .= $string{$i};
+ if ($lastpos == -1 || !( (ctype_space($this->selector[$lastpos]) || $this->is_token($this->selector, $lastpos) && $this->selector[$lastpos] === ',') && ctype_space($string[$i]))) {
+ $this->selector .= $string[$i];
}
}
break;
/* Case in-property */
case 'ip':
if ($this->is_token($string, $i)) {
- if (($string{$i} === ':' || $string{$i} === '=') && $this->property != '') {
+ if (($string[$i] === ':' || $string[$i] === '=') && $this->property != '') {
$this->status = 'iv';
if (!$this->get_cfg('discard_invalid_properties') || $this->property_is_valid($this->property)) {
$this->property = $this->css_new_property($this->at,$this->selector,$this->property);
$this->_add_token(PROPERTY, $this->property);
}
- } elseif ($string{$i} === '/' && @$string{$i + 1} === '*' && $this->property == '') {
+ } elseif ($string[$i] === '/' && @$string[$i + 1] === '*' && $this->property == '') {
$this->status = 'ic';
++$i;
$this->from[] = 'ip';
- } elseif ($string{$i} === '}') {
+ } elseif ($string[$i] === '}') {
$this->explode_selectors();
$this->status = 'is';
$this->invalid_at = false;
$this->selector = '';
$this->property = '';
if ($this->next_selector_at) {
- $this->at = $this->css_new_media_section($this->next_selector_at);
+ $this->at = $this->css_close_media_section($this->at);
+ $this->at = $this->css_new_media_section($this->at, $this->next_selector_at);
$this->next_selector_at = '';
}
- } elseif ($string{$i} === ';') {
+ } elseif ($string[$i] === ';') {
$this->property = '';
- } elseif ($string{$i} === '\\') {
+ } elseif ($string[$i] === '\\') {
$this->property .= $this->_unicode($string, $i);
}
// else this is dumb IE a hack, keep it
// including //
- elseif (($this->property === '' && !ctype_space($string{$i}))
- || ($this->property === '/' || $string{$i} === '/')) {
- $this->property .= $string{$i};
+ elseif (($this->property === '' && !ctype_space($string[$i]))
+ || ($this->property === '/' || $string[$i] === '/')) {
+ $this->property .= $string[$i];
}
- } elseif (!ctype_space($string{$i})) {
- $this->property .= $string{$i};
+ } elseif (!ctype_space($string[$i])) {
+ $this->property .= $string[$i];
}
break;
/* Case in-value */
case 'iv':
- $pn = (($string{$i} === "\n" || $string{$i} === "\r") && $this->property_is_next($string, $i + 1) || $i == strlen($string) - 1);
+ $pn = (($string[$i] === "\n" || $string[$i] === "\r") && $this->property_is_next($string, $i + 1) || $i == strlen($string) - 1);
if ($this->is_token($string, $i) || $pn) {
- if ($string{$i} === '/' && @$string{$i + 1} === '*') {
+ if ($string[$i] === '/' && @$string[$i + 1] === '*') {
$this->status = 'ic';
++$i;
$this->from[] = 'iv';
- } elseif (($string{$i} === '"' || $string{$i} === "'" || $string{$i} === '(')) {
- $this->cur_string[] = $string{$i};
- $this->str_char[] = ($string{$i} === '(') ? ')' : $string{$i};
+ } elseif (($string[$i] === '"' || $string[$i] === "'" || $string[$i] === '(')) {
+ $this->cur_string[] = $string[$i];
+ $this->str_char[] = ($string[$i] === '(') ? ')' : $string[$i];
$this->status = 'instr';
$this->from[] = 'iv';
$this->quoted_string[] = in_array(strtolower($this->property), $quoted_string_properties);
- } elseif ($string{$i} === ',') {
+ } elseif ($string[$i] === ',') {
$this->sub_value = trim($this->sub_value) . ',';
- } elseif ($string{$i} === '\\') {
+ } elseif ($string[$i] === '\\') {
$this->sub_value .= $this->_unicode($string, $i);
- } elseif ($string{$i} === ';' || $pn) {
- if ($this->selector{0} === '@' && isset($at_rules[substr($this->selector, 1)]) && $at_rules[substr($this->selector, 1)] === 'iv') {
+ } elseif ($string[$i] === ';' || $pn) {
+ if ($this->selector[0] === '@' && isset($at_rules[substr($this->selector, 1)]) && $at_rules[substr($this->selector, 1)] === 'iv') {
/* Add quotes to charset, import, namespace */
$this->sub_value_arr[] = trim($this->sub_value);
} else {
$this->status = 'ip';
}
- } elseif ($string{$i} !== '}') {
- $this->sub_value .= $string{$i};
+ } elseif ($string[$i] !== '}') {
+ $this->sub_value .= $string[$i];
}
- if (($string{$i} === '}' || $string{$i} === ';' || $pn) && !empty($this->selector)) {
+ if (($string[$i] === '}' || $string[$i] === ';' || $pn) && !empty($this->selector)) {
if ($this->at == '') {
- $this->at = $this->css_new_media_section(DEFAULT_AT);
+ $this->at = $this->css_new_media_section($this->at,DEFAULT_AT);
}
// case settings
$this->sub_value_arr = array();
$this->value = '';
}
- if ($string{$i} === '}') {
+ if ($string[$i] === '}') {
$this->explode_selectors();
$this->_add_token(SEL_END, $this->selector);
$this->status = 'is';
$this->invalid_at = false;
$this->selector = '';
if ($this->next_selector_at) {
- $this->at = $this->css_new_media_section($this->next_selector_at);
+ $this->at = $this->css_close_media_section($this->at);
+ $this->at = $this->css_new_media_section($this->at, $this->next_selector_at);
$this->next_selector_at = '';
}
}
} elseif (!$pn) {
- $this->sub_value .= $string{$i};
+ $this->sub_value .= $string[$i];
- if (ctype_space($string{$i})) {
+ if (ctype_space($string[$i])) {
$this->optimise->subvalue();
if ($this->sub_value != '') {
$this->sub_value_arr[] = $this->sub_value;
$_str_char = $this->str_char[count($this->str_char)-1];
$_cur_string = $this->cur_string[count($this->cur_string)-1];
$_quoted_string = $this->quoted_string[count($this->quoted_string)-1];
- $temp_add = $string{$i};
+ $temp_add = $string[$i];
// Add another string to the stack. Strings can't be nested inside of quotes, only parentheses, but
// parentheticals can be nested more than once.
- if ($_str_char === ")" && ($string{$i} === "(" || $string{$i} === '"' || $string{$i} === '\'') && !$this->escaped($string, $i)) {
- $this->cur_string[] = $string{$i};
- $this->str_char[] = $string{$i} === '(' ? ')' : $string{$i};
+ if ($_str_char === ")" && ($string[$i] === "(" || $string[$i] === '"' || $string[$i] === '\'') && !$this->escaped($string, $i)) {
+ $this->cur_string[] = $string[$i];
+ $this->str_char[] = $string[$i] === '(' ? ')' : $string[$i];
$this->from[] = 'instr';
- $this->quoted_string[] = ($_str_char === ')' && $string{$i} !== '(' && trim($_cur_string)==='(')?$_quoted_string:!($string{$i} === '(');
+ $this->quoted_string[] = ($_str_char === ')' && $string[$i] !== '(' && trim($_cur_string)==='(')?$_quoted_string:!($string[$i] === '(');
continue 2;
}
- if ($_str_char !== ")" && ($string{$i} === "\n" || $string{$i} === "\r") && !($string{$i - 1} === '\\' && !$this->escaped($string, $i - 1))) {
+ if ($_str_char !== ")" && ($string[$i] === "\n" || $string[$i] === "\r") && !($string[$i - 1] === '\\' && !$this->escaped($string, $i - 1))) {
$temp_add = "\\A";
$this->log('Fixed incorrect newline in string', 'Warning');
}
$_cur_string .= $temp_add;
- if ($string{$i} === $_str_char && !$this->escaped($string, $i)) {
+ if ($string[$i] === $_str_char && !$this->escaped($string, $i)) {
$this->status = array_pop($this->from);
if (!preg_match('|[' . implode('', $this->data['csstidy']['whitespace']) . ']|uis', $_cur_string) && $this->property !== 'content') {
/* Case in-comment */
case 'ic':
- if ($string{$i} === '*' && $string{$i + 1} === '/') {
+ if ($string[$i] === '*' && $string[$i + 1] === '/') {
$this->status = array_pop($this->from);
$i++;
if (strlen($cur_comment) > 1 and strncmp($cur_comment, '!', 1) === 0) {
}
$cur_comment = '';
} else {
- $cur_comment .= $string{$i};
+ $cur_comment .= $string[$i];
}
break;
}
* @version 1.02
*/
static function escaped(&$string, $pos) {
- return!(@($string{$pos - 1} !== '\\') || csstidy::escaped($string, $pos - 1));
+ return!(@($string[$pos - 1] !== '\\') || csstidy::escaped($string, $pos - 1));
}
}
/**
- * Start a new media section.
- * Check if the media is not already known,
- * else rename it with extra spaces
- * to avoid merging
+ * Check if a current media section is the continuation of the last one
+ * if not inc the name of the media section to avoid a merging
*
- * @param string $media
- * @return string
+ * @param int|string $media
+ * @return int|string
*/
- public function css_new_media_section($media) {
- if ($this->get_cfg('preserve_css')) {
+ public function css_check_last_media_section_or_inc($media) {
+ // are we starting?
+ if (!$this->css || !is_array($this->css) || empty($this->css)) {
return $media;
}
// if the last @media is the same as this
// keep it
- if (!$this->css || !is_array($this->css) || empty($this->css)) {
- return $media;
- }
end($this->css);
$at = key($this->css);
if ($at == $media) {
return $media;
}
+
+ // else inc the section in the array
while (isset($this->css[$media]))
if (is_numeric($media))
$media++;
return $media;
}
+ /**
+ * Start a new media section.
+ * Check if the media is not already known,
+ * else rename it with extra spaces
+ * to avoid merging
+ *
+ * @param string $current_media
+ * @param string $media
+ * @param bool $at_root
+ * @return string
+ */
+ public function css_new_media_section($current_media, $new_media, $at_root = false) {
+ if ($this->get_cfg('preserve_css')) {
+ return $new_media;
+ }
+
+ // if we already are in a media and CSS level is 3, manage nested medias
+ if ($current_media
+ && !$at_root
+ // numeric $current_media means DEFAULT_AT or inc
+ && !is_numeric($current_media)
+ && strncmp($this->get_cfg('css_level'), 'CSS3', 4) == 0) {
+
+ $new_media = rtrim($current_media) . "{" . rtrim($new_media);
+ }
+
+ return $this->css_check_last_media_section_or_inc($new_media);
+ }
+
+ /**
+ * Close a media section
+ * Find the parent media we were in before or the root
+ * @param $current_media
+ * @return string
+ */
+ public function css_close_media_section($current_media) {
+ if ($this->get_cfg('preserve_css')) {
+ return '';
+ }
+
+ if (strpos($current_media, '{') !== false) {
+ $current_media = explode('{', $current_media);
+ array_pop($current_media);
+ $current_media = implode('{', $current_media);
+ return $current_media;
+ }
+
+ return '';
+ }
+
/**
* Start a new selector.
* If already referenced in this media section,
/**
* Checks if a property is valid
* @param string $property
- * @return bool;
+ * @return bool
* @access public
* @version 1.0
*/
* @param string
* @return array
*/
-
public function parse_string_list($value) {
$value = trim($value);
$current_string = '';
for ($i = 0, $_len = strlen($value); $i < $_len; $i++) {
- if (($value{$i} === ',' || $value{$i} === ' ') && $in_str === true) {
+ if (($value[$i] === ',' || $value[$i] === ' ') && $in_str === true) {
$in_str = false;
$strings[] = $current_string;
$current_string = '';
- } elseif ($value{$i} === '"' || $value{$i} === "'") {
- if ($in_str === $value{$i}) {
+ } elseif ($value[$i] === '"' || $value[$i] === "'") {
+ if ($in_str === $value[$i]) {
$strings[] = $current_string;
$in_str = false;
$current_string = '';
continue;
} elseif (!$in_str) {
- $in_str = $value{$i};
+ $in_str = $value[$i];
}
} else {
if ($in_str) {
- $current_string .= $value{$i};
+ $current_string .= $value[$i];
} else {
- if (!preg_match("/[\s,]/", $value{$i})) {
+ if (!preg_match("/[\s,]/", $value[$i])) {
$in_str = true;
- $current_string = $value{$i};
+ $current_string = $value[$i];
}
}
}
// #aabbcc -> #abc
if (strlen($color) == 7) {
$color_temp = strtolower($color);
- if ($color_temp{0} === '#' && $color_temp{1} == $color_temp{2} && $color_temp{3} == $color_temp{4} && $color_temp{5} == $color_temp{6}) {
- $color = '#' . $color{1} . $color{3} . $color{5};
+ if ($color_temp[0] === '#' && $color_temp[1] == $color_temp[2] && $color_temp[3] == $color_temp[4] && $color_temp[5] == $color_temp[6]) {
+ $color = '#' . $color[1] . $color[3] . $color[5];
}
}
*/
public function AnalyseCssNumber($string) {
// most simple checks first
- if (strlen($string) == 0 || ctype_alpha($string{0})) {
+ if (strlen($string) == 0 || ctype_alpha($string[0])) {
return false;
}
for ($i = 0, $len = strlen($string); $i < $len; $i++) {
switch ($status) {
case 'st':
- if ($string{$i} == $sep && !$this->parser->escaped($string, $i)) {
+ if ($string[$i] == $sep && !$this->parser->escaped($string, $i)) {
++$num;
- } elseif ($string{$i} === '"' || $string{$i} === '\'' || (!$explode_in_parenthesis && $string{$i} === '(') && !$this->parser->escaped($string, $i)) {
+ } elseif ($string[$i] === '"' || $string[$i] === '\'' || (!$explode_in_parenthesis && $string[$i] === '(') && !$this->parser->escaped($string, $i)) {
$status = 'str';
- $to = ($string{$i} === '(') ? ')' : $string{$i};
- (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
+ $to = ($string[$i] === '(') ? ')' : $string[$i];
+ (isset($output[$num])) ? $output[$num] .= $string[$i] : $output[$num] = $string[$i];
} else {
- (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
+ (isset($output[$num])) ? $output[$num] .= $string[$i] : $output[$num] = $string[$i];
}
break;
case 'str':
- if ($string{$i} == $to && !$this->parser->escaped($string, $i)) {
+ if ($string[$i] == $to && !$this->parser->escaped($string, $i)) {
$status = 'st';
}
- (isset($output[$num])) ? $output[$num] .= $string{$i} : $output[$num] = $string{$i};
+ (isset($output[$num])) ? $output[$num] .= $string[$i] : $output[$num] = $string[$i];
break;
}
}
$have['clip'] = true;
} elseif (in_array($str_value[$i][$j], $origin, true)) {
$return['background-origin'] .= $str_value[$i][$j] . ',';
- } elseif ($str_value[$i][$j]{0} === '(') {
+ } elseif ($str_value[$i][$j][0] === '(') {
$return['background-size'] .= substr($str_value[$i][$j], 1, -1) . ',';
- } elseif (in_array($str_value[$i][$j], $pos, true) || is_numeric($str_value[$i][$j]{0}) || $str_value[$i][$j]{0} === null || $str_value[$i][$j]{0} === '-' || $str_value[$i][$j]{0} === '.') {
+ } elseif (in_array($str_value[$i][$j], $pos, true) || is_numeric($str_value[$i][$j][0]) || $str_value[$i][$j][0] === null || $str_value[$i][$j][0] === '-' || $str_value[$i][$j][0] === '.') {
$return['background-position'] .= $str_value[$i][$j];
if (!$have['pos'])
$return['background-position'] .= ' '; else
} elseif ($have['style'] === false && in_array($str_value[0][$j], $font_style)) {
$return['font-style'] = $str_value[0][$j];
$have['style'] = true;
- } elseif ($have['size'] === false && (is_numeric($str_value[0][$j]{0}) || $str_value[0][$j]{0} === null || $str_value[0][$j]{0} === '.')) {
+ } elseif ($have['size'] === false && (is_numeric($str_value[0][$j][0]) || $str_value[0][$j][0] === null || $str_value[0][$j][0] === '.')) {
$size = $this->explode_ws('/', trim($str_value[0][$j]));
$return['font-size'] = $size[0];
if (isset($size[1])) {
// Fix for 100 and more font-size
if ($have['size'] === false && isset($return['font-weight']) &&
- is_numeric($return['font-weight']{0})) {
+ is_numeric($return['font-weight'][0])) {
$return['font-size'] = $return['font-weight'];
unset($return['font-weight']);
}
$family = trim($family);
$len = strlen($family);
if (strpos($family, ' ') &&
- !(($family{0} === '"' && $family{$len - 1} === '"') ||
- ($family{0} === "'" && $family{$len - 1} === "'"))) {
+ !(($family[0] === '"' && $family[$len - 1] === '"') ||
+ ($family[0] === "'" && $family[$len - 1] === "'"))) {
$family = '"' . $family . '"';
}
$result_families[] = $family;
if (stripos($value, 'left') === false and stripos($value, 'right') === false) {
$values = $this->explode_ws(' ', trim($value));
$values = array_map('trim', $values);
- $values = array_filter($values);
+ $values = array_filter($values, function ($v) { return strlen($v);});
$values = array_values($values);
if (count($values) == 1) {
+ if (in_array($value, array('center', 'top', 'bottom', 'inherit', 'initial', 'unset'))) {
+ return $value;
+ }
return "left $value";
}
if ($values[1] == 'top' or $values[1] == 'bottom') {
+ if ($values[0] === 'center') {
+ return $value;
+ }
return 'left ' . implode(' ', $values);
}
else {
$last = array_pop($values);
+ if ($last === 'center') {
+ return $value;
+ }
return implode(' ', $values) . ' left ' . $last;
}
}
$output .= $template[0] . '@namespace ' . $template[5] . $this->namespace . $template[6] . $template[13];
}
- $in_at_out = '';
+ $in_at_out = [];
$out = & $output;
+ $indent_level = 0;
foreach ($this->tokens as $key => $token) {
switch ($token[0]) {
case AT_START:
$out .= $template[0] . $this->_htmlsp($token[1], $plain) . $template[1];
- $out = & $in_at_out;
+ $indent_level++;
+ if (!isset($in_at_out[$indent_level])) {
+ $in_at_out[$indent_level] = '';
+ }
+ $out = & $in_at_out[$indent_level];
break;
case SEL_START:
if ($this->parser->get_cfg('lowercase_s'))
$token[1] = strtolower($token[1]);
- $out .= ( $token[1]{0} !== '@') ? $template[2] . $this->_htmlsp($token[1], $plain) : $template[0] . $this->_htmlsp($token[1], $plain);
+ $out .= ( $token[1][0] !== '@') ? $template[2] . $this->_htmlsp($token[1], $plain) : $template[0] . $this->_htmlsp($token[1], $plain);
$out .= $template[3];
break;
break;
case AT_END:
- $out = & $output;
- $in_at_out = str_replace("\n\n", "\r\n", $in_at_out); // don't fill empty lines
- $in_at_out = str_replace("\n", "\n" . $template[10], $in_at_out);
- $in_at_out = str_replace("\r\n", "\n\n", $in_at_out);
- $out .= $template[10] . $in_at_out . $template[9];
- $in_at_out = '';
+ if (strlen($template[10])) {
+ // indent the bloc we are closing
+ $out = str_replace("\n\n", "\r\n", $out); // don't fill empty lines
+ $out = str_replace("\n", "\n" . $template[10], $out);
+ $out = str_replace("\r\n", "\n\n", $out);
+ }
+ if ($indent_level > 1) {
+ $out = & $in_at_out[$indent_level-1];
+ }
+ else {
+ $out = & $output;
+ }
+ $out .= $template[10] . $in_at_out[$indent_level];
+ if ($this->_seeknocomment($key, 1) != AT_END) {
+ $out .= $template[9];
+ }
+ else {
+ $out .= rtrim($template[9]);
+ }
+
+ unset($in_at_out[$indent_level]);
+ $indent_level--;
break;
case IMPORTANT_COMMENT:
if (intval($medium) < DEFAULT_AT) {
// un medium vide (contenant @font-face ou autre @) ne produit aucun conteneur
if (strlen(trim($medium))) {
- $this->parser->_add_token(AT_START, $medium, true);
+ $parts_to_open = explode('{', $medium);
+ foreach ($parts_to_open as $part) {
+ $this->parser->_add_token(AT_START, $part, true);
+ }
}
} elseif ($default_media) {
$this->parser->_add_token(AT_START, $default_media, true);
if (intval($medium) < DEFAULT_AT) {
// un medium vide (contenant @font-face ou autre @) ne produit aucun conteneur
if (strlen(trim($medium))) {
- $this->parser->_add_token(AT_END, $medium, true);
+ $parts_to_close = explode('{', $medium);
+ foreach (array_reverse($parts_to_close) as $part) {
+ $this->parser->_add_token(AT_END, $part, true);
+ }
}
} elseif ($default_media) {
$this->parser->_add_token(AT_END, $default_media, true);
* @global array $data['csstidy']['at_rules']
* @version 1.1
*/
-$data['csstidy']['at_rules'] = array('page' => 'is','font-face' => 'atis','charset' => 'iv', 'import' => 'iv','namespace' => 'iv','media' => 'at','keyframes' => 'at','-moz-keyframes' => 'at','-o-keyframes' => 'at','-webkit-keyframes' => 'at','-ms-keyframes' => 'at');
+$data['csstidy']['at_rules'] = array('page' => 'is','font-face' => 'atis','charset' => 'iv', 'import' => 'iv','namespace' => 'iv','media' => 'at', 'supports' => 'at', 'keyframes' => 'at','-moz-keyframes' => 'at','-o-keyframes' => 'at','-webkit-keyframes' => 'at','-ms-keyframes' => 'at');
/**
* Properties that need a value with unit
* @version 1.0
* @see csstidy::load_template()
*/
-$data['csstidy']['predefined_templates']['default'][] = '<span class="at">'; //string before @rule
-$data['csstidy']['predefined_templates']['default'][] = '</span> <span class="format">{</span>'."\n"; //bracket after @-rule
-$data['csstidy']['predefined_templates']['default'][] = '<span class="selector">'; //string before selector
-$data['csstidy']['predefined_templates']['default'][] = '</span> <span class="format">{</span>'."\n"; //bracket after selector
-$data['csstidy']['predefined_templates']['default'][] = '<span class="property">'; //string before property
-$data['csstidy']['predefined_templates']['default'][] = '</span><span class="value">'; //string after property+before value
-$data['csstidy']['predefined_templates']['default'][] = '</span><span class="format">;</span>'."\n"; //string after value
-$data['csstidy']['predefined_templates']['default'][] = '<span class="format">}</span>'; //closing bracket - selector
-$data['csstidy']['predefined_templates']['default'][] = "\n\n"; //space between blocks {...}
-$data['csstidy']['predefined_templates']['default'][] = "\n".'<span class="format">}</span>'. "\n\n"; //closing bracket @-rule
-$data['csstidy']['predefined_templates']['default'][] = ''; //indent in @-rule
-$data['csstidy']['predefined_templates']['default'][] = '<span class="comment">'; // before comment
-$data['csstidy']['predefined_templates']['default'][] = '</span>'."\n"; // after comment
-$data['csstidy']['predefined_templates']['default'][] = "\n"; // after each line @-rule
+$data['csstidy']['predefined_templates']['default'][0] = '<span class="at">'; //string before @rule
+$data['csstidy']['predefined_templates']['default'][1] = '</span> <span class="format">{</span>'."\n"; //bracket after @-rule
+$data['csstidy']['predefined_templates']['default'][2] = '<span class="selector">'; //string before selector
+$data['csstidy']['predefined_templates']['default'][3] = '</span> <span class="format">{</span>'."\n"; //bracket after selector
+$data['csstidy']['predefined_templates']['default'][4] = '<span class="property">'; //string before property
+$data['csstidy']['predefined_templates']['default'][5] = '</span><span class="value">'; //string after property+before value
+$data['csstidy']['predefined_templates']['default'][6] = '</span><span class="format">;</span>'."\n"; //string after value
+$data['csstidy']['predefined_templates']['default'][7] = '<span class="format">}</span>'; //closing bracket - selector
+$data['csstidy']['predefined_templates']['default'][8] = "\n\n"; //space between blocks {...}
+$data['csstidy']['predefined_templates']['default'][9] = "\n".'<span class="format">}</span>'. "\n\n"; //closing bracket @-rule
+$data['csstidy']['predefined_templates']['default'][10] = ''; //indent in @-rule
+$data['csstidy']['predefined_templates']['default'][11] = '<span class="comment">'; // before comment
+$data['csstidy']['predefined_templates']['default'][12] = '</span>'."\n"; // after comment
+$data['csstidy']['predefined_templates']['default'][13] = "\n"; // after each line @-rule
$data['csstidy']['predefined_templates']['high_compression'][] = '<span class="at">';
$data['csstidy']['predefined_templates']['high_compression'][] = '</span> <span class="format">{</span>'."\n";
<auteur>Collectif SPIP</auteur>
+ <credit lien="https://github.com/Cerdic/CSSTidy">Cerdic/CSSTidy</credit>
+
<licence lien="http://www.gnu.org/licenses/gpl-3.0.html">GPL</licence>
<traduire module="compresseur" reference="fr" gestionnaire="salvatore" />
<pipeline nom="ieconfig_metas" inclure="compresseur_ieconfig.php" />
<utilise nom="porte_plume" compatibilite="[1.18.1;]" />
+
+ <procure nom="csstidy" version="1.7.3" />
</paquet>
}
sql_updateq('spip_documents', $champs, 'id_document=' . intval($id_document));
- if ($statut !== $statut_ancien) {
+ if (!empty($champs['statut'])) {
$publier_rubriques = sql_allfetsel(
'id_objet',
'spip_documents_liens',
if (count($publier_rubriques)) {
include_spip('inc/rubriques');
foreach ($publier_rubriques as $r) {
- calculer_rubriques_if($r['id_objet'], array('statut' => $statut), $statut_ancien, false);
+ calculer_rubriques_if($r['id_objet'], array('statut' => $champs['statut']), $statut_ancien, false);
}
}
}
/**
* clear cache
*
- * @param string $filename
- * @param int $filesize
- * @param string $original_filename
+ * @param string $filename
+ * @param int $filesize
+ * @param string $original_filename
+ * @param resource $fp
*
* @return mixed
*/
- public function analyze($filename, $filesize=null, $original_filename='') {
+ public function analyze($filename, $filesize=null, $original_filename='', $fp=null) {
if (file_exists($filename)) {
}
// Miss
- $result = parent::analyze($filename);
+ $result = parent::analyze($filename, $filesize, $original_filename, $fp);
// Save result
if (isset($key) && file_exists($filename)) {
/**
* analyze file
*
- * @param string $filename
- * @param int $filesize
- * @param string $original_filename
+ * @param string $filename
+ * @param int $filesize
+ * @param string $original_filename
+ * @param resource $fp
*
* @return mixed
*/
- public function analyze($filename, $filesize=null, $original_filename='') {
+ public function analyze($filename, $filesize=null, $original_filename='', $fp=null) {
- $filetime = 0;
+ $filetime = 0;
if (file_exists($filename)) {
// Short-hands
}
// Miss
- $analysis = parent::analyze($filename, $filesize, $original_filename);
+ $analysis = parent::analyze($filename, $filesize, $original_filename, $fp);
// Save result
if (file_exists($filename)) {
*/
private $table;
+ /**
+ * @var bool
+ */
+ private $db_structure_check;
+
/**
* constructor - see top of this file for cache type and cache_options
// Connect to database
$this->mysqli = new mysqli($host, $username, $password);
- if (!$this->mysqli) {
- throw new Exception('mysqli_connect() failed - check permissions and spelling.');
+ if ($this->mysqli->connect_error) {
+ throw new Exception('Connect Error (' . $this->mysqli->connect_errno . ') ' . $this->mysqli->connect_error);
}
// Select database
// Create cache table if not exists
$this->create_table();
+ $this->db_structure_check = true; // set to false if you know your table structure has already been migrated to use `hash` as the primary key to avoid
+ $this->migrate_db_structure();
+
// Check version number and clear cache if changed
$version = '';
$SQLquery = 'SELECT `value`';
$SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`';
$SQLquery .= ' WHERE (`filename` = \''.$this->mysqli->real_escape_string(getID3::VERSION).'\')';
- $SQLquery .= ' AND (`filesize` = -1)';
- $SQLquery .= ' AND (`filetime` = -1)';
- $SQLquery .= ' AND (`analyzetime` = -1)';
+ $SQLquery .= ' AND (`hash` = \'getID3::VERSION\')';
if ($this->cursor = $this->mysqli->query($SQLquery)) {
list($version) = $this->cursor->fetch_array();
}
* clear cache
*/
public function clear_cache() {
- $this->mysqli->query('DELETE FROM `'.$this->mysqli->real_escape_string($this->table).'`');
- $this->mysqli->query('INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (\''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')');
+ $this->mysqli->query('TRUNCATE TABLE `'.$this->mysqli->real_escape_string($this->table).'`');
+ $this->mysqli->query('INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`hash`, `filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (\'getID3::VERSION\', \''.getID3::VERSION.'\', -1, -1, -1, \''.getID3::VERSION.'\')');
+ }
+
+
+ /**
+ * migrate database structure if needed
+ */
+ public function migrate_db_structure() {
+ // Check for table structure
+ if ($this->db_structure_check) {
+ $SQLquery = 'SHOW COLUMNS';
+ $SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`';
+ $SQLquery .= ' LIKE \'hash\'';
+ $this->cursor = $this->mysqli->query($SQLquery);
+ if ($this->cursor->num_rows == 0) {
+ // table has not been migrated, add column, add hashes, change index
+ $SQLquery = 'ALTER TABLE `getid3_cache` DROP PRIMARY KEY, ADD `hash` CHAR(32) NOT NULL DEFAULT \'\' FIRST, ADD PRIMARY KEY(`hash`)';
+ $this->mysqli->query($SQLquery);
+
+ $SQLquery = 'UPDATE `getid3_cache` SET';
+ $SQLquery .= ' `hash` = MD5(`filename`, `filesize`, `filetime`)';
+ $SQLquery .= ' WHERE (`filesize` > -1)';
+ $this->mysqli->query($SQLquery);
+
+ $SQLquery = 'UPDATE `getid3_cache` SET';
+ $SQLquery .= ' `hash` = \'getID3::VERSION\'';
+ $SQLquery .= ' WHERE (`filesize` = -1)';
+ $SQLquery .= ' AND (`filetime` = -1)';
+ $SQLquery .= ' AND (`filetime` = -1)';
+ $this->mysqli->query($SQLquery);
+ }
+ }
}
/**
* analyze file
*
- * @param string $filename
- * @param int $filesize
- * @param string $original_filename
+ * @param string $filename
+ * @param int $filesize
+ * @param string $original_filename
+ * @param resource $fp
*
* @return mixed
*/
- public function analyze($filename, $filesize=null, $original_filename='') {
+ public function analyze($filename, $filesize=null, $original_filename='', $fp=null) {
- $filetime = 0;
+ $filetime = 0;
if (file_exists($filename)) {
// Short-hands
// Lookup file
$SQLquery = 'SELECT `value`';
$SQLquery .= ' FROM `'.$this->mysqli->real_escape_string($this->table).'`';
- $SQLquery .= ' WHERE (`filename` = \''.$this->mysqli->real_escape_string($filename).'\')';
- $SQLquery .= ' AND (`filesize` = \''.$this->mysqli->real_escape_string($filesize).'\')';
- $SQLquery .= ' AND (`filetime` = \''.$this->mysqli->real_escape_string($filetime).'\')';
+ $SQLquery .= ' WHERE (`hash` = \''.$this->mysqli->real_escape_string(md5($filename.$filesize.$filetime)).'\')';
$this->cursor = $this->mysqli->query($SQLquery);
if ($this->cursor->num_rows > 0) {
// Hit
}
// Miss
- $analysis = parent::analyze($filename, $filesize, $original_filename);
+ $analysis = parent::analyze($filename, $filesize, $original_filename, $fp);
// Save result
if (file_exists($filename)) {
- $SQLquery = 'INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (';
- $SQLquery .= '\''.$this->mysqli->real_escape_string($filename).'\'';
+ $SQLquery = 'INSERT INTO `'.$this->mysqli->real_escape_string($this->table).'` (`hash`, `filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES (';
+ $SQLquery .= '\''.$this->mysqli->real_escape_string(md5($filename.$filesize.$filetime)).'\'';
+ $SQLquery .= ', \''.$this->mysqli->real_escape_string($filename).'\'';
$SQLquery .= ', \''.$this->mysqli->real_escape_string($filesize).'\'';
$SQLquery .= ', \''.$this->mysqli->real_escape_string($filetime).'\'';
- $SQLquery .= ', \''.$this->mysqli->real_escape_string(time() ).'\'';
- $SQLquery .= ', \''.$this->mysqli->real_escape_string(base64_encode(serialize($analysis))).'\')';
+ $SQLquery .= ', UNIX_TIMESTAMP()';
+ $SQLquery .= ', \''.$this->mysqli->real_escape_string(base64_encode(serialize($analysis))).'\'';
+ $SQLquery .= ')';
$this->cursor = $this->mysqli->query($SQLquery);
}
return $analysis;
* @param bool $drop
*/
private function create_table($drop=false) {
+ if ($drop) {
+ $SQLquery = 'DROP TABLE IF EXISTS `'.$this->mysqli->real_escape_string($this->table).'`';
+ $this->mysqli->query($SQLquery);
+ }
$SQLquery = 'CREATE TABLE IF NOT EXISTS `'.$this->mysqli->real_escape_string($this->table).'` (';
- $SQLquery .= '`filename` VARCHAR(990) NOT NULL DEFAULT \'\'';
+ $SQLquery .= '`hash` CHAR(32) NOT NULL DEFAULT \'\'';
+ $SQLquery .= ', `filename` VARCHAR(1000) NOT NULL DEFAULT \'\'';
$SQLquery .= ', `filesize` INT(11) NOT NULL DEFAULT \'0\'';
$SQLquery .= ', `filetime` INT(11) NOT NULL DEFAULT \'0\'';
$SQLquery .= ', `analyzetime` INT(11) NOT NULL DEFAULT \'0\'';
$SQLquery .= ', `value` LONGTEXT NOT NULL';
- $SQLquery .= ', PRIMARY KEY (`filename`, `filesize`, `filetime`))';
+ $SQLquery .= ', PRIMARY KEY (`hash`))';
$this->cursor = $this->mysqli->query($SQLquery);
echo $this->mysqli->error;
}
/**
* analyze file and cache them, if cached pull from the db
*
- * @param string $filename
- * @param integer $filesize
- * @param string $original_filename
+ * @param string $filename
+ * @param integer $filesize
+ * @param string $original_filename
+ * @param resource $fp
*
* @return mixed|false
*/
- public function analyze($filename, $filesize=null, $original_filename='') {
+ public function analyze($filename, $filesize=null, $original_filename='', $fp=null) {
if (!file_exists($filename)) {
return false;
}
return unserialize(base64_decode($result));
}
// if it hasn't been analyzed before, then do it now
- $analysis = parent::analyze($filename, $filesize, $original_filename);
+ $analysis = parent::analyze($filename, $filesize, $original_filename, $fp);
// Save result
$sql = $this->getQuery('cache_file');
$stmt = $db->prepare($sql);
switch ($name) {
case 'version_check':
return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = '-1' AND filetime = '-1' AND analyzetime = '-1'";
- break;
case 'delete_cache':
return "DELETE FROM $this->table";
- break;
case 'set_version':
return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, -1, -1, -1, :val)";
- break;
case 'get_id3_data':
return "SELECT val FROM $this->table WHERE filename = :filename AND filesize = :filesize AND filetime = :filetime";
- break;
case 'cache_file':
return "INSERT INTO $this->table (filename, dirname, filesize, filetime, analyzetime, val) VALUES (:filename, :dirname, :filesize, :filetime, :atime, :val)";
- break;
case 'make_table':
return "CREATE TABLE IF NOT EXISTS $this->table (filename VARCHAR(255) DEFAULT '', dirname VARCHAR(255) DEFAULT '', filesize INT(11) DEFAULT '0', filetime INT(11) DEFAULT '0', analyzetime INT(11) DEFAULT '0', val text, PRIMARY KEY (filename, filesize, filetime))";
- break;
case 'get_cached_dir':
return "SELECT val FROM $this->table WHERE dirname = :dirname";
- break;
default:
return null;
}
class getid3_lib
{
/**
- * @param string $string
- * @param bool $hex
- * @param bool $spaces
- * @param string $htmlencoding
+ * @param string $string
+ * @param bool $hex
+ * @param bool $spaces
+ * @param string|bool $htmlencoding
*
* @return string
*/
$returnstring = '';
for ($i = 0; $i < strlen($string); $i++) {
if ($hex) {
- $returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
+ $returnstring .= str_pad(dechex(ord($string[$i])), 2, '0', STR_PAD_LEFT);
} else {
- $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string{$i}) ? $string{$i} : '¤');
+ $returnstring .= ' '.(preg_match("#[\x20-\x7E]#", $string[$i]) ? $string[$i] : '¤');
}
if ($spaces) {
$returnstring .= ' ';
public static function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
if (strpos($binarypointnumber, '.') === false) {
$binarypointnumber = '0.'.$binarypointnumber;
- } elseif ($binarypointnumber{0} == '.') {
+ } elseif ($binarypointnumber[0] == '.') {
$binarypointnumber = '0'.$binarypointnumber;
}
$exponent = 0;
- while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
+ while (($binarypointnumber[0] != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
if (substr($binarypointnumber, 1, 1) == '.') {
$exponent--;
$binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
$pointpos = strpos($binarypointnumber, '.');
$exponent += ($pointpos - 1);
$binarypointnumber = str_replace('.', '', $binarypointnumber);
- $binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
+ $binarypointnumber = $binarypointnumber[0].'.'.substr($binarypointnumber, 1);
}
}
$binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
default:
return false;
- break;
}
if ($floatvalue >= 0) {
$signbit = '0';
if (!$bitword) {
return 0;
}
- $signbit = $bitword{0};
+ $signbit = $bitword[0];
$floatvalue = 0;
$exponentbits = 0;
$fractionbits = 0;
// 80-bit Apple SANE format
// http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
$exponentstring = substr($bitword, 1, 15);
- $isnormalized = intval($bitword{16});
+ $isnormalized = intval($bitword[16]);
$fractionstring = substr($bitword, 17, 63);
$exponent = pow(2, self::Bin2Dec($exponentstring) - 16383);
$fraction = $isnormalized + self::DecimalBinary2Float($fractionstring);
$floatvalue *= -1;
}
return $floatvalue;
- break;
default:
return false;
- break;
}
$exponentstring = substr($bitword, 1, $exponentbits);
$fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
for ($i = 0; $i < $bytewordlen; $i++) {
if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
//$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7); // faster, but runs into problems past 2^31 on 32-bit systems
- $intvalue += (ord($byteword{$i}) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
+ $intvalue += (ord($byteword[$i]) & 0x7F) * pow(2, ($bytewordlen - 1 - $i) * 7);
} else {
- $intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
+ $intvalue += ord($byteword[$i]) * pow(256, ($bytewordlen - 1 - $i));
}
}
if ($signed && !$synchsafe) {
$binvalue = '';
$bytewordlen = strlen($byteword);
for ($i = 0; $i < $bytewordlen; $i++) {
- $binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
+ $binvalue .= str_pad(decbin(ord($byteword[$i])), 8, '0', STR_PAD_LEFT);
}
return $binvalue;
}
public static function Bin2Dec($binstring, $signed=false) {
$signmult = 1;
if ($signed) {
- if ($binstring{0} == '1') {
+ if ($binstring[0] == '1') {
$signmult = -1;
}
$binstring = substr($binstring, 1);
}
/**
- * @param array $array1
- * @param array $array2
+ * @param mixed $array1
+ * @param mixed $array2
*
* @return array|false
*/
}
/**
- * @param array $array1
- * @param array $array2
+ * @param mixed $array1
+ * @param mixed $array2
*
* @return array|false
*/
}
/**
- * @param array $array1
- * @param array $array2
+ * @param mixed $array1
+ * @param mixed $array2
*
* @return array|false|null
*/
*/
public static function array_max($arraydata, $returnkey=false) {
$maxvalue = false;
- $maxkey = false;
+ $maxkey = false;
foreach ($arraydata as $key => $value) {
if (!is_array($value)) {
- if ($value > $maxvalue) {
+ if (($maxvalue === false) || ($value > $maxvalue)) {
$maxvalue = $value;
$maxkey = $key;
}
*/
public static function array_min($arraydata, $returnkey=false) {
$minvalue = false;
- $minkey = false;
+ $minkey = false;
foreach ($arraydata as $key => $value) {
if (!is_array($value)) {
- if ($value > $minvalue) {
+ if (($minvalue === false) || ($value < $minvalue)) {
$minvalue = $value;
$minkey = $key;
}
}
/**
- * @param SimpleXMLElement|array $XMLobject
+ * @param SimpleXMLElement|array|mixed $XMLobject
*
- * @return array
+ * @return mixed
*/
public static function SimpleXMLelement2array($XMLobject) {
if (!is_object($XMLobject) && !is_array($XMLobject)) {
}
/**
- * self::md5_data() - returns md5sum for a file from startuing position to absolute end position
- *
- * @author Allan Hansen <ahØartemis*dk>
+ * Returns checksum for a file from starting position to absolute end position.
*
* @param string $file
* @param int $offset
* @param string $algorithm
*
* @return string|false
- * @throws Exception
* @throws getid3_exception
*/
public static function hash_data($file, $offset, $end, $algorithm) {
- static $tempdir = '';
- $windows_call = null;
- $unix_call = null;
- $hash_length = null;
- $hash_function = null;
if (!self::intValueSupported($end)) {
return false;
}
- switch ($algorithm) {
- case 'md5':
- $hash_function = 'md5_file';
- $unix_call = 'md5sum';
- $windows_call = 'md5sum.exe';
- $hash_length = 32;
- break;
-
- case 'sha1':
- $hash_function = 'sha1_file';
- $unix_call = 'sha1sum';
- $windows_call = 'sha1sum.exe';
- $hash_length = 40;
- break;
-
- default:
- throw new Exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
- break;
+ if (!in_array($algorithm, array('md5', 'sha1'))) {
+ throw new getid3_exception('Invalid algorithm ('.$algorithm.') in self::hash_data()');
}
- $size = $end - $offset;
- while (true) {
- if (GETID3_OS_ISWINDOWS) {
- // It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
- // Fall back to create-temp-file method:
- if ($algorithm == 'sha1') {
- break;
- }
-
- $RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
- foreach ($RequiredFiles as $required_file) {
- if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
- // helper apps not available - fall back to old method
- break 2;
- }
- }
- $commandline = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' '.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).' | ';
- $commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
- $commandline .= GETID3_HELPERAPPSDIR.$windows_call;
-
- } else {
-
- $commandline = 'head -c'.$end.' '.escapeshellarg($file).' | ';
- $commandline .= 'tail -c'.$size.' | ';
- $commandline .= $unix_call;
+ $size = $end - $offset;
- }
- if (preg_match('#(1|ON)#i', ini_get('safe_mode'))) {
- //throw new Exception('PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm');
- break;
- }
- return substr(`$commandline`, 0, $hash_length);
+ $fp = fopen($file, 'rb');
+ fseek($fp, $offset);
+ $ctx = hash_init($algorithm);
+ while ($size > 0) {
+ $buffer = fread($fp, min($size, getID3::FREAD_BUFFER_SIZE));
+ hash_update($ctx, $buffer);
+ $size -= getID3::FREAD_BUFFER_SIZE;
}
+ $hash = hash_final($ctx);
+ fclose($fp);
- if (empty($tempdir)) {
- // yes this is ugly, feel free to suggest a better way
- require_once(dirname(__FILE__).'/getid3.php');
- $getid3_temp = new getID3();
- $tempdir = $getid3_temp->tempdir;
- unset($getid3_temp);
- }
- // try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
- if (($data_filename = tempnam($tempdir, 'gI3')) === false) {
- // can't find anywhere to create a temp file, just fail
- return false;
- }
-
- // Init
- $result = false;
-
- // copy parts of file
- try {
- self::CopyFileParts($file, $data_filename, $offset, $end - $offset);
- $result = $hash_function($data_filename);
- } catch (Exception $e) {
- throw new Exception('self::CopyFileParts() failed in getid_lib::hash_data(): '.$e->getMessage());
- }
- unlink($data_filename);
- return $result;
+ return $hash;
}
/**
*
* @return bool
* @throws Exception
+ *
+ * @deprecated Unused, may be removed in future versions of getID3
*/
public static function CopyFileParts($filename_source, $filename_dest, $offset, $length) {
if (!self::intValueSupported($offset + $length)) {
$newcharstring .= "\xEF\xBB\xBF";
}
for ($i = 0; $i < strlen($string); $i++) {
- $charval = ord($string{$i});
+ $charval = ord($string[$i]);
$newcharstring .= self::iconv_fallback_int_utf8($charval);
}
return $newcharstring;
$newcharstring .= "\xFE\xFF";
}
for ($i = 0; $i < strlen($string); $i++) {
- $newcharstring .= "\x00".$string{$i};
+ $newcharstring .= "\x00".$string[$i];
}
return $newcharstring;
}
$newcharstring .= "\xFF\xFE";
}
for ($i = 0; $i < strlen($string); $i++) {
- $newcharstring .= $string{$i}."\x00";
+ $newcharstring .= $string[$i]."\x00";
}
return $newcharstring;
}
$offset = 0;
$stringlength = strlen($string);
while ($offset < $stringlength) {
- if ((ord($string{$offset}) | 0x07) == 0xF7) {
+ if ((ord($string[$offset]) | 0x07) == 0xF7) {
// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
- $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
- ((ord($string{($offset + 1)}) & 0x3F) << 12) &
- ((ord($string{($offset + 2)}) & 0x3F) << 6) &
- (ord($string{($offset + 3)}) & 0x3F);
+ $charval = ((ord($string[($offset + 0)]) & 0x07) << 18) &
+ ((ord($string[($offset + 1)]) & 0x3F) << 12) &
+ ((ord($string[($offset + 2)]) & 0x3F) << 6) &
+ (ord($string[($offset + 3)]) & 0x3F);
$offset += 4;
- } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
+ } elseif ((ord($string[$offset]) | 0x0F) == 0xEF) {
// 1110bbbb 10bbbbbb 10bbbbbb
- $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
- ((ord($string{($offset + 1)}) & 0x3F) << 6) &
- (ord($string{($offset + 2)}) & 0x3F);
+ $charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) &
+ ((ord($string[($offset + 1)]) & 0x3F) << 6) &
+ (ord($string[($offset + 2)]) & 0x3F);
$offset += 3;
- } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
+ } elseif ((ord($string[$offset]) | 0x1F) == 0xDF) {
// 110bbbbb 10bbbbbb
- $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
- (ord($string{($offset + 1)}) & 0x3F);
+ $charval = ((ord($string[($offset + 0)]) & 0x1F) << 6) &
+ (ord($string[($offset + 1)]) & 0x3F);
$offset += 2;
- } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
+ } elseif ((ord($string[$offset]) | 0x7F) == 0x7F) {
// 0bbbbbbb
- $charval = ord($string{$offset});
+ $charval = ord($string[$offset]);
$offset += 1;
} else {
// error? throw some kind of warning here?
$offset = 0;
$stringlength = strlen($string);
while ($offset < $stringlength) {
- if ((ord($string{$offset}) | 0x07) == 0xF7) {
+ if ((ord($string[$offset]) | 0x07) == 0xF7) {
// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
- $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
- ((ord($string{($offset + 1)}) & 0x3F) << 12) &
- ((ord($string{($offset + 2)}) & 0x3F) << 6) &
- (ord($string{($offset + 3)}) & 0x3F);
+ $charval = ((ord($string[($offset + 0)]) & 0x07) << 18) &
+ ((ord($string[($offset + 1)]) & 0x3F) << 12) &
+ ((ord($string[($offset + 2)]) & 0x3F) << 6) &
+ (ord($string[($offset + 3)]) & 0x3F);
$offset += 4;
- } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
+ } elseif ((ord($string[$offset]) | 0x0F) == 0xEF) {
// 1110bbbb 10bbbbbb 10bbbbbb
- $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
- ((ord($string{($offset + 1)}) & 0x3F) << 6) &
- (ord($string{($offset + 2)}) & 0x3F);
+ $charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) &
+ ((ord($string[($offset + 1)]) & 0x3F) << 6) &
+ (ord($string[($offset + 2)]) & 0x3F);
$offset += 3;
- } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
+ } elseif ((ord($string[$offset]) | 0x1F) == 0xDF) {
// 110bbbbb 10bbbbbb
- $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
- (ord($string{($offset + 1)}) & 0x3F);
+ $charval = ((ord($string[($offset + 0)]) & 0x1F) << 6) &
+ (ord($string[($offset + 1)]) & 0x3F);
$offset += 2;
- } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
+ } elseif ((ord($string[$offset]) | 0x7F) == 0x7F) {
// 0bbbbbbb
- $charval = ord($string{$offset});
+ $charval = ord($string[$offset]);
$offset += 1;
} else {
// error? throw some kind of warning here?
$offset = 0;
$stringlength = strlen($string);
while ($offset < $stringlength) {
- if ((ord($string{$offset}) | 0x07) == 0xF7) {
+ if ((ord($string[$offset]) | 0x07) == 0xF7) {
// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
- $charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
- ((ord($string{($offset + 1)}) & 0x3F) << 12) &
- ((ord($string{($offset + 2)}) & 0x3F) << 6) &
- (ord($string{($offset + 3)}) & 0x3F);
+ $charval = ((ord($string[($offset + 0)]) & 0x07) << 18) &
+ ((ord($string[($offset + 1)]) & 0x3F) << 12) &
+ ((ord($string[($offset + 2)]) & 0x3F) << 6) &
+ (ord($string[($offset + 3)]) & 0x3F);
$offset += 4;
- } elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
+ } elseif ((ord($string[$offset]) | 0x0F) == 0xEF) {
// 1110bbbb 10bbbbbb 10bbbbbb
- $charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
- ((ord($string{($offset + 1)}) & 0x3F) << 6) &
- (ord($string{($offset + 2)}) & 0x3F);
+ $charval = ((ord($string[($offset + 0)]) & 0x0F) << 12) &
+ ((ord($string[($offset + 1)]) & 0x3F) << 6) &
+ (ord($string[($offset + 2)]) & 0x3F);
$offset += 3;
- } elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
+ } elseif ((ord($string[$offset]) | 0x1F) == 0xDF) {
// 110bbbbb 10bbbbbb
- $charval = ((ord($string{($offset + 0)}) & 0x1F) << 6) &
- (ord($string{($offset + 1)}) & 0x3F);
+ $charval = ((ord($string[($offset + 0)]) & 0x1F) << 6) &
+ (ord($string[($offset + 1)]) & 0x3F);
$offset += 2;
- } elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
+ } elseif ((ord($string[$offset]) | 0x7F) == 0x7F) {
// 0bbbbbbb
- $charval = ord($string{$offset});
+ $charval = ord($string[$offset]);
$offset += 1;
} else {
// error? maybe throw some warning here?
// mb_convert_encoding() available
if (function_exists('mb_convert_encoding')) {
+ if ((strtoupper($in_charset) == 'UTF-16') && (substr($string, 0, 2) != "\xFE\xFF") && (substr($string, 0, 2) != "\xFF\xFE")) {
+ // if BOM missing, mb_convert_encoding will mishandle the conversion, assume UTF-16BE and prepend appropriate BOM
+ $string = "\xFF\xFE".$string;
+ }
+ if ((strtoupper($in_charset) == 'UTF-16') && (strtoupper($out_charset) == 'UTF-8')) {
+ if (($string == "\xFF\xFE") || ($string == "\xFE\xFF")) {
+ // if string consists of only BOM, mb_convert_encoding will return the BOM unmodified
+ return '';
+ }
+ }
if ($converted_string = @mb_convert_encoding($string, $out_charset, $in_charset)) {
switch ($out_charset) {
case 'ISO-8859-1':
return $converted_string;
}
return $string;
- }
+
// iconv() available
- else if (function_exists('iconv')) {
+ } elseif (function_exists('iconv')) {
if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
switch ($out_charset) {
case 'ISO-8859-1':
case 'utf-8':
$strlen = strlen($string);
for ($i = 0; $i < $strlen; $i++) {
- $char_ord_val = ord($string{$i});
+ $char_ord_val = ord($string[$i]);
$charval = 0;
if ($char_ord_val < 0x80) {
$charval = $char_ord_val;
} elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F && $i+3 < $strlen) {
$charval = (($char_ord_val & 0x07) << 18);
- $charval += ((ord($string{++$i}) & 0x3F) << 12);
- $charval += ((ord($string{++$i}) & 0x3F) << 6);
- $charval += (ord($string{++$i}) & 0x3F);
+ $charval += ((ord($string[++$i]) & 0x3F) << 12);
+ $charval += ((ord($string[++$i]) & 0x3F) << 6);
+ $charval += (ord($string[++$i]) & 0x3F);
} elseif ((($char_ord_val & 0xE0) >> 5) == 0x07 && $i+2 < $strlen) {
$charval = (($char_ord_val & 0x0F) << 12);
- $charval += ((ord($string{++$i}) & 0x3F) << 6);
- $charval += (ord($string{++$i}) & 0x3F);
+ $charval += ((ord($string[++$i]) & 0x3F) << 6);
+ $charval += (ord($string[++$i]) & 0x3F);
} elseif ((($char_ord_val & 0xC0) >> 6) == 0x03 && $i+1 < $strlen) {
$charval = (($char_ord_val & 0x1F) << 6);
- $charval += (ord($string{++$i}) & 0x3F);
+ $charval += (ord($string[++$i]) & 0x3F);
}
if (($charval >= 32) && ($charval <= 127)) {
$HTMLstring .= htmlentities(chr($charval));
* @return array|false
*/
public static function GetDataImageSize($imgData, &$imageinfo=array()) {
+ if (PHP_VERSION_ID >= 50400) {
+ $GetDataImageSize = @getimagesizefromstring($imgData, $imageinfo);
+ if ($GetDataImageSize === false || !isset($GetDataImageSize[0], $GetDataImageSize[1])) {
+ return false;
+ }
+ $GetDataImageSize['height'] = $GetDataImageSize[0];
+ $GetDataImageSize['width'] = $GetDataImageSize[1];
+ return $GetDataImageSize;
+ }
static $tempdir = '';
if (empty($tempdir)) {
if (function_exists('sys_get_temp_dir')) {
// yes this is ugly, feel free to suggest a better way
if (include_once(dirname(__FILE__).'/getid3.php')) {
- if ($getid3_temp = new getID3()) {
- if ($getid3_temp_tempdir = $getid3_temp->tempdir) {
- $tempdir = $getid3_temp_tempdir;
- }
- unset($getid3_temp, $getid3_temp_tempdir);
+ $getid3_temp = new getID3();
+ if ($getid3_temp_tempdir = $getid3_temp->tempdir) {
+ $tempdir = $getid3_temp_tempdir;
}
+ unset($getid3_temp, $getid3_temp_tempdir);
}
}
$GetDataImageSize = false;
/**
* @param array $ThisFileInfo
+ * @param bool $option_tags_html default true (just as in the main getID3 class)
*
* @return bool
*/
- public static function CopyTagsToComments(&$ThisFileInfo) {
-
+ public static function CopyTagsToComments(&$ThisFileInfo, $option_tags_html=true) {
// Copy all entries from ['tags'] into common ['comments']
if (!empty($ThisFileInfo['tags'])) {
+ if (isset($ThisFileInfo['tags']['id3v1'])) {
+ // bubble ID3v1 to the end, if present to aid in detecting bad ID3v1 encodings
+ $ID3v1 = $ThisFileInfo['tags']['id3v1'];
+ unset($ThisFileInfo['tags']['id3v1']);
+ $ThisFileInfo['tags']['id3v1'] = $ID3v1;
+ unset($ID3v1);
+ }
foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
foreach ($tagarray as $tagname => $tagdata) {
foreach ($tagdata as $key => $value) {
break 2;
}
}
+ if (function_exists('mb_convert_encoding')) {
+ if (trim($value) == trim(substr(mb_convert_encoding($existingvalue, $ThisFileInfo['id3v1']['encoding'], $ThisFileInfo['encoding']), 0, 30))) {
+ // value stored in ID3v1 appears to be probably the multibyte value transliterated (badly) into ISO-8859-1 in ID3v1.
+ // As an example, Foobar2000 will do this if you tag a file with Chinese or Arabic or Cyrillic or something that doesn't fit into ISO-8859-1 the ID3v1 will consist of mostly "?" characters, one per multibyte unrepresentable character
+ break 2;
+ }
+ }
} elseif (!is_array($value)) {
$oldvaluelength = strlen(trim($existingvalue));
if ((strlen($existingvalue) > 10) && ($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
$ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
- //break 2;
break;
}
}
if (!is_int($key) && !ctype_digit($key)) {
$ThisFileInfo['comments'][$tagname][$key] = $value;
} else {
- if (isset($ThisFileInfo['comments'][$tagname])) {
+ if (!isset($ThisFileInfo['comments'][$tagname])) {
$ThisFileInfo['comments'][$tagname] = array($value);
} else {
$ThisFileInfo['comments'][$tagname][] = $value;
}
}
- // Copy to ['comments_html']
- if (!empty($ThisFileInfo['comments'])) {
- foreach ($ThisFileInfo['comments'] as $field => $values) {
- if ($field == 'picture') {
- // pictures can take up a lot of space, and we don't need multiple copies of them
- // let there be a single copy in [comments][picture], and not elsewhere
- continue;
- }
- foreach ($values as $index => $value) {
- if (is_array($value)) {
- $ThisFileInfo['comments_html'][$field][$index] = $value;
- } else {
- $ThisFileInfo['comments_html'][$field][$index] = str_replace('�', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
+ if ($option_tags_html) {
+ // Copy ['comments'] to ['comments_html']
+ if (!empty($ThisFileInfo['comments'])) {
+ foreach ($ThisFileInfo['comments'] as $field => $values) {
+ if ($field == 'picture') {
+ // pictures can take up a lot of space, and we don't need multiple copies of them
+ // let there be a single copy in [comments][picture], and not elsewhere
+ continue;
+ }
+ foreach ($values as $index => $value) {
+ if (is_array($value)) {
+ $ThisFileInfo['comments_html'][$field][$index] = $value;
+ } else {
+ $ThisFileInfo['comments_html'][$field][$index] = str_replace('�', '', self::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
+ }
}
}
}
<?php
-
/////////////////////////////////////////////////////////////////
/// getID3() by James Heinrich <info@getid3.org> //
// available at https://github.com/JamesHeinrich/getID3 //
*/
public $encoding_id3v1 = 'ISO-8859-1';
+ /**
+ * ID3v1 should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'Windows-1251' or 'KOI8-R'. If true attempt to detect these encodings, but may return incorrect values for some tags actually in ISO-8859-1 encoding
+ *
+ * @var bool
+ */
+ public $encoding_id3v1_autodetect = false;
+
/*
* Optional tag checks - disable for speed.
*/
*/
protected $startup_warning = '';
- const VERSION = '1.9.16-201810171314';
+ const VERSION = '1.9.20-202006061653';
const FREAD_BUFFER_SIZE = 32768;
const ATTACHMENTS_NONE = false;
}
// Check memory
- $this->memory_limit = ini_get('memory_limit');
- if (preg_match('#([0-9]+) ?M#i', $this->memory_limit, $matches)) {
+ $memoryLimit = ini_get('memory_limit');
+ if (preg_match('#([0-9]+) ?M#i', $memoryLimit, $matches)) {
// could be stored as "16M" rather than 16777216 for example
- $this->memory_limit = $matches[1] * 1048576;
- } elseif (preg_match('#([0-9]+) ?G#i', $this->memory_limit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
+ $memoryLimit = $matches[1] * 1048576;
+ } elseif (preg_match('#([0-9]+) ?G#i', $memoryLimit, $matches)) { // The 'G' modifier is available since PHP 5.1.0
// could be stored as "2G" rather than 2147483648 for example
- $this->memory_limit = $matches[1] * 1073741824;
+ $memoryLimit = $matches[1] * 1073741824;
}
+ $this->memory_limit = $memoryLimit;
+
if ($this->memory_limit <= 0) {
// memory limits probably disabled
} elseif ($this->memory_limit <= 4194304) {
$this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
}
- if (($mbstring_func_overload = ini_get('mbstring.func_overload')) && ($mbstring_func_overload & 0x02)) {
+ if (($mbstring_func_overload = (int) ini_get('mbstring.func_overload')) && ($mbstring_func_overload & 0x02)) {
// http://php.net/manual/en/mbstring.overload.php
// "mbstring.func_overload in php.ini is a positive value that represents a combination of bitmasks specifying the categories of functions to be overloaded. It should be set to 1 to overload the mail() function. 2 for string functions, 4 for regular expression functions"
// getID3 cannot run when string functions are overloaded. It doesn't matter if mail() or ereg* functions are overloaded since getID3 does not use those.
$this->startup_error .= 'WARNING: php.ini contains "mbstring.func_overload = '.ini_get('mbstring.func_overload').'", getID3 cannot run with this setting (bitmask 2 (string functions) cannot be set). Recommended to disable entirely.'."\n";
}
- // Check for magic_quotes_runtime
- if (function_exists('get_magic_quotes_runtime')) {
- if (get_magic_quotes_runtime()) {
- $this->startup_error .= 'magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).'."\n";
+ // check for magic quotes in PHP < 7.4.0 (when these functions became deprecated)
+ if (version_compare(PHP_VERSION, '7.4.0', '<')) {
+ // Check for magic_quotes_runtime
+ if (function_exists('get_magic_quotes_runtime')) {
+ if (get_magic_quotes_runtime()) {
+ $this->startup_error .= 'magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).'."\n";
+ }
}
- }
-
- // Check for magic_quotes_gpc
- if (function_exists('magic_quotes_gpc')) {
- if (get_magic_quotes_gpc()) {
- $this->startup_error .= 'magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).'."\n";
+ // Check for magic_quotes_gpc
+ if (function_exists('get_magic_quotes_gpc')) {
+ if (get_magic_quotes_gpc()) {
+ $this->startup_error .= 'magic_quotes_gpc must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_gpc(0) and set_magic_quotes_gpc(1).'."\n";
+ }
}
}
// Needed for Windows only:
// Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
- // as well as other helper functions such as head, tail, md5sum, etc
+ // as well as other helper functions such as head, etc
// This path cannot contain spaces, but the below code will attempt to get the
// 8.3-equivalent path automatically
// IMPORTANT: This path must include the trailing slash
}
/**
- * @param string $filename
- * @param int $filesize
+ * @param string $filename
+ * @param int $filesize
+ * @param resource $fp
*
* @return bool
*
* @throws getid3_exception
*/
- public function openfile($filename, $filesize=null) {
+ public function openfile($filename, $filesize=null, $fp=null) {
try {
if (!empty($this->startup_error)) {
throw new getid3_exception($this->startup_error);
// open local file
//if (is_readable($filename) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) { // see https://www.getid3.org/phpBB3/viewtopic.php?t=1720
- if ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
+ if (($fp != null) && ((get_resource_type($fp) == 'file') || (get_resource_type($fp) == 'stream'))) {
+ $this->fp = $fp;
+ } elseif ((is_readable($filename) || file_exists($filename)) && is_file($filename) && ($this->fp = fopen($filename, 'rb'))) {
// great
} else {
$errormessagelist = array();
/**
* analyze file
*
- * @param string $filename
- * @param int $filesize
- * @param string $original_filename
+ * @param string $filename
+ * @param int $filesize
+ * @param string $original_filename
+ * @param resource $fp
*
* @return array
*/
- public function analyze($filename, $filesize=null, $original_filename='') {
+ public function analyze($filename, $filesize=null, $original_filename='', $fp=null) {
try {
- if (!$this->openfile($filename, $filesize)) {
+ if (!$this->openfile($filename, $filesize, $fp)) {
return $this->info;
}
$header = fread($this->fp, 10);
if ((substr($header, 0, 3) == 'ID3') && (strlen($header) == 10)) {
$this->info['id3v2']['header'] = true;
- $this->info['id3v2']['majorversion'] = ord($header{3});
- $this->info['id3v2']['minorversion'] = ord($header{4});
+ $this->info['id3v2']['majorversion'] = ord($header[3]);
+ $this->info['id3v2']['minorversion'] = ord($header[4]);
$this->info['avdataoffset'] += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
}
}
// DSS - audio - Digital Speech Standard
'dss' => array(
- 'pattern' => '^[\\x02-\\x06]ds[s2]',
+ 'pattern' => '^[\\x02-\\x08]ds[s2]',
'group' => 'audio',
'module' => 'dss',
'mime_type' => 'application/octet-stream',
),
+ // DSDIFF - audio - Direct Stream Digital Interchange File Format
+ 'dsdiff' => array(
+ 'pattern' => '^FRM8',
+ 'group' => 'audio',
+ 'module' => 'dsdiff',
+ 'mime_type' => 'audio/dsd',
+ ),
+
// DTS - audio - Dolby Theatre System
'dts' => array(
'pattern' => '^\\x7F\\xFE\\x80\\x01',
'fail_ape' => 'ERROR',
),
+ // TAK - audio - Tom's lossless Audio Kompressor
+ 'tak' => array(
+ 'pattern' => '^tBaK',
+ 'group' => 'audio',
+ 'module' => 'tak',
+ 'mime_type' => 'application/octet-stream',
+ ),
+
// TTA - audio - TTA Lossless Audio Compressor (http://tta.corecodec.org)
'tta' => array(
'pattern' => '^TTA', // could also be '^TTA(\\x01|\\x02|\\x03|2|1)'
'mime_type' => 'video/x-flv',
),
+ // IVF - audio/video - IVF
+ 'ivf' => array(
+ 'pattern' => '^DKIF',
+ 'group' => 'audio-video',
+ 'module' => 'ivf',
+ 'mime_type' => 'video/x-ivf',
+ ),
+
// MKAV - audio/video - Mastroka
'matroska' => array(
'pattern' => '^\\x1A\\x45\\xDF\\xA3',
'mime_type' => 'video/MP2T',
),
+ // WTV - audio/video - Windows Recorded TV Show
+ 'wtv' => array(
+ 'pattern' => '^\\xB7\\xD8\\x00\\x20\\x37\\x49\\xDA\\x11\\xA6\\x4E\\x00\\x07\\xE9\\x5E\\xAD\\x8D',
+ 'group' => 'audio-video',
+ 'module' => 'wtv',
+ 'mime_type' => 'video/x-ms-wtv',
+ ),
+
// Still-Image formats
'iconv_req' => false,
),
+ // HPK - data - HPK compressed data
+ 'hpk' => array(
+ 'pattern' => '^BPUL',
+ 'group' => 'archive',
+ 'module' => 'hpk',
+ 'mime_type' => 'application/octet-stream',
+ 'fail_id3' => 'ERROR',
+ 'fail_ape' => 'ERROR',
+ ),
+
// RAR - data - RAR compressed data
'rar' => array(
'pattern' => '^Rar\\!',
'group' => 'archive',
'module' => 'rar',
- 'mime_type' => 'application/octet-stream',
+ 'mime_type' => 'application/vnd.rar',
'fail_id3' => 'ERROR',
'fail_ape' => 'ERROR',
),
'fail_ape' => 'ERROR',
),
+ // XZ - data - XZ compressed data
+ 'xz' => array(
+ 'pattern' => '^\\xFD7zXZ\\x00',
+ 'group' => 'archive',
+ 'module' => 'xz',
+ 'mime_type' => 'application/x-xz',
+ 'fail_id3' => 'ERROR',
+ 'fail_ape' => 'ERROR',
+ ),
+
// Misc other formats
if (preg_match('#\\.mp[123a]$#i', $filename)) {
- // Too many mp3 encoders on the market put gabage in front of mpeg files
+ // Too many mp3 encoders on the market put garbage in front of mpeg files
// use assume format on these if format detection failed
$GetFileFormatArray = $this->GetFileFormatArray();
$info = $GetFileFormatArray['mp3'];
'flac' => array('vorbiscomment' , 'UTF-8'),
'divxtag' => array('divx' , 'ISO-8859-1'),
'iptc' => array('iptc' , 'ISO-8859-1'),
+ 'dsdiff' => array('dsdiff' , 'ISO-8859-1'),
);
}
}
}
if ($tag_key == 'picture') {
+ // pictures can take up a lot of space, and we don't need multiple copies of them; let there be a single copy in [comments][picture], and not elsewhere
unset($this->info[$comment_name]['comments'][$tag_key]);
}
}
if ($this->option_tags_html) {
foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
+ if ($tag_key == 'picture') {
+ // Do not to try to convert binary picture data to HTML
+ // https://github.com/JamesHeinrich/getID3/issues/178
+ continue;
+ }
$this->info['tags_html'][$tag_name][$tag_key] = getid3_lib::recursiveMultiByteCharString2HTML($valuearray, $this->info[$comment_name]['encoding']);
}
}
}
- // pictures can take up a lot of space, and we don't need multiple copies of them
- // let there be a single copy in [comments][picture], and not elsewhere
+ // pictures can take up a lot of space, and we don't need multiple copies of them; let there be a single copy in [comments][picture], and not elsewhere
if (!empty($this->info['tags'])) {
$unset_keys = array('tags', 'tags_html');
foreach ($this->info['tags'] as $tagtype => $tagarray) {
return true;
}
+ /**
+ * Calls getid3_lib::CopyTagsToComments() but passes in the option_tags_html setting from this instance of getID3
+ *
+ * @param array $ThisFileInfo
+ *
+ * @return bool
+ */
+ public function CopyTagsToComments(&$ThisFileInfo) {
+ return getid3_lib::CopyTagsToComments($ThisFileInfo, $this->option_tags_html);
+ }
+
/**
* @param string $algorithm
*
default:
return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
- break;
}
if (!empty($this->info['fileformat']) && !empty($this->info['dataformat']) && ($this->info['fileformat'] == 'ogg') && ($this->info['audio']['dataformat'] == 'vorbis')) {
*
* @return bool
*/
- public static function is_writable ($filename) {
- $ret = is_writable($filename);
-
- if (!$ret) {
- $perms = fileperms($filename);
- $ret = ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002);
- }
-
- return $ret;
- }
+ public static function is_writable ($filename) {
+ $ret = is_writable($filename);
+ if (!$ret) {
+ $perms = fileperms($filename);
+ $ret = ($perms & 0x0080) || ($perms & 0x0010) || ($perms & 0x0002);
+ }
+ return $ret;
+ }
}
*/
$contents = '';
do {
- if (($this->getid3->memory_limit > 0) && ($bytes > $this->getid3->memory_limit)) {
+ //if (($this->getid3->memory_limit > 0) && ($bytes > $this->getid3->memory_limit)) {
+ if (($this->getid3->memory_limit > 0) && (($bytes / $this->getid3->memory_limit) > 0.99)) { // enable a more-fuzzy match to prevent close misses generating errors like "PHP Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 33554464 bytes)"
throw new getid3_exception('cannot fread('.$bytes.' from '.$this->ftell().') that is more than available PHP memory ('.$this->getid3->memory_limit.')', 10);
}
$part = fread($this->getid3->fp, $bytes);
return fseek($this->getid3->fp, $bytes, $whence);
}
+ /**
+ * @return string|false
+ *
+ * @throws getid3_exception
+ */
+ protected function fgets() {
+ // must be able to handle CR/LF/CRLF but not read more than one lineend
+ $buffer = ''; // final string we will return
+ $prevchar = ''; // save previously-read character for end-of-line checking
+ if ($this->data_string_flag) {
+ while (true) {
+ $thischar = substr($this->data_string, $this->data_string_position++, 1);
+ if (($prevchar == "\r") && ($thischar != "\n")) {
+ // read one byte too many, back up
+ $this->data_string_position--;
+ break;
+ }
+ $buffer .= $thischar;
+ if ($thischar == "\n") {
+ break;
+ }
+ if ($this->data_string_position >= $this->data_string_length) {
+ // EOF
+ break;
+ }
+ $prevchar = $thischar;
+ }
+
+ } else {
+
+ // Ideally we would just use PHP's fgets() function, however...
+ // it does not behave consistently with regards to mixed line endings, may be system-dependent
+ // and breaks entirely when given a file with mixed \r vs \n vs \r\n line endings (e.g. some PDFs)
+ //return fgets($this->getid3->fp);
+ while (true) {
+ $thischar = fgetc($this->getid3->fp);
+ if (($prevchar == "\r") && ($thischar != "\n")) {
+ // read one byte too many, back up
+ fseek($this->getid3->fp, -1, SEEK_CUR);
+ break;
+ }
+ $buffer .= $thischar;
+ if ($thischar == "\n") {
+ break;
+ }
+ if (feof($this->getid3->fp)) {
+ break;
+ }
+ $prevchar = $thischar;
+ }
+
+ }
+ return $buffer;
+ }
+
/**
* @return bool
*/
// //
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_gzip extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_rar extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_szip extends getid3_handler
{
// //
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_tar extends getid3_handler
{
// check the block
$checksum = 0;
for ($i = 0; $i < 148; $i++) {
- $checksum += ord($buffer{$i});
+ $checksum += ord($buffer[$i]);
}
for ($i = 148; $i < 156; $i++) {
$checksum += ord(' ');
}
for ($i = 156; $i < 512; $i++) {
- $checksum += ord($buffer{$i});
+ $checksum += ord($buffer[$i]);
}
$attr = unpack($unpack_header, $buffer);
$name = (isset($attr['fname'] ) ? trim($attr['fname'] ) : '');
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_zip extends getid3_handler
{
}
}
+ // check for EPUB files
+ if (!empty($info['zip']['entries'][0]['filename']) &&
+ ($info['zip']['entries'][0]['filename'] == 'mimetype') &&
+ ($info['zip']['entries'][0]['compression_method'] == 'store') &&
+ ($info['zip']['entries'][0]['uncompressed_size'] == 20) &&
+ isset($info['zip']['entries'][0]['data_offset'])) {
+ // http://idpf.org/epub/30/spec/epub30-ocf.html
+ // "3.3 OCF ZIP Container Media Type Identification
+ // OCF ZIP Containers must include a mimetype file as the first file in the Container, and the contents of this file must be the MIME type string application/epub+zip.
+ // The contents of the mimetype file must not contain any leading padding or whitespace, must not begin with the Unicode signature (or Byte Order Mark),
+ // and the case of the MIME type string must be exactly as presented above. The mimetype file additionally must be neither compressed nor encrypted,
+ // and there must not be an extra field in its ZIP header."
+ $this->fseek($info['zip']['entries'][0]['data_offset']);
+ if ($this->fread(20) == 'application/epub+zip') {
+ $info['fileformat'] = 'zip.epub';
+ $info['mime_type'] = 'application/epub+zip';
+ }
+ }
+
+ // check for Office Open XML files (e.g. .docx, .xlsx)
if (!empty($info['zip']['files']['[Content_Types].xml']) &&
!empty($info['zip']['files']['_rels']['.rels']) &&
!empty($info['zip']['files']['docProps']['app.xml']) &&
!empty($info['zip']['files']['docProps']['core.xml'])) {
- // http://technet.microsoft.com/en-us/library/cc179224.aspx
- $info['fileformat'] = 'zip.msoffice';
- if (!empty($ThisFileInfo['zip']['files']['ppt'])) {
- $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
- } elseif (!empty($ThisFileInfo['zip']['files']['xl'])) {
- $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
- } elseif (!empty($ThisFileInfo['zip']['files']['word'])) {
- $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
- }
+ // http://technet.microsoft.com/en-us/library/cc179224.aspx
+ $info['fileformat'] = 'zip.msoffice';
+ if (!empty($ThisFileInfo['zip']['files']['ppt'])) {
+ $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
+ } elseif (!empty($ThisFileInfo['zip']['files']['xl'])) {
+ $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
+ } elseif (!empty($ThisFileInfo['zip']['files']['word'])) {
+ $info['mime_type'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
+ }
}
return true;
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_asf extends getid3_handler
$thisfile_audio['codec'] = $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['name']);
if (!isset($thisfile_audio['bitrate']) && strstr($AudioCodecBitrate, 'kbps')) {
- $thisfile_audio['bitrate'] = (int) (trim(str_replace('kbps', '', $AudioCodecBitrate)) * 1000);
+ $thisfile_audio['bitrate'] = (int) trim(str_replace('kbps', '', $AudioCodecBitrate)) * 1000;
}
//if (!isset($thisfile_video['bitrate']) && isset($thisfile_audio['bitrate']) && isset($thisfile_asf['file_properties_object']['max_bitrate']) && ($thisfile_asf_codeclistobject['codec_entries_count'] > 1)) {
if (empty($thisfile_video['bitrate']) && !empty($thisfile_audio['bitrate']) && !empty($info['bitrate'])) {
case 'wm/tracknumber':
case 'tracknumber':
// be careful casting to int: casting unicode strings to int gives unexpected results (stops parsing at first non-numeric character)
- $thisfile_asf_comments['track'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
- foreach ($thisfile_asf_comments['track'] as $key => $value) {
+ $thisfile_asf_comments['track_number'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+ foreach ($thisfile_asf_comments['track_number'] as $key => $value) {
if (preg_match('/^[0-9\x00]+$/', $value)) {
- $thisfile_asf_comments['track'][$key] = intval(str_replace("\x00", '', $value));
+ $thisfile_asf_comments['track_number'][$key] = intval(str_replace("\x00", '', $value));
}
}
break;
case 'wm/track':
- if (empty($thisfile_asf_comments['track'])) {
- $thisfile_asf_comments['track'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+ if (empty($thisfile_asf_comments['track_number'])) {
+ $thisfile_asf_comments['track_number'] = array(1 + (int) $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
}
break;
* @return string
*/
public static function BytestringToGUID($Bytestring) {
- $GUIDstring = str_pad(dechex(ord($Bytestring{3})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{2})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{1})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{0})), 2, '0', STR_PAD_LEFT);
+ $GUIDstring = str_pad(dechex(ord($Bytestring[3])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[2])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[1])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[0])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= '-';
- $GUIDstring .= str_pad(dechex(ord($Bytestring{5})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{4})), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[5])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[4])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= '-';
- $GUIDstring .= str_pad(dechex(ord($Bytestring{7})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{6})), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[7])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[6])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= '-';
- $GUIDstring .= str_pad(dechex(ord($Bytestring{8})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{9})), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[8])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[9])), 2, '0', STR_PAD_LEFT);
$GUIDstring .= '-';
- $GUIDstring .= str_pad(dechex(ord($Bytestring{10})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{11})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{12})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{13})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{14})), 2, '0', STR_PAD_LEFT);
- $GUIDstring .= str_pad(dechex(ord($Bytestring{15})), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[10])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[11])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[12])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[13])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[14])), 2, '0', STR_PAD_LEFT);
+ $GUIDstring .= str_pad(dechex(ord($Bytestring[15])), 2, '0', STR_PAD_LEFT);
return strtoupper($GUIDstring);
}
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_bink extends getid3_handler
{
switch ($fileTypeID) {
case 'BIK':
return $this->ParseBink();
- break;
case 'SMK':
return $this->ParseSmacker();
- break;
default:
$this->error('Expecting "BIK" or "SMK" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($fileTypeID).'"');
return false;
- break;
}
-
- return true;
-
}
/**
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
+
define('GETID3_FLV_TAG_AUDIO', 8);
define('GETID3_FLV_TAG_VIDEO', 9);
define('GETID3_FLV_TAG_META', 18);
// null
case 6:
return null;
- break;
// Mixed array
case 8:
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
define('EBML_ID_CHAPTERS', 0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation.
define('EBML_ID_SEEKHEAD', 0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
break;*/
}
- $info['video']['streams'][] = $track_info;
+ $info['video']['streams'][$trackarray['TrackUID']] = $track_info;
break;
case 2: // Audio
switch ($trackarray['CodecID']) {
case 'A_PCM/INT/LIT':
case 'A_PCM/INT/BIG':
- $track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth'];
+ $track_info['bitrate'] = $track_info['sample_rate'] * $track_info['channels'] * $trackarray['BitDepth'];
break;
case 'A_AC3':
// create temp instance
$getid3_temp = new getID3();
if ($track_info['dataformat'] != 'flac') {
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
}
$getid3_temp->info['avdataoffset'] = $info['matroska']['track_data_offsets'][$trackarray['TrackNumber']]['offset'];
if ($track_info['dataformat'][0] == 'm' || $track_info['dataformat'] == 'flac') {
break;
}
- $info['audio']['streams'][] = $track_info;
+ $info['audio']['streams'][$trackarray['TrackUID']] = $track_info;
break;
}
}
unset($info['mime_type']);
}
+ // use _STATISTICS_TAGS if available to set audio/video bitrates
+ if (!empty($info['matroska']['tags'])) {
+ $_STATISTICS_byTrackUID = array();
+ foreach ($info['matroska']['tags'] as $key1 => $value1) {
+ if (!empty($value1['Targets']['TagTrackUID'][0]) && !empty($value1['SimpleTag'])) {
+ foreach ($value1['SimpleTag'] as $key2 => $value2) {
+ if (!empty($value2['TagName']) && isset($value2['TagString'])) {
+ $_STATISTICS_byTrackUID[$value1['Targets']['TagTrackUID'][0]][$value2['TagName']] = $value2['TagString'];
+ }
+ }
+ }
+ }
+ foreach (array('audio','video') as $avtype) {
+ if (!empty($info[$avtype]['streams'])) {
+ foreach ($info[$avtype]['streams'] as $trackUID => $trackdata) {
+ if (!isset($trackdata['bitrate']) && !empty($_STATISTICS_byTrackUID[$trackUID]['BPS'])) {
+ $info[$avtype]['streams'][$trackUID]['bitrate'] = (int) $_STATISTICS_byTrackUID[$trackUID]['BPS'];
+ @$info[$avtype]['bitrate'] += $info[$avtype]['streams'][$trackUID]['bitrate'];
+ }
+ }
+ }
+ }
+ }
+
return true;
}
while ($this->getEBMLelement($subelement, $track_entry['end'], array(EBML_ID_VIDEO, EBML_ID_AUDIO, EBML_ID_CONTENTENCODINGS, EBML_ID_CODECPRIVATE))) {
switch ($subelement['id']) {
- case EBML_ID_TRACKNUMBER:
case EBML_ID_TRACKUID:
+ $track_entry[$subelement['id_name']] = getid3_lib::PrintHexBytes($subelement['data'], true, false);
+ break;
+ case EBML_ID_TRACKNUMBER:
case EBML_ID_TRACKTYPE:
case EBML_ID_MINCACHE:
case EBML_ID_MAXCACHE:
case EBML_ID_TAGEDITIONUID:
case EBML_ID_TAGCHAPTERUID:
case EBML_ID_TAGATTACHMENTUID:
- $targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::BigEndian2Int($sub_sub_subelement['data']);
+ $targets_entry[$sub_sub_subelement['id_name']][] = getid3_lib::PrintHexBytes($sub_sub_subelement['data'], true, false);
break;
default:
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
class getid3_mpeg extends getid3_handler
break;
case 0xB3: // sequence_header_code
- /*
- Note: purposely doing the less-pretty (and probably a bit slower) method of using string of bits rather than bitwise operations.
- Mostly because PHP 32-bit doesn't handle unsigned integers well for bitwise operation.
- Also the MPEG stream is designed as a bitstream and often doesn't align nicely with byte boundaries.
- */
+ // Note: purposely doing the less-pretty (and probably a bit slower) method of using string of bits rather than bitwise operations.
+ // Mostly because PHP 32-bit doesn't handle unsigned integers well for bitwise operation.
+ // Also the MPEG stream is designed as a bitstream and often doesn't align nicely with byte boundaries.
$info['video']['codec'] = 'MPEG-1'; // will be updated if extension_start_code found
$bitstream = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $StartCodeOffset + 4, 8));
$info['mpeg']['packed_elementary_streams'][$PackedElementaryStream['stream_type']][$PackedElementaryStream['stream_id']][] = $PackedElementaryStream;
*/
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_temp->info = $info;
$getid3_mp3 = new getid3_mp3($getid3_temp);
for ($i = 0; $i <= 7; $i++) {
* @param int $bits_to_read
* @param bool $return_singlebit_as_boolean
*
- * @return bool|float|int
+ * @return bool|int
*/
private function readBitsFromStream(&$bitstream, &$bitstreamoffset, $bits_to_read, $return_singlebit_as_boolean=true) {
$return = bindec(substr($bitstream, $bitstreamoffset, $bits_to_read));
/**
* @param int $rawaspectratio
- * @param int $mpeg_version
+ * @param int $mpeg_version
*
* @return string
*/
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_nsv extends getid3_handler
{
default:
$this->error('Expecting "NSVs" or "NSVf" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($NSVheader).'"');
return false;
- break;
}
if (!isset($info['nsv']['NSVf'])) {
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup
$offset = 0;
$atomcounter = 0;
- $atom_data_read_buffer_size = max($this->getid3->option_fread_buffer_size * 1024, ($info['php_memory_limit'] ? round($info['php_memory_limit'] / 4) : 1024)); // set read buffer to 25% of PHP memory limit (if one is specified), otherwise use option_fread_buffer_size [default: 32MB]
+ $atom_data_read_buffer_size = $info['php_memory_limit'] ? round($info['php_memory_limit'] / 4) : $this->getid3->option_fread_buffer_size * 1024; // set read buffer to 25% of PHP memory limit (if one is specified), otherwise use option_fread_buffer_size [default: 32MB]
while ($offset < $info['avdataend']) {
if (!getid3_lib::intValueSupported($offset)) {
$this->error('Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions');
$atomsize = getid3_lib::BigEndian2Int($this->fread(8));
}
- $info['quicktime'][$atomname]['name'] = $atomname;
- $info['quicktime'][$atomname]['size'] = $atomsize;
- $info['quicktime'][$atomname]['offset'] = $offset;
-
if (($offset + $atomsize) > $info['avdataend']) {
+ $info['quicktime'][$atomname]['name'] = $atomname;
+ $info['quicktime'][$atomname]['size'] = $atomsize;
+ $info['quicktime'][$atomname]['offset'] = $offset;
$this->error('Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)');
return false;
}
-
if ($atomsize == 0) {
// Furthermore, for historical reasons the list of atoms is optionally
// terminated by a 32-bit integer set to 0. If you are writing a program
// to read user data atoms, you should allow for the terminating 0.
+ $info['quicktime'][$atomname]['name'] = $atomname;
+ $info['quicktime'][$atomname]['size'] = $atomsize;
+ $info['quicktime'][$atomname]['offset'] = $offset;
break;
}
+
$atomHierarchy = array();
- $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
+ $parsedAtomData = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
+ $parsedAtomData['name'] = $atomname;
+ $parsedAtomData['size'] = $atomsize;
+ $parsedAtomData['offset'] = $offset;
+ if (in_array($atomname, array('uuid'))) {
+ @$info['quicktime'][$atomname][] = $parsedAtomData;
+ } else {
+ $info['quicktime'][$atomname] = $parsedAtomData;
+ }
$offset += $atomsize;
$atomcounter++;
if (!empty($info['quicktime']['comments']['location.ISO6709'])) {
// https://en.wikipedia.org/wiki/ISO_6709
foreach ($info['quicktime']['comments']['location.ISO6709'] as $ISO6709string) {
- $latitude = false;
- $longitude = false;
- $altitude = false;
+ $ISO6709parsed = array('latitude'=>false, 'longitude'=>false, 'altitude'=>false);
if (preg_match('#^([\\+\\-])([0-9]{2}|[0-9]{4}|[0-9]{6})(\\.[0-9]+)?([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?(([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?)?/$#', $ISO6709string, $matches)) {
@list($dummy, $lat_sign, $lat_deg, $lat_deg_dec, $lon_sign, $lon_deg, $lon_deg_dec, $dummy, $alt_sign, $alt_deg, $alt_deg_dec) = $matches;
if (strlen($lat_deg) == 2) { // [+-]DD.D
- $latitude = floatval(ltrim($lat_deg, '0').$lat_deg_dec);
+ $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim($lat_deg, '0').$lat_deg_dec);
} elseif (strlen($lat_deg) == 4) { // [+-]DDMM.M
- $latitude = floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0').$lat_deg_dec / 60);
+ $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0').$lat_deg_dec / 60);
} elseif (strlen($lat_deg) == 6) { // [+-]DDMMSS.S
- $latitude = floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lat_deg, 4, 2), '0').$lat_deg_dec / 3600);
+ $ISO6709parsed['latitude'] = (($lat_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lat_deg, 4, 2), '0').$lat_deg_dec / 3600);
}
if (strlen($lon_deg) == 3) { // [+-]DDD.D
- $longitude = floatval(ltrim($lon_deg, '0').$lon_deg_dec);
+ $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim($lon_deg, '0').$lon_deg_dec);
} elseif (strlen($lon_deg) == 5) { // [+-]DDDMM.M
- $longitude = floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0').$lon_deg_dec / 60);
+ $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0').$lon_deg_dec / 60);
} elseif (strlen($lon_deg) == 7) { // [+-]DDDMMSS.S
- $longitude = floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lon_deg, 4, 2), '0').$lon_deg_dec / 3600);
+ $ISO6709parsed['longitude'] = (($lon_sign == '-') ? -1 : 1) * floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lon_deg, 4, 2), '0').$lon_deg_dec / 3600);
}
if (strlen($alt_deg) == 3) { // [+-]DDD.D
- $altitude = floatval(ltrim($alt_deg, '0').$alt_deg_dec);
+ $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim($alt_deg, '0').$alt_deg_dec);
} elseif (strlen($alt_deg) == 5) { // [+-]DDDMM.M
- $altitude = floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0').$alt_deg_dec / 60);
+ $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0').$alt_deg_dec / 60);
} elseif (strlen($alt_deg) == 7) { // [+-]DDDMMSS.S
- $altitude = floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($alt_deg, 4, 2), '0').$alt_deg_dec / 3600);
+ $ISO6709parsed['altitude'] = (($alt_sign == '-') ? -1 : 1) * floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($alt_deg, 4, 2), '0').$alt_deg_dec / 3600);
}
- if ($latitude !== false) {
- $info['quicktime']['comments']['gps_latitude'][] = (($lat_sign == '-') ? -1 : 1) * floatval($latitude);
- }
- if ($longitude !== false) {
- $info['quicktime']['comments']['gps_longitude'][] = (($lon_sign == '-') ? -1 : 1) * floatval($longitude);
- }
- if ($altitude !== false) {
- $info['quicktime']['comments']['gps_altitude'][] = (($alt_sign == '-') ? -1 : 1) * floatval($altitude);
+ foreach (array('latitude', 'longitude', 'altitude') as $key) {
+ if ($ISO6709parsed[$key] !== false) {
+ $value = (($lat_sign == '-') ? -1 : 1) * floatval($ISO6709parsed[$key]);
+ if (!isset($info['quicktime']['comments']['gps_'.$key]) || !in_array($value, $info['quicktime']['comments']['gps_'.$key])) {
+ @$info['quicktime']['comments']['gps_'.$key][] = (($lat_sign == '-') ? -1 : 1) * floatval($ISO6709parsed[$key]);
+ }
+ }
}
}
- if ($latitude === false) {
+ if ($ISO6709parsed['latitude'] === false) {
$this->warning('location.ISO6709 string not parsed correctly: "'.$ISO6709string.'", please submit as a bug');
}
break;
$atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see https://www.getid3.org/phpBB3/viewtopic.php?t=1717
array_push($atomHierarchy, $atomname);
+ $atom_structure = array();
$atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
$atom_structure['name'] = $atomname;
$atom_structure['size'] = $atomsize;
case 'mdia': // MeDIA container atom
case 'minf': // Media INFormation container atom
case 'dinf': // Data INFormation container atom
+ case 'nmhd': // Null Media HeaDer container atom
case 'udta': // User DaTA container atom
case 'cmov': // Compressed MOVie container atom
case 'rmra': // Reference Movie Record Atom
} elseif (preg_match('#^GIF#', $atom_structure['data'])) {
$atom_structure['image_mime'] = 'image/gif';
}
+ $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover');
break;
case 'atID':
} elseif (preg_match('#^GIF#', $atom_structure['data'])) {
$atom_structure['image_mime'] = 'image/gif';
}
+ $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover');
}
break;
}
}
}
- $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
- break;
+ $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
+ break;
case 'play': // auto-PLAY atom
$atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
$stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
+ if (substr($atom_structure['sample_description_table'][$i]['data'], 1, 54) == 'application/octet-stream;type=com.parrot.videometadata') {
+ // special handling for apparently-malformed (TextMetaDataSampleEntry?) data for some version of Parrot drones
+ $atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['mime_type'] = substr($atom_structure['sample_description_table'][$i]['data'], 1, 55);
+ $atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['metadata_version'] = (int) substr($atom_structure['sample_description_table'][$i]['data'], 55, 1);
+ unset($atom_structure['sample_description_table'][$i]['data']);
+$this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in this version of getID3() ['.$this->getid3->version().']');
+ continue;
+ }
+
$atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2));
$atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2));
$atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
case 'stco': // Sample Table Chunk Offset atom
+// if (true) {
if ($ParseAllPossibleAtoms) {
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
$atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
$atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
$atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
- $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24));
+ $atom_structure['component_name'] = $this->MaybePascal2String(substr($atom_data, 24));
if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
$info['video']['dataformat'] = 'quicktimevr';
if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
$info['comments']['language'][] = $atom_structure['language'];
}
+ $info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix'];
+ $info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix'];
break;
$atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
$atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
+ $info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modification_date_unix'];
break;
}
$atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
$atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
+ $info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix'];
+ $info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix'];
$info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
$info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
$info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
$atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008);
$atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
$atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
+ $info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix'];
+ $info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix'];
// https://www.getid3.org/phpBB3/viewtopic.php?t=1908
// attempt to compute rotation from matrix values
$info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_mp3 = new getid3_mp3($getid3_temp);
}
break;
+ case 'uuid': // user-defined atom often seen containing XML data, also used for potentially many other purposes, only a few specifically handled by getID3 (e.g. 360fly spatial data)
+ //Get the UUID ID in first 16 bytes
+ $uuid_bytes_read = unpack('H8time_low/H4time_mid/H4time_hi/H4clock_seq_hi/H12clock_seq_low', substr($atom_data, 0, 16));
+ $atom_structure['uuid_field_id'] = implode('-', $uuid_bytes_read);
+
+ switch ($atom_structure['uuid_field_id']) { // http://fileformats.archiveteam.org/wiki/Boxes/atoms_format#UUID_boxes
+
+ case '0537cdab-9d0c-4431-a72a-fa561f2a113e': // Exif - http://fileformats.archiveteam.org/wiki/Exif
+ case '2c4c0100-8504-40b9-a03e-562148d6dfeb': // Photoshop Image Resources - http://fileformats.archiveteam.org/wiki/Photoshop_Image_Resources
+ case '33c7a4d2-b81d-4723-a0ba-f1a3e097ad38': // IPTC-IIM - http://fileformats.archiveteam.org/wiki/IPTC-IIM
+ case '8974dbce-7be7-4c51-84f9-7148f9882554': // PIFF Track Encryption Box - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format
+ case '96a9f1f1-dc98-402d-a7ae-d68e34451809': // GeoJP2 World File Box - http://fileformats.archiveteam.org/wiki/GeoJP2
+ case 'a2394f52-5a9b-4f14-a244-6c427c648df4': // PIFF Sample Encryption Box - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format
+ case 'b14bf8bd-083d-4b43-a5ae-8cd7d5a6ce03': // GeoJP2 GeoTIFF Box - http://fileformats.archiveteam.org/wiki/GeoJP2
+ case 'd08a4f18-10f3-4a82-b6c8-32d8aba183d3': // PIFF Protection System Specific Header Box - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format
+ $this->warning('Unhandled (but recognized) "uuid" atom identified by "'.$atom_structure['uuid_field_id'].'" at offset '.$atom_structure['offset'].' ('.strlen($atom_data).' bytes)');
+ break;
+
+ case 'be7acfcb-97a9-42e8-9c71-999491e3afac': // XMP data (in XML format)
+ $atom_structure['xml'] = substr($atom_data, 16, strlen($atom_data) - 16 - 8); // 16 bytes for UUID, 8 bytes header(?)
+ break;
+
+ case 'efe1589a-bb77-49ef-8095-27759eb1dc6f': // 360fly data
+ /* 360fly code in this block by Paul Lewis 2019-Oct-31 */
+ /* Sensor Timestamps need to be calculated using the recordings base time at ['quicktime']['moov']['subatoms'][0]['creation_time_unix']. */
+ $atom_structure['title'] = '360Fly Sensor Data';
+
+ //Get the UUID HEADER data
+ $uuid_bytes_read = unpack('vheader_size/vheader_version/vtimescale/vhardware_version/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/', substr($atom_data, 16, 32));
+ $atom_structure['uuid_header'] = $uuid_bytes_read;
+
+ $start_byte = 48;
+ $atom_SENSOR_data = substr($atom_data, $start_byte);
+ $atom_structure['sensor_data']['data_type'] = array(
+ 'fusion_count' => 0, // ID 250
+ 'fusion_data' => array(),
+ 'accel_count' => 0, // ID 1
+ 'accel_data' => array(),
+ 'gyro_count' => 0, // ID 2
+ 'gyro_data' => array(),
+ 'magno_count' => 0, // ID 3
+ 'magno_data' => array(),
+ 'gps_count' => 0, // ID 5
+ 'gps_data' => array(),
+ 'rotation_count' => 0, // ID 6
+ 'rotation_data' => array(),
+ 'unknown_count' => 0, // ID ??
+ 'unknown_data' => array(),
+ 'debug_list' => '', // Used to debug variables stored as comma delimited strings
+ );
+ $debug_structure['debug_items'] = array();
+ // Can start loop here to decode all sensor data in 32 Byte chunks:
+ foreach (str_split($atom_SENSOR_data, 32) as $sensor_key => $sensor_data) {
+ // This gets me a data_type code to work out what data is in the next 31 bytes.
+ $sensor_data_type = substr($sensor_data, 0, 1);
+ $sensor_data_content = substr($sensor_data, 1);
+ $uuid_bytes_read = unpack('C*', $sensor_data_type);
+ $sensor_data_array = array();
+ switch ($uuid_bytes_read[1]) {
+ case 250:
+ $atom_structure['sensor_data']['data_type']['fusion_count']++;
+ $uuid_bytes_read = unpack('cmode/Jtimestamp/Gyaw/Gpitch/Groll/x*', $sensor_data_content);
+ $sensor_data_array['mode'] = $uuid_bytes_read['mode'];
+ $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
+ $sensor_data_array['yaw'] = $uuid_bytes_read['yaw'];
+ $sensor_data_array['pitch'] = $uuid_bytes_read['pitch'];
+ $sensor_data_array['roll'] = $uuid_bytes_read['roll'];
+ array_push($atom_structure['sensor_data']['data_type']['fusion_data'], $sensor_data_array);
+ break;
+ case 1:
+ $atom_structure['sensor_data']['data_type']['accel_count']++;
+ $uuid_bytes_read = unpack('cmode/Jtimestamp/Gyaw/Gpitch/Groll/x*', $sensor_data_content);
+ $sensor_data_array['mode'] = $uuid_bytes_read['mode'];
+ $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
+ $sensor_data_array['yaw'] = $uuid_bytes_read['yaw'];
+ $sensor_data_array['pitch'] = $uuid_bytes_read['pitch'];
+ $sensor_data_array['roll'] = $uuid_bytes_read['roll'];
+ array_push($atom_structure['sensor_data']['data_type']['accel_data'], $sensor_data_array);
+ break;
+ case 2:
+ $atom_structure['sensor_data']['data_type']['gyro_count']++;
+ $uuid_bytes_read = unpack('cmode/Jtimestamp/Gyaw/Gpitch/Groll/x*', $sensor_data_content);
+ $sensor_data_array['mode'] = $uuid_bytes_read['mode'];
+ $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
+ $sensor_data_array['yaw'] = $uuid_bytes_read['yaw'];
+ $sensor_data_array['pitch'] = $uuid_bytes_read['pitch'];
+ $sensor_data_array['roll'] = $uuid_bytes_read['roll'];
+ array_push($atom_structure['sensor_data']['data_type']['gyro_data'], $sensor_data_array);
+ break;
+ case 3:
+ $atom_structure['sensor_data']['data_type']['magno_count']++;
+ $uuid_bytes_read = unpack('cmode/Jtimestamp/Gmagx/Gmagy/Gmagz/x*', $sensor_data_content);
+ $sensor_data_array['mode'] = $uuid_bytes_read['mode'];
+ $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
+ $sensor_data_array['magx'] = $uuid_bytes_read['magx'];
+ $sensor_data_array['magy'] = $uuid_bytes_read['magy'];
+ $sensor_data_array['magz'] = $uuid_bytes_read['magz'];
+ array_push($atom_structure['sensor_data']['data_type']['magno_data'], $sensor_data_array);
+ break;
+ case 5:
+ $atom_structure['sensor_data']['data_type']['gps_count']++;
+ $uuid_bytes_read = unpack('cmode/Jtimestamp/Glat/Glon/Galt/Gspeed/nbearing/nacc/x*', $sensor_data_content);
+ $sensor_data_array['mode'] = $uuid_bytes_read['mode'];
+ $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
+ $sensor_data_array['lat'] = $uuid_bytes_read['lat'];
+ $sensor_data_array['lon'] = $uuid_bytes_read['lon'];
+ $sensor_data_array['alt'] = $uuid_bytes_read['alt'];
+ $sensor_data_array['speed'] = $uuid_bytes_read['speed'];
+ $sensor_data_array['bearing'] = $uuid_bytes_read['bearing'];
+ $sensor_data_array['acc'] = $uuid_bytes_read['acc'];
+ array_push($atom_structure['sensor_data']['data_type']['gps_data'], $sensor_data_array);
+ //array_push($debug_structure['debug_items'], $uuid_bytes_read['timestamp']);
+ break;
+ case 6:
+ $atom_structure['sensor_data']['data_type']['rotation_count']++;
+ $uuid_bytes_read = unpack('cmode/Jtimestamp/Grotx/Groty/Grotz/x*', $sensor_data_content);
+ $sensor_data_array['mode'] = $uuid_bytes_read['mode'];
+ $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp'];
+ $sensor_data_array['rotx'] = $uuid_bytes_read['rotx'];
+ $sensor_data_array['roty'] = $uuid_bytes_read['roty'];
+ $sensor_data_array['rotz'] = $uuid_bytes_read['rotz'];
+ array_push($atom_structure['sensor_data']['data_type']['rotation_data'], $sensor_data_array);
+ break;
+ default:
+ $atom_structure['sensor_data']['data_type']['unknown_count']++;
+ break;
+ }
+ }
+ //if (isset($debug_structure['debug_items']) && count($debug_structure['debug_items']) > 0) {
+ // $atom_structure['sensor_data']['data_type']['debug_list'] = implode(',', $debug_structure['debug_items']);
+ //} else {
+ $atom_structure['sensor_data']['data_type']['debug_list'] = 'No debug items in list!';
+ //}
+ break;
+
+ default:
+ $this->warning('Unhandled "uuid" atom identified by "'.$atom_structure['uuid_field_id'].'" at offset '.$atom_structure['offset'].' ('.strlen($atom_data).' bytes)');
+ }
+ break;
+
case 'gps ':
// https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730
// The 'gps ' contains simple look up table made up of 8byte rows, that point to the 'free' atoms that contains the actual GPS data.
// $GPRMC,094347.000,A,5342.0061,N,00737.9908,W,0.01,156.75,140217,,,A*7D
if (preg_match('#\\$GPRMC,([0-9\\.]*),([AV]),([0-9\\.]*),([NS]),([0-9\\.]*),([EW]),([0-9\\.]*),([0-9\\.]*),([0-9]*),([0-9\\.]*),([EW]?)(,[A])?(\\*[0-9A-F]{2})#', $GPS_free_data, $matches)) {
$GPS_this_GPRMC = array();
+ $GPS_this_GPRMC_raw = array();
list(
- $GPS_this_GPRMC['raw']['gprmc'],
- $GPS_this_GPRMC['raw']['timestamp'],
- $GPS_this_GPRMC['raw']['status'],
- $GPS_this_GPRMC['raw']['latitude'],
- $GPS_this_GPRMC['raw']['latitude_direction'],
- $GPS_this_GPRMC['raw']['longitude'],
- $GPS_this_GPRMC['raw']['longitude_direction'],
- $GPS_this_GPRMC['raw']['knots'],
- $GPS_this_GPRMC['raw']['angle'],
- $GPS_this_GPRMC['raw']['datestamp'],
- $GPS_this_GPRMC['raw']['variation'],
- $GPS_this_GPRMC['raw']['variation_direction'],
+ $GPS_this_GPRMC_raw['gprmc'],
+ $GPS_this_GPRMC_raw['timestamp'],
+ $GPS_this_GPRMC_raw['status'],
+ $GPS_this_GPRMC_raw['latitude'],
+ $GPS_this_GPRMC_raw['latitude_direction'],
+ $GPS_this_GPRMC_raw['longitude'],
+ $GPS_this_GPRMC_raw['longitude_direction'],
+ $GPS_this_GPRMC_raw['knots'],
+ $GPS_this_GPRMC_raw['angle'],
+ $GPS_this_GPRMC_raw['datestamp'],
+ $GPS_this_GPRMC_raw['variation'],
+ $GPS_this_GPRMC_raw['variation_direction'],
$dummy,
- $GPS_this_GPRMC['raw']['checksum'],
+ $GPS_this_GPRMC_raw['checksum'],
) = $matches;
+ $GPS_this_GPRMC['raw'] = $GPS_this_GPRMC_raw;
$hour = substr($GPS_this_GPRMC['raw']['timestamp'], 0, 2);
$minute = substr($GPS_this_GPRMC['raw']['timestamp'], 2, 2);
$ms = substr($GPS_this_GPRMC['raw']['timestamp'], 6); // may contain decimal seconds
$day = substr($GPS_this_GPRMC['raw']['datestamp'], 0, 2);
$month = substr($GPS_this_GPRMC['raw']['datestamp'], 2, 2);
- $year = substr($GPS_this_GPRMC['raw']['datestamp'], 4, 2);
+ $year = (int) substr($GPS_this_GPRMC['raw']['datestamp'], 4, 2);
$year += (($year > 90) ? 1900 : 2000); // complete lack of foresight: datestamps are stored with 2-digit years, take best guess
$GPS_this_GPRMC['timestamp'] = $year.'-'.$month.'-'.$day.' '.$hour.':'.$minute.':'.$second.$ms;
case 'thma': // subatom to "frea" -- "ThumbnailImage"
// https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea
if (strlen($atom_data) > 0) {
- $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg');
+ $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg', 'description'=>'ThumbnailImage');
}
break;
case 'scra': // subatom to "frea" -- "PreviewImage"
// https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea
// but the only sample file I've seen has no useful data here
if (strlen($atom_data) > 0) {
- $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg');
+ $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg', 'description'=>'PreviewImage');
}
break;
+ case 'cdsc': // timed metadata reference
+ // A QuickTime movie can contain none, one, or several timed metadata tracks. Timed metadata tracks can refer to multiple tracks.
+ // Metadata tracks are linked to the tracks they describe using a track-reference of type 'cdsc'. The metadata track holds the 'cdsc' track reference.
+ $atom_structure['track_number'] = getid3_lib::BigEndian2Int($atom_data);
+ break;
default:
$this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset);
}
return $atom_structure;
}
+ if (strlen($subatomdata) < ($subatomsize - 8)) {
+ // we don't have enough data to decode the subatom.
+ // this may be because we are refusing to parse large subatoms, or it may be because this atom had its size set too large
+ // so we passed in the start of a following atom incorrectly?
+ return $atom_structure;
+ }
$atom_structure[$subatomcounter++] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
$subatomoffset += $subatomsize;
}
$handyatomtranslatorarray["\xA9".'src'] = 'source_credit';
$handyatomtranslatorarray["\xA9".'swr'] = 'software';
$handyatomtranslatorarray["\xA9".'too'] = 'encoding_tool'; // iTunes 4.0
- $handyatomtranslatorarray["\xA9".'trk'] = 'track';
+ $handyatomtranslatorarray["\xA9".'trk'] = 'track_number';
$handyatomtranslatorarray["\xA9".'url'] = 'url';
$handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
$handyatomtranslatorarray["\xA9".'wrt'] = 'composer';
}
if ($comment_key) {
if ($comment_key == 'picture') {
- if (!is_array($data)) {
- $image_mime = '';
- if (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $data)) {
- $image_mime = 'image/png';
- } elseif (preg_match('#^\xFF\xD8\xFF#', $data)) {
- $image_mime = 'image/jpeg';
- } elseif (preg_match('#^GIF#', $data)) {
- $image_mime = 'image/gif';
- } elseif (preg_match('#^BM#', $data)) {
- $image_mime = 'image/bmp';
- }
- $data = array('data'=>$data, 'image_mime'=>$image_mime);
- }
+ // already copied directly into [comments][picture] elsewhere, do not re-copy here
+ return true;
}
$gooddata = array($data);
if ($comment_key == 'genre') {
$gooddata = explode(';', $data);
}
foreach ($gooddata as $data) {
+ if (!empty($info['quicktime']['comments'][$comment_key]) && in_array($data, $info['quicktime']['comments'][$comment_key], true)) {
+ // avoid duplicate copies of identical data
+ continue;
+ }
$info['quicktime']['comments'][$comment_key][] = $data;
}
}
*
* @return string
*/
- public function LociString($lstring, &$count) {
- // Loci strings are UTF-8 or UTF-16 and null (x00/x0000) terminated. UTF-16 has a BOM
- // Also need to return the number of bytes the string occupied so additional fields can be extracted
- $len = strlen($lstring);
- if ($len == 0) {
- $count = 0;
- return '';
- }
- if ($lstring[0] == "\x00") {
- $count = 1;
- return '';
- }
- //check for BOM
- if ($len > 2 && (($lstring[0] == "\xFE" && $lstring[1] == "\xFF") || ($lstring[0] == "\xFF" && $lstring[1] == "\xFE"))) {
- //UTF-16
- if (preg_match('/(.*)\x00/', $lstring, $lmatches)){
- $count = strlen($lmatches[1]) * 2 + 2; //account for 2 byte characters and trailing \x0000
- return getid3_lib::iconv_fallback_utf16_utf8($lmatches[1]);
- } else {
- return '';
- }
- } else {
- //UTF-8
- if (preg_match('/(.*)\x00/', $lstring, $lmatches)){
- $count = strlen($lmatches[1]) + 1; //account for trailing \x00
- return $lmatches[1];
- }else {
- return '';
- }
-
- }
- }
+ public function LociString($lstring, &$count) {
+ // Loci strings are UTF-8 or UTF-16 and null (x00/x0000) terminated. UTF-16 has a BOM
+ // Also need to return the number of bytes the string occupied so additional fields can be extracted
+ $len = strlen($lstring);
+ if ($len == 0) {
+ $count = 0;
+ return '';
+ }
+ if ($lstring[0] == "\x00") {
+ $count = 1;
+ return '';
+ }
+ // check for BOM
+ if (($len > 2) && ((($lstring[0] == "\xFE") && ($lstring[1] == "\xFF")) || (($lstring[0] == "\xFF") && ($lstring[1] == "\xFE")))) {
+ // UTF-16
+ if (preg_match('/(.*)\x00/', $lstring, $lmatches)) {
+ $count = strlen($lmatches[1]) * 2 + 2; //account for 2 byte characters and trailing \x0000
+ return getid3_lib::iconv_fallback_utf16_utf8($lmatches[1]);
+ } else {
+ return '';
+ }
+ }
+ // UTF-8
+ if (preg_match('/(.*)\x00/', $lstring, $lmatches)) {
+ $count = strlen($lmatches[1]) + 1; //account for trailing \x00
+ return $lmatches[1];
+ }
+ return '';
+ }
/**
* @param string $nullterminatedstring
return substr($pascalstring, 1);
}
+ /**
+ * @param string $pascalstring
+ *
+ * @return string
+ */
+ public function MaybePascal2String($pascalstring) {
+ // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
+ // Check if string actually is in this format or written incorrectly, straight string, or null-terminated string
+ if (ord(substr($pascalstring, 0, 1)) == (strlen($pascalstring) - 1)) {
+ return substr($pascalstring, 1);
+ } elseif (substr($pascalstring, -1, 1) == "\x00") {
+ // appears to be null-terminated instead of Pascal-style
+ return substr($pascalstring, 0, -1);
+ }
+ return $pascalstring;
+ }
+
/**
* Helper functions for m4b audiobook chapters
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_real extends getid3_handler
$ParsedArray['fourcc'] = $ParsedArray['fourcc3'];
}
+ /** @var string[]|false[] $value */
foreach ($ParsedArray['comments'] as $key => $value) {
- if ($ParsedArray['comments'][$key][0] === false) {
+ if ($value[0] === false) {
$ParsedArray['comments'][$key][0] = '';
}
}
* @todo Rewrite RIFF parser totally
*/
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, true);
$thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
$thisfile_riff_audio = &$thisfile_riff['audio'];
$thisfile_riff_video = &$thisfile_riff['video'];
- $thisfile_riff_WAVE = array();
+ $thisfile_riff_WAVE = array();
$Original['avdataoffset'] = $info['avdataoffset'];
$Original['avdataend'] = $info['avdataend'];
unset($thisfile_riff_audio[$streamindex]['raw']);
$thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
- $thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
+ $thisfile_audio = (array) getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
$this->warning('Audio codec = '.$thisfile_audio['codec']);
}
'capturedfile' => 0x00010000,
'copyrighted' => 0x00020010,
);
- foreach ($flags as $flag => $value) {
+ foreach ($flags as $flag => $value) {
$thisfile_riff_raw_avih['flags'][$flag] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & $value);
}
// shortcut
$thisfile_riff_video[$streamindex] = array();
- /** @var array $thisfile_riff_video_current */
+ /** @var array $thisfile_riff_video_current */
$thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
// http://en.wikipedia.org/wiki/CD-DA
case 'CDDA':
$info['fileformat'] = 'cda';
- unset($info['mime_type']);
+ unset($info['mime_type']);
$thisfile_audio_dataformat = 'cda';
$thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
$thisfile_riff_CDDA_fmt_0['playtime_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
- $info['comments']['track'] = $thisfile_riff_CDDA_fmt_0['track_num'];
+ $info['comments']['track_number'] = $thisfile_riff_CDDA_fmt_0['track_num'];
$info['playtime_seconds'] = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
// hardcoded data for CD-audio
}
break;
- // http://en.wikipedia.org/wiki/AIFF
+ // http://en.wikipedia.org/wiki/AIFF
case 'AIFF':
case 'AIFC':
$info['fileformat'] = 'aiff';
if (isset($thisfile_riff[$RIFFsubtype]['ID3 '])) {
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_id3v2 = new getid3_id3v2($getid3_temp);
$getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['ID3 '][0]['offset'] + 8;
if ($thisfile_riff[$RIFFsubtype]['ID3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, true);
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_mpeg = new getid3_mpeg($getid3_temp);
$getid3_mpeg->Analyze();
if (empty($getid3_temp->info['error'])) {
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_id3v2 = new getid3_id3v2($getid3_temp);
$getid3_id3v2->StartingOffset = $thisfile_riff[$RIFFsubtype]['id3 '][0]['offset'] + 8;
if ($thisfile_riff[$RIFFsubtype]['id3 '][0]['valid'] = $getid3_id3v2->Analyze()) {
// MP3
if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
$getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
$getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
// AC3
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $this->ftell() - 4;
$getid3_temp->info['avdataend'] = $this->ftell() + $AudioChunkSize;
$getid3_ac3 = new getid3_ac3($getid3_temp);
// Probably is MP3 data
if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($testData, 0, 4))) {
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_mp3 = new getid3_mp3($getid3_temp, __CLASS__);
// This is probably AC-3 data
$getid3_temp = new getID3();
if ($isRegularAC3) {
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend'];
}
// This is probably DTS data
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, null, $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_dts = new getid3_dts($getid3_temp);
$getid3_dts->Analyze();
// $info['divxtag']['comments'] = self::ParseDIVXTAG($this->fread($chunksize));
// break;
+ case 'scot':
+ // https://cmsdk.com/node-js/adding-scot-chunk-to-wav-file.html
+ $RIFFchunk[$chunkname][$thisindex]['data'] = $this->fread($chunksize);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['alter'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 0, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['attrib'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 1, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['artnum'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 2, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['title'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 4, 43); // "name" in other documentation
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['copy'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 47, 4);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['padd'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 51, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['asclen'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 52, 5);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['startseconds'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 57, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['starthundredths'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 59, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['endseconds'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 61, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['endhundreths'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 63, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['sdate'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 65, 6);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['kdate'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 71, 6);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['start_hr'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 77, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['kill_hr'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 78, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['digital'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 79, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 80, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['stereo'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 82, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['compress'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 83, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['eomstrt'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 84, 4));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['eomlen'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 88, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['attrib2'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 90, 4));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['future1'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 94, 12);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['catfontcolor'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 106, 4));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['catcolor'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 110, 4));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['segeompos'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 114, 4));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['vt_startsecs'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 118, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['vt_starthunds'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 120, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['priorcat'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 122, 3);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['priorcopy'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 125, 4);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['priorpadd'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 129, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['postcat'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 130, 3);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['postcopy'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 133, 4);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['postpadd'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 137, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['hrcanplay'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 138, 21);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['future2'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 159, 108);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['artist'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 267, 34);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['comment'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 301, 34); // "trivia" in other documentation
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['intro'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 335, 2);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['end'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 337, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['year'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 338, 4);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['obsolete2'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 342, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['rec_hr'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 343, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['rdate'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 344, 6);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['mpeg_bitrate'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 350, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['pitch'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 352, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['playlevel'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 354, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['lenvalid'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 356, 1);
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 357, 4));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['newplaylevel'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 361, 2));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['chopsize'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 363, 4));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['vteomovr'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 367, 4));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['desiredlen'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 371, 4));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['triggers'] = getid3_lib::LittleEndian2Int(substr($RIFFchunk[$chunkname][$thisindex]['data'], 375, 4));
+ $RIFFchunk[$chunkname][$thisindex]['parsed']['fillout'] = substr($RIFFchunk[$chunkname][$thisindex]['data'], 379, 33);
+
+ foreach (array('title', 'artist', 'comment') as $key) {
+ if (trim($RIFFchunk[$chunkname][$thisindex]['parsed'][$key])) {
+ $info['riff']['comments'][$key] = array($RIFFchunk[$chunkname][$thisindex]['parsed'][$key]);
+ }
+ }
+ if ($RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'] && !empty($info['filesize']) && ($RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'] != $info['filesize'])) {
+ $this->warning('RIFF.WAVE.scot.filelength ('.$RIFFchunk[$chunkname][$thisindex]['parsed']['filelength'].') different from actual filesize ('.$info['filesize'].')');
+ }
+ break;
+
default:
if (!empty($LISTchunkParent) && isset($LISTchunkMaxOffset) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
$RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_swf extends getid3_handler
{
unset($info['swf']);
unset($info['fileformat']);
return false;
- break;
}
$info['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1));
$info['swf']['header']['length'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4));
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_ts extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_aa extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_aac extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_ac3 extends getid3_handler
{
/**
* @var array
*/
- private $AC3header = array();
+ private $AC3header = array();
/**
* @var int
*/
- private $BSIoffset = 0;
+ private $BSIoffset = 0;
- const syncword = 0x0B77;
+ const syncword = 0x0B77;
/**
* @return bool
} else {
$this->error('Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 16. Please submit a support ticket with a sample file.');
- unset($info['ac3']);
+ unset($info['ac3']);
return false;
}
/**
* @param int $length
*
- * @return float|int
+ * @return int
*/
private function readHeaderBSI($length) {
$data = substr($this->AC3header['bsi'], $this->BSIoffset, $length);
// -8 -42.14 dB
$fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
- if ($fourbit{0} == '1') {
+ if ($fourbit[0] == '1') {
$log_gain = -8 + bindec(substr($fourbit, 1));
} else {
$log_gain = bindec(substr($fourbit, 1));
18 => array(2560, 2786, 3840) // 640 kbps
);
}
+ $paddingBytes = 0;
if (($fscod == 1) && $padding) {
// frame lengths are padded by 1 word (16 bits) at 44100
- $frameSizeLookup[$frmsizecod] += 2;
+ // (fscode==1) means 44100Hz (see sampleRateCodeLookup)
+ $paddingBytes = 2;
}
- return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] : false);
+ return (isset($frameSizeLookup[$framesizeid][$fscod]) ? $frameSizeLookup[$framesizeid][$fscod] + $paddingBytes : false);
}
/**
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_amr extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_au extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_avr extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_bonk extends getid3_handler
{
$this->fseek(0 - $BonkTagSize, SEEK_CUR);
$BonkTagOffset = $this->ftell();
$TagHeaderTest = $this->fread(5);
- if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
+ if (($TagHeaderTest[0] != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
$this->error('Expecting "'.getid3_lib::PrintHexBytes("\x00".strtoupper(substr($PossibleBonkTag, 4, 4))).'" at offset '.$BonkTagOffset.', found "'.getid3_lib::PrintHexBytes($TagHeaderTest).'"');
return false;
}
// ID3v2 checking is optional
if (class_exists('getid3_id3v2')) {
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_id3v2 = new getid3_id3v2($getid3_temp);
$getid3_id3v2->StartingOffset = $info['bonk'][' ID3']['offset'] + 2;
$info['bonk'][' ID3']['valid'] = $getid3_id3v2->Analyze();
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
class getid3_dsf extends getid3_handler
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_dss extends getid3_handler
{
$this->fseek($info['avdataoffset']);
$DSSheader = $this->fread(1540);
- if (!preg_match('#^[\\x02-\\x06]ds[s2]#', $DSSheader)) {
- $this->error('Expecting "[02-06] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"');
+ if (!preg_match('#^[\\x02-\\x08]ds[s2]#', $DSSheader)) {
+ $this->error('Expecting "[02-08] 64 73 [73|32]" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes(substr($DSSheader, 0, 4)).'"');
return false;
}
// 32-37 = "FE FF FE FF F7 FF" in all the sample files I've seen
$info['dss']['date_create_unix'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
$info['dss']['date_complete_unix'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
- $info['dss']['playtime_sec'] = intval((substr($DSSheader, 62, 2) * 3600) + (substr($DSSheader, 64, 2) * 60) + substr($DSSheader, 66, 2)); // approximate file playtime in HHMMSS
+ $info['dss']['playtime_sec'] = ((int) substr($DSSheader, 62, 2) * 3600) + ((int) substr($DSSheader, 64, 2) * 60) + (int) substr($DSSheader, 66, 2); // approximate file playtime in HHMMSS
if ($info['dss']['version'] <= 3) {
$info['dss']['playtime_ms'] = getid3_lib::LittleEndian2Int(substr($DSSheader, 512, 4)); // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512
$info['dss']['priority'] = ord(substr($DSSheader, 793, 1));
* @return int|false
*/
public function DSSdateStringToUnixDate($datestring) {
- $y = substr($datestring, 0, 2);
+ $y = (int) substr($datestring, 0, 2);
$m = substr($datestring, 2, 2);
$d = substr($datestring, 4, 2);
$h = substr($datestring, 6, 2);
// //
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
/**
* @tutorial http://wiki.multimedia.cx/index.php?title=DTS
// check syncword
$sync = substr($DTSheader, 0, 4);
- if (($encoding = array_search($sync, self::$syncwords)) !== false) {
+ if (($encoding = array_search($sync, self::$syncwords)) !== false) {
- $info['dts']['raw']['magic'] = $sync;
+ $info['dts']['raw']['magic'] = $sync;
$this->readBinDataOffset = 32;
- } elseif ($this->isDependencyFor('matroska')) {
+ } elseif ($this->isDependencyFor('matroska')) {
// Matroska contains DTS without syncword encoded as raw big-endian format
$encoding = 0;
$this->readBinDataOffset = 0;
- } else {
+ } else {
unset($info['fileformat']);
return $this->error('Expecting "'.implode('| ', array_map('getid3_lib::PrintHexBytes', self::$syncwords)).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($sync).'"');
* @param string $bin
* @param int $length
*
- * @return float|int
+ * @return int
*/
private function readBinData($bin, $length) {
$data = substr($bin, $this->readBinDataOffset, $length);
switch ($index) {
case 0:
return 1;
- break;
case 1:
case 2:
case 3:
case 4:
return 2;
- break;
case 5:
case 6:
return 3;
- break;
case 7:
case 8:
return 4;
- break;
case 9:
return 5;
- break;
case 10:
case 11:
case 12:
return 6;
- break;
case 13:
return 7;
- break;
case 14:
case 15:
return 8;
- break;
}
return false;
}
switch ($version) {
case 7:
return 0 - $index;
- break;
case 6:
return 0 - 16 - $index;
- break;
}
return false;
}
// ///
/////////////////////////////////////////////////////////////////
-
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
/**
if (isset($info['flac']['STREAMINFO']['audio_signature'])) {
if ($info['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
- $this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
+ $this->warning('FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)');
}
else {
$info['md5_data_source'] = '';
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_la extends getid3_handler
$this->error('Not a LA (Lossless-Audio) file');
}
return false;
- break;
}
$info['audio']['channels'] = $info['la']['channels'];
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_lpac extends getid3_handler
}
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_temp->info = $info;
$getid3_riff = new getid3_riff($getid3_temp);
$getid3_riff->Analyze();
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
+
define('GETID3_MIDI_MAGIC_MTHD', 'MThd'); // MIDI file header magic
define('GETID3_MIDI_MAGIC_MTRK', 'MTrk'); // MIDI track header magic
break;
case 0x58: // Time signature
- $timesig_numerator = getid3_lib::BigEndian2Int($METAeventData{0});
- $timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData{1})); // $02 -> x/4, $03 -> x/8, etc
- $timesig_32inqnote = getid3_lib::BigEndian2Int($METAeventData{2}); // number of 32nd notes to the quarter note
+ $timesig_numerator = getid3_lib::BigEndian2Int($METAeventData[0]);
+ $timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData[1])); // $02 -> x/4, $03 -> x/8, etc
+ $timesig_32inqnote = getid3_lib::BigEndian2Int($METAeventData[2]); // number of 32nd notes to the quarter note
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote'] = $timesig_32inqnote;
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator'] = $timesig_numerator;
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator;
break;
case 0x59: // Keysignature
- $keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData{0});
+ $keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData[0]);
if ($keysig_sharpsflats & 0x80) {
// (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
$keysig_sharpsflats -= 256;
}
- $keysig_majorminor = getid3_lib::BigEndian2Int($METAeventData{1}); // 0 -> major, 1 -> minor
+ $keysig_majorminor = getid3_lib::BigEndian2Int($METAeventData[1]); // 0 -> major, 1 -> minor
$keysigs = array(-7=>'Cb', -6=>'Gb', -5=>'Db', -4=>'Ab', -3=>'Eb', -2=>'Bb', -1=>'F', 0=>'C', 1=>'G', 2=>'D', 3=>'A', 4=>'E', 5=>'B', 6=>'F#', 7=>'C#');
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats'] = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_mod extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_monkey extends getid3_handler
{
$info['md5_data_source'] = '';
$md5 = $thisfile_monkeysaudio_raw['cFileMD5'];
for ($i = 0; $i < strlen($md5); $i++) {
- $info['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
+ $info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT);
}
if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
unset($info['md5_data_source']);
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
// number of frames to scan to determine if MPEG-audio sequence is valid
// Lower this number to 5-20 for faster scanning
if ($thisfile_mpeg_audio['xing_flags']['toc']) {
$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
for ($i = 0; $i < 100; $i++) {
- $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
+ $thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData[$i]);
}
}
if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
$thisfile_mpeg_audio_lame['long_version'] = substr($headerstring, $VBRidOffset + 120, 20);
$thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
+ $thisfile_mpeg_audio_lame['numeric_version'] = str_replace('LAME', '', $thisfile_mpeg_audio_lame['short_version']);
+ if (preg_match('#^LAME([0-9\\.a-z]+)#', $thisfile_mpeg_audio_lame['long_version'], $matches)) {
+ $thisfile_mpeg_audio_lame['short_version'] = $matches[0];
+ $thisfile_mpeg_audio_lame['numeric_version'] = $matches[1];
+ }
+ foreach (explode('.', $thisfile_mpeg_audio_lame['numeric_version']) as $key => $number) {
+ $thisfile_mpeg_audio_lame['integer_version'][$key] = intval($number);
+ }
- if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
+ //if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
+ if ((($thisfile_mpeg_audio_lame['integer_version'][0] * 1000) + $thisfile_mpeg_audio_lame['integer_version'][1]) >= 3090) { // cannot use string version compare, may have "LAME3.90" or "LAME3.100" -- see https://github.com/JamesHeinrich/getID3/issues/207
// extra 11 chars are not part of version string when LAMEtag present
unset($thisfile_mpeg_audio_lame['long_version']);
$SyncPattern1 = substr($MPEGaudioData, 0, 4);
// may be different pattern due to padding
- $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
+ $SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) | 0x02).$SyncPattern1[3];
if ($SyncPattern2 === $SyncPattern1) {
- $SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
+ $SyncPattern2 = $SyncPattern1[0].$SyncPattern1[1].chr(ord($SyncPattern1[2]) & 0xFD).$SyncPattern1[3];
}
$framelength = false;
if (strlen($head4) < 4) {
break;
}
- if ($head4{0} != "\xFF") {
+ if ($head4[0] != "\xFF") {
for ($i = 1; $i < 4; $i++) {
- if ($head4{$i} == "\xFF") {
+ if ($head4[$i] == "\xFF") {
$this->fseek($i - 4, SEEK_CUR);
continue 2;
}
$WhereWeWere = $this->ftell();
$this->fseek($MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
$next4 = $this->fread(4);
- if ($next4{0} == "\xFF") {
+ if ($next4[0] == "\xFF") {
if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
$MPEGaudioHeaderDecodeCache[$next4] = self::MPEGaudioHeaderDecode($next4);
}
if ($MPEGaudioHeaderValidCache[$next4]) {
$this->fseek(-4, SEEK_CUR);
- getid3_lib::safe_inc($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]);
- getid3_lib::safe_inc($Distribution['layer'][$LongMPEGlayerLookup[$head4]]);
- getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]);
- getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]);
- getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]);
- if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
+ $Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]] = isset($Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]) ? ++$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]] : 1;
+ $Distribution['layer'][$LongMPEGlayerLookup[$head4]] = isset($Distribution['layer'][$LongMPEGlayerLookup[$head4]]) ? ++$Distribution['layer'][$LongMPEGlayerLookup[$head4]] : 1;
+ $Distribution['version'][$LongMPEGversionLookup[$head4]] = isset($Distribution['version'][$LongMPEGversionLookup[$head4]]) ? ++$Distribution['version'][$LongMPEGversionLookup[$head4]] : 1;
+ $Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])] = isset($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]) ? ++$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])] : 1;
+ $Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]] = isset($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]) ? ++$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]] : 1;
+ if (++$frames_scanned >= $max_frames_scan) {
$pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']);
$this->warning('too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.');
foreach ($Distribution as $key1 => $value1) {
static $MPEGaudioLayerLookup;
static $MPEGaudioBitrateLookup;
if (empty($MPEGaudioVersionLookup)) {
- $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
- $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
- $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
-
+ $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
+ $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
+ $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
}
$this->fseek($avdataoffset);
return false;
}
- if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
+ if (($header[$SynchSeekOffset] == "\xFF") && ($header[($SynchSeekOffset + 1)] > "\xE0")) { // synch detected
$FirstFrameAVDataOffset = null;
if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
$FirstFrameThisfileInfo = $info;
$this->fseek($scan_start_offset[$current_segment]);
$buffer_4k = $this->fread(4096);
for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
- if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
+ if (($buffer_4k[$j] == "\xFF") && ($buffer_4k[($j + 1)] > "\xE0")) { // synch detected
if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
$calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
* @return bool
*/
public static function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
- if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
+ if (!isset($rawarray['synch']) || ($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
return false;
}
}
$MPEGrawHeader['synch'] = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
- $MPEGrawHeader['version'] = (ord($Header4Bytes{1}) & 0x18) >> 3; // BB
- $MPEGrawHeader['layer'] = (ord($Header4Bytes{1}) & 0x06) >> 1; // CC
- $MPEGrawHeader['protection'] = (ord($Header4Bytes{1}) & 0x01); // D
- $MPEGrawHeader['bitrate'] = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
- $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes{2}) & 0x0C) >> 2; // FF
- $MPEGrawHeader['padding'] = (ord($Header4Bytes{2}) & 0x02) >> 1; // G
- $MPEGrawHeader['private'] = (ord($Header4Bytes{2}) & 0x01); // H
- $MPEGrawHeader['channelmode'] = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
- $MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; // JJ
- $MPEGrawHeader['copyright'] = (ord($Header4Bytes{3}) & 0x08) >> 3; // K
- $MPEGrawHeader['original'] = (ord($Header4Bytes{3}) & 0x04) >> 2; // L
- $MPEGrawHeader['emphasis'] = (ord($Header4Bytes{3}) & 0x03); // MM
+ $MPEGrawHeader['version'] = (ord($Header4Bytes[1]) & 0x18) >> 3; // BB
+ $MPEGrawHeader['layer'] = (ord($Header4Bytes[1]) & 0x06) >> 1; // CC
+ $MPEGrawHeader['protection'] = (ord($Header4Bytes[1]) & 0x01); // D
+ $MPEGrawHeader['bitrate'] = (ord($Header4Bytes[2]) & 0xF0) >> 4; // EEEE
+ $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes[2]) & 0x0C) >> 2; // FF
+ $MPEGrawHeader['padding'] = (ord($Header4Bytes[2]) & 0x02) >> 1; // G
+ $MPEGrawHeader['private'] = (ord($Header4Bytes[2]) & 0x01); // H
+ $MPEGrawHeader['channelmode'] = (ord($Header4Bytes[3]) & 0xC0) >> 6; // II
+ $MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4; // JJ
+ $MPEGrawHeader['copyright'] = (ord($Header4Bytes[3]) & 0x08) >> 3; // K
+ $MPEGrawHeader['original'] = (ord($Header4Bytes[3]) & 0x04) >> 2; // L
+ $MPEGrawHeader['emphasis'] = (ord($Header4Bytes[3]) & 0x03); // MM
return $MPEGrawHeader;
}
public static function XingVBRidOffset($version, $channelmode) {
static $XingVBRidOffsetCache = array();
if (empty($XingVBRidOffsetCache)) {
- $XingVBRidOffsetCache = array (
+ $XingVBRidOffsetCache = array (
'1' => array ('mono' => 0x15, // 4 + 17 = 21
'stereo' => 0x24, // 4 + 32 = 36
'joint stereo' => 0x24,
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_mpc extends getid3_handler
{
default:
$this->error('Found unhandled key type "'.$thisPacket['key'].'" at offset '.$thisPacket['offset']);
return false;
- break;
}
if (!empty($thisPacket)) {
$info['mpc']['packets'][] = $thisPacket;
$info['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_version_major'].' instead';
unset($info['mpc']);
return false;
- break;
}
if (($thisfile_mpc_header['stream_version_major'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) {
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
class getid3_ogg extends getid3_handler
default:
return false;
- break;
}
$VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
class getid3_optimfrog extends getid3_handler
$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_riff = new getid3_riff($getid3_temp);
$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
$getid3_temp->info['avdataend'] = $info['avdataend'];
$getid3_riff = new getid3_riff($getid3_temp);
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_rkau extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
-
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_shorten extends getid3_handler
{
/**
if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) {
+ if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+ }
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
$fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4));
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_tta extends getid3_handler
{
return false;
}
- switch ($ttaheader{3}) {
+ switch ($ttaheader[3]) {
case "\x01": // TTA v1.x
case "\x02": // TTA v1.x
case "\x03": // TTA v1.x
$info['tta']['major_version'] = 1;
$info['avdataoffset'] += 16;
- $info['tta']['compression_level'] = ord($ttaheader{3});
+ $info['tta']['compression_level'] = ord($ttaheader[3]);
$info['tta']['channels'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 4, 2));
$info['tta']['bits_per_sample'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 6, 2));
$info['tta']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 8, 4));
break;
default:
- $this->error('This version of getID3() ['.$this->getid3->version().'] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3});
+ $this->error('This version of getID3() ['.$this->getid3->version().'] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader[3]);
return false;
- break;
}
$info['audio']['encoder'] = 'TTA v'.$info['tta']['major_version'];
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_voc extends getid3_handler
{
$BlockOffset = $this->ftell();
$BlockData = $this->fread(4);
- $BlockType = ord($BlockData{0});
+ $BlockType = ord($BlockData[0]);
$BlockSize = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3));
$ThisBlock = array();
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_vqf extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
-
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_wavpack extends getid3_handler
{
/**
return false;
}
- $info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8});
- $info['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9});
+ $info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader[8]);
+ $info['wavpack']['blockheader']['major_version'] = ord($wavpackheader[9]);
if (($info['wavpack']['blockheader']['major_version'] != 4) ||
(($info['wavpack']['blockheader']['minor_version'] < 4) &&
return false;
}
- $info['wavpack']['blockheader']['track_number'] = ord($wavpackheader{10}); // unused
- $info['wavpack']['blockheader']['index_number'] = ord($wavpackheader{11}); // unused
+ $info['wavpack']['blockheader']['track_number'] = ord($wavpackheader[10]); // unused
+ $info['wavpack']['blockheader']['index_number'] = ord($wavpackheader[11]); // unused
$info['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12, 4));
$info['wavpack']['blockheader']['block_index'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16, 4));
$info['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20, 4));
if (feof($this->getid3->fp)) {
break;
}
- $metablock['id'] = ord($metablockheader{0});
+ $metablock['id'] = ord($metablockheader[0]);
$metablock['function_id'] = ($metablock['id'] & 0x3F);
$metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']);
}
$metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words
$metablock['data'] = null;
+ $metablock['comments'] = array();
if ($metablock['size'] > 0) {
$original_wav_filesize = getid3_lib::LittleEndian2Int(substr($metablock['data'], 4, 4));
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_riff = new getid3_riff($getid3_temp);
$getid3_riff->ParseRIFFdata($metablock['data']);
$metablock['riff'] = $getid3_temp->info['riff'];
$startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2);
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_temp->info['avdataend'] = $info['avdataend'];
//$getid3_temp->info['fileformat'] = 'riff';
$getid3_riff = new getid3_riff($getid3_temp);
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_bmp extends getid3_handler
{
case 1:
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
- $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
+ $paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]);
for ($i = 7; $i >= 0; $i--) {
$paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
case 4:
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
- $paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
+ $paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]);
for ($i = 1; $i >= 0; $i--) {
$paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
case 8:
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
- $paletteindex = ord($BMPpixelData{$pixeldataoffset++});
+ $paletteindex = ord($BMPpixelData[$pixeldataoffset++]);
$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
}
while (($pixeldataoffset % 4) != 0) {
case 24:
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
- $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
+ $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData[$pixeldataoffset+2]) << 16) | (ord($BMPpixelData[$pixeldataoffset+1]) << 8) | ord($BMPpixelData[$pixeldataoffset]);
$pixeldataoffset += 3;
}
while (($pixeldataoffset % 4) != 0) {
case 32:
for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
- $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
+ $thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData[$pixeldataoffset+3]) << 24) | (ord($BMPpixelData[$pixeldataoffset+2]) << 16) | (ord($BMPpixelData[$pixeldataoffset+1]) << 8) | ord($BMPpixelData[$pixeldataoffset]);
$pixeldataoffset += 4;
}
while (($pixeldataoffset % 4) != 0) {
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_efax extends getid3_handler
{
$this->error('eFax parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
return false;
-
- return true;
}
}
* @link https://www.w3.org/Graphics/GIF/spec-gif89a.txt
* @link http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
*/
+
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
+
class getid3_gif extends getid3_handler
{
/**
return false;
}
+ //if (!$this->getid3->option_extra_info) {
+ // $this->warning('GIF Extensions and Global Color Table not returned due to !getid3->option_extra_info');
+ //}
+
$info['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3);
$offset += 3;
$info['gif']['header']['raw']['width'] = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
if ($info['gif']['header']['flags']['global_color_table']) {
$GIFcolorTable = $this->fread(3 * $info['gif']['header']['global_color_size']);
- $offset = 0;
- for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) {
- $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
- $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
- $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
- $info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
+ if ($this->getid3->option_extra_info) {
+ $offset = 0;
+ for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) {
+ $red = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
+ $green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
+ $blue = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
+ $info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
+ $info['gif']['global_color_table_rgb'][$i] = sprintf('%02X%02X%02X', $red, $green, $blue);
+ }
}
}
}
}
- $info['gif']['extension_blocks'][] = $ExtensionBlock;
+ if ($this->getid3->option_extra_info) {
+ $info['gif']['extension_blocks'][] = $ExtensionBlock;
+ }
break;
case ';':
// ///
/////////////////////////////////////////////////////////////////
-
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_jpg extends getid3_handler
{
/**
// ///
/////////////////////////////////////////////////////////////////
-
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_pcd extends getid3_handler
{
public $ExtractData = 0;
if ($this->ExtractData > 3) {
$this->error('Cannot extract PSD image data for detail levels above BASE (level-3) because encrypted with Kodak-proprietary compression/encryption.');
+ return false;
} elseif ($this->ExtractData > 0) {
for ($x = 0; $x < $PCD_width; $x++) {
if ($PCDisVertical) {
- $info['pcd']['data'][$PCD_width - $x][$y] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)}));
- $info['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)}));
+ $info['pcd']['data'][$PCD_width - $x][$y] = $this->YCbCr2RGB(ord($PCD_data_Y1[$x]), ord($PCD_data_Cb[(int) floor($x / 2)]), ord($PCD_data_Cr[(int) floor($x / 2)]));
+ $info['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2[$x]), ord($PCD_data_Cb[(int) floor($x / 2)]), ord($PCD_data_Cr[(int) floor($x / 2)]));
} else {
- $info['pcd']['data'][$y][$x] = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)}));
- $info['pcd']['data'][$y + 1][$x] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{(int) floor($x / 2)}), ord($PCD_data_Cr{(int) floor($x / 2)}));
+ $info['pcd']['data'][$y][$x] = $this->YCbCr2RGB(ord($PCD_data_Y1[$x]), ord($PCD_data_Cb[(int) floor($x / 2)]), ord($PCD_data_Cr[(int) floor($x / 2)]));
+ $info['pcd']['data'][$y + 1][$x] = $this->YCbCr2RGB(ord($PCD_data_Y2[$x]), ord($PCD_data_Cb[(int) floor($x / 2)]), ord($PCD_data_Cr[(int) floor($x / 2)]));
}
}
}
}
+ return false;
}
/**
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_png extends getid3_handler
{
switch ($color_type) {
case 0: // Each pixel is a grayscale sample.
return $bit_depth;
- break;
case 2: // Each pixel is an R,G,B triple
return 3 * $bit_depth;
- break;
case 3: // Each pixel is a palette index; a PLTE chunk must appear.
return $bit_depth;
- break;
case 4: // Each pixel is a grayscale sample, followed by an alpha sample.
return 2 * $bit_depth;
- break;
case 6: // Each pixel is an R,G,B triple, followed by an alpha sample.
return 4 * $bit_depth;
- break;
}
return false;
}
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_svg extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_tiff extends getid3_handler
{
default:
$this->error('Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$info['avdataoffset']);
return false;
- break;
}
$info['fileformat'] = 'tiff';
$info['video']['dataformat'] = 'tiff';
$info['video']['lossless'] = true;
$info['tiff']['ifd'] = array();
- $CurrentIFD = array();
+ $CurrentIFD = array();
$FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8);
$CurrentIFD['fieldcount'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) {
- $CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
- $CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
- $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
- $CurrentIFD['fields'][$i]['raw']['offset'] = $this->fread(4);
-
+ $CurrentIFD['fields'][$i]['raw']['tag'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
+ $CurrentIFD['fields'][$i]['raw']['type'] = $this->TIFFendian2Int($this->fread(2), $info['tiff']['byte_order']);
+ $CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int($this->fread(4), $info['tiff']['byte_order']);
+ $CurrentIFD['fields'][$i]['raw']['valoff'] = $this->fread(4); // To save time and space the Value Offset contains the Value instead of pointing to the Value if and only if the Value fits into 4 bytes. If the Value is shorter than 4 bytes, it is left-justified within the 4-byte Value Offset, i.e., stored in the lowernumbered bytes. Whether the Value fits within 4 bytes is determined by the Type and Count of the field.
$CurrentIFD['fields'][$i]['raw']['tag_name'] = $this->TIFFcommentName($CurrentIFD['fields'][$i]['raw']['tag']);
switch ($CurrentIFD['fields'][$i]['raw']['type']) {
case 1: // BYTE An 8-bit unsigned integer.
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
- $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $info['tiff']['byte_order']);
+ $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['valoff'], 0, 1), $info['tiff']['byte_order']);
} else {
- $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
+ $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
}
break;
case 2: // ASCII 8-bit bytes that store ASCII codes; the last byte must be null.
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
- $CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3);
+ $CurrentIFD['fields'][$i]['value'] = substr($CurrentIFD['fields'][$i]['raw']['valoff'], 3);
} else {
- $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
+ $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
}
break;
case 3: // SHORT A 16-bit (2-byte) unsigned integer.
if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) {
- $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $info['tiff']['byte_order']);
+ $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['valoff'], 0, 2), $info['tiff']['byte_order']);
} else {
- $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
+ $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
}
break;
case 4: // LONG A 32-bit (4-byte) unsigned integer.
- if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) {
- $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
+ if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
+ $CurrentIFD['fields'][$i]['value'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
} else {
- $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $info['tiff']['byte_order']);
+ $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
}
break;
case 5: // RATIONAL Two LONG_s: the first represents the numerator of a fraction, the second the denominator.
+ case 7: // UNDEFINED An 8-bit byte that may contain anything, depending on the definition of the field.
+ $CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['valoff'], $info['tiff']['byte_order']);
+ break;
+
+ // Warning: It is possible that other TIFF field types will be added in the future. Readers should skip over fields containing an unexpected field type.
+ // In TIFF 6.0, some new field types have been defined:
+ // These new field types are also governed by the byte order (II or MM) in the TIFF header.
+ case 6: // SBYTE An 8-bit signed (twos-complement) integer.
+ case 8: // SSHORT A 16-bit (2-byte) signed (twos-complement) integer.
+ case 9: // SLONG A 32-bit (4-byte) signed (twos-complement) integer.
+ case 10: // SRATIONAL Two SLONGs: the first represents the numerator of a fraction, the second the denominator.
+ case 11: // FLOAT Single precision (4-byte) IEEE format
+ case 12: // DOUBLE Double precision (8-byte) IEEE format
+ default:
+ $this->warning('unhandled IFD field type '.$CurrentIFD['fields'][$i]['raw']['type'].' for IFD entry '.$i);
break;
}
}
}
break;
+ case 700:
+ $XMPmagic = '<?xpacket';
+ $this->fseek($fieldarray['offset']);
+ $xmpkey = (isset($info['tiff']['XMP']) ? count($info['tiff']['XMP']) : 0);
+ $info['tiff']['XMP'][$xmpkey]['raw'] = $this->fread($fieldarray['raw']['length']);
+ if (substr($info['tiff']['XMP'][$xmpkey]['raw'], 0, strlen($XMPmagic)) != $XMPmagic) {
+ $this->warning('did not find expected XMP data at offset '.$fieldarray['offset']);
+ unset($info['tiff']['XMP'][$xmpkey]['raw']);
+ }
+ break;
}
switch ($fieldarray['raw']['tag']) {
case 256: // ImageWidth
* @return string
*/
public function TIFFcompressionMethod($id) {
+ // https://en.wikipedia.org/wiki/TIFF#TIFF_Compression_Tag
static $TIFFcompressionMethod = array();
if (empty($TIFFcompressionMethod)) {
$TIFFcompressionMethod = array(
- 1 => 'Uncompressed',
- 2 => 'Huffman',
- 3 => 'Fax - CCITT 3',
- 5 => 'LZW',
- 32773 => 'PackBits',
+ 0x0001 => 'Uncompressed',
+ 0x0002 => 'Huffman',
+ 0x0003 => 'CCITT T.4',
+ 0x0004 => 'CCITT T.6',
+ 0x0005 => 'LZW',
+ 0x0006 => 'JPEG-old',
+ 0x0007 => 'JPEG',
+ 0x0008 => 'deflate',
+ 0x0009 => 'JBIG ITU-T T.85',
+ 0x000A => 'JBIG ITU-T T.43',
+ 0x7FFE => 'NeXT RLE 2-bit',
+ 0x8005 => 'PackBits',
+ 0x8029 => 'ThunderScan RLE 4-bit',
+ 0x807F => 'RasterPadding',
+ 0x8080 => 'RLE-LW',
+ 0x8081 => 'RLE-CT',
+ 0x8082 => 'RLE-BL',
+ 0x80B2 => 'deflate-PK',
+ 0x80B3 => 'Kodak-DCS',
+ 0x8765 => 'JBIG',
+ 0x8798 => 'JPEG2000',
+ 0x8799 => 'Nikon NEF',
+ 0x879B => 'JBIG2',
);
}
return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')');
* A CueSheet class used to open and parse cuesheets.
*
*/
+
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
+
class getid3_cue extends getid3_handler
{
public $cuesheet = array();
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_exe extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_iso extends getid3_handler
{
$this->fseek(2048 * $i);
$ISOheader = $this->fread(2048);
if (substr($ISOheader, 1, 5) == 'CD001') {
- switch (ord($ISOheader{0})) {
+ switch (ord($ISOheader[0])) {
case 1:
$info['iso']['primary_volume_descriptor']['offset'] = 2048 * $i;
$this->ParsePrimaryVolumeDescriptor($ISOheader);
$DirectoryRecordData = $this->fread(1);
$DirectoryRecord = array();
- while (ord($DirectoryRecordData{0}) > 33) {
+ while (ord($DirectoryRecordData[0]) > 33) {
- $DirectoryRecordData .= $this->fread(ord($DirectoryRecordData{0}) - 1);
+ $DirectoryRecordData .= $this->fread(ord($DirectoryRecordData[0]) - 1);
$ThisDirectoryRecord = array();
// 6: second of the minute from 0 to 59
// 7: Offset from Greenwich Mean Time in number of 15 minute intervals from -48 (West) to +52 (East)
- $UNIXyear = ord($ISOtime{0}) + 1900;
- $UNIXmonth = ord($ISOtime{1});
- $UNIXday = ord($ISOtime{2});
- $UNIXhour = ord($ISOtime{3});
- $UNIXminute = ord($ISOtime{4});
- $UNIXsecond = ord($ISOtime{5});
- $GMToffset = $this->TwosCompliment2Decimal(ord($ISOtime{5}));
+ $UNIXyear = ord($ISOtime[0]) + 1900;
+ $UNIXmonth = ord($ISOtime[1]);
+ $UNIXday = ord($ISOtime[2]);
+ $UNIXhour = ord($ISOtime[3]);
+ $UNIXminute = ord($ISOtime[4]);
+ $UNIXsecond = ord($ISOtime[5]);
+ $GMToffset = $this->TwosCompliment2Decimal(ord($ISOtime[5]));
return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
}
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_msoffice extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_par2 extends getid3_handler
{
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_pdf extends getid3_handler
{
+ public $returnXREF = false; // return full details of PDF Cross-Reference Table (XREF)
+
/**
* @return bool
*/
public function Analyze() {
$info = &$this->getid3->info;
- $info['fileformat'] = 'pdf';
+ $this->fseek(0);
+ if (preg_match('#^%PDF-([0-9\\.]+)$#', rtrim($this->fgets()), $matches)) {
+ $info['pdf']['header']['version'] = floatval($matches[1]);
+ $info['fileformat'] = 'pdf';
+
+ // the PDF Cross-Reference Table (XREF) is located near the end of the file
+ // the starting offset is specified in the penultimate section, on the two lines just before "%%EOF"
+ // the first line is "startxref", the second line is the byte offset of the XREF.
+ // We know the length of "%%EOF" and "startxref", but the offset could be 2-10 bytes,
+ // and we're not sure if the line ends are one or two bytes, so we might find "startxref" as little as 18(?) bytes
+ // from EOF, but it could 30 bytes, so we start 40 bytes back just to be safe and do a search for the data we want.
+ $this->fseek(-40, SEEK_END);
+ if (preg_match('#[\r\n]startxref[ \r\n]+([0-9]+)[ \r\n]+#', $this->fread(40), $matches)) {
+ $info['pdf']['trailer']['startxref'] = intval($matches[1]);
+ $this->parseXREF($info['pdf']['trailer']['startxref']);
+ if (!empty($info['pdf']['xref']['offset'])) {
+ while (!$this->feof() && (max(array_keys($info['pdf']['xref']['offset'])) > $info['pdf']['xref']['count'])) {
+ // suspect that there may be another XREF entry somewhere in the file, brute-force scan for it
+ /*
+ // starting at last known entry of main XREF table
+ $this->fseek(max($info['pdf']['xref']['offset']));
+ */
+ // starting at the beginning of the file
+ $this->fseek(0);
+ while (!$this->feof()) {
+ $XREFoffset = $this->ftell();
+ if (rtrim($this->fgets()) == 'xref') {
+ if (empty($info['pdf']['xref']['xref_offsets']) || !in_array($XREFoffset, $info['pdf']['xref']['xref_offsets'])) {
+ $this->parseXREF($XREFoffset);
+ break;
+ }
+ }
+ }
+ }
+ foreach ($info['pdf']['xref']['offset'] as $objectNumber => $offset) {
+ if ($info['pdf']['xref']['entry'][$objectNumber] == 'f') {
+ // "free" object means "deleted", ignore
+ continue;
+ }
+ $this->fseek($offset);
+ $line = rtrim($this->fgets());
+ if (preg_match('#^'.$objectNumber.' ([0-9]+) obj#', $line, $matches)) {
+ if (strlen($line) > strlen($matches[0])) {
+ // object header line not actually on its own line, rewind file pointer to start reading data
+ $this->fseek($offset + strlen($matches[0]));
+ }
+ $objectData = '';
+ while (true) {
+ $line = $this->fgets();
+ if (rtrim($line) == 'endobj') {
+ break;
+ }
+ $objectData .= $line;
+ }
+ if (preg_match('#^<<[\r\n\s]*(/Type|/Pages|/Parent [0-9]+ [0-9]+ [A-Z]|/Count [0-9]+|/Kids *\\[[0-9A-Z ]+\\]|[\r\n\s])+[\r\n\s]*>>#', $objectData, $matches)) {
+ if (preg_match('#/Count ([0-9]+)#', $objectData, $matches)) {
+ $info['pdf']['pages'] = (int) $matches[1];
+ break; // for now this is the only data we're looking for in the PDF not need to loop through every object in the file (and a large PDF may contain MANY objects). And it MAY be possible that there are other objects elsewhere in the file that define additional (or removed?) pages
+ }
+ }
+ } else {
+ $this->error('Unexpected structure "'.$line.'" at offset '.$offset);
+ break;
+ }
+ }
+ if (!$this->returnXREF) {
+ unset($info['pdf']['xref']['offset'], $info['pdf']['xref']['generation'], $info['pdf']['xref']['entry']);
+ }
+
+ } else {
+ $this->error('Did not find "xref" at offset '.$info['pdf']['trailer']['startxref']);
+ }
+ } else {
+ $this->error('Did not find "startxref" in the last 40 bytes of the PDF');
+ }
- $this->error('PDF parsing not enabled in this version of getID3() ['.$this->getid3->version().']');
+ $this->warning('PDF parsing incomplete in this version of getID3() ['.$this->getid3->version().']');
+ return true;
+ }
+ $this->error('Did not find "%PDF" at the beginning of the PDF');
return false;
}
+ /**
+ * @return bool
+ */
+ private function parseXREF($XREFoffset) {
+ $info = &$this->getid3->info;
+
+ $this->fseek($XREFoffset);
+ if (rtrim($this->fgets()) == 'xref') {
+
+ $info['pdf']['xref']['xref_offsets'][$XREFoffset] = $XREFoffset;
+ list($firstObjectNumber, $XREFcount) = explode(' ', rtrim($this->fgets()));
+ $XREFcount = (int) $XREFcount;
+ $info['pdf']['xref']['count'] = $XREFcount + (!empty($info['pdf']['xref']['count']) ? $info['pdf']['xref']['count'] : 0);
+ for ($i = 0; $i < $XREFcount; $i++) {
+ $line = rtrim($this->fgets());
+ if (preg_match('#^([0-9]+) ([0-9]+) ([nf])$#', $line, $matches)) {
+ $info['pdf']['xref']['offset'][($firstObjectNumber + $i)] = (int) $matches[1];
+ $info['pdf']['xref']['generation'][($firstObjectNumber + $i)] = (int) $matches[2];
+ $info['pdf']['xref']['entry'][($firstObjectNumber + $i)] = $matches[3];
+ } else {
+ $this->error('failed to parse XREF entry #'.$i.' in XREF table at offset '.$XREFoffset);
+ return false;
+ }
+ }
+ sort($info['pdf']['xref']['xref_offsets']);
+ return true;
+
+ }
+ $this->warning('failed to find expected XREF structure at offset '.$XREFoffset);
+ return false;
+ }
+
}
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
+
class getid3_apetag extends getid3_handler
{
/**
switch (strtolower($item_key)) {
// http://wiki.hydrogenaud.io/index.php?title=ReplayGain#MP3Gain
case 'replaygain_track_gain':
- if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
- $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+ if (preg_match('#^([\\-\\+][0-9\\.,]{8})( dB)?$#', $thisfile_ape_items_current['data'][0], $matches)) {
+ $thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
} else {
$this->warning('MP3gainTrackGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
break;
case 'replaygain_track_peak':
- if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
- $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+ if (preg_match('#^([0-9\\.,]{8})$#', $thisfile_ape_items_current['data'][0], $matches)) {
+ $thisfile_replaygain['track']['peak'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
$thisfile_replaygain['track']['originator'] = 'unspecified';
if ($thisfile_replaygain['track']['peak'] <= 0) {
$this->warning('ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")');
break;
case 'replaygain_album_gain':
- if (preg_match('#^[\\-\\+][0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
- $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+ if (preg_match('#^([\\-\\+][0-9\\.,]{8})( dB)?$#', $thisfile_ape_items_current['data'][0], $matches)) {
+ $thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
} else {
$this->warning('MP3gainAlbumGain value in APEtag appears invalid: "'.$thisfile_ape_items_current['data'][0].'"');
break;
case 'replaygain_album_peak':
- if (preg_match('#^[0-9\\.,]{8}$#', $thisfile_ape_items_current['data'][0])) {
- $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+ if (preg_match('#^([0-9\\.,]{8})$#', $thisfile_ape_items_current['data'][0], $matches)) {
+ $thisfile_replaygain['album']['peak'] = (float) str_replace(',', '.', $matches[1]); // float casting will see "0,95" as zero!
$thisfile_replaygain['album']['originator'] = 'unspecified';
if ($thisfile_replaygain['album']['peak'] <= 0) {
$this->warning('ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")');
case 'tracknumber':
if (is_array($thisfile_ape_items_current['data'])) {
foreach ($thisfile_ape_items_current['data'] as $comment) {
- $thisfile_ape['comments']['track'][] = $comment;
+ $thisfile_ape['comments']['track_number'][] = $comment;
}
}
break;
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_id3v1 extends getid3_handler
{
// If second-last byte of comment field is null and last byte of comment field is non-null
// then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
- if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
- $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
- $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
+ if (($id3v1tag[125] === "\x00") && ($id3v1tag[126] !== "\x00")) {
+ $ParsedID3v1['track_number'] = ord(substr($ParsedID3v1['comment'], 29, 1));
+ $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
}
$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
foreach ($ParsedID3v1 as $key => $value) {
$ParsedID3v1['comments'][$key][0] = $value;
}
- // ID3v1 encoding detection hack START
- // ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets
- // Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess
- $ID3v1encoding = 'ISO-8859-1';
- foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) {
- foreach ($valuearray as $key => $value) {
- if (preg_match('#^[\\x00-\\x40\\xA8\\B8\\x80-\\xFF]+$#', $value)) {
- foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
- if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) {
- $ID3v1encoding = $id3v1_bad_encoding;
- break 3;
- } elseif (function_exists('iconv') && @iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) {
- $ID3v1encoding = $id3v1_bad_encoding;
- break 3;
+ $ID3v1encoding = $this->getid3->encoding_id3v1;
+ if ($this->getid3->encoding_id3v1_autodetect) {
+ // ID3v1 encoding detection hack START
+ // ID3v1 is defined as always using ISO-8859-1 encoding, but it is not uncommon to find files tagged with ID3v1 using Windows-1251 or other character sets
+ // Since ID3v1 has no concept of character sets there is no certain way to know we have the correct non-ISO-8859-1 character set, but we can guess
+ foreach ($ParsedID3v1['comments'] as $tag_key => $valuearray) {
+ foreach ($valuearray as $key => $value) {
+ if (preg_match('#^[\\x00-\\x40\\x80-\\xFF]+$#', $value) && !ctype_digit((string) $value)) { // check for strings with only characters above chr(128) and punctuation/numbers, but not just numeric strings (e.g. track numbers or years)
+ foreach (array('Windows-1251', 'KOI8-R') as $id3v1_bad_encoding) {
+ if (function_exists('mb_convert_encoding') && @mb_convert_encoding($value, $id3v1_bad_encoding, $id3v1_bad_encoding) === $value) {
+ $ID3v1encoding = $id3v1_bad_encoding;
+ $this->warning('ID3v1 detected as '.$id3v1_bad_encoding.' text encoding in '.$tag_key);
+ break 3;
+ } elseif (function_exists('iconv') && @iconv($id3v1_bad_encoding, $id3v1_bad_encoding, $value) === $value) {
+ $ID3v1encoding = $id3v1_bad_encoding;
+ $this->warning('ID3v1 detected as '.$id3v1_bad_encoding.' text encoding in '.$tag_key);
+ break 3;
+ }
}
}
}
}
+ // ID3v1 encoding detection hack END
}
- // ID3v1 encoding detection hack END
// ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
$GoodFormatID3v1tag = $this->GenerateID3v1Tag(
$ParsedID3v1['year'],
(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
$ParsedID3v1['comment'],
- (!empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : ''));
+ (!empty($ParsedID3v1['track_number']) ? $ParsedID3v1['track_number'] : ''));
$ParsedID3v1['padding_valid'] = true;
if ($id3v1tag !== $GoodFormatID3v1tag) {
$ParsedID3v1['padding_valid'] = false;
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
class getid3_id3v2 extends getid3_handler
$header = $this->fread(10);
if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
- $thisfile_id3v2['majorversion'] = ord($header{3});
- $thisfile_id3v2['minorversion'] = ord($header{4});
+ $thisfile_id3v2['majorversion'] = ord($header[3]);
+ $thisfile_id3v2['minorversion'] = ord($header[4]);
// shortcut
$id3v2_majorversion = &$thisfile_id3v2['majorversion'];
}
- $id3_flags = ord($header{5});
+ $id3_flags = ord($header[5]);
switch ($id3v2_majorversion) {
case 2:
// %ab000000 in v2.2
$thisfile_id3v2['padding']['length'] = strlen($framedata);
$thisfile_id3v2['padding']['valid'] = true;
for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
- if ($framedata{$i} != "\x00") {
+ if ($framedata[$i] != "\x00") {
$thisfile_id3v2['padding']['valid'] = false;
$thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
$this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
$len = strlen($framedata);
for ($i = 0; $i < $len; $i++) {
- if ($framedata{$i} != "\x00") {
+ if ($framedata[$i] != "\x00") {
$thisfile_id3v2['padding']['valid'] = false;
$thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
$this->warning('Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)');
$footer = $this->fread(10);
if (substr($footer, 0, 3) == '3DI') {
$thisfile_id3v2['footer'] = true;
- $thisfile_id3v2['majorversion_footer'] = ord($footer{3});
- $thisfile_id3v2['minorversion_footer'] = ord($footer{4});
+ $thisfile_id3v2['majorversion_footer'] = ord($footer[3]);
+ $thisfile_id3v2['minorversion_footer'] = ord($footer[4]);
}
if ($thisfile_id3v2['majorversion_footer'] <= 4) {
- $id3_flags = ord($footer{5});
+ $id3_flags = ord($footer[5]);
$thisfile_id3v2_flags['unsynch_footer'] = (bool) ($id3_flags & 0x80);
$thisfile_id3v2_flags['extfoot_footer'] = (bool) ($id3_flags & 0x40);
$thisfile_id3v2_flags['experim_footer'] = (bool) ($id3_flags & 0x20);
unset($key, $value, $genres, $genre);
}
- if (isset($thisfile_id3v2['comments']['track'])) {
- foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
+ if (isset($thisfile_id3v2['comments']['track_number'])) {
+ foreach ($thisfile_id3v2['comments']['track_number'] as $key => $value) {
if (strstr($value, '/')) {
- list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
+ list($thisfile_id3v2['comments']['track_number'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track_number'][$key]);
}
}
}
if (($this->getid3->info['id3v2']['majorversion'] == 3) && !preg_match('#[\x00]#', $genrestring)) {
// note: MusicBrainz Picard incorrectly stores plaintext genres separated by "/" when writing in ID3v2.3 mode, hack-fix here:
// replace / with NULL, then replace back the two ID3v1 genres that legitimately have "/" as part of the single genre name
- if (preg_match('#/#', $genrestring)) {
+ if (strpos($genrestring, '/') !== false) {
+ $LegitimateSlashedGenreList = array( // https://github.com/JamesHeinrich/getID3/issues/223
+ 'Pop/Funk', // ID3v1 genre #62 - https://en.wikipedia.org/wiki/ID3#standard
+ 'Cut-up/DJ', // Discogs - https://www.discogs.com/style/cut-up/dj
+ 'RnB/Swing', // Discogs - https://www.discogs.com/style/rnb/swing
+ 'Funk / Soul', // Discogs (note spaces) - https://www.discogs.com/genre/funk+%2F+soul
+ );
$genrestring = str_replace('/', "\x00", $genrestring);
- $genrestring = str_replace('Pop'."\x00".'Funk', 'Pop/Funk', $genrestring);
- $genrestring = str_replace('Rock'."\x00".'Rock', 'Folk/Rock', $genrestring);
+ foreach ($LegitimateSlashedGenreList as $SlashedGenre) {
+ $genrestring = str_ireplace(str_replace('/', "\x00", $SlashedGenre), $SlashedGenre, $genrestring);
+ }
}
// some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
- if (preg_match('#;#', $genrestring)) {
+ if (strpos($genrestring, ';') !== false) {
$genrestring = str_replace(';', "\x00", $genrestring);
}
}
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
- if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
- // if description only contains a BOM or terminator then make it blank
- $frame_description = '';
- }
+ $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+ $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
- $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $frame_description));
+ $parsedFrame['description'] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['description']));
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
+ $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator);
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
if (!isset($info['id3v2']['comments'][$parsedFrame['framenameshort']]) || !array_key_exists($commentkey, $info['id3v2']['comments'][$parsedFrame['framenameshort']])) {
//unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
- } elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
+ } elseif ($parsedFrame['frame_name'][0] == 'T') { // 4.2. T??[?] Text information frame
// There may only be one text information frame of its kind in an tag.
// <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
// excluding 'TXXX' described in 4.2.6.>
}
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
+ $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
-
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
// ID3v2.3 specs say that TPE1 (and others) can contain multiple artist values separated with /
// This of course breaks when an artist name contains slash character, e.g. "AC/DC"
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
- if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
- // if description only contains a BOM or terminator then make it blank
- $frame_description = '';
- }
- $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
-
- $frame_terminatorpos = strpos($parsedFrame['data'], $frame_textencoding_terminator);
- if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
- $frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
- }
- if ($frame_terminatorpos) {
- // there are null bytes after the data - this is not according to spec
- // only use data up to first null byte
- $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
- } else {
- // no null bytes following data, just use all data
- $frame_urldata = (string) $parsedFrame['data'];
- }
-
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
+ $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset); // according to the frame text encoding
+ $parsedFrame['url'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator)); // always ISO-8859-1
+ $parsedFrame['description'] = $this->RemoveStringTerminator($parsedFrame['description'], $frame_textencoding_terminator);
+ $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
- $parsedFrame['url'] = $frame_urldata; // always ISO-8859-1
- $parsedFrame['description'] = $frame_description; // according to the frame text encoding
if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback('ISO-8859-1', $info['id3v2']['encoding'], $parsedFrame['url']);
}
unset($parsedFrame['data']);
- } elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
+ } elseif ($parsedFrame['frame_name'][0] == 'W') { // 4.3. W??? URL link frames
// There may only be one URL link frame of its kind in a tag,
// except when stated otherwise in the frame description
// <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
- if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
- // if description only contains a BOM or terminator then make it blank
- $frame_description = '';
- }
+ $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+ $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
+ $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $frame_textencoding_terminator);
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
$parsedFrame['language'] = $frame_language;
$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
- $parsedFrame['description'] = $frame_description;
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
}
$parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
$frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($frame_textencoding_terminator));
- if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
+ if (($timestampindex == 0) && (ord($frame_remainingdata[0]) != 0)) {
// timestamp probably omitted for first data item
} else {
$parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
- if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
- // if description only contains a BOM or terminator then make it blank
- $frame_description = '';
- }
+ $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+ $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
+ $frame_text = $this->RemoveStringTerminator($frame_text, $frame_textencoding_terminator);
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
$parsedFrame['language'] = $frame_language;
$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
- $parsedFrame['description'] = $frame_description;
$parsedFrame['data'] = $frame_text;
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$commentkey = ($parsedFrame['description'] ? $parsedFrame['description'] : (!empty($info['id3v2']['comments'][$parsedFrame['framenameshort']]) ? count($info['id3v2']['comments'][$parsedFrame['framenameshort']]) : 0));
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
- if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
- // if description only contains a BOM or terminator then make it blank
- $frame_description = '';
- }
- $parsedFrame['encodingid'] = $frame_textencoding;
- $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
+ $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+ $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
+ $parsedFrame['encodingid'] = $frame_textencoding;
+ $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
if ($id3v2_majorversion == 2) {
- $parsedFrame['imagetype'] = isset($frame_imagetype) ? $frame_imagetype : null;
+ $parsedFrame['imagetype'] = isset($frame_imagetype) ? $frame_imagetype : null;
} else {
- $parsedFrame['mime'] = isset($frame_mimetype) ? $frame_mimetype : null;
+ $parsedFrame['mime'] = isset($frame_mimetype) ? $frame_mimetype : null;
}
- $parsedFrame['picturetypeid'] = $frame_picturetype;
- $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
- $parsedFrame['description'] = $frame_description;
- $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
- $parsedFrame['datalength'] = strlen($parsedFrame['data']);
+ $parsedFrame['picturetypeid'] = $frame_picturetype;
+ $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
+ $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator));
+ $parsedFrame['datalength'] = strlen($parsedFrame['data']);
- $parsedFrame['image_mime'] = '';
+ $parsedFrame['image_mime'] = '';
$imageinfo = array();
if ($imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo)) {
if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
- if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
- // if description only contains a BOM or terminator then make it blank
- $frame_description = '';
- }
+ $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+ $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
$parsedFrame['mime'] = $frame_mimetype;
$parsedFrame['filename'] = $frame_filename;
- $parsedFrame['description'] = $frame_description;
unset($parsedFrame['data']);
$frame_offset = $frame_terminatorpos + strlen("\x00");
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
- if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
- // if description only contains a BOM or terminator then make it blank
- $frame_description = '';
- }
+ $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+ $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$frame_offset = $frame_terminatorpos + strlen("\x00");
$parsedFrame['ownerid'] = $frame_ownerid;
$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
- $parsedFrame['description'] = $frame_description;
unset($parsedFrame['data']);
$parsedFrame['encodingid'] = $frame_textencoding;
$parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
- $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
+ $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
+ $parsedFrame['data'] = $this->RemoveStringTerminator($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
$info['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
}
$frame_offset += 8;
$parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
+ $parsedFrame['seller'] = $this->RemoveStringTerminator($parsedFrame['seller'], $this->TextEncodingTerminatorLookup($frame_textencoding));
unset($parsedFrame['data']);
if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($frame_textencoding_terminator), 1)) === 0) {
$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
}
- $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
- if (in_array($frame_description, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
- // if description only contains a BOM or terminator then make it blank
- $frame_description = '';
- }
+ $parsedFrame['description'] = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+ $parsedFrame['description'] = $this->MakeUTF16emptyStringEmpty($parsedFrame['description']);
$frame_offset = $frame_terminatorpos + strlen($frame_textencoding_terminator);
$frame_terminatorpos = strpos($parsedFrame['data'], "\x00", $frame_offset);
$parsedFrame['receivedasid'] = $frame_receivedasid;
$parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
$parsedFrame['sellername'] = $frame_sellername;
- $parsedFrame['description'] = $frame_description;
$parsedFrame['mime'] = $frame_mimetype;
$parsedFrame['logo'] = $frame_sellerlogo;
unset($parsedFrame['data']);
// Element ID <text string> $00
// Start time $xx xx xx xx
// End time $xx xx xx xx
- // Start offset $xx xx xx xx
- // End offset $xx xx xx xx
- // <Optional embedded sub-frames>
+ // Start offset $xx xx xx xx
+ // End offset $xx xx xx xx
+ // <Optional embedded sub-frames>
$frame_offset = 0;
@list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
$subframe['encodingid'] = ord(substr($subframe_rawdata, 0, 1));
$subframe['text'] = substr($subframe_rawdata, 1);
$subframe['encoding'] = $this->TextEncodingNameLookup($subframe['encodingid']);
- $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));;
+ $encoding_converted_text = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe['text']));
switch (substr($encoding_converted_text, 0, 2)) {
case "\xFF\xFE":
case "\xFE\xFF":
break;
}
- if (($subframe['name'] == 'TIT2') || ($subframe['name'] == 'TIT3')) {
- if ($subframe['name'] == 'TIT2') {
+ switch ($subframe['name']) {
+ case 'TIT2':
$parsedFrame['chapter_name'] = $encoding_converted_text;
- } elseif ($subframe['name'] == 'TIT3') {
+ $parsedFrame['subframes'][] = $subframe;
+ break;
+ case 'TIT3':
$parsedFrame['chapter_description'] = $encoding_converted_text;
- }
- $parsedFrame['subframes'][] = $subframe;
- } else {
- $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (only TIT2 and TIT3)');
+ $parsedFrame['subframes'][] = $subframe;
+ break;
+ case 'WXXX':
+ list($subframe['chapter_url_description'], $subframe['chapter_url']) = explode("\x00", $encoding_converted_text, 2);
+ $parsedFrame['chapter_url'][$subframe['chapter_url_description']] = $subframe['chapter_url'];
+ $parsedFrame['subframes'][] = $subframe;
+ break;
+ case 'APIC':
+ if (preg_match('#^([^\\x00]+)*\\x00(.)([^\\x00]+)*\\x00(.+)$#s', $subframe['text'], $matches)) {
+ list($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata) = $matches;
+ $subframe['image_mime'] = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_mime));
+ $subframe['picture_type'] = $this->APICPictureTypeLookup($subframe_apic_picturetype);
+ $subframe['description'] = trim(getid3_lib::iconv_fallback($subframe['encoding'], $info['encoding'], $subframe_apic_description));
+ if (strlen($this->TextEncodingTerminatorLookup($subframe['encoding'])) == 2) {
+ // the null terminator between "description" and "picture data" could be either 1 byte (ISO-8859-1, UTF-8) or two bytes (UTF-16)
+ // the above regex assumes one byte, if it's actually two then strip the second one here
+ $subframe_apic_picturedata = substr($subframe_apic_picturedata, 1);
+ }
+ $subframe['data'] = $subframe_apic_picturedata;
+ unset($dummy, $subframe_apic_mime, $subframe_apic_picturetype, $subframe_apic_description, $subframe_apic_picturedata);
+ unset($subframe['text'], $parsedFrame['text']);
+ $parsedFrame['subframes'][] = $subframe;
+ $parsedFrame['picture_present'] = true;
+ } else {
+ $this->warning('ID3v2.CHAP subframe #'.(count($parsedFrame['subframes']) + 1).' "'.$subframe['name'].'" not in expected format');
+ }
+ break;
+ default:
+ $this->warning('ID3v2.CHAP subframe "'.$subframe['name'].'" not handled (supported: TIT2, TIT3, WXXX, APIC)');
+ break;
}
}
unset($subframe_rawdata, $subframe, $encoding_converted_text);
+ unset($parsedFrame['data']); // debatable whether this this be here, without it the returned structure may contain a large amount of duplicate data if chapters contain APIC
}
$id3v2_chapter_entry = array();
- foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description') as $id3v2_chapter_key) {
+ foreach (array('id', 'time_begin', 'time_end', 'offset_begin', 'offset_end', 'chapter_name', 'chapter_description', 'chapter_url', 'picture_present') as $id3v2_chapter_key) {
if (isset($parsedFrame[$id3v2_chapter_key])) {
$id3v2_chapter_entry[$id3v2_chapter_key] = $parsedFrame[$id3v2_chapter_key];
}
// CTOC flags %xx
// Entry count $xx
// Child Element ID <string>$00 /* zero or more child CHAP or CTOC entries */
- // <Optional embedded sub-frames>
+ // <Optional embedded sub-frames>
$frame_offset = 0;
@list($parsedFrame['element_id']) = explode("\x00", $parsedFrame['data'], 2);
return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
}
+ /**
+ * @param string $string
+ * @param string $terminator
+ *
+ * @return string
+ */
+ public static function RemoveStringTerminator($string, $terminator) {
+ // Null terminator at end of comment string is somewhat ambiguous in the specification, may or may not be implemented by various taggers. Remove terminator only if present.
+ // https://github.com/JamesHeinrich/getID3/issues/121
+ // https://community.mp3tag.de/t/x-trailing-nulls-in-id3v2-comments/19227
+ if (substr($string, -strlen($terminator), strlen($terminator)) === $terminator) {
+ $string = substr($string, 0, -strlen($terminator));
+ }
+ return $string;
+ }
+
+ /**
+ * @param string $string
+ *
+ * @return string
+ */
+ public static function MakeUTF16emptyStringEmpty($string) {
+ if (in_array($string, array("\x00", "\x00\x00", "\xFF\xFE", "\xFE\xFF"))) {
+ // if string only contains a BOM or terminator then make it actually an empty string
+ $string = '';
+ }
+ return $string;
+ }
+
/**
* @param string $framename
* @param int $id3v2majorversion
switch ($id3v2majorversion) {
case 2:
return preg_match('#[A-Z][A-Z0-9]{2}#', $framename);
- break;
case 3:
case 4:
return preg_match('#[A-Z][A-Z0-9]{3}#', $framename);
- break;
}
return false;
}
*/
public static function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
for ($i = 0; $i < strlen($numberstring); $i++) {
- if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
- if (($numberstring{$i} == '.') && $allowdecimal) {
+ if ((chr($numberstring[$i]) < chr('0')) || (chr($numberstring[$i]) > chr('9'))) {
+ if (($numberstring[$i] == '.') && $allowdecimal) {
// allowed
- } elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
+ } elseif (($numberstring[$i] == '-') && $allownegative && ($i == 0)) {
// allowed
} else {
return false;
// ///
/////////////////////////////////////////////////////////////////
-
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
class getid3_lyrics3 extends getid3_handler
{
/**
$this->fseek((0 - 128 - 9 - 6), SEEK_END); // end - ID3v1 - "LYRICSEND" - [Lyrics3size]
$lyrics3_id3v1 = $this->fread(128 + 9 + 6);
- $lyrics3lsz = substr($lyrics3_id3v1, 0, 6); // Lyrics3size
+ $lyrics3lsz = (int) substr($lyrics3_id3v1, 0, 6); // Lyrics3size
$lyrics3end = substr($lyrics3_id3v1, 6, 9); // LYRICSEND or LYRICS200
$id3v1tag = substr($lyrics3_id3v1, 15, 128); // ID3v1
$GETID3_ERRORARRAY = &$info['warning'];
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
$getid3_temp = new getID3();
- $getid3_temp->openfile($this->getid3->filename);
+ $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp);
$getid3_apetag = new getid3_apetag($getid3_temp);
$getid3_apetag->overrideendoffset = $info['lyrics3']['tag_offset_start'];
$getid3_apetag->Analyze();
default:
$this->error('Cannot process Lyrics3 version '.$version.' (only v1 and v2)');
return false;
- break;
}
$data = fread($filehnd, 2);
// Check that the third character is 0xFF (Start of first segment header)
- if ($data{0} != "\xFF")
+ if ($data[0] != "\xFF")
{
// NO FF found - close file and return - JPEG is probably corrupted
fclose($filehnd);
// Flag that we havent yet hit the compressed image data
$hit_compressed_image_data = false;
- $headerdata = array();
+ $headerdata = array();
// Cycle through the file until, one of: 1) an EOI (End of image) marker is hit,
// 2) we have hit the compressed image data (no more headers are allowed after data)
// 3) or end of file is hit
- while (($data{1} != "\xD9") && (!$hit_compressed_image_data) && (!feof($filehnd)))
+ while (($data[1] != "\xD9") && (!$hit_compressed_image_data) && (!feof($filehnd)))
{
// Found a segment to look at.
// Check that the segment marker is not a Restart marker - restart markers don't have size or data after them
- if ((ord($data{1}) < 0xD0) || (ord($data{1}) > 0xD7))
+ if ((ord($data[1]) < 0xD0) || (ord($data[1]) > 0xD7))
{
// Segment isn't a Restart marker
// Read the next two bytes (size)
// Store the segment information in the output array
$headerdata[] = array(
- 'SegType' => ord($data{1}),
- 'SegName' => $GLOBALS['JPEG_Segment_Names'][ord($data{1})],
+ 'SegType' => ord($data[1]),
+ 'SegName' => $GLOBALS['JPEG_Segment_Names'][ord($data[1])],
'SegDataStart' => $segdatastart,
'SegData' => $segdata,
);
}
// If this is a SOS (Start Of Scan) segment, then there is no more header data - the compressed image data follows
- if ($data{1} == "\xDA")
+ if ($data[1] == "\xDA")
{
// Flag that we have hit the compressed image data - exit loop as no more headers available.
$hit_compressed_image_data = true;
$data = fread($filehnd, 2);
// Check that the first byte of the two is 0xFF as it should be for a marker
- if ($data{0} != "\xFF")
+ if ($data[0] != "\xFF")
{
// NO FF found - close file and return - JPEG is probably corrupted
fclose($filehnd);
{
// Check whether we want this details from this attribute
// if (in_array($key, $GLOBALS['XMP_tag_captions']))
- if (true)
- {
+// if (true)
+// {
// Attribute wanted
$xmp_array[$key] = $xml_elem['attributes'][$key];
- }
+// }
}
}
break;
default:
// Check whether we want the details from this attribute
// if (in_array($xml_elem['tag'], $GLOBALS['XMP_tag_captions']))
- if (true)
- {
+// if (true)
+// {
switch ($xml_elem['type'])
{
case 'open':
// ignore
break;
}
- }
+// }
break;
}
// ///
/////////////////////////////////////////////////////////////////
-
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
class getid3_write_apetag
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
class getid3_write_id3v1
} else {
fseek($fp_source, 0, SEEK_END); // append new ID3v1 tag
}
- $this->tag_data['track'] = (isset($this->tag_data['track']) ? $this->tag_data['track'] : (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : (isset($this->tag_data['tracknumber']) ? $this->tag_data['tracknumber'] : '')));
+ $this->tag_data['track_number'] = (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : '');
$new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
- (isset($this->tag_data['title'] ) ? $this->tag_data['title'] : ''),
- (isset($this->tag_data['artist'] ) ? $this->tag_data['artist'] : ''),
- (isset($this->tag_data['album'] ) ? $this->tag_data['album'] : ''),
- (isset($this->tag_data['year'] ) ? $this->tag_data['year'] : ''),
- (isset($this->tag_data['genreid']) ? $this->tag_data['genreid'] : ''),
- (isset($this->tag_data['comment']) ? $this->tag_data['comment'] : ''),
- (isset($this->tag_data['track'] ) ? $this->tag_data['track'] : ''));
+ (isset($this->tag_data['title'] ) ? $this->tag_data['title'] : ''),
+ (isset($this->tag_data['artist'] ) ? $this->tag_data['artist'] : ''),
+ (isset($this->tag_data['album'] ) ? $this->tag_data['album'] : ''),
+ (isset($this->tag_data['year'] ) ? $this->tag_data['year'] : ''),
+ (isset($this->tag_data['genreid'] ) ? $this->tag_data['genreid'] : ''),
+ (isset($this->tag_data['comment'] ) ? $this->tag_data['comment'] : ''),
+ (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : ''));
fwrite($fp_source, $new_id3v1_tag_data, 128);
fclose($fp_source);
return true;
// ///
/////////////////////////////////////////////////////////////////
+if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers
+ exit;
+}
getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
class getid3_write_id3v2
public $filename;
/**
- * @var array
+ * @var array|null
*/
public $tag_data;
default:
return false;
- break;
}
return chr(bindec($flag));
}
public function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) {
$flag1 = null;
$flag2 = null;
- switch ($this->majorversion) {
+ switch ($this->majorversion) {
case 4:
// %0abc0000 %0h00kmnp
$flag1 = '0';
default:
return false;
- break;
}
return chr(bindec($flag1)).chr(bindec($flag2));
$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
} elseif (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) {
$this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion;
- } elseif (($this->majorversion >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) {
+ } elseif ((!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) {
$this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion;
} elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false))) {
//$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
// Email to user <text string> $00
// Rating $xx
// Counter $xx xx xx xx (xx ...)
+ if (!$this->IsValidEmail($source_data_array['email'])) {
+ // https://github.com/JamesHeinrich/getID3/issues/216
+ // https://en.wikipedia.org/wiki/ID3#ID3v2_rating_tag_issue
+ // ID3v2 specs say it should be an email address, but Windows instead uses string like "Windows Media Player 9 Series"
+ $this->warnings[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')';
+ }
if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) {
$this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)';
- } elseif (!$this->IsValidEmail($source_data_array['email'])) {
- $this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')';
} else {
$framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00";
$framedata .= chr($source_data_array['rating']);
$this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
} else {
$framedata .= chr($source_data_array['encodingid']);
- unset($pricestring);
$pricestrings = array();
foreach ($source_data_array['price'] as $key => $val) {
if ($this->ID3v2IsValidPriceString($key.$val['value'])) {
break;
default:
- if ((($this->majorversion == 2) && (strlen($frame_name) != 3)) || (($this->majorversion > 2) && (strlen($frame_name) != 4))) {
+ if (/*(($this->majorversion == 2) && (strlen($frame_name) != 3)) || (($this->majorversion > 2) && (*/strlen($frame_name) != 4/*))*/) {
$this->errors[] = 'Invalid frame name "'.$frame_name.'" for ID3v2.'.$this->majorversion;
- } elseif ($frame_name{0} == 'T') {
+ } elseif ($frame_name[0] == 'T') {
// 4.2. T??? Text information frames
// Text encoding $xx
// Information <text string(s) according to encoding>
$framedata .= chr($source_data_array['encodingid']);
$framedata .= $source_data_array['data'];
}
- } elseif ($frame_name{0} == 'W') {
+ } elseif ($frame_name[0] == 'W') {
// 4.3. W??? URL link frames
// URL <text string>
if (!$this->IsValidURL($source_data_array['data'], false)) {
break;
default:
- if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+ if (($frame_name[0] != 'T') && ($frame_name[0] != 'W')) {
$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
}
break;
break;
default:
- if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+ if (($frame_name[0] != 'T') && ($frame_name[0] != 'W')) {
$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
}
break;
break;
default:
- if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+ if (($frame_name[0] != 'T') && ($frame_name[0] != 'W')) {
$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
}
break;
if ($noerrorsonly) {
return false;
} else {
- unset($frame_name);
+ $frame_name = null;
}
}
} else {
// ignore any invalid frame names, including 'title', 'header', etc
$this->warnings[] = 'Ignoring invalid ID3v2 frame type: "'.$frame_name.'"';
- unset($frame_name);
+ $frame_name = null;
unset($frame_length);
unset($frame_flags);
unset($frame_data);
}
- if (isset($frame_name) && isset($frame_length) && isset($frame_flags) && isset($frame_data)) {
+ if (null !== $frame_name && isset($frame_length) && isset($frame_flags) && isset($frame_data)) {
$tagstring .= $frame_name.$frame_length.$frame_flags.$frame_data;
}
}
}
$footer = false; // ID3v2 footers not yet supported in getID3()
- if (!$footer && ($this->paddedlength > (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion)))) {
+ if (/*!$footer && */($this->paddedlength > (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion)))) {
// pad up to $paddedlength bytes if unpadded tag is shorter than $paddedlength
// "Furthermore it MUST NOT have any padding when a tag footer is added to the tag."
if (($this->paddedlength - strlen($tagstring) - getid3_id3v2::ID3v2HeaderLength($this->majorversion)) > 0) {
switch ($framename) {
case 'RGAD':
return false;
- break;
default:
return false;
- break;
}
}
$unsyncheddata = '';
$datalength = strlen($data);
for ($i = 0; $i < $datalength; $i++) {
- $thischar = $data{$i};
+ $thischar = $data[$i];
$unsyncheddata .= $thischar;
if ($thischar == "\xFF") {
- $nextchar = ord($data{$i + 1});
+ $nextchar = ord($data[$i + 1]);
if (($nextchar & 0xE0) == 0xE0) {
// previous byte = 11111111, this byte = 111?????
$unsyncheddata .= "\x00";
$ID3v2ShortFrameNameLookup[4]['performer_sort_order'] = 'TSOP';
$ID3v2ShortFrameNameLookup[4]['title_sort_order'] = 'TSOT';
$ID3v2ShortFrameNameLookup[4]['set_subtitle'] = 'TSST';
+ $ID3v2ShortFrameNameLookup[4]['year'] = 'TDRC'; // subset of ISO 8601: valid timestamps are yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and yyyy-MM-ddTHH:mm:ss. All time stamps are UTC
}
return (isset($ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)]) ? $ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)] : '');
//$AllowedTagFormats = array('metaflac');
$this->errors[] = 'metaflac is not (yet) compatible with OggFLAC files';
return false;
- break;
case 'vorbis':
$AllowedTagFormats = array('vorbiscomment');
break;
default:
$this->errors[] = 'metaflac is not (yet) compatible with Ogg files other than OggVorbis';
return false;
- break;
}
break;
default:
$this->errors[] = 'unknown tag format "'.$tagformat.'" in $tagformats in WriteTags()';
return false;
- break;
}
}
}
}
- // Convert "TRACK" to "TRACKNUMBER" (if needed) for compatability with all formats
- if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACKNUMBER'])) {
- $this->tag_data['TRACKNUMBER'] = $this->tag_data['TRACK'];
+ // Convert "TRACK" to "TRACK_NUMBER" (if needed) for compatability with all formats
+ if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACK_NUMBER'])) {
+ $this->tag_data['TRACK_NUMBER'] = $this->tag_data['TRACK'];
unset($this->tag_data['TRACK']);
}
default:
$this->errors[] = 'Invalid tag format to write: "'.$tagformat.'"';
return false;
- break;
}
if (!$success) {
return false;
default:
$this->errors[] = 'Invalid tag format to delete: "'.$DeleteTagFormat.'"';
return false;
- break;
}
if (!$success) {
return false;
// do nothing - ignore previous data
} else {
throw new Exception('$this->overwrite_tags=false is known to be buggy in this version of getID3. Check http://github.com/JamesHeinrich/getID3 for a newer version.');
- if (!isset($this->ThisFileInfo['tags'][$TagFormat])) {
- return false;
- }
- $tag_data = array_merge_recursive($tag_data, $this->ThisFileInfo['tags'][$TagFormat]);
+// if (!isset($this->ThisFileInfo['tags'][$TagFormat])) {
+// return false;
+// }
+// $tag_data = array_merge_recursive($tag_data, $this->ThisFileInfo['tags'][$TagFormat]);
}
return true;
}
}
}
}
- $tag_data_id3v1['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE'] ) ? $this->tag_data['TITLE'] : array())));
- $tag_data_id3v1['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST'] ) ? $this->tag_data['ARTIST'] : array())));
- $tag_data_id3v1['album'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ALBUM'] ) ? $this->tag_data['ALBUM'] : array())));
- $tag_data_id3v1['year'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['YEAR'] ) ? $this->tag_data['YEAR'] : array())));
- $tag_data_id3v1['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COMMENT'] ) ? $this->tag_data['COMMENT'] : array())));
- $tag_data_id3v1['track'] = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TRACKNUMBER']) ? $this->tag_data['TRACKNUMBER'] : array()))));
- if ($tag_data_id3v1['track'] <= 0) {
- $tag_data_id3v1['track'] = '';
+ $tag_data_id3v1['title'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TITLE'] ) ? $this->tag_data['TITLE'] : array())));
+ $tag_data_id3v1['artist'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ARTIST'] ) ? $this->tag_data['ARTIST'] : array())));
+ $tag_data_id3v1['album'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['ALBUM'] ) ? $this->tag_data['ALBUM'] : array())));
+ $tag_data_id3v1['year'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['YEAR'] ) ? $this->tag_data['YEAR'] : array())));
+ $tag_data_id3v1['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['COMMENT'] ) ? $this->tag_data['COMMENT'] : array())));
+ $tag_data_id3v1['track_number'] = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', implode(' ', (isset($this->tag_data['TRACK_NUMBER']) ? $this->tag_data['TRACK_NUMBER'] : array()))));
+ if ($tag_data_id3v1['track_number'] <= 0) {
+ $tag_data_id3v1['track_number'] = '';
}
$this->MergeExistingTagData('id3v1', $tag_data_id3v1);
// note: some software, notably Windows Media Player and iTunes are broken and treat files tagged with UTF-16BE (with BOM) as corrupt
// therefore we force data to UTF-16LE and manually prepend the BOM
$ID3v2_tag_data_converted = false;
- if (!$ID3v2_tag_data_converted && ($this->tag_encoding == 'ISO-8859-1')) {
+ if (/*!$ID3v2_tag_data_converted && */($this->tag_encoding == 'ISO-8859-1')) {
// great, leave data as-is for minimum compatability problems
$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 0;
$tag_data_id3v2[$ID3v2_framename][$key]['data'] = $value;
do {
// if UTF-8 string does not include any characters above chr(127) then it is identical to ISO-8859-1
for ($i = 0; $i < strlen($value); $i++) {
- if (ord($value{$i}) > 127) {
+ if (ord($value[$i]) > 127) {
break 2;
}
}
<paquet
prefix="medias"
categorie="multimedia"
- version="2.20.34"
+ version="2.20.35"
etat="stable"
compatibilite="[3.2.0;3.2.*]"
logo="prive/themes/spip/images/portfolio-32.png"
[(#REM) puis les images du portfolio]
<B_portfolio>
-<h3><:medias:info_portfolio:></h3>
+<h3 class="portfolios__titre"><:medias:info_portfolio:></h3>
<div class="liste_items documents ordonner_rang_lien" id="portfolio#ENV{id_unique}" data-cookie-affichage="portfolio" data-lien="[(#OBJET|concat{'/',#ID_OBJET}|attribut_html)]">
[<p class="pagination">(#PAGINATION{prive})</p>]
<div class="sortable">
[(#REM) puis les documents]
<B_documents>
-<h3><:medias:info_documents:></h3>
+<h3 class="portfolios__titre"><:medias:info_documents:></h3>
<div class="liste_items documents ordonner_rang_lien" id="documents#ENV{id_unique}" data-cookie-affichage="documents" data-lien="[(#OBJET|concat{'/',#ID_OBJET}|attribut_html)]">
[<p class="pagination">(#PAGINATION{prive})</p>]
<div class="sortable">
'DESCRIPTION' => $row['texte'],
'SUMMARY' => $row['titre'],
'CATEGORIES' => $cat,
- 'ATTENDEE' => (count($auteurs) == 0) ? '' : join($auteurs, ', ')
+ 'ATTENDEE' => (count($auteurs) == 0) ? '' : implode(', ', $auteurs)
);
}
if (!$fin_old) {
// Paragraphes supprimes jusqu'au paragraphe courant
if (!isset($i_old)) {
- list($i_old, $p_old) = each($paras_old);
+ $i_old = key($paras_old);
+ $p_old = current($paras_old);
+ next($paras_old);
if (!$p_old) {
$fin_old = true;
}
$this->diff->supprimer($p_old);
}
unset($i_old);
- list($i_old, $p_old) = each($paras_old);
+ $i_old = key($paras_old);
+ $p_old = current($paras_old);
+ next($paras_old);
if (!$p_old) {
$fin_old = true;
}
// Paragraphes supprimes a la fin du texte
if (!$fin_old) {
if (!isset($i_old)) {
- list($i_old, $p_old) = each($paras_old);
+ $i_old = key($paras_old);
+ $p_old = current($paras_old);
+ next($paras_old);
if (!strlen($p_old)) {
$fin_old = true;
}
if (!isset($trans_rev[$i_old])) {
$this->diff->supprimer($p_old);
}
- list($i_old, $p_old) = each($paras_old);
+ $i_old = key($paras_old);
+ $p_old = current($paras_old);
+ next($paras_old);
if (!$p_old) {
$fin_old = true;
}
// distinctif (pour eviter la violation d'unicite)
// et un titre contenant en fait le moment de l'insertion
list($ms, $sec) = explode(' ', microtime());
- $date = $sec . substr($ms, 1,
- 4) - 20; // SQL ne ramene que 4 chiffres significatifs apres la virgule pour 0.0+titre_version
+ // SQL ne ramene que 4 chiffres significatifs apres la virgule pour 0.0+titre_version
+ $date = ($sec . substr($ms, 1, 4)) - 20;
$datediff = ($sec - mktime(0, 0, 0, 9, 1, 2007)) * 1000000 + substr($ms, 2, strlen($ms) - 4);
$valeurs = array(
for ($i = 0; $i < $n; $i++) {
while ($i >= $paras_champ[$nom]) {
- list($nom, ) = each($champs);
+ $nom = key($champs);
+ next($champs);
}
// Lier au fragment existant si possible, sinon creer un nouveau fragment
$id_fragment = isset($trans[$i]) ? $trans[$i] : $next++;
\r
function scanCharacter() {\r
if ($this->position < $this->length) {\r
- return $this->rawtext{$this->position++};\r
+ return $this->rawtext[$this->position++];\r
}\r
}\r
\r
\r
function scanUntilCharacters($string) {\r
$startpos = $this->position;\r
- while ($this->position < $this->length && strpos($string, $this->rawtext{$this->position}) === FALSE) {\r
+ while ($this->position < $this->length && strpos($string, $this->rawtext[$this->position]) === FALSE) {\r
$this->position++;\r
}\r
return substr($this->rawtext, $startpos, $this->position - $startpos);\r
\r
function ignoreWhitespace() {\r
while ($this->position < $this->length && \r
- strpos(" \n\r\t", $this->rawtext{$this->position}) !== FALSE) {\r
+ strpos(" \n\r\t", $this->rawtext[$this->position]) !== FALSE) {\r
$this->position++;\r
}\r
}\r
foreach ($this->blackProtocols as $proto) {
$preg = "/[\s\x01-\x1F]*";
for ($i=0; $i<strlen($proto); $i++) {
- $preg .= $proto{$i} . "[\s\x01-\x1F]*";
+ $preg .= $proto[$i] . "[\s\x01-\x1F]*";
}
$preg .= ":/i";
$this->protoRegexps[] = $preg;
) {
if ($matches[1]) {
$bornes['min']['valeur'] = trim($matches[1]);
- $bornes['min']['incluse'] = ($intervalle{0} == "[");
+ $bornes['min']['incluse'] = ($intervalle[0] == "[");
}
if ($matches[2]) {
$bornes['max']['valeur'] = trim($matches[2]);
}
$mineure = $regs[1];
+
$majeure = preg_replace(',\.99$,', '.*', $regs[2]);
- $mineure_inc = $intervalle{0} == "[";
+ $mineure_inc = $intervalle[0] == "[";
$majeure_inc = substr($intervalle, -1) == "]";
if (strlen($mineure)) {
if (!strlen($majeure)) {
protected $ruleset;
protected static $subwheel = array();
+ // Experimental : projet de compilation PHP d'une wheel
+ // pour generation d'un fichier php execute a la place de ->text()
protected $compiled = array();
/**
foreach ($rules as $name => $rule) {
$rule->name = $name;
$this->initRule($rule);
- if (is_string($rule->replace)
- and isset($this->compiled[$rule->replace])
- and $fun = $this->compiled[$rule->replace]
+ if ($rule->replace
+ and $compiledEntry = $this->ruleCompiledEntryName($rule->replace)
+ and isset($this->compiled[$compiledEntry])
+ and $fun = $this->compiled[$compiledEntry]
) {
$pre[] = "\n###\n## $name\n###\n" . $fun;
preg_match(',function (\w+), ', $fun, $r);
- $rule->compilereplace = $r[1]; # ne pas modifier ->replace sinon on casse l'execution...
+ $rule->compilereplace = "'".$r[1]."'"; # ne pas modifier ->replace sinon on casse l'execution...
}
$r = "\t/* $name */\n";
if ($rule->func_replace !== 'replace_identity') {
$fun = 'TextWheel::' . $rule->func_replace;
+ $call = '';
switch ($fun) {
case 'TextWheel::replace_all_cb':
- $fun = $rule->replace; # trim()...
+ if (is_string($rule->replace)) {
+ $fun = $rule->replace;
+ }
+ elseif ($rule->compilereplace) {
+ $fun = trim($rule->compilereplace, "'");
+ };
+ if ($fun) {
+ $call = "\$t = $fun(\$t);";
+ }
break;
case 'TextWheel::replace_preg':
$fun = 'preg_replace';
default:
break;
}
- $r .= "\t" . '$t = ' . $fun . '(' . TextWheel::export($rule->match) . ', ' . TextWheel::export($rule->replace) . ', $t);' . "\n";
+ if (!$call) {
+ if (empty($rule->compilereplace)) {
+ $rule->compilereplace = TextWheel::export($rule->replace);
+ }
+ $call = '$t = ' . $fun . '(' . TextWheel::export($rule->match) . ', ' . $rule->compilereplace . ', $t);';
+ }
+ $r .= "\t$call\n";
}
$comp[] = $r;
return $tw;
}
+ /**
+ * @param $replace
+ * @return string
+ */
+ protected function ruleCompiledEntryName($replace) {
+ if (is_array($replace)) {
+ return serialize($replace);
+ }
+ elseif (is_object($replace)) {
+ return get_class($replace) . ':' . spl_object_hash($replace);
+ }
+ return $replace;
+ }
+
/**
* Initializing a rule a first call
* including file, creating function or wheel
}
if ($rule->create_replace) {
+ // DEPRECATED : rule->create_replace, on ne peut rien faire de mieux ici
+ // mais c'est voue a disparaitre
$compile = $rule->replace . '($t)';
$rule->replace = create_function('$m', $rule->replace);
- $this->compiled[$rule->replace] = $compile;
+ $this->compiled[$this->ruleCompiledEntryName($rule->replace)] = $compile;
$rule->create_replace = false;
$rule->is_callback = true;
- } elseif ($rule->is_wheel) {
- $n = count(TextWheel::$subwheel);
+ }
+ elseif ($rule->is_wheel) {
+ $rule_number = count(TextWheel::$subwheel);
TextWheel::$subwheel[] = $this->createSubWheel($rule->replace);
- $var = '$m[' . intval($rule->pick_match) . ']';
+ $cname = 'compiled_' . str_replace('-', '_', $rule->name) . '_' . substr(md5(spl_object_hash($rule)),0,7);
if ($rule->type == 'all' or $rule->type == 'str' or $rule->type == 'split' or !isset($rule->match)) {
- $var = '$m';
+ $rule->replace = function ($m) use ($rule_number) {
+ return TextWheel::getSubWheel($rule_number)->text($m);
+ };
+ $rule->compilereplace = "'$cname'";
+ }
+ else {
+ $pick_match = intval($rule->pick_match);
+ $rule->replace = function ($m) use ($rule_number, $pick_match) {
+ return TextWheel::getSubWheel($rule_number)->text($m[$pick_match]);
+ };
+ $rule->compilereplace = 'function ($m) { return '.$cname.'($m['.$pick_match.']) }';
}
- $code = 'return TextWheel::getSubWheel(' . $n . ')->text(' . $var . ');';
- $rule->replace = create_function('$m', $code);
- $cname = 'compiled_' . str_replace('-', '_', $rule->name);
- $compile = TextWheel::getSubWheel($n)->compile($cname);
- $this->compiled[$rule->replace] = $compile;
$rule->is_wheel = false;
$rule->is_callback = true;
+ $compile = TextWheel::getSubWheel($rule_number)->compile($cname);
+ $this->compiled[$this->ruleCompiledEntryName($rule->replace)] = $compile;
}
# optimization
# optional
# language specific
public $require; # file to require_once
+ # DEPRECATED : create_function deprecated a partir de PHP 7.2, ne plus utiliser
public $create_replace; # do create_function('$m', %) on $this->replace, $m is the matched array
# optimizations
public $func_replace;
+ public $compilereplace;
/**
* Rule constructor
$lignes = array();
$debut_table = $summary = '';
$l = 0;
- $numeric = true;
// Traiter chaque ligne
$reg_line1 = ',^(\|(' . _RACCOURCI_TH_SPAN . '))+$,sS';
if (
(1 == count($keys) && '0' == $keys[0])
||
- (count($keys) > 1 && array_reduce($keys, create_function('$v,$w', 'return (integer) $v + $w;'), 0) == count($keys) * (count($keys) - 1) / 2))
+ (count($keys) > 1 && array_reduce($keys, function($v,$w) { return (integer) $v + $w; }, 0) == count($keys) * (count($keys) - 1) / 2))
{
$output = array();
foreach ($value as $val)
// evaluate the string
$buffer = str_replace(array('\\n', '\\r'), array("\n", "\r"), $buffer);
if (strpos($buffer,'\\x')!==false){
- $buffer = preg_replace_callback(',\\\\x([0-9a-f]+),', create_function('$m', 'return chr(hexdec($m[1]));'), $buffer);
+ $buffer = preg_replace_callback(',\\\\x([0-9a-f]+),', function($m) { return chr(hexdec($m[1])); }, $buffer);
}
}
<paquet
prefix="tw"
categorie="edition"
- version="1.5.5"
+ version="1.5.6"
etat="stable"
compatibilite="[3.2.0;3.2.*]"
logo="textwheel-32.png"
#HTTP_HEADER{"Pragma: no-cache"}
<div class='formulaire_spip formulaire_login'>
- [<p class="reponse_formulaire reponse_formulaire_ok">(#ENV*{_deja_loge})</p>]
+ [<p class="reponse_formulaire reponse_formulaire_ok">(#ENV**{_deja_loge})</p>]
[<p class="reponse_formulaire reponse_formulaire_ok">(#ENV*{message_ok})</p>]
[<p class='reponse_formulaire reponse_formulaire_erreur'>(#ENV*{message_erreur})</p>]
if (isset($res['redirect']) and $res['redirect']) {
include_spip('inc/headers');
# preparer un lien pour quand redirige_formulaire ne fonctionne pas
- $m = redirige_formulaire($res['redirect'], '', 'ajaxform');
+ $m = redirige_formulaire($res['redirect']);
$valeurs['_deja_loge'] = inserer_attribut(
'<a>' . _T('login_par_ici') . "</a>$m",
'href',
<:info_webmestre_forces{file_options=#GET{options}}:>
]
[(#VAL{_ID_WEBMESTRES}|defined|non)
- [(#BOUTON_ACTION{<:info_admin_etre_webmestre:>,[(#URL_ACTION_AUTEUR{etre_webmestre,[(#REM|time)],#SELF})]})]
+ [(#BOUTON_ACTION{<:info_admin_etre_webmestre:>,[(#URL_ACTION_AUTEUR{etre_webmestre,[(#EVAL{'time()'})],#SELF})]})]
]
]
<INCLURE{fond=prive/objets/infos/inc-auteur-rubriques,id_auteur,statut} />
if (count($res) > 1) {
$out = implode(' ', $res);
}
- $memo = $res = null;
+ $memo = null;
+ $res = array();
}
return $out;
<iframe src="#URL_ACTION_AUTEUR{calculer_taille_cache,skel}" style="width: 100%;height: 3em;overflow: hidden;"></iframe>
</noscript>
- #SET{cache_inhib,#CONFIG{cache_inhib}|sinon{0}|>{#REM|time}|oui}
+ #SET{cache_inhib,#CONFIG{cache_inhib}|sinon{0}|>{#EVAL{'time()'}}|oui}
[(#GET{cache_inhib}|oui)
<div><strong><:info_cache_desactive:></strong>