Compare commits

..

304 commits
v2.1 ... master

Author SHA1 Message Date
Anael MOBILIA
f8252e4425 Ajout d'une constante pour le nombre d'affichages par jour étant totalement abusif + remontée dans le cron de chaque heure 2024-12-20 01:03:33 +01:00
Anael MOBILIA
2616a68778 Fix tests 2024-12-20 00:51:36 +01:00
Anael MOBILIA
793ae79e6c Fix typos 2024-12-20 00:46:18 +01:00
Anael MOBILIA
3a6fcb6366 Ajout de la détection des images dont le nombre d'affichages est clairement abusif. 2024-12-20 00:45:10 +01:00
Anael MOBILIA
3681e2264b Fix codeStyle 2024-12-09 22:28:43 +01:00
Anael MOBILIA
4294c9bfa3 #45 - Ne pas bloquer l'image 404 si l'image demandée n'existe plus 2024-12-09 22:22:05 +01:00
Anael MOBILIA
610ef1deb2 Avoir toutes les erreurs PHP
Cf https://github.com/shivammathur/setup-php/issues/450
2024-12-09 01:09:32 +01:00
Anael MOBILIA
f431c3b69e Fix tests 2024-12-09 01:01:47 +01:00
Anael MOBILIA
14cced79f9 Ne pas générer d'email lorsque c'est PhpUnit 2024-12-09 00:50:48 +01:00
Anael MOBILIA
1fa288198a Fix tests - besoin d'une image sur le disque 2024-12-09 00:45:11 +01:00
Anael MOBILIA
1ddf88519a Fix #45 - détection des userAgent 2024-12-09 00:39:03 +01:00
Anael MOBILIA
a42f472301 Fix tests - double chargement de la config 2024-12-09 00:33:34 +01:00
Anael MOBILIA
9c13d4cff4 Fix tests 2024-12-09 00:28:25 +01:00
Anael MOBILIA
af4ac289f9 Fix tests 2024-12-09 00:25:58 +01:00
Anael MOBILIA
7c74e2e080 Ajout de PHP 8.4 dans les tests 2024-12-09 00:22:44 +01:00
Anael MOBILIA
19f8763129 Fix tests / codestyle 2024-12-09 00:22:30 +01:00
Anael MOBILIA
673492abe8 #45 : ajout de la possiblité de bloquer des images en fonction des User-Agent utilisés 2024-12-09 00:15:05 +01:00
Anael MOBILIA
5c0af81225 Gestion du tri des images 2024-11-11 18:49:20 +01:00
Anael MOBILIA
f5a90ebcd6 Fix valeur par défaut 2024-11-11 18:43:05 +01:00
Anael MOBILIA
03afea0772 Ajout de la date d'envoi du message 2024-11-11 18:31:01 +01:00
Anael MOBILIA
ea809f670c Bootstrap++ 2024-11-10 23:07:59 +01:00
Anael MOBILIA
bfdca5deff Stats : ajout du nombre total d'images dans le temps 2024-11-10 22:57:00 +01:00
Anael MOBILIA
1fac38bd54 Ajout d'index pour optimiser les performances de recherche pour les abus 2024-11-10 22:56:21 +01:00
Anael MOBILIA
ebc9028fb9 Relever toutes les erreurs 2024-11-10 22:55:35 +01:00
Anael MOBILIA
c19e83bfa9 Fix notice si tâche cron 2024-11-10 22:54:24 +01:00
Anael MOBILIA
a76cebf46f lastUpload : mise en exergue des images qui ont un état particulier + standardisation du code 2024-11-10 22:53:52 +01:00
Anael MOBILIA
fc763d96d1 Abuse : ajout de la liste des images approuvables + filtres bloquées / approuvées 2024-11-10 22:53:52 +01:00
Anael MOBILIA
003d978b38 Gestion du chargement multiple d'images pour ne pas effectuer des requêtes SQL en boucle 2024-11-10 22:44:47 +01:00
Anael MOBILIA
7384f93f4a Update doc d'install 2024-11-10 20:35:46 +01:00
Anael MOBILIA
bbe62bc134 Gestion des extensions des images en minuscules 2024-10-08 03:00:07 +02:00
Anael MOBILIA
62d45a9fd9 upload : toujours optimiser l'image (permettra de comparer son hash avec celles déjà stockées) 2024-10-08 03:00:03 +02:00
Anael MOBILIA
09c725c451 Tor : En cas d'erreur, par défaut, faire confiance à l'IP 2024-10-08 02:59:59 +02:00
Anael MOBILIA
0b94e0f789 getNbDoublons() -> getNbUsages()
+ harmonisation de code
2024-10-08 02:59:54 +02:00
Anael MOBILIA
200fd93450 Code cleanup 2024-10-08 02:59:50 +02:00
Anael MOBILIA
779130c08f Factorisation de code 2024-10-08 02:59:46 +02:00
Anael MOBILIA
352e5f44c4 Meilleure gestion du message d'erreur 2024-10-08 02:59:42 +02:00
Anael MOBILIA
941442f5ea Meilleure vérification des attributs + fix tests 2024-10-08 02:59:39 +02:00
Anael MOBILIA
0b66c3f7c6 Le namespace de PHPUnit est désormais "PHPUnitPHAR" => génériser la détection 2024-10-08 02:59:34 +02:00
Anael MOBILIA
d5426fe324 Tests : fix + codeStyle
A noter : pour certains types d'images, PHP change les entêtes de fichier en fonction des libs installées sur le serveur.
On ne compare plus le résultat à un fichier déjà fourni, mais on vérifie les caractéristiques du fichier généré. #38
2024-10-08 02:59:29 +02:00
Anael MOBILIA
33fe810af1 Certaines images sont envoyées plusieurs fois d'affilée -> dédoublonner la liste 2024-03-25 23:14:57 +01:00
Anael MOBILIA
4274601a2c Meilleure protection du paramètre 2024-03-25 23:14:27 +01:00
Anael MOBILIA
28bba299ba Effacer les logs de connexion au bout d'un an 2024-03-08 22:22:41 +01:00
Anael MOBILIA
44a321fd9e Meilleure description du script 2024-03-08 22:22:05 +01:00
Anael MOBILIA
1de0d48e1d Fix tests 2024-03-08 22:02:51 +01:00
Anael MOBILIA
9b6a4ae061 Fix codeStyle 2024-03-08 21:49:33 +01:00
Anael MOBILIA
49ce8a95d6 Fix #43 : la définition du statut approuvé / bloqué est contaminant pour les images possédant le même MD5 2024-03-08 21:48:03 +01:00
Anael MOBILIA
c978a35134 Amélioration des debug 2024-02-22 22:01:13 +01:00
Anael MOBILIA
204563ba11 Ne mettre à jour la liste des noeuds Tor que si on a pu la récupérer 2024-02-22 22:00:10 +01:00
Anael MOBILIA
d0909b42ca Fix codestyle 2024-02-15 23:43:26 +01:00
Anael MOBILIA
28837c75aa Ajout d'une projection d'utilisation de l'image pour prédire les images qui risquent de dépasser les limites d'affichage autorisées 2024-02-15 23:26:56 +01:00
Anael MOBILIA
94ff35cc9f Fix tests 2024-02-15 23:25:16 +01:00
Anael MOBILIA
dd583f55cd Ne pas charger systématiquement le template pour pouvoir faire un redirect si création de compte OK 2024-02-15 21:56:06 +01:00
Anael MOBILIA
c0c67c8956 Avoir le détail des paramètres des méthodes dans les stack traces 2024-02-15 21:55:27 +01:00
Anael MOBILIA
b3e8da9149 Fix error_handler + création d'une Exception custom pour l'application 2024-02-15 21:55:07 +01:00
Anael MOBILIA
cc26a494cb Fix tests 2024-02-07 00:55:29 +01:00
Anael MOBILIA
f3e5fdd2b2 Fix tests 2024-02-07 00:51:18 +01:00
Anael MOBILIA
27975e72e8 Code cleanup 2024-02-07 00:42:04 +01:00
Anael MOBILIA
c3d3580498 Fix tests 2024-02-07 00:41:30 +01:00
Anael MOBILIA
f71d25067b Fix tests 2024-02-07 00:33:34 +01:00
Anael MOBILIA
e7c4468d29 Fix tests 2024-02-07 00:30:20 +01:00
Anael MOBILIA
fe1d1d93eb PHPUnit 10 -> 11 2024-02-07 00:20:18 +01:00
Anael MOBILIA
12cce10500 Fix tests 2024-02-06 23:51:07 +01:00
Anael MOBILIA
f4afe87a3d Update des github action 2024-02-06 23:47:46 +01:00
Anael MOBILIA
7e49c50bc0 Code cleanup 2024-02-06 23:20:01 +01:00
Anael MOBILIA
e2999844dd Deps++ 2024-02-06 21:54:34 +01:00
Anael MOBILIA
8254167cc2 Passage à PHP8.3 + code clean 2024-02-06 21:44:28 +01:00
Anael MOBILIA
36cabb1fcd Ajout d'un error_handler en complément du exception_handler 2024-02-06 21:00:27 +01:00
Anael MOBILIA
e98571db9e Fix warning float/int (PHP 8.3) 2024-02-06 20:59:57 +01:00
Anael MOBILIA
4a9597ab3c Formatage 2024-02-04 00:25:09 +01:00
Anael MOBILIA
879f93b826 Fix texte 2024-02-04 00:24:58 +01:00
Anael MOBILIA
19c30e044c Fix rendu sur écrans plus petits 2024-02-04 00:24:38 +01:00
Anael MOBILIA
58a2786a14 N'afficher un warning pour les images webp animées que si un traitement est demandé 2024-02-02 22:06:20 +01:00
Anael MOBILIA
47991395af Ne pas effectuer de traitement si une erreur est rencontrée en amont. 2024-01-27 22:52:37 +01:00
Anael MOBILIA
3d850bd348 PHP ne gère pas les images webp animées.
-> Ne pas faire de modification sur ces fichiers
Fix #42
2024-01-26 23:08:15 +01:00
Anael MOBILIA
d375fee38c Year++ 2024-01-15 00:29:33 +01:00
Anael MOBILIA
ad566db4d5 v2.5 2024-01-15 00:24:02 +01:00
Anael MOBILIA
1082e20b7b Prise en charge du format WEBP 2024-01-15 00:23:36 +01:00
Anael MOBILIA
6cd8e71916 Factorisation de variables dans la configuration du projet 2024-01-14 23:28:06 +01:00
Anael MOBILIA
e190ed45a0 Gestion des abus : bloquer l'envoi d'images sur le site au bout d'un certain nombre d'images bloquées 2024-01-10 16:15:13 +01:00
Anael MOBILIA
201730adbe Désactiver tous les boutons de la page d'accueil si on a un problème d'espace disque ou avec l'adresse IP 2024-01-10 16:13:09 +01:00
Anael MOBILIA
d57980783d Ajout de abuse_network dans le jeu de données de tests 2024-01-10 15:52:06 +01:00
Anael MOBILIA
a85af1bf18 Harmonisation de variables 2024-01-10 15:46:00 +01:00
Anael MOBILIA
b1be8cbef7 Renommage de tables 2024-01-08 16:20:49 +01:00
Anael MOBILIA
4821839b5c Ajout de détails dans les CGU sur les images non autorisées 2024-01-02 21:54:43 +01:00
Anael MOBILIA
d1a1206f8c Calculer le réseau d'origine d'une image dès le moment où elle est créée pour éviter un abus entre l'envoi et le prochain cron abuse.php (permet de considérer comme suspecte une image dès son envoi et d'en limiter le nombre d'affichage en instantanné) 2023-12-31 04:18:17 +01:00
Anael MOBILIA
1ceacda262 Forcer l'utilisation du domaine en www 2023-12-30 02:36:33 +01:00
Anael MOBILIA
d9b8ff2ca0 Fix SQL (',' restait en fin de chaîne) 2023-12-23 22:23:19 +01:00
Anael MOBILIA
04864fa8eb Update icône 2023-12-23 02:30:47 +01:00
Anael MOBILIA
b0075a9f43 N'inclure un placholder vide que si aucune donnée n'a été retournée 2023-12-23 02:30:28 +01:00
Anael MOBILIA
91d9cffc29 Suite de 45cbebe9 2023-12-23 02:23:13 +01:00
Anael MOBILIA
d112eaa7c7 Le seuil réduit d'affichages avant blocage des images suspectes n'était pris en compte que dans le cron. Désormais chaque image possède sa réputation également et peut donc être bloquée dès qu'elle est trop affichée (en prenant en compte le seuil réduit) 2023-12-23 02:19:22 +01:00
Anael MOBILIA
45cbebe978 ForkMeOnGithub -> passage en bootstrap icon 2023-12-23 01:53:48 +01:00
Anael MOBILIA
c5180cec26 .htaccess, simplification des redirections image-heberg.fr.cr + HTTPS 2023-12-23 01:44:37 +01:00
Anael MOBILIA
a234af3645 Bootstrap icons - gérer l'emplacement des fichiers de fonts 2023-12-23 01:44:09 +01:00
Anael MOBILIA
0051501576 Bootstrap icons 1.10.5 -> 1.11.2 2023-12-23 01:28:04 +01:00
Anael MOBILIA
8c67f1ec0d Admin - liste des réseaux suspects 2023-12-23 01:18:36 +01:00
Anael MOBILIA
55636553a5 Ne pas remonter les images signalées dans les images trop affichées (elles sont déjà bloquées) 2023-12-14 23:34:03 +01:00
Anael MOBILIA
dcc4ab0d24 v2.4 2023-12-03 01:05:54 +01:00
Anael MOBILIA
a5e658218d Fix GIF - utilisation uniquement de la première frame 2023-12-03 00:44:25 +01:00
Anael MOBILIA
33a6e7e2fc Ajout debug PHPUNIT 2023-11-21 01:18:03 +01:00
Anael MOBILIA
d38e64a717 Fix mauvais calcul date de dernière utilisation suite à 2e3d9b10 2023-11-21 01:07:10 +01:00
Anael MOBILIA
f140db4c28 #15 - Ajout nsfwjs 2023-11-21 01:04:33 +01:00
Anael MOBILIA
e924a68057 Ajout de boutons sur les actions des images 2023-11-18 23:45:33 +01:00
Anael MOBILIA
5ff3d7b351 Meilleurs gestion de la grille + ajout lien avec <img> 2023-11-18 23:29:39 +01:00
Anael MOBILIA
58a85b4bb3 Ajout liste des derniers fichiers envoyés 2023-11-16 22:42:37 +01:00
Anael MOBILIA
efc0417c8a id est déjà indéxé via la PRIMARY KEY 2023-11-16 20:43:18 +01:00
Anael MOBILIA
a05da69b33 Prendre en compte les affichages des miniatures dans le nombre total d'affichage d'une image 2023-11-15 01:48:15 +01:00
Anael MOBILIA
1a85248e61 Images suspectes : utiliser également le MD5 2023-11-15 01:45:24 +01:00
Anael MOBILIA
4ec93dcdef Certains fichiers ont des noms avec un encodage incohérent : remplacer les caractères mal encodés (au lieu de retourner vide) 2023-11-15 01:21:05 +01:00
Anael MOBILIA
cf086d358c Ajout d'une division des seuils d'affichage pour les images suspectes 2023-11-12 03:47:49 +01:00
Anael MOBILIA
822744611f Forcer le HTTPS 2023-11-12 03:05:27 +01:00
Anael MOBILIA
f4f6772e1d Ne pas lever un mail d'alerte si l'admin vérifie une image déjà trop affichée 2023-11-12 02:24:34 +01:00
Anael MOBILIA
6c3dd07040 Stats d'affichage : ne mettre à jour que les champs nombre d'affichage et date pour améliorer les performances (évite également d'écraser le statut bloqué d'une image en cas de flood) 2023-11-10 01:12:07 +01:00
Anael MOBILIA
0d4bbb5e40 Suppression phpDoc inutile 2023-11-10 01:07:19 +01:00
Anael MOBILIA
264e278c4c trimer la valeur 2023-11-10 00:42:32 +01:00
Anael MOBILIA
ccf1dbf54e Filtrer plus large en IPv6 (Orange fournit un /56 et non pas un /64) 2023-11-04 01:39:52 +01:00
Anael MOBILIA
9f37c75096 Trier par date d'arrivée 2023-11-04 01:01:05 +01:00
Anael MOBILIA
4dbbf40c6e Pouvoir bloquer une image précédemment approuvée 2023-11-04 00:35:36 +01:00
Anael MOBILIA
3a94acccad Renommage méthode 2023-11-03 23:58:51 +01:00
Anael MOBILIA
5805ab41d8 Schèma de BDD : optimisation du type de champ 2023-11-03 22:56:59 +01:00
Anael MOBILIA
992505172f Gestion du cas aucune miniature / affichage (int + int + null => null) 2023-11-02 22:48:40 +01:00
Anael MOBILIA
dac7c3c1ba Recharger l'image pour ne pas écraser les modifs faites par le cron 2023-11-02 22:48:11 +01:00
Anael MOBILIA
cb1f4bbdf2 Test des dernières fonctions anti abus 2023-11-01 23:41:40 +01:00
Anael MOBILIA
19b5bc9eb7 Codestyle 2023-11-01 23:18:27 +01:00
Anael MOBILIA
415ec17b72 Comptabiliser également les affichages des miniatures pour le nombre d'affichage par jour 2023-11-01 23:07:24 +01:00
Anael MOBILIA
8a218658a6 Tracer l'image ayant généré l'appel au cron 2023-11-01 22:54:32 +01:00
Anael MOBILIA
511a3b0531 Vérifier si l'image n'a pas été trop affichée lorsqu'on l'affiche 2023-10-31 23:01:38 +01:00
Anael MOBILIA
9ec1fa358e Amélioration index 2023-10-31 22:45:21 +01:00
Anael MOBILIA
06ba72b1e6 Détection des abus : ajout d'une recherche sur le réseau IP utilisé 2023-10-29 22:32:58 +01:00
Anael MOBILIA
242472cc81 Ajout d'un champ de recherche sur le nom dans le système dans l'admin / gestion des abus 2023-10-29 20:57:07 +01:00
Anael MOBILIA
a0bf03c9e2 Détection des abus : ajout des images suspectes dans les notifications 2023-10-26 22:19:26 +02:00
Anael MOBILIA
23a025137e Détection des abus : la première journée échappait à la détection 2023-10-20 23:04:20 +02:00
Anael MOBILIA
48c1120241 GUI : information si l'image est bloquée ou validée 2023-10-16 21:54:38 +02:00
Anael MOBILIA
a34e0f53eb Barrer les images déjà bloquées 2023-10-15 00:57:37 +02:00
Anael MOBILIA
4fd35d642a Ajout d'un champ de recherche dans l'admin / gestion des abus 2023-10-15 00:50:05 +02:00
Anael MOBILIA
03c1b66c70 Factorisation de code 2023-10-15 00:34:12 +02:00
Anael MOBILIA
648ef10ab6 Abuse - ajout des images potentiellement suspectes 2023-10-13 01:45:00 +02:00
Anael MOBILIA
1aa2a1578f Champs md5 => 32 chrs + index && formatage 2023-10-13 01:34:40 +02:00
Anael MOBILIA
975880f637 Une adresse IPv6 fera au plus 45 chr 2023-10-13 01:33:42 +02:00
Anael MOBILIA
4cbc55f0ce Abuse - Ajout de l'ID du propriétaire de l'image 2023-10-13 00:45:05 +02:00
Anael MOBILIA
ec6921bbd7 Factorisation des tableaux 2023-10-13 00:23:39 +02:00
Anael MOBILIA
b26a044b77 PSR-12 2023-10-13 00:13:29 +02:00
Anael MOBILIA
66580c5a71 Nettoyage du code avec phpinspectionsea
https://github.com/kalessil/phpinspectionsea
2023-10-12 23:22:33 +02:00
Anael MOBILIA
e31f3e5cd6 Ajout des images bloquées + tri par "gravité" (warning -> signalé -> bloqué) 2023-10-12 22:38:43 +02:00
Anael MOBILIA
174b60c371 MàJ des notes suite à e8a5ee85e2 2023-10-10 22:44:21 +02:00
Anael MOBILIA
7c52ba9a27 Aligner le calcul PHP sur celui du SQL qui est plus avantageux pour les utilisateurs (toute journée entamée est comptabilisée) 2023-09-05 22:58:59 +02:00
Anael MOBILIA
893ddf63f9 Calcul par jour entier pour mieux gérer les premières heures d'une image 2023-08-27 22:35:26 +02:00
Anael MOBILIA
e8a5ee85e2 fontawesome -> bootstrap icons 2023-08-26 02:22:40 +02:00
Anael MOBILIA
b33b45b197 Crons : amélioration du log 2023-08-26 01:31:22 +02:00
Anael MOBILIA
f0089f7ff9 Bootstrap++ 2023-08-26 01:21:05 +02:00
Anael MOBILIA
550d70564b Blocage automatique des images qui abusent clairement du service 2023-08-26 01:13:20 +02:00
Anael MOBILIA
3c9ac61691 Crons : appeler directement les méthodes au lieu de simuler l'utilisation des pages de l'admin 2023-08-26 00:53:27 +02:00
Anael MOBILIA
c789bde9ad Fix var name 2023-08-26 00:31:01 +02:00
Anael MOBILIA
74c36ee5bd Meilleure gestion de l'exécution des scripts via un cron 2023-08-26 00:11:16 +02:00
Anael MOBILIA
d76d670b1a Qualité de code ++ 2023-08-25 23:58:47 +02:00
Anael MOBILIA
c575ffc744 Harmonisation des séparateurs de chaînes en PHP 2023-08-25 23:45:43 +02:00
Anael MOBILIA
fc6840769f Fix test 2023-08-25 02:31:35 +02:00
Anael MOBILIA
c337df2df1 Mieux récupèrer le contenu du buffer de sortie 2023-08-25 02:29:14 +02:00
Anael MOBILIA
fbbf89fab7 Mettre le path pour les require du cron 2023-08-25 02:28:17 +02:00
Anael MOBILIA
b56675d95b Générer une erreur si on ne peur pas charger le fichier du cron 2023-08-25 01:31:30 +02:00
Anael MOBILIA
48532249ec Abuse : meilleure vérification du nom du fichier + variable d'état pour savoir si une image a été bloquée automatiquement 2023-08-25 01:19:17 +02:00
Anael MOBILIA
ff17798ce2 On utilise pas le retour de sauver() 2023-08-25 01:17:36 +02:00
Anael MOBILIA
e74e4b3695 Ajout de lien https depuis l'admin (on a le cookie de session de l'admin si on forceDisplay=1 2023-08-24 03:43:47 +02:00
Anael MOBILIA
36e2d34cee Bloquer l'accès au répertoire cron 2023-08-24 03:43:47 +02:00
Anael MOBILIA
28cf0b4937 Ajout d'un cron pour superviser les comportements anormaux d'affichage 2023-08-24 03:43:47 +02:00
Anael MOBILIA
909533a984 Ajout de la notion d'image approuvée (ne peut pas être bloquée automatiquement et ne remonte pas dans les images à comportement étrange) 2023-08-24 02:49:08 +02:00
Anael MOBILIA
5b880f1f16 Admin - vue Abuse (images signalées et images trop affichées) 2023-08-24 01:50:21 +02:00
Anael MOBILIA
5452768a82 Meilleure gestion pour les images jamais affichées 2023-08-24 01:36:05 +02:00
Anael MOBILIA
8050e80553 Permettre à l'administrateur d'afficher des images bloquée (?forceDisplay=1 2023-08-24 01:09:54 +02:00
Anael MOBILIA
9d29488894 Permettre à l'administrateur d'afficher des images bloquée (?forceDisplay=1) 2023-08-24 01:06:58 +02:00
Anael MOBILIA
041427dc68 Nettoyer les paramètres passés dans le nom de l'image (sinon image non trouvée) 2023-08-24 01:06:17 +02:00
Anael MOBILIA
9fa757fc7e Utiliser le nom du site dans les mails d'erreur 2023-08-24 00:31:59 +02:00
Anael MOBILIA
7707108a22 Découpage en helpers abstract 2023-08-24 00:30:13 +02:00
Anael MOBILIA
3af8bbd8db Mise en avant signalement abus 2023-08-24 00:04:36 +02:00
Anael MOBILIA
d99fce555e Fix var name 2023-08-23 23:52:01 +02:00
Anael MOBILIA
1116aa3b11 Fix test 2023-08-20 23:01:27 +02:00
Anael MOBILIA
8dc98ebeae Fix test 2023-08-20 21:49:26 +02:00
Anael MOBILIA
a0bec132b3 Fix test 2023-08-20 21:46:48 +02:00
Anael MOBILIA
7ac18b725b Fix test 2023-08-20 21:43:42 +02:00
Anael MOBILIA
51371c59fa Fix test - Image n'était plus créée car déjà existante dans la BDD 2023-08-20 21:26:53 +02:00
Anael MOBILIA
ea19ceabf2 Parallélisation des tests 2023-08-20 02:28:29 +02:00
Anael MOBILIA
513dbe49a2 Fix test 2023-08-20 02:25:43 +02:00
Anael MOBILIA
f8f94bad00 Fix test 2023-08-20 02:22:58 +02:00
Anael MOBILIA
9b12884cdb Fix comparaison 2023-08-20 02:19:20 +02:00
Anael MOBILIA
91b38e4f9f Fix comparaison 2023-08-20 02:16:32 +02:00
Anael MOBILIA
5d730d00ac Afficher le MD5 du fichier envoyé (debug) 2023-08-20 02:15:50 +02:00
Anael MOBILIA
570326c946 Fix pour phpunit v10 (PHP > 8.0) 2023-08-20 02:11:57 +02:00
Anael MOBILIA
cddb893f6c Fix SQL 2023-08-20 02:02:38 +02:00
Anael MOBILIA
ce75af0197 Passage à la nouvelle version de checkout (node12 -> node16) 2023-08-20 02:00:50 +02:00
Anael MOBILIA
f07d42db2c Bloquer l'image lors de l'envoi si son MD5 est déjà bloqué par ailleurs 2023-08-20 01:53:51 +02:00
Anael MOBILIA
4333ce57c1 year++ 2023-08-19 04:04:51 +02:00
Anael MOBILIA
1615c078e2 Masquer les informations sensibles lors de l'affichage des erreurs à l'utilisateur 2023-08-19 01:05:42 +02:00
Anael MOBILIA
5fa5c9df58 Fix nom de la variable à utiliser 2023-08-18 22:17:05 +02:00
Anael MOBILIA
3d08eeef91 Permettre de charger les ressources par new_name, MD5 et ID.
-> permet d'avoir plusieurs miniatures pour une même image
-> permet de gérer l'effacement depuis des MD5
2022-12-18 03:53:59 +01:00
Anael MOBILIA
5cfcf45834 Fix comparaison 2022-12-18 03:30:49 +01:00
Anael MOBILIA
8ee02c6a9e Fix data 2022-12-18 03:25:02 +01:00
Anael MOBILIA
289c728b5b Alignement de la conf avec celle d'OVH 2022-12-18 03:13:02 +01:00
Anael MOBILIA
17d274a4db Afficher la configuration (strict_mode) myqsl de github 2022-12-18 03:10:34 +01:00
Anael MOBILIA
7280704638 Amélioration schéma BDD avec pilotage des valeurs par défaut dans le code 2022-12-18 02:58:02 +01:00
Anael MOBILIA
9b8e235563 php lint 2022-12-18 02:47:27 +01:00
Anael MOBILIA
2e3d9b1051 Utiliser '0000-00-00' au lieu de null 2022-12-18 02:26:22 +01:00
Anael MOBILIA
3f8b83b264 Fix test DisplayPics (vérification de la présence de l'image sur le disque) 2022-12-18 02:15:24 +01:00
Anael MOBILIA
d3b1ab9429 Fix test AbuseTest (si l'image n'a pas été affichée, comment la signaler ?) 2022-12-18 02:10:54 +01:00
Anael MOBILIA
c7ba7d09d9 Fix tests 2022-12-18 02:05:22 +01:00
Anael MOBILIA
3c128e6f5b PHP 7.4 est EOL 2022-12-18 01:57:47 +01:00
Anael MOBILIA
dd8006b2ac Fix comparaison 2022-12-18 01:56:31 +01:00
Anael MOBILIA
24248776f0 Préserver z_cache également (utilisé pour lister les @ IP de TOR) 2022-12-18 01:50:29 +01:00
Anael MOBILIA
0c02740618 Fix liste de toutes les miniatures 2022-12-18 01:50:08 +01:00
Anael MOBILIA
d318877a0b Fix Exception non définie 2022-12-18 01:49:38 +01:00
Anael MOBILIA
b1f1fe494b Formatage 2022-12-18 01:49:17 +01:00
Anael MOBILIA
1f0228c03a Afficher une 404 si le fichier existe en BDD mais pas sur le disque 2022-12-18 01:16:42 +01:00
Anael MOBILIA
76811ea9ce Cohérence des noms des champs en BDD 2022-12-18 01:10:32 +01:00
Anael MOBILIA
e093329b2e Passer l'email en minuscules en BDD 2022-12-18 00:44:05 +01:00
Anael MOBILIA
0d95fbcbc7 Suppression des comptes jamais utilisés (admin + cron) 2022-12-18 00:18:18 +01:00
Anael MOBILIA
daf564afbc Gestion des pluriels automatiquement 2022-12-18 00:04:13 +01:00
Anael MOBILIA
5331d07263 Gestion des pluriels automatiquement 2022-12-17 23:54:58 +01:00
Anael MOBILIA
ad1d20a16f Ne pas permettre de créer plusieurs comptes avec le même email 2022-12-17 23:21:49 +01:00
Anael MOBILIA
79908f73d8 Affichage de la version de PHP 2022-12-17 22:47:03 +01:00
Anael MOBILIA
d6f85509be Ajout vérification du PATH 2022-12-17 22:39:38 +01:00
Anael MOBILIA
71973b4c6b Throwable inclut Exception et Error (second type possible pour l'appel à la fonction) 2022-10-18 19:01:08 +02:00
Anael MOBILIA
53d289cb85 Vérifier l'existence d'un fichier avant de le supprimer 2022-10-18 18:58:24 +02:00
Anael MOBILIA
70b9016b60 PHP7.4 est requis pour typer les propriétés d'une classe
https://www.php.net/manual/en/language.oop5.properties.php#language.oop5.properties.typed-properties
2022-10-01 02:48:28 +02:00
Anael MOBILIA
aef8a4b4f3 Fix jeu de données 2022-10-01 02:42:49 +02:00
Anael MOBILIA
b0700f5587 Fix phpcs 2022-10-01 02:41:50 +02:00
Anael MOBILIA
2bd70e4f33 Définir une valeur pour le token 2022-10-01 02:39:06 +02:00
Anael MOBILIA
224c1abc7c Définir une valeur pour le token 2022-10-01 02:37:02 +02:00
Anael MOBILIA
f3be68cef2 year++ 2022-10-01 02:33:05 +02:00
Anael MOBILIA
6bd7f6b69b Ajout PHP8.1 2022-10-01 02:31:41 +02:00
Anael MOBILIA
5510aaff13 Fix code style 2022-10-01 02:31:04 +02:00
Anael MOBILIA
b86f02e2bb Valeur par défaut du token à "" 2022-10-01 02:20:04 +02:00
Anael MOBILIA
bc9a5f75be Typage++ 2022-10-01 02:07:01 +02:00
Anael MOBILIA
4080cc7fc8 Fix HTML 2022-10-01 01:50:35 +02:00
Anael MOBILIA
20e19a70ec Fix condition d'entrée initiale 2022-10-01 01:50:24 +02:00
Anael MOBILIA
5a327ca6cf Utiliser l'URL en HTTPS par défaut pour le contenu du site 2022-01-14 22:43:50 +01:00
Anael MOBILIA
f7749217e9 Lazy loading des images 2022-01-02 01:51:17 +01:00
Anael MOBILIA
dcf3496e7f Ouvrir les images dans un nouvel onglet 2021-12-21 01:40:33 +01:00
Anael MOBILIA
07bb91c402 Au moins une seconde doit s'écouler pour compléter un formulaire ! 2021-12-21 00:59:31 +01:00
Anael MOBILIA
8b0e44a26c Fix pluriels 2021-12-21 00:59:10 +01:00
Anael MOBILIA
83c8fb864f v2.2 2021-12-21 00:51:39 +01:00
Anael MOBILIA
aa6f0fccd0 Espace membre : affichage d'une miniature des images possédées en grille.
Génération automatique de la miniature si non existante
2021-12-21 00:46:58 +01:00
Anael MOBILIA
cdf84b65e6 Création d'une méthode getMiniatures() 2021-12-20 23:35:51 +01:00
Anael MOBILIA
2de440411d Ajout de la notion "is_preview" sur les miniatures 2021-12-20 23:35:11 +01:00
Anael MOBILIA
6bbc4023ff bootstrap 5.0.0-beta2 -> 5.1.3 2021-12-20 21:11:38 +01:00
Anael MOBILIA
eebe724c27 Fix typo 2021-12-20 21:02:05 +01:00
Anael MOBILIA
be6a8a8c53 Forcer le chemin du cron 2021-10-18 01:49:12 +02:00
Anael MOBILIA
ebd02dc2e0 Forcer le chemin du cron 2021-10-18 00:37:27 +02:00
Anael MOBILIA
7ccfb9e5ad Fournir un retour à l'utilisateur 2021-10-18 00:30:13 +02:00
Anael MOBILIA
57a136a601 Ne pas tester des versions de PHP non encore en production 2021-10-16 01:40:54 +02:00
Anael MOBILIA
09c19cf660 Fix GitHub Actions pour MySQL 8.0 2021-10-16 01:39:28 +02:00
Anael MOBILIA
7ffabf79f8 Fix phpcs 2021-10-16 01:23:24 +02:00
Anael MOBILIA
dcc8309ef5 Qualité du code 2021-10-16 01:07:09 +02:00
Anael MOBILIA
9de9c5b03d Fix tests automatiques (inclusion de fichiers pour les IP filtrées pour Tor) 2021-10-16 00:03:43 +02:00
Anael MOBILIA
36593471d4 Fix #17 : Ajout d'un cron pour effacer les images obsolètes de manière automatique 2021-10-15 23:57:09 +02:00
Anael MOBILIA
b99f9db280 Ajout de la notion de IS_CRON 2021-10-15 23:39:21 +02:00
Anael MOBILIA
9038726dc4 Fix #36 2021-10-15 23:29:04 +02:00
Anael MOBILIA
d45f71bce5 Ajout d'une option pour bloquer l'envoi d'images via Tor 2021-10-15 23:16:14 +02:00
Anael Mobilia
e80111c1be
Ajout d'un entête en cas d'image bloquée 2021-05-17 23:48:41 +02:00
Anael Mobilia
2beb107670 Gestion de memory_limit = - 1 (cas de Github Actions) 2021-03-03 23:26:24 +01:00
Anael Mobilia
7570356c4d Il faut définir le fuzz avant de charger l'image 2021-03-03 23:22:38 +01:00
Anael Mobilia
06091a2753 Afficher plus de détail sur la configuration PHP 2021-03-03 23:22:16 +01:00
Anael Mobilia
f1845a4afe Ajout d'un fuzz dans les comparaisons des images pour annuler les petites différences liées à la stack ImageMagick 2021-03-03 22:56:30 +01:00
Anael Mobilia
47e6202508 Regénération des images 2021-03-03 22:54:19 +01:00
Anael Mobilia
8a71b2ca02 Passage par un helper pour avoir uen image la plus identique possible 2021-02-28 05:22:14 +01:00
Anael Mobilia
62d454dec5 Fix paramètres (https://www.geeksforgeeks.org/php-imagick-thumbnailimage-function/) 2021-02-28 05:00:57 +01:00
Anael Mobilia
b2342b2192 Recharger les fichiers pour tenter de gommer les petites différences 2021-02-28 04:41:02 +01:00
Anael Mobilia
6218082614 Recharger les fichiers pour tenter de gommer les petites différences 2021-02-28 04:34:33 +01:00
Anael Mobilia
90d42535a7 memory_limit=0 est possible 2021-02-28 04:01:34 +01:00
Anael Mobilia
a4fcef9e29 Ajotu détail sur les erreurs + passage à méthodes natives assertBoolean 2021-02-28 03:50:11 +01:00
Anael Mobilia
eab4721259 Ajout debug 2021-02-28 03:21:03 +01:00
Anael Mobilia
76b941a7a4 Détection des cas incohérents pour le redimensionnement d'une image 2021-02-28 03:15:41 +01:00
Anael Mobilia
81856fd767 Mise à jour image test 2021-02-28 03:08:07 +01:00
Anael Mobilia
986a88a0cf Mise à jour image test 2021-02-28 02:59:43 +01:00
Anael Mobilia
a584d07d9b Mise à jour image test 2021-02-28 02:47:12 +01:00
Anael Mobilia
fbb73944d7 Pas de compression destructive des images 2021-02-28 02:31:53 +01:00
Anael Mobilia
41e5090397 Fix image de référence 2021-02-28 02:12:44 +01:00
Anael Mobilia
6938c6ead3 Le PDO MySQL n'est pas chargé sur l'image 20.04 / PHP 7.3 -> La forcer 2021-02-28 02:03:50 +01:00
Anael Mobilia
df1f2bd406 Passage ubuntu 20.04 pour avoir une version plus récente de Imagick 2021-02-28 01:45:17 +01:00
Anael Mobilia
31ccd56592 Ajout de la version d'Imagick 2021-02-28 01:39:10 +01:00
Anael Mobilia
17a85077f6 Ajout Imagick & rendu plus visuel 2021-02-28 01:37:54 +01:00
Anael Mobilia
51481cdf68 Suppression ancien debug 2021-02-28 01:08:05 +01:00
Anael Mobilia
81ab966624 Ajout dépendance imagick 2021-02-28 01:02:28 +01:00
Anael Mobilia
a2ab5fb039 PHPCS 2021-02-28 01:00:48 +01:00
Anael Mobilia
9cd486f86b Second lot Imagick pour les tests 2021-02-28 00:39:19 +01:00
Anael Mobilia
49e00cdc15 phpdoc 2021-02-28 00:37:40 +01:00
Anael Mobilia
49eff28e3f PHPCS 2021-02-28 00:37:30 +01:00
Anael Mobilia
3bc7e284c0 Fix #16 - Message de confirmation de suppression sur la page d'accueil 2021-02-28 00:37:04 +01:00
Anael Mobilia
a03dbd394d Premier lot d'images Imagick pour les tests 2021-02-28 00:36:11 +01:00
Anael Mobilia
4f319c045e On ne charge que les classes que l'on a 2021-02-27 23:56:30 +01:00
Anael Mobilia
22704b977b Rotation horaire est plus naturel 2021-02-27 23:31:33 +01:00
Anael Mobilia
bc669ed3dc Ajout imagick 2021-02-27 23:30:17 +01:00
Anael Mobilia
1f50e70f10 Passage à Imagick 2021-02-27 23:29:02 +01:00
Anael Mobilia
bdbaa428a5 PHPDoc 2021-02-27 23:28:30 +01:00
Anael Mobilia
8a7e446675 Réduction de la précision 2021-02-27 22:23:23 +01:00
Anael Mobilia
40f2f9d8e4 Ne pas prendre les fichiers pour améliorer les performances 2021-02-27 22:20:54 +01:00
Anael Mobilia
f44efed6cf Factorisation du code 2021-02-27 22:01:09 +01:00
Anael Mobilia
e28dc48e60 fontawesome 5.12 -> 5.15 2021-02-27 21:55:43 +01:00
Anael Mobilia
67198b5d34 Exclusion de l'erreur des require du fichier de conf générique 2021-02-27 21:42:15 +01:00
Anael Mobilia
23175e3131 Debug à false par défaut 2021-02-27 21:41:59 +01:00
Anael Mobilia
feea9a4233 PHPCS 2021-02-27 21:41:46 +01:00
Anael Mobilia
03e2c1d061 Exécution push, PR, manuelle pour les actions github 2021-02-27 21:14:45 +01:00
Anael Mobilia
9aa18e9b9e Pas d'accent dans le titre du runner 2021-02-27 21:13:13 +01:00
Anael Mobilia
d1379a3a97 Bootstrap 4 -> 5 2021-02-27 21:12:31 +01:00
Anael Mobilia
454fca732a Exécution sur toutes les branches 2021-02-27 21:12:04 +01:00
Anael Mobilia
3c3e26b846 PHP 7.1 n'est plus supporté 2021-02-27 21:03:41 +01:00
121 changed files with 6194 additions and 8041 deletions

View file

@ -1,16 +1,10 @@
# This is a basic workflow to help you get started with Actions
name: PHP_CodeSniffer
name: PHP_CodeSniffer - Qualité du code
# Controls when the action will run.
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Triggers the workflow on push or pull request events
# Allows you to run this workflow manually from the Actions tab
on: [push, pull_request, workflow_dispatch]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
@ -22,13 +16,15 @@ jobs:
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.0'
php-version: '8.3'
tools: cs2pr, phpcs
- name: Run phpcs
run: phpcs -q --standard=$GITHUB_WORKSPACE/.phpcs_ruleset.xml --report=checkstyle $GITHUB_WORKSPACE | cs2pr
run: |
phpcs --version
phpcs -q --standard=$GITHUB_WORKSPACE/.phpcs_ruleset.xml --report=checkstyle $GITHUB_WORKSPACE | cs2pr

View file

@ -1,38 +1,57 @@
name: Tests de l'application
on: [push, pull_request]
name: PHPUnit - Tests de l'application
on: [ push, pull_request, workflow_dispatch ]
jobs:
run:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: ['7.3', '7.4', '8.0']
matrix:
php-versions: [ '8.3', '8.4' ]
name: PHP ${{ matrix.php-versions }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Checkout
uses: actions/checkout@v4
- name: Configuration PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: phpunit
- name: Configuration PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: imagick, pdo_mysql
tools: phpunit
ini-values: error_reporting=E_ALL, display_errors=On
- name: Lancement de MySQL
run: sudo /etc/init.d/mysql start
- name: Lancement de MySQL
run: sudo systemctl start mysql.service
- name: Informations sur l'environnement
run: |
php --version
mysql -V
phpunit --version
- name: Mise en place de l'environnement
run: |
mysql -u root -proot -e "CREATE DATABASE imageheberg;"
mysql -u root -proot imageheberg < database.sql
sh -c "mv __tests/config/config.php config/"
mysql -u root -proot imageheberg < __tests/data.sql
- name: Informations sur l'environnement
run: |
php --version
mysql -V
phpunit --version
php -r 'echo Imagick::getVersion()["versionString"];'
php -r 'echo phpinfo();'
mysql -u root -proot -e "SELECT @@GLOBAL.sql_mode global, @@SESSION.sql_mode session"
- name : Test de l'application
run: phpunit --colors --debug __tests/
- name: Mise en place de l'environnement
run: |
mysql -u root -proot -e "SET GLOBAL sql_mode = 'NO_ENGINE_SUBSTITUTION';"
mysql -u root -proot -e "alter user 'root'@'localhost' identified with mysql_native_password by 'root';"
mysql -u root -proot -e "CREATE DATABASE imageheberg;"
mysql -u root -proot imageheberg < database.sql
sh -c "mv __tests/config/config.php config/"
sh -c "mv __tests/ipv*.txt files/z_cache/"
# Pour ImageUploadAndDeleteTest
sh -c "find files/ -name _dummy -type f -delete"
# Pour AbuseTest
sh -c "mv __tests/images/image_33.png files/d/d0a77eeeff5ef764505fe5b119b913bf"
# Injection des données
mysql -u root -proot imageheberg < __tests/data.sql
# Adaptation à l'environnement actuel
php '__tests/_bootstrap.php'
- name: Test de l'application - phpunit v11
if: ${{ matrix.php-versions != '8.0' }}
run: phpunit --colors --display-warnings --display-errors __tests/

View file

@ -1,5 +1,5 @@
#/*
#* Copyright 2008-2020 Anael MOBILIA
#* Copyright 2008-2024 Anael MOBILIA
#*
#* This file is part of image-heberg.fr.
#*
@ -23,29 +23,27 @@ Options -Indexes
# Ré-écriture d'URL
RewriteEngine On
# Redirection systématique - si requise - vers www.image-heberg.fr
# HTTPS
RewriteCond %{HTTPS} on
# Redirection systématique - si requise - vers **www**.image-heberg.fr
RewriteCond %{HTTP_HOST} image-heberg\.fr\.cr [NC,OR]
RewriteCond %{HTTP_HOST} ^image-heberg\.fr [NC]
RewriteRule (.*) https://www.image-heberg.fr/$1 [R=301,L,NE]
# HTTP
RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} image-heberg\.fr\.cr [NC,OR]
RewriteCond %{HTTP_HOST} ^image-heberg\.fr [NC]
RewriteRule (.*) https://www.image-heberg.fr/$1 [R=301,L,NE]
# Protection des répertoires classes, config, __tests
RewriteCond %{REQUEST_URI} ^/classes/ [OR]
RewriteCond %{REQUEST_URI} ^/config/ [OR]
RewriteCond %{REQUEST_URI} ^/__tests/
RewriteRule .* - [F]
# Images : redirection pour affichage
RewriteCond %{REQUEST_URI} ^/files/
RewriteRule .* displayPics.php [END]
# HTTPS obligatoire pour le site (hors affichage image)
RewriteCond %{HTTPS} off
RewriteCond %{REQUEST_URI} !^/files/
RewriteRule (.*) https://www.image-heberg.fr/$1 [R=301,L,NE]
RewriteRule (.*) https://www.image-heberg.fr/$1 [R=301,L,NE]
# Protection des répertoires classes, config, cron, __tests
RewriteCond %{REQUEST_URI} ^/classes/ [OR]
RewriteCond %{REQUEST_URI} ^/config/ [OR]
RewriteCond %{REQUEST_URI} ^/cron/ [OR]
RewriteCond %{REQUEST_URI} ^/__tests/
RewriteRule .* - [F]
# Bootstrap icons - fichiers de fonts
RewriteCond %{REQUEST_URI} ^/template/css/fonts/
RewriteRule .*/(fonts/.*) https://www.image-heberg.fr/template/$1 [L,NE]
# Images : redirection pour affichage
RewriteCond %{REQUEST_URI} ^/files/
RewriteRule .* displayPics.php [END]

View file

@ -2,7 +2,7 @@
<ruleset name="myRuleset">
<description>Regles pour le projet.</description>
<!-- Ne pas traiter les ficheirs minifiés -->
<!-- Ne pas traiter les fichiers minifiés -->
<exclude-pattern>*.min.*</exclude-pattern>
<!-- Inclure PSR-12 -->
<rule ref="PSR12">
@ -10,5 +10,11 @@
<exclude name="Generic.Files.LineEndings.InvalidEOLChar" />
<!-- Exclure la longueur des lignes -->
<exclude name="Generic.Files.LineLength.TooLong" />
</rule>
</ruleset>
<!-- Exclure les fichiers de conf qui ont un require sur le fichier générique à la fin -->
<rule ref="PSR1.Files.SideEffects.FoundWithSymbols">
<exclude-pattern>*/config/*.php</exclude-pattern>
<exclude-pattern>*/cron/*.php</exclude-pattern>
</rule>
</ruleset>

View file

@ -3,7 +3,8 @@
Service d'hébergement d'images en ligne
# Configuration requise
- PHP 7.3, 7.4 [*(Préférez une version de PHP maintenue !)*](https://www.php.net/supported-versions.php)
- PHP 8.3 [*(Préférez une version de PHP maintenue !)*](https://www.php.net/supported-versions.php)
- Imagick
- MySQL ou MariaDB
- Serveur web gérant les fichiers .htaccess
@ -12,10 +13,13 @@ Service d'hébergement d'images en ligne
- Copier les fichiers dans le répertoire du serveur web
- Renommer le fichier config_empty.php en config.php et compléter les différents champs
- Ajouter votre favicon dans template/images/monSite.ico
- Ajouter votre css dans dans template/css/monSite.css
- Ajouter votre css dans template/css/monSite.css
- Configurer l'URL du site dans le fichier .htaccess
- Valider l'installation de base en appelant le fichier install.php (example.com/install.php).
- Se connecter avec le compte admin / password. Ce compte est le compte de l'administrateur du site. (pensez à mettre à jour l'adresse mail associée !)
- Valider l'installation de base en appelant le fichier install.php (example.com/install.php)
- Mettre en place un cron sur cron/updateTorIp.php, cron/cleanImages.php, cron/cleanAccounts.php, cron/abuse.php
- Se connecter avec le compte admin / password. Ce compte est le compte de l'administrateur du site.
- Modifier le mot de passe du compte
- Mettre à jour l'adresse email associée
# Changer de thème
- Choisir un thème sur [bootswatch](https://bootswatch.com/)

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -21,44 +21,435 @@
namespace ImageHebergTests;
use ImageHeberg\HelperAbuse;
use ImageHeberg\ImageObject;
use ImageHeberg\MaBDD;
use ImageHeberg\MetaObject;
use ImageHeberg\MiniatureObject;
use ImageHeberg\Outils;
use ImageHeberg\RessourceInterface;
use ImageHeberg\HelperAdmin;
use ImageHeberg\RessourceObject;
use ImageHeberg\SessionObject;
use ImageHeberg\UtilisateurObject;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
use PHPUnit\Framework\TestCase;
class AbuseTest extends TestCase
{
/**
* Signalement d'une image
* @runInSeparateProcess
*/
public function testAbuse()
#[RunInSeparateProcess]
public function testAbuse(): void
{
require 'config/config.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_POST['Submit'] = 1;
$_SESSION['flag'] = true;
$_POST['userMail'] = "john.doe@example.com";
$_POST['urlImage'] = "http://www.example.com/files/15.png";
$_POST['userMail'] = 'john.doe@example.com';
$_POST['urlImage'] = 'https://www.example.com/files/image_15.png';
ob_start();
require 'abuse.php';
ob_end_clean();
$imageBloquee = new ImageObject("15.png");
$imageMemeMd5 = new ImageObject("16.png");
$this->assertEquals(true, $imageBloquee->isSignalee(), "Image signalée doit l'être");
$this->assertEquals(
true,
$imageBloquee = new ImageObject('image_15.png');
$imageMemeMd5 = new ImageObject('image_16.png');
$this->assertEmpty(
$msgErreur,
__FUNCTION__ . ' ne devrait pas lever de message d\'erreur - Erreur : ' . $msgErreur
);
$this->assertTrue(
$imageBloquee->isSignalee(),
'Image signalée doit l\'être'
);
$this->assertTrue(
$imageMemeMd5->isSignalee(),
"Image avec même MD5 qu'une image signalée doit l'être également"
'Image avec même MD5 qu\'une image signalée doit l\'être également'
);
}
/**
* Renvoi d'une image bloquée et demande de son affichage
*/
#[RunInSeparateProcess]
public function testAbuseRenvoiImage(): void
{
require 'config/config.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_POST['Submit'] = 1;
$_SESSION['flag'] = true;
$_FILES['fichier']['size'] = 146;
$_FILES['fichier']['name'] = 'imageDejaBloquee.gif';
$_FILES['fichier']['tmp_name'] = _PATH_TESTS_IMAGES_ . $_FILES['fichier']['name'];
ob_start();
require 'upload.php';
ob_end_clean();
$this->assertEmpty(
$msgErreur,
__FUNCTION__ . ' ne devrait pas lever de message d\'erreur - Erreur : ' . $msgErreur
);
$this->assertEmpty(
$msgWarning,
__FUNCTION__ . ' ne devrait pas lever de message de warning - Warning : ' . $msgWarning
);
$this->assertTrue(
$monImage->isBloquee(),
'Renvoi image déjà bloquée doit être isBloquée en BDD'
);
}
/**
* Signalement d'une image approuvée
*/
#[RunInSeparateProcess]
public function testAbuseImageApprouvee(): void
{
require 'config/config.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_POST['Submit'] = 1;
$_SESSION['flag'] = true;
$_POST['userMail'] = 'john.doe@example.com';
$_POST['urlImage'] = 'https://www.example.com/files/_image_404.png';
ob_start();
require 'abuse.php';
ob_end_clean();
$imageSignalee = new ImageObject('_image_404.png');
$this->assertEmpty(
$msgErreur,
__FUNCTION__ . ' ne devrait pas lever de message d\'erreur - Erreur : ' . $msgErreur
);
$this->assertFalse(
$imageSignalee->isSignalee(),
'Image approuvée qui est signalée ne doit pas être bloquée'
);
}
/**
* Approbation d'une image signalée
*/
#[RunInSeparateProcess]
public function testAbuseImageSignaleePuisApprouvee(): void
{
require 'config/config.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
// Flagguer l'image comme signalée
$image = new ImageObject('_image_banned.png');
$image->setSignalee(true);
$image->sauver();
// Se connecter en tant que l'admin
$monMembre = new UtilisateurObject();
$monMembre->connexion('admin', 'password');
// Approuver l'image dans l'admin
$_GET['approuver'] = '1';
$_GET['idImage'] = '2';
ob_start();
require 'admin/abuse.php';
ob_end_clean();
$image = new ImageObject('_image_banned.png');
$this->assertFalse(
$image->isSignalee(),
'Image signalée qui est approuvée ne doit plus être signalée'
);
}
/**
* Renvoi d'une image déjà bloquée
*/
#[RunInSeparateProcess]
public function testAbuseImageRenvoiImageBloqueeDepuisReseauMalveillant(): void
{
require 'config/config.php';
$imagesAvantEnvoi = HelperAdmin::getImagesPotentiellementIndesirables();
$_SERVER['REMOTE_ADDR'] = '10.10.10.11';
$_POST['Submit'] = 1;
$_SESSION['flag'] = true;
$_FILES['fichier']['size'] = 146;
$_FILES['fichier']['name'] = 'imageDejaBloquee.gif';
$_FILES['fichier']['tmp_name'] = _PATH_TESTS_IMAGES_ . $_FILES['fichier']['name'];
ob_start();
require 'upload.php';
ob_end_clean();
$imagesApresEnvoi = HelperAdmin::getImagesPotentiellementIndesirables();
$this->assertEmpty(
$msgErreur,
__FUNCTION__ . ' ne devrait pas lever de message d\'erreur - Erreur : ' . $msgErreur
);
$this->assertEmpty(
$msgWarning,
__FUNCTION__ . ' ne devrait pas lever de message de warning - Warning : ' . $msgWarning
);
$this->assertEquals(
$imagesAvantEnvoi->count(),
$imagesApresEnvoi->count(),
'Le renvoi d\'une image bloquée doit être bloqué'
);
}
/**
* Envoi d'une image depuis le même réseau qu'une image bloquée
*/
#[RunInSeparateProcess]
public function testAbuseImageEnvoiDepuisReseauMalveillant(): void
{
require 'config/config.php';
$imagesAvantEnvoi = HelperAdmin::getImagesPotentiellementIndesirables();
$_SERVER['REMOTE_ADDR'] = '10.10.10.11';
$_POST['Submit'] = 1;
$_SESSION['flag'] = true;
$_FILES['fichier']['size'] = 146;
$_FILES['fichier']['name'] = 'rotation_original.gif';
$_FILES['fichier']['tmp_name'] = _PATH_TESTS_IMAGES_ . $_FILES['fichier']['name'];
ob_start();
require 'upload.php';
ob_end_clean();
$imagesApresEnvoi = HelperAdmin::getImagesPotentiellementIndesirables();
$this->assertEmpty(
$msgErreur,
__FUNCTION__ . ' ne devrait pas lever de message d\'erreur - Erreur : ' . $msgErreur
);
$this->assertEmpty(
$msgWarning,
__FUNCTION__ . ' ne devrait pas lever de message de warning - Warning : ' . $msgWarning
);
$this->assertEquals(
($imagesAvantEnvoi->count() + 1),
$imagesApresEnvoi->count(),
'L\'image envoyée devrait être considérée comme potentiellement indésirable : ' . var_export($imagesApresEnvoi, true)
);
}
/**
* Image avec une miniature ENORMEMENT affichée
*/
#[RunInSeparateProcess]
public function testAbuseImageMiniatureTropAffichee(): void
{
require 'config/config.php';
$images = HelperAdmin::getImagesTropAffichees(_ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_);
$this->assertNotEmpty(
$images,
'Les affichages des miniatures doivent compter dans les affichages d\'une image pour la détection des abus : ' . var_export($images, true)
);
}
/**
* Division des seuils de détection pour une image envoyée du même réseau qu'une image déjà bloquée
*/
#[RunInSeparateProcess]
public function testAbuseDivisionSeuilDetectionSiReseauMalveillant(): void
{
require 'config/config.php';
// Liste des images suspectes
$listeImagesSuspectes = HelperAdmin::getImagesPotentiellementIndesirables();
$imagesTropAffichees = HelperAdmin::getImagesTropAffichees((_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_), true);
$this->assertContains(
'image_20.gif',
$listeImagesSuspectes,
'L\'image 19 est suspecte car envoyée d\'un même réseau que l\'image 18'
);
$this->assertContains(
'image_20.gif',
$imagesTropAffichees,
'L\'image 19 a été trop affichée -> WARNING (elle est suspecte)'
);
}
/**
* Réputation des adresses IP basées sur les images déjà bloquées pour leur réseau
*/
#[RunInSeparateProcess]
public function testAbuseReputationIp(): void
{
require 'config/config.php';
// Adresse IP ayant envoyé les fichiers bloqués
$this->assertEquals(
5,
HelperAbuse::checkIpReputation('192.168.0.1'),
'Le réseau 192.168.0.0/24 a 5 images bloquées'
);
// Adresse IP du même réseau que celle ayant envoyé les fichiers bloqués
$this->assertEquals(
5,
HelperAbuse::checkIpReputation('192.168.0.100'),
'Le réseau 192.168.0.0/24 a 5 images bloquées'
);
// Adresse IP random qui n'a pas d'images bloqués
$this->assertEquals(
0,
HelperAbuse::checkIpReputation('2a01:ab51:8880:e010:1da5:be67:6a52:a5bf'),
'Aucune image bloquée dans le réseau de cette adresse IP'
);
}
/**
* Projection du nombre d'affichage d'une image pour détecter une atteinte de limite ultérieure
*/
#[RunInSeparateProcess]
public function testAbuseProjectionAffichages(): void
{
require 'config/config.php';
$images = HelperAdmin::getImagesTropAffichees(_ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_, false, true);
$this->assertContains(
'image_27.png',
$images,
'L\'image 27 doit être détectée comme dépassant le nombre d\'affichage en projection : ' . var_export($images, true)
);
}
/**
* Le blocage d'une image bloque toutes celles partageant le même MD5
*/
#[RunInSeparateProcess]
public function testAbuseContaminationMD5Blocage(): void
{
require 'config/config.php';
$monImage = new ImageObject('28', RessourceObject::SEARCH_BY_ID);
$monImage->bloquer();
$image28 = new ImageObject('28', RessourceObject::SEARCH_BY_ID);
$image29 = new ImageObject('29', RessourceObject::SEARCH_BY_ID);
$this->assertTrue(
$image28->isBloquee(),
'L\'image 28 doit être bloquée'
);
$this->assertTrue(
$image29->isBloquee(),
'L\'image 29 doit être bloquée'
);
$this->assertFalse(
$image28->isApprouvee(),
'L\'image 28 ne doit pas être approuvée'
);
$this->assertFalse(
$image29->isApprouvee(),
'L\'image 29 ne doit pas être approuvée'
);
}
/**
* L'approbation d'une image approuve toutes celles partageant le même MD5
*/
#[RunInSeparateProcess]
public function testAbuseContaminationMD5Approbation(): void
{
require 'config/config.php';
$monImage = new ImageObject('30', RessourceObject::SEARCH_BY_ID);
$monImage->approuver();
$image30 = new ImageObject('30', RessourceObject::SEARCH_BY_ID);
$image31 = new ImageObject('31', RessourceObject::SEARCH_BY_ID);
$image32 = new ImageObject('32', RessourceObject::SEARCH_BY_ID);
$this->assertFalse(
$image30->isBloquee(),
'L\'image 30 ne doit pas être bloquée'
);
$this->assertFalse(
$image31->isBloquee(),
'L\'image 31 ne doit pas être bloquée'
);
$this->assertFalse(
$image32->isBloquee(),
'L\'image 32 ne doit pas être bloquée'
);
$this->assertFalse(
$image30->isSignalee(),
'L\'image 30 ne doit pas être signalée'
);
$this->assertFalse(
$image31->isSignalee(),
'L\'image 31 ne doit pas être signalée'
);
$this->assertFalse(
$image32->isSignalee(),
'L\'image 32 ne doit pas être signalée'
);
$this->assertTrue(
$image30->isApprouvee(),
'L\'image 30 doit être approuvée'
);
$this->assertTrue(
$image31->isApprouvee(),
'L\'image 31 doit être approuvée'
);
$this->assertTrue(
$image32->isApprouvee(),
'L\'image 32 doit être approuvée'
);
}
/**
* Une image demandée par un User-Agent suspect doit être signalée.
*/
#[RunInSeparateProcess]
public function testBlocageParUserAgent(): void
{
require 'config/config.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['HTTP_USER_AGENT'] = 'someUserAgentNumberOne';
$_SERVER['REQUEST_URI'] = 'files/image_33.png';
$monImage = new ImageObject('33', RessourceObject::SEARCH_BY_ID);
$this->assertFalse(
$monImage->isSignalee(),
'L\'image ne doit pas être signalée de base.'
);
ob_start();
require 'displayPics.php';
ob_end_clean();
$monImage = new ImageObject('33', RessourceObject::SEARCH_BY_ID);
$this->assertTrue(
$monImage->isSignalee(),
'Signalement de l\'image basé sur le User-Agent présenté'
);
}
/**
* Projection du nombre d'affichage d'une image pour détecter une atteinte de limite ultérieure
*/
#[RunInSeparateProcess]
public function testAbuseNombreAffichagesAbusifs(): void
{
require 'config/config.php';
$images = HelperAdmin::getImagesTropAffichees(_ABUSE_NB_AFFICHAGES_PAR_JOUR_ABUSIF_, false, true, true);
$this->assertContains(
'image_34.png',
$images,
'L\'image 34 doit être détectée comme ayant un nombre d\'affichages abusif : ' . var_export($images, true)
);
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -21,92 +21,119 @@
namespace ImageHebergTests;
use ImageHeberg\ImageObject;
use ImageHeberg\MaBDD;
use ImageHeberg\MetaObject;
use ImageHeberg\MiniatureObject;
use ImageHeberg\Outils;
use ImageHeberg\RessourceInterface;
use ImageHeberg\RessourceObject;
use ImageHeberg\SessionObject;
use ImageHeberg\UtilisateurObject;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
use PHPUnit\Framework\TestCase;
class DisplayPicsTest extends TestCase
{
/**
* Affichage d'une image inexistante
* @runInSeparateProcess
*/
public function testImageInexistante()
#[RunInSeparateProcess]
public function testImageInexistante(): void
{
require 'config/config.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['REQUEST_URI'] = 'files/fichierInexistant.jpg';
ob_start();
require 'displayPics.php';
ob_end_clean();
/* @var $monObjet RessourceObject */
$this->assertEquals(_IMAGE_404_, $monObjet->getNomNouveau(), "image_404 si inexistante");
$this->assertEquals(
_IMAGE_404_,
$monObjet->getNomNouveau(),
'image_404 si inexistante'
);
}
/**
* Affichage d'une image inexistante
* @runInSeparateProcess
*/
public function testMiniatureInexistante()
#[RunInSeparateProcess]
public function testMiniatureInexistante(): void
{
require 'config/config.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['REQUEST_URI'] = 'files/thumbs/fichierInexistant.jpg';
ob_start();
require 'displayPics.php';
ob_end_clean();
/* @var $monObjet RessourceObject */
$this->assertEquals(_IMAGE_404_, $monObjet->getNomNouveau(), "image_404 si inexistante");
$this->assertEquals(
_IMAGE_404_,
$monObjet->getNomNouveau(),
'image_404 si inexistante'
);
}
/**
* Affichage d'une image inexistante
* @runInSeparateProcess
*/
public function testRepertoireInexistant()
#[RunInSeparateProcess]
public function testRepertoireInexistant(): void
{
require 'config/config.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['REQUEST_URI'] = 'files/repertoireInexistant/fichierInexistant.jpg';
ob_start();
require 'displayPics.php';
ob_end_clean();
/* @var $monObjet RessourceObject */
$this->assertEquals(_IMAGE_404_, $monObjet->getNomNouveau(), "image_404 si mauvais sous répertoire");
$this->assertEquals(
_IMAGE_404_,
$monObjet->getNomNouveau(),
'image_404 si mauvais sous répertoire'
);
}
/**
* Affichage d'une image bloquée
* @runInSeparateProcess
*/
public function testImageBloquee()
#[RunInSeparateProcess]
public function testImageBloquee(): void
{
require 'config/config.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['REQUEST_URI'] = 'files/imageBloquee.jpg';
$_SERVER['REQUEST_URI'] = 'files/image_10.png';
ob_start();
require 'displayPics.php';
ob_end_clean();
/* @var $monObjet RessourceObject */
$this->assertEquals(_IMAGE_BAN_, $monObjet->getNomNouveau(), "image_ban si image bloquée");
$this->assertEquals(
_IMAGE_BAN_,
$monObjet->getNomNouveau(),
'image_ban si image bloquée'
);
}
/**
* Affichage d'une image signaléee
* @runInSeparateProcess
*/
public function testImageSignalee()
#[RunInSeparateProcess]
public function testImageSignalee(): void
{
require 'config/config.php';
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$_SERVER['REQUEST_URI'] = 'files/imageSignalee.png';
$_SERVER['REQUEST_URI'] = 'files/image_18.png';
ob_start();
require 'displayPics.php';
ob_end_clean();
/* @var $monObjet RessourceObject */
$this->assertEquals(_IMAGE_BAN_, $monObjet->getNomNouveau(), "image_ban si image signalée");
$this->assertEquals(
_IMAGE_BAN_,
$monObjet->getNomNouveau(),
'image_ban si image signalée'
);
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -22,209 +22,175 @@
namespace ImageHebergTests;
use ImageHeberg\ImageObject;
use ImageHeberg\MaBDD;
use ImageHeberg\MetaObject;
use ImageHeberg\MiniatureObject;
use ImageHeberg\Outils;
use ImageHeberg\RessourceInterface;
use ImageHeberg\RessourceObject;
use ImageHeberg\SessionObject;
use ImageHeberg\UtilisateurObject;
use ImagickException;
use ImagickPixelException;
use PHPUnit\Framework\Attributes\RunInSeparateProcess;
use PHPUnit\Framework\TestCase;
use Imagick;
/**
* L'entête des fichiers contient des informations sur la bibliothéque système les ayant produit
* <CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 100
*/
class ImageObjectTest extends TestCase
{
// Rotation pour les images
private const array ROTATION_ANGLES = [0, 90, 180, 270];
// Couleurs des 4 coins : HG / BG / BD / HD
private const array ROTATION_COULEURS = [
0 => [
'r' => 255,
'g' => 0,
'b' => 0,
],
1 => [
'r' => 0,
'g' => 255,
'b' => 0,
],
2 => [
'r' => 0,
'g' => 0,
'b' => 255,
],
3 => [
'r' => 255,
'g' => 255,
'b' => 255,
],
];
// Dimensions d'origine de l'image
private const array ROTATION_DIM_ORIGINE = [640, 150];
/**
* Rotation des images PNG
* Les couleurs des images sont conservées à +/- 1 point de valeur
* @param array $reference Couleur de référence
* @param array $valeur Couleur à comparer
* @return bool
*/
public function testRotationImagesPNG()
private function compareColor(array $reference, array $valeur): bool
{
$monRetour = true;
foreach ($reference as $key => $value) {
if (
$valeur[$key] !== $value
&& $valeur[$key] !== ($value - 1)
&& $valeur[$key] !== ($value + 1)
) {
$monRetour = false;
break;
}
}
// Debug
if (!$monRetour) {
echo PHP_EOL . 'Expected : ' . var_export($reference, true) . PHP_EOL . 'Actual : ' . var_export($valeur, true) . PHP_EOL;
}
return $monRetour;
}
/**
* Rotation des images
* @throws ImagickPixelException
* @throws ImagickException
*/
#[RunInSeparateProcess]
public function testRotationImages(): void
{
require 'config/config.php';
$monImage = new ImageObject();
$angle = 90;
$monImage->rotation(
$angle,
_PATH_TESTS_IMAGES_ . 'image_banned.png',
_PATH_TESTS_OUTPUT_ . 'image_banned.png-' . $angle
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_banned-' . $angle . '-a-partir-php-7.2.png',
_PATH_TESTS_OUTPUT_ . 'image_banned.png-' . $angle,
"Rotation PNG " . $angle
);
foreach (_ACCEPTED_EXTENSIONS_ as $uneExtension) {
foreach (self::ROTATION_ANGLES as $unAngle) {
$monImage->rotation(
$unAngle,
_PATH_TESTS_IMAGES_ . 'rotation_original.' . $uneExtension,
_PATH_TESTS_OUTPUT_ . 'rotation_original-' . $unAngle . '.' . $uneExtension
);
$angle = 180;
$monImage->rotation(
$angle,
_PATH_TESTS_IMAGES_ . 'image_banned.png',
_PATH_TESTS_OUTPUT_ . 'image_banned.png-' . $angle
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_banned-' . $angle . '-jusqua-php-7.1.png',
_PATH_TESTS_OUTPUT_ . 'image_banned.png-' . $angle,
"Rotation PNG " . $angle
);
// Calcul des dimensions théoriques
$indiceLargeur = (($unAngle % 180) === 0 ? 0 : 1);
$indiceHauteur = (($unAngle % 180) === 0 ? 1 : 0);
$angle = 270;
$monImage->rotation(
$angle,
_PATH_TESTS_IMAGES_ . 'image_banned.png',
_PATH_TESTS_OUTPUT_ . 'image_banned.png-' . $angle
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_banned-' . $angle . '-jusqua-php-7.1.png',
_PATH_TESTS_OUTPUT_ . 'image_banned.png-' . $angle,
"Rotation PNG " . $angle
);
}
// Vérifier les dimensions des images
$imageInfo = getimagesize(_PATH_TESTS_OUTPUT_ . 'rotation_original-' . $unAngle . '.' . $uneExtension);
$this->assertEquals(
self::ROTATION_DIM_ORIGINE[$indiceLargeur],
$imageInfo[0],
'Largeur d\'image - Rotation ' . $uneExtension . ' ' . $unAngle
);
$this->assertEquals(
self::ROTATION_DIM_ORIGINE[$indiceHauteur],
$imageInfo[1],
'Hauteur d\'image - Rotation ' . $uneExtension . ' ' . $unAngle
);
/**
* Rotation des images JPG
* @depends testRotationImagesPNG
*/
public function testRotationImagesJPG()
{
$monImage = new ImageObject();
$angle = 90;
$monImage->rotation(
$angle,
_PATH_TESTS_IMAGES_ . 'image_banned.jpg',
_PATH_TESTS_OUTPUT_ . 'image_banned.jpg-' . $angle
);
$monImage->rotation(
0,
_PATH_TESTS_IMAGES_ . 'image_banned-' . $angle . '-a-partir-php-7.2.jpg',
_PATH_TESTS_OUTPUT_ . 'ATTENDU_image_banned.jpg-' . $angle
);
$this->assertFileEquals(
_PATH_TESTS_OUTPUT_ . 'ATTENDU_image_banned.jpg-' . $angle,
_PATH_TESTS_OUTPUT_ . 'image_banned.jpg-' . $angle,
"Rotation JPG " . $angle
);
$angle = 180;
$monImage->rotation(
$angle,
_PATH_TESTS_IMAGES_ . 'image_banned.jpg',
_PATH_TESTS_OUTPUT_ . 'image_banned.jpg-' . $angle
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_banned-' . $angle . '-jusqua-php-7.1.jpg',
_PATH_TESTS_OUTPUT_ . 'image_banned.jpg-' . $angle,
"Rotation JPG " . $angle
);
$angle = 270;
$monImage->rotation(
$angle,
_PATH_TESTS_IMAGES_ . 'image_banned.jpg',
_PATH_TESTS_OUTPUT_ . 'image_banned.jpg-' . $angle
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_banned-' . $angle . '-jusqua-php-7.1.jpg',
_PATH_TESTS_OUTPUT_ . 'image_banned.jpg-' . $angle,
"Rotation JPG " . $angle
);
}
/**
* Rotation des images GIF
* Pas de changement en fonction des versions de PHP
* @depends testRotationImagesJPG
*/
public function testRotationImagesGIF()
{
$monImage = new ImageObject();
$angle = 90;
$monImage->rotation(
$angle,
_PATH_TESTS_IMAGES_ . 'image_banned.gif',
_PATH_TESTS_OUTPUT_ . 'image_banned.gif-' . $angle
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_banned-' . $angle . '.gif',
_PATH_TESTS_OUTPUT_ . 'image_banned.gif-' . $angle,
"Rotation GIF " . $angle
);
$angle = 180;
$monImage->rotation(
$angle,
_PATH_TESTS_IMAGES_ . 'image_banned.gif',
_PATH_TESTS_OUTPUT_ . 'image_banned.gif-' . $angle
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_banned-' . $angle . '.gif',
_PATH_TESTS_OUTPUT_ . 'image_banned.gif-' . $angle,
"Rotation GIF " . $angle
);
$angle = 270;
$monImage->rotation(
$angle,
_PATH_TESTS_IMAGES_ . 'image_banned.gif',
_PATH_TESTS_OUTPUT_ . 'image_banned.gif-' . $angle
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_banned-' . $angle . '.gif',
_PATH_TESTS_OUTPUT_ . 'image_banned.gif-' . $angle,
"Rotation GIF " . $angle
);
// Vérifier les couleurs
$image = new Imagick(_PATH_TESTS_OUTPUT_ . 'rotation_original-' . $unAngle . '.' . $uneExtension);
$this->assertTrue(
$this->compareColor(self::ROTATION_COULEURS[round($unAngle / 90)], $image->getImagePixelColor(0, 0)->getColor()),
'Pixel (0,0) - Couleur ' . $uneExtension . ' - Rotation ' . $unAngle
);
$this->assertEquals(
$this->compareColor(self::ROTATION_COULEURS[(round($unAngle / 90) + 1) % 4], $image->getImagePixelColor(0, self::ROTATION_DIM_ORIGINE[$indiceHauteur])->getColor()),
'Pixel (0,' . self::ROTATION_DIM_ORIGINE[$indiceHauteur] . ') - Couleur ' . $uneExtension . ' - Rotation ' . $unAngle
);
$this->assertEquals(
$this->compareColor(self::ROTATION_COULEURS[(round($unAngle / 90) + 2) % 4], $image->getImagePixelColor(self::ROTATION_DIM_ORIGINE[$indiceLargeur], self::ROTATION_DIM_ORIGINE[$indiceHauteur])->getColor()),
'Pixel (' . self::ROTATION_DIM_ORIGINE[$indiceLargeur] . ',' . self::ROTATION_DIM_ORIGINE[$indiceHauteur] . ') - Couleur ' . $uneExtension . ' - Rotation ' . $unAngle
);
$this->assertEquals(
$this->compareColor(self::ROTATION_COULEURS[(round($unAngle / 90) + 3) % 4], $image->getImagePixelColor(self::ROTATION_DIM_ORIGINE[$indiceLargeur], 0)->getColor()),
'Pixel (' . self::ROTATION_DIM_ORIGINE[$indiceLargeur] . ',0) - Couleur ' . $uneExtension . ' - Rotation ' . $unAngle
);
}
}
}
/**
* Redimensionnement des images
* @depends testRotationImagesGIF
*/
public function testRedimensionnementImages()
#[RunInSeparateProcess]
public function testRedimensionnementImages(): void
{
require 'config/config.php';
$monImage = new ImageObject();
/*
* Cas null
* Cas incohérents => Ne rien faire
*/
$this->assertEquals(
null,
$this->assertFalse(
$monImage->redimensionner(_PATH_TESTS_IMAGES_ . 'image_portrait_600x800.png', '', 600, 800),
"Pas d'agrandissement"
'Pas d\'agrandissement'
);
$this->assertEquals(
null,
$this->assertFalse(
$monImage->redimensionner(_PATH_TESTS_IMAGES_ . 'image_portrait_600x800.png', '', 601, 800),
"Pas d'agrandissement"
'Pas d\'agrandissement'
);
$this->assertEquals(
null,
$this->assertFalse(
$monImage->redimensionner(_PATH_TESTS_IMAGES_ . 'image_portrait_600x800.png', '', 600, 801),
"Pas d'agrandissement"
'Pas d\'agrandissement'
);
$this->assertEquals(
null,
$this->assertFalse(
$monImage->redimensionner(_PATH_TESTS_IMAGES_ . 'image_portrait_600x800.png', '', 599, 800),
"Pas d'agrandissement"
'Pas d\'agrandissement'
);
$this->assertEquals(
null,
$this->assertFalse(
$monImage->redimensionner(_PATH_TESTS_IMAGES_ . 'image_portrait_600x800.png', '', 600, 799),
"Pas d'agrandissement"
'Pas d\'agrandissement'
);
$this->assertEquals(
null,
$this->assertFalse(
$monImage->redimensionner(_PATH_TESTS_IMAGES_ . 'image_portrait_600x800.png', '', 0, 799),
"Image de taille zéro"
'Image de taille zéro'
);
$this->assertEquals(
null,
$this->assertFalse(
$monImage->redimensionner(_PATH_TESTS_IMAGES_ . 'image_portrait_600x800.png', '', 0, 0),
"Image de taille zéro"
'Image de taille zéro'
);
$this->assertEquals(
null,
$this->assertFalse(
$monImage->redimensionner(_PATH_TESTS_IMAGES_ . 'image_portrait_600x800.png', '', 10, 0),
"Image de taille zéro"
'Image de taille zéro'
);
/*
@ -237,10 +203,16 @@ class ImageObjectTest extends TestCase
200,
400
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_portrait_200x400.png-a-partir-php-7.2',
_PATH_TESTS_OUTPUT_ . 'image_portrait_200x400.png',
"Redimensionnement portrait 200x400"
$imageInfo = getimagesize(_PATH_TESTS_OUTPUT_ . 'image_portrait_200x400.png');
$this->assertEquals(
200,
$imageInfo[0],
'Redimensionnement 600x800 -> 200x400'
);
$this->assertEquals(
267,
$imageInfo[1],
'Redimensionnement 600x800 -> 200x400'
);
// Doit être 150x200
@ -250,10 +222,16 @@ class ImageObjectTest extends TestCase
400,
200
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_portrait_400x200.png-a-partir-php-7.2',
_PATH_TESTS_OUTPUT_ . 'image_portrait_400x200.png',
"Redimensionnement portrait 400x200"
$imageInfo = getimagesize(_PATH_TESTS_OUTPUT_ . 'image_portrait_400x200.png');
$this->assertEquals(
150,
$imageInfo[0],
'Redimensionnement 600x800 -> 400x200'
);
$this->assertEquals(
200,
$imageInfo[1],
'Redimensionnement 600x800 -> 400x200'
);
/*
@ -266,10 +244,16 @@ class ImageObjectTest extends TestCase
400,
200
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_paysage_400x200.png-a-partir-php-7.2',
_PATH_TESTS_OUTPUT_ . 'image_paysage_400x200.png',
"Redimensionnement paysage 400x200"
$imageInfo = getimagesize(_PATH_TESTS_OUTPUT_ . 'image_paysage_400x200.png');
$this->assertEquals(
267,
$imageInfo[0],
'Redimensionnement 800x600 -> 400x200'
);
$this->assertEquals(
200,
$imageInfo[1],
'Redimensionnement 800x600 -> 400x200'
);
// Doit être 200x150
@ -279,10 +263,16 @@ class ImageObjectTest extends TestCase
200,
400
);
$this->assertFileEquals(
_PATH_TESTS_IMAGES_ . 'image_paysage_200x400.png-a-partir-php-7.2',
_PATH_TESTS_OUTPUT_ . 'image_paysage_200x400.png',
"Redimensionnement paysage 200x400"
$imageInfo = getimagesize(_PATH_TESTS_OUTPUT_ . 'image_paysage_200x400.png');
$this->assertEquals(
200,
$imageInfo[0],
'Redimensionnement 800x600 -> 200x400'
);
$this->assertEquals(
150,
$imageInfo[1],
'Redimensionnement 800x600 -> 200x400'
);
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -21,26 +21,16 @@
namespace ImageHebergTests;
use ImageHeberg\ImageObject;
use ImageHeberg\MaBDD;
use ImageHeberg\MetaObject;
use ImageHeberg\MiniatureObject;
use ImageHeberg\Outils;
use ImageHeberg\RessourceInterface;
use ImageHeberg\RessourceObject;
use ImageHeberg\SessionObject;
use ImageHeberg\UtilisateurObject;
use PHPUnit\Framework\TestCase;
class InstallationTest extends TestCase
{
public function testValidationInstallation()
public function testValidationInstallation(): void
{
/**
* Valeur attendue
*/
$this->expectOutputString("L'installation est OK !");
$this->expectOutputRegex('#.*L\'installation est OK !$#U');
/**
* Exécution du script...

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -21,25 +21,19 @@
namespace ImageHebergTests;
use ImageHeberg\ImageObject;
use ImageHeberg\MaBDD;
use ImageHeberg\MetaObject;
use ImageHeberg\MiniatureObject;
use ImageHeberg\Outils;
use ImageHeberg\RessourceInterface;
use ImageHeberg\RessourceObject;
use ImageHeberg\SessionObject;
use ImageHeberg\UtilisateurObject;
use PDO;
use PHPUnit\Framework\Attributes\Depends;
use PHPUnit\Framework\TestCase;
class MembreTest extends TestCase
{
/**
* Fonction requise par l'extension Database
* @return type
* @return mixed
*/
public function getConnection()
public function getConnection(): mixed
{
$pdo = new PDO('sqlite::memory:');
return $this->createDefaultDBConnection($pdo, ':memory:');
@ -47,14 +41,14 @@ class MembreTest extends TestCase
/**
* Fonction requise par l'extension Database
* @return \PHPUnit_Extensions_Database_DataSet_DefaultDataSet
* @return PHPUnit_Extensions_Database_DataSet_DefaultDataSet
*/
public function getDataSet()
public function getDataSet(): PHPUnit_Extensions_Database_DataSet_DefaultDataSet
{
return new PHPUnit_Extensions_Database_DataSet_DefaultDataSet();
}
public function testConnexionMembreExistant()
public function testConnexionMembreExistant(): void
{
// Chargement de la configuration
require_once 'config/config.php';
@ -78,14 +72,18 @@ class MembreTest extends TestCase
* Vérification des valeurs
*/
$maSession = new SessionObject();
$this->assertEquals(UtilisateurObject::LEVEL_ADMIN, $maSession->getLevel(), "connexion : doit être OK");
$this->assertEquals(
UtilisateurObject::LEVEL_ADMIN,
$maSession->getLevel(),
'connexion : doit être OK'
);
}
/**
* Création d'un compte membre avec un nom déjà existant
* @depends testConnexionMembreExistant
*/
public function testMembreCreerCompteDoublon()
#[Depends('testConnexionMembreExistant')]
public function testMembreCreerCompteDoublon(): void
{
unset($_POST);
/**
@ -109,18 +107,17 @@ class MembreTest extends TestCase
* Vérification des valeurs
*/
$monMembre = new UtilisateurObject();
$this->assertEquals(
false,
$this->assertFalse(
$monMembre->connexion($_POST['userName'], $_POST['userPassword']),
"connexion : le nom d'utilisateur doit être unique"
'connexion : le nom d\'utilisateur doit être unique'
);
}
/**
* Création d'un compte membre.
* @depends testMembreCreerCompteDoublon
*/
public function testMembreCreerCompte()
#[Depends('testMembreCreerCompteDoublon')]
public function testMembreCreerCompte(): void
{
unset($_POST);
/**
@ -149,23 +146,41 @@ class MembreTest extends TestCase
* Vérification des valeurs
*/
// Email
$this->assertEquals('myMail@example.com', $monMembre->getEmail(), "Vérification email");
$this->assertEquals(
'mymail@example.com',
$monMembre->getEmail(),
'Vérification email'
);
// ID
$this->assertEquals(3, $monMembre->getId());
$this->assertEquals(
3,
$monMembre->getId()
);
// @ IP d'inscription
$this->assertEquals('127.0.0.1', $monMembre->getIpInscription());
$this->assertEquals(
'127.0.0.1',
$monMembre->getIpInscription()
);
// Niveau de droits
$this->assertEquals(UtilisateurObject::LEVEL_USER, $monMembre->getLevel());
$this->assertEquals(
UtilisateurObject::LEVEL_USER,
$monMembre->getLevel()
);
// Nom
$this->assertEquals('username', $monMembre->getUserName());
$this->assertEquals(true, $monMembre->connexion($_POST['userName'], $_POST['userPassword']));
$this->assertEquals(
'username',
$monMembre->getUserName()
);
$this->assertTrue(
$monMembre->connexion($_POST['userName'], $_POST['userPassword'])
);
}
/**
* Modification du mail
* @depends testMembreCreerCompte
*/
public function testMembreModifierMail()
#[Depends('testMembreCreerCompte')]
public function testMembreModifierMail(): void
{
unset($_POST);
/**
@ -180,7 +195,10 @@ class MembreTest extends TestCase
* Simulation d'une connexion
*/
$unMembre = new UtilisateurObject();
$this->assertEquals(true, $unMembre->connexion('username', $_POST['userPasswordMail']), "connexion avant");
$this->assertTrue(
$unMembre->connexion('username', $_POST['userPasswordMail']),
'connexion avant'
);
/**
* Appel de la page
@ -198,15 +216,22 @@ class MembreTest extends TestCase
* Vérification des valeurs
*/
// Email
$this->assertEquals('john.doe@example.com', $monMembre->getEmail(), "getEmail");
$this->assertEquals(true, $monMembre->connexion('username', $_POST['userPasswordMail']), "connexion après");
$this->assertEquals(
'john.doe@example.com',
$monMembre->getEmail(),
'getEmail'
);
$this->assertTrue(
$monMembre->connexion('username', $_POST['userPasswordMail']),
'connexion après'
);
}
/**
* Modification du mot de passe
* @depends testMembreModifierMail
*/
public function testMembreModifierPassword()
#[Depends('testMembreModifierMail')]
public function testMembreModifierPassword(): void
{
unset($_POST);
/**
@ -221,7 +246,10 @@ class MembreTest extends TestCase
* Simulation d'une connexion
*/
$unMembre = new UtilisateurObject();
$this->assertEquals(true, $unMembre->connexion('username', $_POST['oldUserPassword']), "connexion avant");
$this->assertTrue(
$unMembre->connexion('username', $_POST['oldUserPassword']),
'connexion avant'
);
/**
* Appel de la page
@ -238,15 +266,21 @@ class MembreTest extends TestCase
/**
* Vérification des valeurs
*/
$this->assertEquals(true, $monMembre->connexion('username', $_POST['newUserPassword']), "connexion");
$this->assertEquals(false, $monMembre->connexion('username', $_POST['oldUserPassword']), "connexion");
$this->assertTrue(
$monMembre->connexion('username', $_POST['newUserPassword']),
'connexion'
);
$this->assertFalse(
$monMembre->connexion('username', $_POST['oldUserPassword']),
'connexion'
);
}
/**
* Suppression du compte sans cochage de la checkbox
* @depends testMembreModifierPassword
*/
public function testMembreSupprimerCompteRequiertCheckbox()
#[Depends('testMembreModifierPassword')]
public function testMembreSupprimerCompteRequiertCheckbox(): void
{
unset($_POST);
/**
@ -260,7 +294,10 @@ class MembreTest extends TestCase
* Simulation d'une connexion
*/
$unMembre = new UtilisateurObject();
$this->assertEquals(true, $unMembre->connexion('username', $_POST['userPasswordDelete']), "connexion avant");
$this->assertTrue(
$unMembre->connexion('username', $_POST['userPasswordDelete']),
'connexion avant'
);
/**
* Appel de la page
@ -277,18 +314,17 @@ class MembreTest extends TestCase
/**
* Vérification des valeurs
*/
$this->assertEquals(
true,
$this->assertTrue(
$monMembre->connexion('username', $_POST['userPasswordDelete']),
"connexion devrait être possible"
'connexion devrait être possible'
);
}
/**
* Suppression du compte
* @depends testMembreSupprimerCompteRequiertCheckbox
*/
public function testMembreSupprimerCompte()
#[Depends('testMembreSupprimerCompteRequiertCheckbox')]
public function testMembreSupprimerCompte(): void
{
unset($_POST);
/**
@ -303,7 +339,10 @@ class MembreTest extends TestCase
* Simulation d'une connexion
*/
$unMembre = new UtilisateurObject();
$this->assertEquals(true, $unMembre->connexion('username', $_POST['userPasswordDelete']), "connexion avant");
$this->assertTrue(
$unMembre->connexion('username', $_POST['userPasswordDelete']),
'connexion avant'
);
/**
* Appel de la page
@ -320,18 +359,17 @@ class MembreTest extends TestCase
/**
* Vérification des valeurs
*/
$this->assertEquals(
false,
$this->assertFalse(
$monMembre->connexion('username', $_POST['userPasswordDelete']),
"connexion ne devrait plus être possible"
'connexion ne devrait plus être possible'
);
}
/**
* Connexion au compte créé lors de la création de la BDD
* @depends testMembreSupprimerCompte
*/
public function testConnexionCompteHistorique()
#[Depends('testMembreSupprimerCompte')]
public function testConnexionCompteHistorique(): void
{
unset($_POST);
/**
@ -346,10 +384,9 @@ class MembreTest extends TestCase
/**
* Vérification des valeurs
*/
$this->assertEquals(
true,
$this->assertTrue(
$monMembre->connexion('admin', 'password'),
"connexion au compte créé à l'import de la BDD devrait être possible"
'connexion au compte créé à l\'import de la BDD devrait être possible'
);
}
}

98
__tests/_bootstrap.php Normal file
View file

@ -0,0 +1,98 @@
<?php
/**
* Préconfiguration de l'environnement de tests
* - Calcul des MD5 des images locales (dépendant de la version de PHP)
* - Mise à jour en BDD des MD5 enregistrés
* - Copie des fichiers requis
*/
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHebergTests;
use ImageHeberg\HelperImage;
use ImageHeberg\MaBDD;
require 'config/config.php';
// Faire la liste des fichiers
$tabFiles = [];
foreach (scandir(_PATH_TESTS_IMAGES_A_IMPORTER_) as $file) {
if (is_file(_PATH_TESTS_IMAGES_A_IMPORTER_ . $file)) {
if (
in_array(HelperImage::getExtension(_PATH_TESTS_IMAGES_A_IMPORTER_ . $file), _ACCEPTED_EXTENSIONS_, true)
&& HelperImage::isModifiableEnMemoire(_PATH_TESTS_IMAGES_A_IMPORTER_ . $file)
) {
$tabFiles[$file] = _PATH_TESTS_IMAGES_A_IMPORTER_ . $file;
}
// Remettre à disposition le fichier pour les tests
copy(_PATH_TESTS_IMAGES_A_IMPORTER_ . $file, _PATH_TESTS_IMAGES_ . $file);
}
}
// Optimiser localement chaque image et en calculer le MD5 résultant
foreach ($tabFiles as $file => $path) {
// Créer un fichier temporaire pour travailler dessus
$fileTmp = tempnam('/tmp/', 'ih');
// PHP ne gère pas les images WebP animée -> ne pas faire de traitements
if (!HelperImage::isAnimatedWebp($path)) {
// Optimiser l'image (permettra de comparer son hash avec celles déjà stockées)
HelperImage::setImage(HelperImage::getImage($path), HelperImage::getType($path), $fileTmp);
} else {
copy($path, $fileTmp);
}
// Calculer le MD5 de l'image
$md5 = md5_file($fileTmp);
// Corriger l'information en BDD
$req = MaBDD::getInstance()->prepare('UPDATE images SET md5 = :md5 WHERE old_name = :oldName');
$req->bindValue(':md5', $md5);
$req->bindValue(':oldName', $file);
$req->execute();
// Copier le fichier dans le bon répertoire
$fileDst = _PATH_IMAGES_ . substr($md5, 0, 1) . '/' . $md5;
copy($fileTmp, $fileDst);
echo $file . ' -> ' . $fileDst . PHP_EOL;
}
// Traitement pour ImageUploadAndDeleteTest::testSuppressionImagePlusieursMiniatures()
$tabThumbs = [
'image_a_supprimerMultiple-100x100.png' => _PATH_TESTS_IMAGES_ . 'image_a_supprimerMultiple-100x100.png',
'image_a_supprimerMultiple-200x200.png' => _PATH_TESTS_IMAGES_ . 'image_a_supprimerMultiple-200x200.png',
];
foreach ($tabThumbs as $file => $path) {
// Calculer le MD5 de l'image
$md5 = md5_file($path);
// Corriger l'information en BDD
$req = MaBDD::getInstance()->prepare('UPDATE thumbnails SET md5 = :md5 WHERE new_name = :newName');
$req->bindValue(':md5', $md5);
$req->bindValue(':newName', $file);
$req->execute();
// Copier le fichier dans le bon répertoire
$fileDst = _PATH_MINIATURES_ . substr($md5, 0, 1) . '/' . $md5;
copy($path, $fileDst);
echo $file . ' -> ' . $fileDst . PHP_EOL;
}

View file

@ -1 +0,0 @@
10

View file

@ -1 +0,0 @@
4

View file

@ -1 +0,0 @@
2

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -24,50 +24,70 @@
*/
/* Base de données */
// Serveur de base de données
define('_BDD_HOST_', 'localhost');
const _BDD_HOST_ = 'localhost';
// Utilisateur SQL
define('_BDD_USER_', 'root');
const _BDD_USER_ = 'root';
// Mot de passe SQL
define('_BDD_PASS_', 'root');
const _BDD_PASS_ = 'root';
// Nom de la base de données
define('_BDD_NAME_', 'imageheberg');
const _BDD_NAME_ = 'imageheberg';
/* Système de fichiers */
// Emplacement de votre site sur le système de fichiers de votre hébergeur
define('_PATH_', '/home/runner/work/image-heberg.fr/image-heberg.fr/');
const _PATH_ = '/home/runner/work/image-heberg.fr/image-heberg.fr/';
/* A propos de l'outil */
// Nom affiché du service
define('_SITE_NAME_', 'monSite');
const _SITE_NAME_ = 'monSite';
// URL du site
define('_BASE_URL_', 'www.example.com/');
const _BASE_URL_ = 'www.example.com/';
// Administrateur du site
define('_ADMINISTRATEUR_NOM_', 'John DOE');
const _ADMINISTRATEUR_NOM_ = 'John DOE';
// Site web de l'administrateur
define('_ADMINISTRATEUR_SITE_', '//www.example.com/');
const _ADMINISTRATEUR_SITE_ = '//www.example.com/';
// Mail de l'administrateur (non affiché)
define('_ADMINISTRATEUR_EMAIL_', 'john.doe@example.com');
const _ADMINISTRATEUR_EMAIL_ = 'john.doe@example.com';
/* Informations légales */
// Hébergeur du site
define('_HEBERGEUR_NOM_', 'OVH');
const _HEBERGEUR_NOM_ = 'OVH';
// Site web de l'hébergeur
define('_HEBERGEUR_SITE_', '//www.ovh.com');
const _HEBERGEUR_SITE_ = '//www.ovh.com';
/* Configurations spécifiques de l'outil */
// Poids maximal des fichiers
define('_IMAGE_POIDS_MAX_', 5242880);
const _IMAGE_POIDS_MAX_ = 5242880;
// Délai de conservation d'une image jamais affichée (en jours)
define('_DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_', 7);
const _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ = 7;
// Délai depuis le dernier affichage d'une image avant de la supprimer (en jours)
define('_DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_', 365);
const _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ = 365;
// Volume maximal de stockage d'images (en Go)
define('_QUOTA_MAXIMAL_IMAGES_GO_', 90);
const _QUOTA_MAXIMAL_IMAGES_GO_ = 90;
// Affichage des messages d'erreur
define('_DEBUG_', true);
const _DEBUG_ = true;
/* Gestion des abus */
// Nombre d'affichages par jour à partir duquel une image est suspecte
const _ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ = 1500;
// Nombre d'affichages par jour à partir duquel une image est automatiquement bloquée
const _ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_ = 100000;
// Nombre d'affichages par jour à partir duquel une image est clairement abusive;
const _ABUSE_NB_AFFICHAGES_PAR_JOUR_ABUSIF_ = 10 * _ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_;
// Division des seuils d'abus si une image est considérée comme suspecte
const _ABUSE_DIVISION_SEUILS_SI_SUSPECT_ = 2;
// Désactiver l'envoi d'images depuis un noeud de sortie Tor
const _TOR_DISABLE_UPLOAD_ = true;
// Désactiver l'envoi d'images au bout de x images bloquées (mettre 0 pour ne pas l'activer)
const _ABUSE_DISABLE_UPLOAD_AFTER_X_IMAGES_ = 5;
// User-Agent pour lesquels bloquer les images
const _ABUSE_DISABLE_PICS_WHEN_USERE_AGENT_ = ['someUserAgentNumberOne', 'AnoterUserAgentNumberTwo'];
/**
* FIN DES CHAMPS A CONFIGURER
@ -77,11 +97,11 @@ define('_DEBUG_', true);
*/
// Salt pour les mots de passe
// Legacy - n'est plus requis !!
define('_GRAIN_DE_SEL_', '');
const _GRAIN_DE_SEL_ = '';
/**
* FIN DES CHAMPS A COMPLETER UNIQUEMENT SI VOUS AVEZ UNE VERSION ANTERIEURE A v2.0.4
*/
// Activation des tests Tests TRAVIS-CI
define('_PHPUNIT_', true);
const _PHPUNIT_ = true;
require _PATH_ . 'config/image-heberg.php';

View file

@ -1,5 +1,5 @@
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -19,43 +19,109 @@
--
-- Image bloquée
--
INSERT INTO `images` (`id`, `ip_envoi`, `date_envoi`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`) VALUES
(10, '127.0.0.1', '2008-01-01 00:00:00', 'imageBloquee.jpg', 'imageBloquee.jpg', 10, 10, 10, NULL, 0, 0, '6858ce6ddc171a0fd9640831a5e74dfd', 1, 0);
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(10, '127.0.0.1', '2008-01-01 00:00:00', 'image_a_supprimer.png', 'image_10.png', 10, 10, 10, '0000-00-00', 0, 0, 'to-be-calculatedto-be-calculated', 1, 0, 0, '127.0.2');
--
-- Images à supprimer
--
INSERT INTO `images` (`id`, `ip_envoi`, `date_envoi`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`) VALUES
(11, '127.0.0.1', '2008-01-01 00:00:00', 'image_a_supprimer.png', '100000019001334055750.png', 4239, 400, 640, NULL, 0, 0, 'e656d1b6582a15f0f458006898b40e29', 0, 0),
(12, '127.0.0.10', NOW(), 'image_a_supprimer.png', '147834019001334055750.png', 4239, 400, 640, NULL, 0, 0, 'e656d1b6582a15f0f458006898b40e29', 0, 0),
(13, '127.0.0.1', '2016-01-01 00:00:00', 'image.png', '146734019451334055750.png', 4239, 400, 640, NULL, 0, 0, 'a876d1b6582a15f0f458006898b40e29', 0, 0),
(14, '127.0.0.1', '2016-01-01 00:00:00', 'image_a_supprimerMultiple.png', '14777777.png', 4239, 400, 640, NULL, 0, 0, 'aec65c6b4469bb7267d2d55af5fbd87b', 0, 0),
(15, '127.0.0.1', '2016-01-01 00:00:00', 'imageQuiSeraBloquee.png', '15.png', 4239, 400, 640, NULL, 0, 0, 'bec65c6b4469bb7267d2d55af5fbd87b', 0, 0),
(16, '127.0.0.1', '2016-01-01 00:00:00', 'imageAvecMemeMd5QuiDoitEtreBloquee.png', '16.png', 4239, 400, 640, NULL, 0, 0, 'bec65c6b4469bb7267d2d55af5fbd87b', 0, 0);
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(11, '127.0.0.1', '2008-01-01 00:00:00', 'image_a_supprimer.png', 'image_11.png', 4239, 400, 640, '0000-00-00', 0, 0, 'to-be-calculatedto-be-calculated', 0, 0, 0, '127.0.0'),
(12, '127.0.0.10', NOW(), 'image_portrait_600x800.png', 'image_12.png', 4239, 400, 640, '0000-00-00', 0, 0, 'to-be-calculatedto-be-calculated', 0, 0, 0, '127.0.0'),
(13, '127.0.0.10', NOW(), 'imageBleue10.png', 'image_13.png', 4239, 400, 640, '0000-00-00', 0, 0, 'to-be-calculatedto-be-calculated', 0, 0, 0, '127.0.0'),
(14, '127.0.0.1', '2016-01-01 00:00:00', 'image_a_supprimerMultiple.png', 'image_14.png', 4239, 400, 640, '0000-00-00', 0, 0, 'to-be-calculatedto-be-calculated', 0, 0, 0, '127.0.0'),
(15, '127.0.0.1', '2016-01-01 00:00:00', 'imageQuiSeraBloquee.png', 'image_15.png', 4239, 400, 640, '0000-00-00', 0, 0, '97a3a88502d6-theSameMd5-97a3a88502d6', 0, 0, 0, '127.0.0'),
(16, '127.0.0.1', '2016-01-01 00:00:00', 'imageAvecMemeMd5QuiDoitEtreBloquee.png', 'image_16.png', 4239, 400, 640, '0000-00-00', 0, 0, '97a3a88502d6-theSameMd5-97a3a88502d6', 0, 0, 0, '127.0.0'),
(17, '127.0.0.1', '2023-01-01 00:00:00', 'imagePeuAfficheeMaisMignatureBeaucoupAffichee.png', 'image_17.png', 4239, 400, 640, '0000-00-00', 1000, 1000, 'not-used--be5e3e8d65ecefdc0dbcca', 0, 0, 0, '127.0.0');
--
-- Image signalée
--
INSERT INTO `images` (`id`, `ip_envoi`, `date_envoi`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`) VALUES
(17, '127.0.0.1', '2008-01-01 00:00:00', 'imageSignalee.png', 'imageSignalee.png', 4239, 400, 640, NULL, 0, 0, 'd456d1b6582a15f0f458006898b40e29', 0, 1);
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(18, '127.0.0.1', '2008-01-01 00:00:00', 'test.webp', 'image_18.png', 4239, 400, 640, '0000-00-00', 0, 0, 'to-be-calculatedto-be-calculated', 0, 1, 0, '127.0.1');
--
-- Image bloquée
--
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(19, '10.10.10.10', '2016-01-01 00:00:00', 'imageDejaBloquee.gif', 'image_19.gif', 146, 25, 37, '0000-00-00', 0, 0, 'to-be-calculatedto-be-calculated', 1, 0, 0, '10.10.10');
--
-- Image du même réseau que celle bloquée
--
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(20, '10.10.10.200', '2016-01-01 00:00:00', 'imageMemeReseauQueDejaBloquee.gif', 'image_20.gif', 146, 25, 37, '0000-00-00', 751, 0, 'not-used--dea392173d746c107beda4', 0, 0, 0, '10.10.10');
--
-- Réputation des réseaux
--
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(21, '192.168.0.1', '2024-01-01 00:00:00', 'Capture.jpg', 'image_21.jpg', 1, 1, 1, '0000-00-00', 0, 0, 'not-used--ab4d682db12defa28f09a6', 1, 0, 0, '192.168.0'),
(22, '192.168.0.1', '2024-01-01 00:00:00', 'Capture.jpg', 'image_22.jpg', 1, 1, 1, '0000-00-00', 0, 0, 'not-used--009a3908f941f94f9d9d5a', 1, 0, 0, '192.168.0'),
(23, '192.168.0.1', '2024-01-01 00:00:00', 'Capture.jpg', 'image_23.jpg', 1, 1, 1, '0000-00-00', 0, 0, 'not-used--17dc9dab5ec4aaf13cb5d8', 1, 0, 0, '192.168.0'),
(24, '192.168.0.1', '2024-01-01 00:00:00', 'Capture.jpg', 'image_24.jpg', 1, 1, 1, '0000-00-00', 0, 0, 'not-used--1a18d35e0fed1f2963ac77', 1, 0, 0, '192.168.0'),
(25, '192.168.0.1', '2024-01-01 00:00:00', 'Capture.jpg', 'image_25.jpg', 1, 1, 1, '0000-00-00', 0, 0, 'not-used--4dd6365d4aea4e3bde009f', 1, 0, 0, '192.168.0'),
(26, '192.168.100.1', '2024-01-01 00:00:00', 'Capture.jpg', 'image_26.jpg', 1, 1, 1, '0000-00-00', 0, 0, 'not-used--48c026fca13b23c786ed87', 1, 0, 0, '192.168.100');
--
-- Image qui sera trop affichée EN PROJECTION
--
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(27, '127.0.3.1', (NOW() - INTERVAL 2 HOUR), 'image_trop_affichee_en_projection.png', 'image_27.png', 1, 1, 1, NOW(), 5000, 5000, 'not-used--ab48e8f727e3329aaa6cf4', 0, 0, 0, '127.0.3');
--
-- Images qui seront bloquées
--
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(28, '127.0.1.1', NOW(), 'image_a_bloquer_en_prog.png', 'image_28.png', 1, 1, 1, NOW(), 50, 50, 'ab5fe1f77dfb-theSameMd5-ab5fe1f77dfb', 0, 0, 0, '127.0.1'),
(29, '127.0.1.1', NOW(), 'image_qui_sera_aussi_bloquee_car_md5_identique.png', 'image_29.png', 1, 1, 1, NOW(), 50, 50, 'ab5fe1f77dfb-theSameMd5-ab5fe1f77dfb', 0, 0, 1, '127.0.1');
--
-- Images qui seront validées
--
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(30, '127.0.0.1', NOW(), 'image_a_valider_prog.png', 'image_30.png', 1, 1, 1, NOW(), 50, 50, 'f3a7c514d2-theSameMd5-f3a7c514d2', 0, 0, 0, '127.0.0'),
(31, '127.0.0.1', NOW(), 'image_a_valider_prog.png', 'image_31.png', 1, 1, 1, NOW(), 50, 50, 'f3a7c514d2-theSameMd5-f3a7c514d2', 0, 1, 0, '127.0.0'),
(32, '127.0.0.1', NOW(), 'image_a_valider_prog.png', 'image_32.png', 1, 1, 1, NOW(), 50, 50, 'f3a7c514d2-theSameMd5-f3a7c514d2', 0, 0, 1, '127.0.0');
--
-- Image qui sera bloqué lors de son affichage avec un User-Agent malveillant
--
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(33, '127.0.4.1', NOW(), 'imageQuiSeraSignaleeParUserAgent.png', 'image_33.png', 1, 1, 1, '0000-00-00', 0, 0, 'd0a77eeeff5ef764505fe5b119b913bf', 0, 0, 0, '127.0.4');
--
-- Image beaucoup trop affichée
--
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`) VALUES
(34, '127.0.0.1', DATE_SUB(NOW(), INTERVAL 3 DAY), 'imageBeaucoupTropAffichee.png', 'image_34.png', 1, 1, 1, NOW(), 99999999, 99999999, 'not-used--fd9cb5a0afba67138bd328', 0, 0, 0, '127.0.0');
--
-- Agrandir la taille du champ pour bien gérer le _bootstrap
--
ALTER TABLE `thumbnails` MODIFY `new_name` VARCHAR(50) ;
--
-- Miniatures à supprimer
--
INSERT INTO `thumbnails` (`id`, `id_image`, `date_creation`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`) VALUES
(1, 14, '2016-01-01', '14777777.png', 10316, 100, 100, '2016-01-01', 19, 0, '031328c1a7ffe7eed0a2cab4eca05a63'),
(2, 14, '2016-01-01', '147777772.png', 10316, 200, 200, '2016-01-01', 19, 0, '278a70a02e036cc85e0d7e605fdc517f');
INSERT INTO `thumbnails` (`id`, `images_id`, `date_action`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`) VALUES
(1, 14, '2016-01-01', 'image_a_supprimerMultiple-100x100.png', 10316, 100, 100, '2016-01-01', 19, 0, 'to-be-calculatedto-be-calculated'),
(2, 14, '2016-01-01', 'image_a_supprimerMultiple-200x200.png', 10316, 200, 200, '2016-01-01', 19, 0, 'to-be-calculatedto-be-calculated');
--
-- Miniature beaucoup affichée
--
INSERT INTO `thumbnails` (`id`, `images_id`, `date_action`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`) VALUES
(3, 20, '2023-01-01', '14777777.png', 10316, 100, 100, '2023-01-01', 999999999999, 999999999999, 'not-used--f12d4a42776aba3a16761e');
--
-- Possessions
--
INSERT INTO `possede` (`image_id`, `pk_membres`) VALUES ('11', '2'),
INSERT INTO `possede` (`images_id`, `membres_id`) VALUES ('11', '2'),
('14', '1');
--
-- Second compte utilisateur
--
INSERT INTO `membres` (`id`, `email`, `login`, `password`, `date_inscription`, `ip_inscription`, `lvl`) VALUES
(2, 'john.doe2@example.com', 'user', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', DATE(NOW()), '127.0.0.1', 1);
INSERT INTO `membres` (`id`, `email`, `login`, `password`, `date_action`, `remote_addr`, `lvl`, `token`) VALUES
(2, 'john.doe2@example.com', 'user', '5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8', DATE(NOW()), '127.0.0.1', 1, '');

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

View file

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View file

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 KiB

BIN
__tests/images/image_33.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 790 B

Binary file not shown.

1
__tests/ipv4.txt Normal file

File diff suppressed because one or more lines are too long

1
__tests/ipv6.txt Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -24,89 +24,91 @@ namespace ImageHeberg;
require 'config/config.php';
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>A propos</small></h1>
<h1 class="mb-3"><small>A propos</small></h1>
<div class="card card-default">
<div class="card-body">
Ce site est administré par <a href="<?= _ADMINISTRATEUR_SITE_ ?>"><?= _ADMINISTRATEUR_NOM_ ?></a>
<br />
Pour toute demande ou information concernant ce site, merci d'utiliser
<a href="/contact.php">le formulaire de contact</a>.
</div>
</div>
<div class="card">
<div class="card-header">
Licences
</div>
<div class="card-body">
Auteur de l'outil : <a href="//www.anael.eu">Anael MOBILIA</a>
<br />
Le <a href="//github.com/AnaelMobilia/image-heberg.fr">code source est disponible</a> sous licence GNU GPLv3.
<br />
Graphismes : <a href="//getbootstrap.com/">Bootstrap</a>
<br />
Logos : <a href="//fontawesome.com">Font Awesome</a>
<br />
Technologies : PHP, MySQL, HTML5, CSS3
</div>
</div>
<div class="card">
<div class="card-header">
Hébergeur
</div>
<div class="card-body">
Ce site est hébergé chez <a href="<?= _HEBERGEUR_SITE_ ?> "><?= _HEBERGEUR_NOM_ ?> </a>
<br />
Vous trouverez leur adresse et téléphone sur <a href="<?= _HEBERGEUR_SITE_ ?>">leur site internet</a>.
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#collapseCNIL">
Conservations de données à caractère privé
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="collapseCNIL" class="collapse">
<div class="card card-body">
L'utilisation du service conduit à l'enregistrement de cookies techniques (gestion des sessions) sur votre ordinateur. Ces cookies seront supprimés lors de la fermeture de votre navigateur.
<br />
Votre adresse IP est enregistrée dans la base de données lors de l'envoi d'une image, de la création et à la connexion à votre espace membre.
<br />
La suppression d'une image envoyée sur le service conduit à la suppression de toutes les informations liées dans la base de données. Les images qui ne sont plus affichées depuis plus de
<?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ ?> jours seront automatiquement effacées.
<br />
La suppression de votre compte entraine la suppression de toutes les informations liées à ce dernier.
<br />
Conformément à la directive 2006/24/CE sur la conservation des données, tous les accès au service sont enregistrés et conservés durant une durée d'au moins 12 mois et au plus 24 mois.
<br />
Aucune donnée n'est utilisée à but publicitaire, ni transmise à des tiers, ou réutilisée en dehors du présent service. L'ensemble des données sont stockées au sein de l'Union Européenne.
<div class="card card-default">
<div class="card-body">
Ce site est administré par <a href="<?= _ADMINISTRATEUR_SITE_ ?>"><?= _ADMINISTRATEUR_NOM_ ?></a>
<br/>
Pour toute demande ou information concernant ce site, merci d'utiliser
<a href="/contact.php">le formulaire de contact</a>.
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#collapseCNIL2">
Responsable du traitement : <?= _ADMINISTRATEUR_NOM_ ?>
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="collapseCNIL2" class="collapse">
<div class="card card-body">
Les informations recueillies font lobjet dun traitement informatique destiné à personnaliser votre utilisation du service.
<div class="card">
<div class="card-header">
Licences
</div>
<div class="card-body">
Auteur de l'outil : <a href="//www.anael.eu">Anael MOBILIA</a>
<br/>
Le <a href="//github.com/AnaelMobilia/image-heberg.fr">code source est disponible</a> sous licence GNU GPLv3.
<br/>
Graphismes : <a href="//getbootstrap.com/">Bootstrap</a>
<br/>
Logos : <a href="//icons.getbootstrap.com/">Bootstrap Icons</a>
<br/>
IA de classification : <a href="//github.com/infinitered/nsfwjs">nsfwjs</a>
<br />
Vous n'êtes pas obligé de créer un espace membre pour utiliser le service.
<br />
Le destinataire des données est <?= _ADMINISTRATEUR_NOM_ ?>.
<br />
Conformément à la loi « informatique et libertés » du 6 janvier 1978 modifiée en 2004, vous bénéficiez d'un droit d'accès et de rectification aux informations qui vous concernent, que vous pouvez exercer en vous adressant à <?= _ADMINISTRATEUR_NOM_ ?> via le formulaire de contact.
<br />
Vous pouvez également, pour des motifs légitimes, vous opposer au traitement des données vous concernant.
Technologies : PHP, MySQL, HTML5, CSS3
</div>
</div>
</div>
<?php require _TPL_BOTTOM_ ?>
<div class="card">
<div class="card-header">
Hébergeur
</div>
<div class="card-body">
Ce site est hébergé chez <a href="<?= _HEBERGEUR_SITE_ ?> "><?= _HEBERGEUR_NOM_ ?> </a>
<br/>
Vous trouverez leur adresse et téléphone sur <a href="<?= _HEBERGEUR_SITE_ ?>">leur site internet</a>.
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#collapseCNIL">
Conservations de données à caractère privé
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="collapseCNIL" class="collapse">
<div class="card card-body">
L'utilisation du service conduit à l'enregistrement de cookies techniques (gestion des sessions) sur votre ordinateur. Ces cookies seront supprimés lors de la fermeture de votre navigateur.
<br/>
Votre adresse IP est enregistrée dans la base de données lors de l'envoi d'une image, de la création et à la connexion à votre espace membre.
<br/>
La suppression d'une image envoyée sur le service conduit à la suppression de toutes les informations liées dans la base de données. Les images qui ne sont plus affichées depuis plus de
<?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ ?> jours seront automatiquement effacées.
<br/>
La suppression de votre compte entraine la suppression de toutes les informations liées à ce dernier.
<br/>
Conformément à la directive 2006/24/CE sur la conservation des données, tous les accès au service sont enregistrés et conservés durant une durée d'au moins 12 mois et au plus 24 mois.
<br/>
Aucune donnée n'est utilisée à but publicitaire, ni transmise à des tiers, ou réutilisée en dehors du présent service. L'ensemble des données sont stockées au sein de l'Union Européenne.
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#collapseCNIL2">
Responsable du traitement : <?= _ADMINISTRATEUR_NOM_ ?>
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="collapseCNIL2" class="collapse">
<div class="card card-body">
Les informations recueillies font lobjet dun traitement informatique destiné à personnaliser votre utilisation du service.
<br/>
Vous n'êtes pas obligé de créer un espace membre pour utiliser le service.
<br/>
Le destinataire des données est <?= _ADMINISTRATEUR_NOM_ ?>.
<br/>
Conformément à la loi « informatique et libertés » du 6 janvier 1978 modifiée en 2004, vous bénéficiez d'un droit d'accès et de rectification aux informations qui vous concernent, que vous pouvez exercer en vous adressant à <?= _ADMINISTRATEUR_NOM_ ?> via le formulaire de contact.
<br/>
Vous pouvez également, pour des motifs légitimes, vous opposer au traitement des données vous concernant.
</div>
</div>
</div>
<?php require _TPL_BOTTOM_ ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -24,70 +24,85 @@ namespace ImageHeberg;
if (!defined('_PHPUNIT_')) {
require 'config/config.php';
}
require _TPL_TOP_;
// Anti flood
$maSession = new SessionObject();
$msgErreur = '';
require _TPL_TOP_;
// En cas de validation du formulaire
if (isset($_POST['Submit']) && $maSession->checkFlag()) {
if (
isset($_POST['Submit']) && $maSession->checkFlag()
&& !empty($_POST['userMail']) && !empty($_POST['urlImage'])
) {
// Suivi du traitement
$isTraitee = false;
// Vérification du bon format de l'adresse mail
if (filter_var($_POST['userMail'], FILTER_VALIDATE_EMAIL) !== false) {
// On essaie de matcher l'image
$result = preg_match("#^.*\/([\d]*.[pngjpif]{3})$#", trim($_POST['urlImage']), $idImage);
if ($result) {
// On essaie de matcher l'image - nettoyage des paramètres
$fileName = basename(parse_url(trim($_POST['urlImage']), PHP_URL_PATH));
if (
preg_match('#^[\d]+\.(?:' . implode('|', _ACCEPTED_EXTENSIONS_) . ')$#', $fileName)
|| (_PHPUNIT_ && $fileName === 'image_15.png')
) {
// Suivi du traitement
$isTraitee = true;
// On flaggue l'image en signalée en BDD
$monImage = new ImageObject($idImage[1]);
$monImage->setSignalee(true);
$monImage->sauver();
// On cherche les autres images avec le même MD5
$images = MetaObject::getImageByMd5($monImage->getMd5());
foreach ($images as $uneImage) {
// On flaggue en signalée...
$monImage = new ImageObject($uneImage);
$monImage = new ImageObject($fileName);
// Si l'image est approuvée, on ne la bloque pas en automatique
if (!$monImage->isApprouvee()) {
$monImage->setSignalee(true);
$monImage->sauver();
// On cherche les autres images avec le même MD5
$images = HelperAdmin::getImageByMd5($monImage->getMd5());
foreach ($images as $uneImage) {
// On flaggue en signalée...
$monImage = new ImageObject($uneImage);
$monImage->setSignalee(true);
$monImage->sauver();
}
// Les miniatures reprennent automatiquement les informations de l'image parent
} else {
$isTraitee = false;
}
// Les miniatures reprennent automatiquement les informations de l'image parent
}
// Gestion travis
if (!_PHPUNIT_) {
// Je complète le message avec l'IP de mon émeteur
$message = "URL : " . $_POST['urlImage'];
$message .= "\r\n\r\nBlocage automatique : " . ($result ? 'OK' : 'KO');
$message .= "\r\n\r\nRaison : " . $_POST['raison'];
$message .= "\r\n\r\nMessage : " . $_POST['userMessage'];
$message .= "\r\n\r\n---------------------------------------------";
$message .= "\r\n\r\nIP : " . $_SERVER['REMOTE_ADDR'];
$message .= "\r\n\r\nBROWSER : " . $_SERVER['HTTP_USER_AGENT'];
$message = 'URL : ' . $_POST['urlImage'];
$message .= PHP_EOL . 'Blocage automatique : ' . ($isTraitee ? 'OK' : 'KO');
$message .= PHP_EOL . 'Raison : ' . $_POST['raison'];
$message .= PHP_EOL . 'Message : ' . $_POST['userMessage'];
$message .= PHP_EOL . '---------------------------------------------';
$message .= PHP_EOL . 'IP : ' . $_SERVER['REMOTE_ADDR'];
$message .= PHP_EOL . 'BROWSER : ' . $_SERVER['HTTP_USER_AGENT'];
$message .= PHP_EOL . 'DATE : ' . date('Y-m-d H:i:s');
// Tout va bien, on envoit un mail
$subject = "[" . _SITE_NAME_ . "] - Signalement d'image";
mail(_ADMINISTRATEUR_EMAIL_, $subject, $message, "From: " . $_POST['userMail']);
$subject = '[' . _SITE_NAME_ . '] - Signalement d\'image';
mail(_ADMINISTRATEUR_EMAIL_, $subject, $message, 'From: ' . $_POST['userMail']);
$maSession->removeFlag();
}
// Retour utilisateur
echo '<div class="alert alert-success">Votre signalement a été envoyé !</div>';
echo '<div class="alert alert-success">Votre signalement a été envoyé, merci !</div>';
} else {
// Adresse mail invalide
echo '<div class = "alert alert-danger">Votre adresse mail n\'est pas valide !<br /><pre>' . $_POST['userMail'] . '</pre></div>';
}
} else {
// Premier affichage de la page
if (!isset($_POST['Submit'])) {
// Activation de la protection robot
$maSession->setFlag();
$msgErreur = '<div class="alert alert-danger">Votre adresse mail n\'est pas valide !<br /><pre>' . $_POST['userMail'] . '</pre></div>';
}
} elseif (!isset($_POST['Submit'])) {
// Premier affichage de la page => activation de la protection robot
$maSession->setFlag();
}
?>
<?php if ($maSession->checkFlag()) : ?>
<?php if (!isset($_POST['Submit']) || $maSession->checkFlag()) : ?>
<h1 class="mb-3"><small>Signaler une image</small></h1>
<?= $msgErreur ?>
<form method="post">
<div class="mb-3 form-floating">
<input type="text" class="form-control" name="urlImage" id="urlImage" required="required" value="<?= (isset($_POST['urlImage'])) ? $_POST['urlImage'] : '' ?>">
<input type="text" class="form-control" name="urlImage" id="urlImage" required="required" value="<?= $_POST['urlImage'] ?? '' ?>">
<label for="urlImage">Adresse de l'image</label>
<div class="form-text text-muted">
Indiquer toute l'URL de l'image, telle qu'affichée dans le navigateur (<?= _URL_IMAGES_ . _IMAGE_BAN_ ?>).
@ -97,21 +112,22 @@ if (isset($_POST['Submit']) && $maSession->checkFlag()) {
<select name="raison" id="raison" class="form-select" required="required">
<option value="" selected>-- Sélectionner une raison --</option>
<option value="porno">Pornographie et érotisme</option>
<option value="phishing">Spam et phishing</option>
<option value="legislation">Non respect de la législation française (à préciser)</option>
<option value="autre">Autre (à préciser)</option>
</select>
<label for="raison">Raison du signalement</label>
</div>
<div class="mb-3 form-floating">
<input type="email" class="form-control" name="userMail" id="userMail" required="required" value="<?= (isset($_POST['userMail'])) ? $_POST['userMail'] : '' ?>">
<input type="email" class="form-control" name="userMail" id="userMail" required="required" value="<?= $_POST['userMail'] ?? '' ?>">
<label for="userMail">Votre adresse courriel</label>
<div class="form-text text-muted">Sera utilisée uniquement pour vous apporter une réponse.</div>
</div>
<div class="mb-3 form-floating">
<textarea class="form-control" rows="5" name="userMessage" id="userMessage" placeholder="Informations complémentaires sur la raison de votre demande" required="required"><?= (isset($_POST['userMessage'])) ? $_POST['userMessage'] : '' ?></textarea>
<textarea class="form-control" rows="5" name="userMessage" id="userMessage" placeholder="Informations complémentaires sur la raison de votre demande" required="required"><?= $_POST['userMessage'] ?? '' ?></textarea>
<label for="userMessage">Votre message</label>
</div>
<button type="submit" name="Submit" class="btn btn-success">Envoyer</button>
</form>
<?php endif; ?>
<?php require _TPL_BOTTOM_ ?>
<?php require _TPL_BOTTOM_ ?>

59
admin/abuse-network.php Normal file
View file

@ -0,0 +1,59 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
if (!defined('_PHPUNIT_')) {
require '../config/config.php';
}
// Vérification des droits d'accès
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
$listeIp = HelperAdmin::getBadNetworks();
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Réseaux IP suspects</small></h1>
<div class="card">
<div class="card-header">
<?= count($listeIp) ?> réseau<?= (count($listeIp) > 1 ? 'x' : '') ?> d'addresses IP
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Réseau</th>
<th>Nombre d'images bloquées</th>
</tr>
</thead>
<tbody id="tbody">
<?php foreach ((array)$listeIp as $key => $value) : ?>
<tr>
<td><?= $key ?></td>
<td><?= $value ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php require _TPL_BOTTOM_; ?>

180
admin/abuse.php Normal file
View file

@ -0,0 +1,180 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
if (!defined('_PHPUNIT_')) {
require '../config/config.php';
}
// Vérification des droits d'accès
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Gestion des abus</small></h1>
<?php
$message = '';
$tabTables = [];
// Action à effectuer sur une image
if (isset($_GET['idImage']) && is_numeric($_GET['idImage'])) {
$monImage = new ImageObject($_GET['idImage'], RessourceObject::SEARCH_BY_ID);
if (isset($_GET['bloquer'])) {
// Blocage de l'image
$monImage->bloquer();
$message .= 'Image ' . $monImage->getNomNouveau() . ' bloquée !';
} elseif (isset($_GET['approuver'])) {
// Approbation de l'image
$monImage->approuver();
$message .= 'Image ' . $monImage->getNomNouveau() . ' approuvée !';
}
} else {
/**
* Images à traiter
*/
// Liste des images avec un ratio d'affichage incohérent
$tabTables[] = [
'legende' => 'affichée## > ' . _ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ . ' fois/jour <small>(blocage automatique à ' . _ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_ . '</small>)',
'values' => HelperAdmin::getImagesTropAffichees(_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_)
];
// Liste des images avec un ratio d'affichage incohérent EN PROJECTION
$tabTables[] = [
'legende' => 'projetée## avec un nombre d\'affichages > ' . _ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ . ' fois/jour <small>(blocage automatique à ' . _ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_ . '</small>)',
'values' => HelperAdmin::getImagesTropAffichees(_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_, false, true)
];
// Liste des images suspectes avec un ratio d'affichage incohérent
$tabTables[] = [
'legende' => '<b>suspecte##</b> affichée## > ' . (_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_) . ' fois/jour <small>(blocage automatique à ' . (_ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_) . '</small>)',
'values' => HelperAdmin::getImagesTropAffichees((_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_), true)
];
// Liste des images suspectes avec un ratio d'affichage incohérent EN PROJECTION
$tabTables[] = [
'legende' => '<b>suspecte##</b> projetée## avec un nombre d\'affichages > ' . (_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_) . ' fois/jour <small>(blocage automatique à ' . (_ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_) . '</small>)',
'values' => HelperAdmin::getImagesTropAffichees((_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_), true, true)
];
// Liste des images signalées
$tabTables[] = [
'legende' => 'signalée##',
'values' => HelperAdmin::getImagesSignalees()
];
// Liste des images suspectes
$tabTables[] = [
'legende' => 'suspecte##',
'values' => HelperAdmin::getImagesPotentiellementIndesirables()
];
// Liste des images approuvables
$tabTables[] = [
'legende' => 'approuvable##',
'values' => HelperAdmin::getImagesPotentiellementApprouvables()
];
// Liste de TOUTES les images avec un ratio d'affichage abusif
$tabTables[] = [
'legende' => 'affichée## > ' . _ABUSE_NB_AFFICHAGES_PAR_JOUR_ABUSIF_ . ' fois/jour',
'values' => HelperAdmin::getImagesTropAffichees(_ABUSE_NB_AFFICHAGES_PAR_JOUR_ABUSIF_, false, true, true)
];
}
/**
* Recherche
*/
$tabSearch = [
'Adresse IP' => 'SELECT new_name FROM images WHERE remote_addr LIKE \'%##value##%\' ORDER BY id DESC',
'Nom originel' => 'SELECT new_name FROM images WHERE old_name LIKE \'%##value##%\' ORDER BY id DESC',
'Nouveau nom' => 'SELECT new_name FROM images WHERE new_name LIKE \'%##value##%\' ORDER BY id DESC',
'Utilisateur' => 'SELECT im.new_name FROM images im LEFT JOIN possede po ON po.images_id = im.id WHERE po.membres_id = \'##value##\' ORDER BY im.id DESC',
'Bloquée' => 'SELECT new_name FROM images WHERE isBloquee = \'1\' ORDER BY id DESC',
'Approuvée' => 'SELECT new_name FROM images WHERE isApprouvee = \'1\' ORDER BY id DESC',
];
if (isset($_POST['Submit']) && !empty($_POST['champ']) && !empty($_POST['valeur'])) {
$reqValue = trim(str_replace('\'', '_', $_POST['valeur']));
$req = str_replace('##value##', $reqValue, $tabSearch[$_POST['champ']]);
array_unshift($tabTables, [
'legende' => 'trouvée## -> recherche sur le champ "' . $_POST['champ'] . '" = "' . $_POST['valeur'] . '"',
'values' => HelperAdmin::queryOnNewName($req)
]);
}
?>
<?php if (!empty($message)) : ?>
<div class="alert alert-success">
<?= $message ?>
</div>
<?php endif; ?>
<div class="alert alert-info">
<form method="post">
<div class="mb-3 form-floating">
<select name="champ" id="champ" class="form-select" required="required">
<option value="" selected>-- Sélectionner un champ --</option>
<?php foreach (array_keys($tabSearch) as $key) : ?>
<option value="<?= $key ?>"><?= $key ?></option>
<?php endforeach; ?>
</select>
<label for="champ">Champ à utiliser</label>
</div>
<div class="mb-3 form-floating">
<input type="text" class="form-control" name="valeur" id="valeur" required="required" value="">
<label for="valeur">Valeur recherchée</label>
</div>
<button type="submit" name="Submit" class="btn btn-success">Rechercher</button>
</form>
</div>
<?php foreach ($tabTables as $uneTable) : ?>
<?php $mesImages = ImageObject::chargerMultiple($uneTable['values'], RessourceObject::SEARCH_BY_NAME);?>
<div class="card">
<div class="card-header">
<?= count($uneTable['values']) ?> image<?= (count($uneTable['values']) > 1 ? 's' : '') . ' ' . str_replace('##', (count($uneTable['values']) > 1 ? 's' : ''), $uneTable['legende']) ?>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Image</th>
<th>Actions</th>
<th class="text-break">Nom originel</th>
<th class="text-break">Date d'envoi</th>
<th class="text-break">IP envoi</th>
<th class="text-break">Nb vues</th>
<th class="text-break">Dernier affichage</th>
<th class="text-break">Utilisateur</th>
</tr>
</thead>
<tbody>
<?php foreach ($mesImages as $uneImage) : ?>
<tr>
<td><a href="<?= $uneImage->getURL(true) ?>?forceDisplay=1" target="_blank" style="<?= ($uneImage->isBloquee() ? 'text-decoration: line-through double red;' : '') . ($uneImage->isApprouvee() ? 'text-decoration: underline double green;' : '') ?>"><?= $uneImage->getNomNouveau() ?></a></td>
<td>
<a href="<?= _URL_ADMIN_ ?>abuse.php?approuver=1&idImage=<?= $uneImage->getId() ?>" title="Approuver"><span class="bi-hand-thumbs-up-fill" style="color: green"></span></a>
<a href="<?= _URL_ADMIN_ ?>abuse.php?bloquer=1&idImage=<?= $uneImage->getId() ?>" title="Bloquer"><span class="bi-hand-thumbs-down-fill" style="color: red"></span></a>
</td>
<td class="text-break"><?= $uneImage->getNomOriginalFormate() ?></td>
<td class="text-break"><?= $uneImage->getDateEnvoiFormatee() ?></td>
<td class="text-break"><?= $uneImage->getIpEnvoi() ?></td>
<td class="text-break"><?= $uneImage->getNbViewTotal() ?><small> (<?= $uneImage->getNbViewPerDay() ?>/jour)</small></td>
<td class="text-break"><?= $uneImage->getLastViewFormate() ?></td>
<td class="text-break"><?= $uneImage->getIdProprietaire() ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<br>
<?php endforeach; ?>
<?php require _TPL_BOTTOM_; ?>

View file

@ -0,0 +1,73 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
require '../config/config.php';
// Vérification des droits d'accès
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Nettoyage des comptes jamais utilisés</small></h1>
<?php
$message = '';
// Je récupère la liste des comptes jamais utilisés
$listeComptes = HelperAdmin::getUnusedAccounts();
$isPlural = ($listeComptes->count() > 1 ? 's' : '');
// Si l'effacement est demandé
if (isset($_POST['effacer'])) :
foreach ((array)$listeComptes as $value) {
$message .= '<br />Suppression du compte ' . $value;
// Je crée mon objet et lance la suppression
$monUtilisateur = new UtilisateurObject($value);
$monUtilisateur->supprimer();
}
$message .= '<br />Effacement terminé !';
?>
<div class="alert alert-success">
<?= $message ?>
</div>
<?php else : ?>
<div class="card">
<div class="card-header">
<?= $listeComptes->count() ?> compte<?= $isPlural ?> créé<?= $isPlural ?> il y a au moins <?= _DELAI_EFFACEMENT_COMPTES_JAMAIS_UTILISES_ ?> jour<?= _DELAI_EFFACEMENT_COMPTES_JAMAIS_UTILISES_ > 1 ? 's' : '' ?> et sans images associées
</div>
<div class="card-body">
<ul>
<?php foreach ((array)$listeComptes as $value) : ?>
<li><?= $value ?></li>
<?php endforeach; ?>
</ul>
</div>
<form method="post">
<button class="btn btn-danger" type="submit" name="effacer">
<span class="bi-trash"></span>
&nbsp;Effacer ce<?= $isPlural ?> compte<?= $isPlural ?>
</button>
</form>
</div>
<?php endif; ?>
<?php require _TPL_BOTTOM_; ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -28,47 +28,47 @@ require '../config/config.php';
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Nettoyage des incohérences</small></h1>
<h1 class="mb-3"><small>Nettoyage des incohérences</small></h1>
<?php
$message = '';
// Je récupère la liste des images en BDD
$listeImagesBDD = MetaObject::getAllImagesNameBDD();
$listeImagesBDD = HelperAdmin::getAllImagesNameBDD();
// Je récupère la liste des images sur le HDD
$listeImagesHDD = MetaObject::getAllImagesNameHDD(_PATH_IMAGES_);
$listeImagesHDD = HelperAdmin::getAllImagesNameHDD(_PATH_IMAGES_);
// Je recherche les erreurs sur les images
// Pour chaque images en BDD
$listeErreursImagesBDD = new ArrayObject(array_diff((array) $listeImagesBDD, (array) $listeImagesHDD));
$listeErreursImagesBDD = new ArrayObject(array_diff((array)$listeImagesBDD, (array)$listeImagesHDD));
// Pour chaque images en HDD
$listeErreursImagesHDD = new ArrayObject(array_diff((array) $listeImagesHDD, (array) $listeImagesBDD));
$listeErreursImagesHDD = new ArrayObject(array_diff((array)$listeImagesHDD, (array)$listeImagesBDD));
// Je récupère la liste des miniatures en BDD
$listeMiniaturesBDD = MetaObject::getAllMiniaturesNameBDD();
$listeMiniaturesBDD = HelperAdmin::getAllMiniaturesNameBDD();
// Je récupère la liste des miniatures en HDD
$listeMiniaturesHDD = MetaObject::getAllImagesNameHDD(_PATH_MINIATURES_);
$listeMiniaturesHDD = HelperAdmin::getAllImagesNameHDD(_PATH_MINIATURES_);
// Je recherche les erreurs sur les miniatures
// Pour chaque miniatures en BDD
$listeErreursMiniaturesBDD = new ArrayObject(array_diff((array) $listeMiniaturesBDD, (array) $listeMiniaturesHDD));
$listeErreursMiniaturesBDD = new ArrayObject(array_diff((array)$listeMiniaturesBDD, (array)$listeMiniaturesHDD));
// Pour chaque miniatures en HDD
$listeErreursMiniaturesHDD = new ArrayObject(array_diff((array) $listeMiniaturesHDD, (array) $listeMiniaturesBDD));
$listeErreursMiniaturesHDD = new ArrayObject(array_diff((array)$listeMiniaturesHDD, (array)$listeMiniaturesBDD));
// Si l'effacement est demandé
if (isset($_POST['effacer'])) :
// Images uniquement en BDD
foreach ((array) $listeErreursImagesBDD as $value) {
foreach ((array)$listeErreursImagesBDD as $value) {
$message .= '<br />Suppression de la BDD de l\'image ' . $value;
// Je crée mon objet et lance la suppression
$monImage = new ImageObject($value);
$monImage = new ImageObject($value, RessourceObject::SEARCH_BY_MD5);
$monImage->supprimer();
}
// Images uniquement en HDD
foreach ((array) $listeErreursImagesHDD as $value) {
foreach ((array)$listeErreursImagesHDD as $value) {
$message .= '<br />Suppression du disque de l\'image ' . $value;
// Suppression du fichier
@ -77,16 +77,16 @@ if (isset($_POST['effacer'])) :
unlink($pathFinal);
}
// Miniatures uniquement en BDD
foreach ((array) $listeErreursMiniaturesBDD as $value) {
foreach ((array)$listeErreursMiniaturesBDD as $value) {
$message .= '<br />Suppression de la BDD de la miniature ' . $value;
// Je crée mon objet et lance la suppression
$maMiniature = new MiniatureObject($value);
$maMiniature = new MiniatureObject($value, RessourceObject::SEARCH_BY_MD5);
$maMiniature->supprimer();
}
// Miniatures uniquement en HDD
foreach ((array) $listeErreursMiniaturesHDD as $value) {
foreach ((array)$listeErreursMiniaturesHDD as $value) {
$message .= '<br />Suppression du disque de la miniature ' . $value;
// Suppression du fichier
@ -103,14 +103,11 @@ if (isset($_POST['effacer'])) :
<?php else : ?>
<div class="card">
<div class="card-header">
<?= $listeErreursImagesBDD->count() ?>
image<?= ($listeErreursImagesBDD->count() > 1) ? 's' : '' ?>
présente<?= ($listeErreursImagesBDD->count() > 1) ? 's' : '' ?>
uniquement en BDD
<?= $listeErreursImagesBDD->count() ?> image<?= ($listeErreursImagesBDD->count() > 1) ? 's' : '' ?> présente<?= ($listeErreursImagesBDD->count() > 1) ? 's' : '' ?> uniquement en BDD
</div>
<div class="card-body">
<ul>
<?php foreach ((array) $listeErreursImagesBDD as $value) : ?>
<?php foreach ((array)$listeErreursImagesBDD as $value) : ?>
<li><?= $value ?></li>
<?php endforeach; ?>
</ul>
@ -118,14 +115,11 @@ if (isset($_POST['effacer'])) :
</div>
<div class="card">
<div class="card-header">
<?= $listeErreursImagesHDD->count() ?>
image<?= ($listeErreursImagesHDD->count() > 1) ? 's' : '' ?>
présente<?= ($listeErreursImagesHDD->count() > 1) ? 's' : '' ?>
uniquement sur HDD
<?= $listeErreursImagesHDD->count() ?> image<?= ($listeErreursImagesHDD->count() > 1) ? 's' : '' ?> présente<?= ($listeErreursImagesHDD->count() > 1) ? 's' : '' ?> uniquement sur HDD
</div>
<div class="card-body">
<ul>
<?php foreach ((array) $listeErreursImagesHDD as $value) : ?>
<?php foreach ((array)$listeErreursImagesHDD as $value) : ?>
<li><?= $value ?></li>
<?php endforeach; ?>
</ul>
@ -133,14 +127,11 @@ if (isset($_POST['effacer'])) :
</div>
<div class="card">
<div class="card-header">
<?= $listeErreursMiniaturesBDD->count() ?>
miniature<?= ($listeErreursMiniaturesBDD->count() > 1) ? 's' : '' ?>
présente<?= ($listeErreursMiniaturesBDD->count() > 1) ? 's' : '' ?>
uniquement en BDD
<?= $listeErreursMiniaturesBDD->count() ?> miniature<?= ($listeErreursMiniaturesBDD->count() > 1) ? 's' : '' ?> présente<?= ($listeErreursMiniaturesBDD->count() > 1) ? 's' : '' ?> uniquement en BDD
</div>
<div class="card-body">
<ul>
<?php foreach ((array) $listeErreursMiniaturesBDD as $value) : ?>
<?php foreach ((array)$listeErreursMiniaturesBDD as $value) : ?>
<li><?= $value ?></li>
<?php endforeach; ?>
</ul>
@ -148,14 +139,11 @@ if (isset($_POST['effacer'])) :
</div>
<div class="card">
<div class="card-header">
<?= $listeErreursMiniaturesHDD->count() ?>
miniature<?= ($listeErreursMiniaturesHDD->count() > 1) ? 's' : '' ?>
présente<?= ($listeErreursMiniaturesHDD->count() > 1) ? 's' : '' ?>
uniquement sur HDD
<?= $listeErreursMiniaturesHDD->count() ?> miniature<?= ($listeErreursMiniaturesHDD->count() > 1) ? 's' : '' ?> présente<?= ($listeErreursMiniaturesHDD->count() > 1) ? 's' : '' ?> uniquement sur HDD
</div>
<div class="card-body">
<ul>
<?php foreach ((array) $listeErreursMiniaturesHDD as $value) : ?>
<?php foreach ((array)$listeErreursMiniaturesHDD as $value) : ?>
<li><?= $value ?></li>
<?php endforeach; ?>
</ul>
@ -163,10 +151,9 @@ if (isset($_POST['effacer'])) :
</div>
<form method="post">
<button class="btn btn-danger" type="submit" name="effacer">
<span class="fas fa-trash"></span>
&nbsp;
Supprimer les incohérences
<span class="bi-trash"></span>
&nbsp;Supprimer les incohérences
</button>
</form>
<?php endif; ?>
<?php require _TPL_BOTTOM_; ?>
<?php require _TPL_BOTTOM_; ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -22,53 +22,54 @@
namespace ImageHeberg;
require '../config/config.php';
// Vérification des droits d'accès
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Nettoyage des fichiers jamais utilisés</small></h1>
<h1 class="mb-3"><small>Nettoyage des fichiers jamais utilisés</small></h1>
<?php
$message = '';
// Je récupère la liste des images jamais affichées
$listeImages = MetaObject::getNeverUsedFiles();
$listeImages = HelperAdmin::getNeverUsedFiles();
$isPlural = ($listeImages->count() > 1 ? 's' : '');
// Si l'effacement est demandé
if (isset($_POST['effacer'])) :
foreach ((array) $listeImages as $value) {
$message .= '<br />Suppression de l\'image ' . $value;
// Je crée mon objet et lance la suppression
$monImage = new ImageObject($value);
$mesImages = ImageObject::chargerMultiple($listeImages, RessourceObject::SEARCH_BY_NAME);
/* @var ImageObject[] $mesImages */
foreach ($mesImages as $monImage) {
$message .= '<br />Suppression de l\'image ' . $monImage->getNomNouveau();
$monImage->supprimer();
}
$message .= '<br />Effacement terminé !';
?>
<div class = "alert alert-success">
<div class="alert alert-success">
<?= $message ?>
</div>
<?php else : ?>
<div class="card">
<div class="card-header">
<?= $listeImages->count() ?> image(s) envoyée(s) il y a
au moins <?= _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ ?> jour(s) et jamais affichée(s)
<?= $listeImages->count() ?> image<?= $isPlural ?> envoyée<?= $isPlural ?> il y a au moins <?= _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ ?> jour<?= _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ > 1 ? 's' : '' ?> et jamais affichée<?= $isPlural ?>
</div>
<div class="card-body">
<ul>
<?php foreach ((array) $listeImages as $value) : ?>
<li><?= $value ?></li>
<?php foreach ((array)$listeImages as $value) : ?>
<li>
<a href="<?= str_replace('http:', 'https:', _URL_IMAGES_) . $value ?>?forceDisplay=1" target="_blank"><?= $value ?></a>
</li>
<?php endforeach; ?>
</ul>
</div>
<form method="post">
<button class="btn btn-danger" type="submit" name="effacer">
<span class="fas fa-trash"></span>
&nbsp;
Effacer ces fichiers
<span class="bi-trash"></span>
&nbsp;Effacer ce<?= $isPlural ?> fichier<?= $isPlural ?>
</button>
</form>
</div>
<?php endif; ?>
<?php require _TPL_BOTTOM_; ?>
<?php require _TPL_BOTTOM_; ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -22,25 +22,26 @@
namespace ImageHeberg;
require '../config/config.php';
// Vérification des droits d'accès
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Nettoyage des fichiers dormants</small></h1>
<h1 class="mb-3"><small>Nettoyage des fichiers dormants</small></h1>
<?php
$message = '';
// Je récupère la liste des images non affichées depuis xx jours
$listeImages = MetaObject::getUnusedFiles();
$listeImages = HelperAdmin::getUnusedFiles();
$isPlural = ($listeImages->count() > 1 ? 's' : '');
// Si l'effacement est demandé
if (isset($_POST['effacer'])) :
foreach ((array) $listeImages as $value) {
$message .= '<br />Suppression de l\'image ' . $value;
// Je crée mon objet et lance la suppression
$monImage = new ImageObject($value);
$mesImages = ImageObject::chargerMultiple($listeImages, RessourceObject::SEARCH_BY_NAME);
/* @var ImageObject[] $mesImages */
foreach ($mesImages as $monImage) {
$message .= '<br />Suppression de l\'image ' . $monImage->getNomNouveau();
$monImage->supprimer();
}
$message .= '<br />Effacement terminé !';
@ -51,24 +52,24 @@ if (isset($_POST['effacer'])) :
<?php else : ?>
<div class="card">
<div class="card-header">
<?= $listeImages->count() ?> image(s) non affichée(s)
depuis au moins <?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ ?> jour(s).
<?= $listeImages->count() ?> image<?= $isPlural ?> non affichée<?= $isPlural ?> depuis au moins <?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ ?> jour<?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ > 1 ? 's' : '' ?>.
</div>
<div class="card-body">
<ul>
<?php foreach ((array) $listeImages as $value) : ?>
<li><?= $value ?></li>
<?php foreach ((array)$listeImages as $value) : ?>
<li>
<a href="<?= str_replace('http:', 'https:', _URL_IMAGES_) . $value ?>?forceDisplay=1" target="_blank"><?= $value ?></a>
</li>
<?php endforeach; ?>
</ul>
</div>
<form method="post">
<button class="btn btn-danger" type="submit" name="effacer">
<span class="fas fa-trash"></span>
&nbsp;
Effacer ces fichiers
<span class="bi-trash"></span>
&nbsp;Effacer ce<?= $isPlural ?> fichier<?= $isPlural ?>
</button>
</form>
</div>
<?php endif; ?>
<?php require _TPL_BOTTOM_; ?>
<?php require _TPL_BOTTOM_; ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -26,54 +26,77 @@ require '../config/config.php';
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Panneau d'administration</small></h1>
<div class="card">
<div class="card-header">
Gestion du site
<h1 class="mb-3"><small>Panneau d'administration</small></h1>
<div class="card">
<div class="card-header">
Gestion du site
</div>
<div class="card-body">
<a href="<?= _URL_ADMIN_ ?>abuse.php" class="btn btn-success">
<span class="bi-radioactive"></span>
&nbsp;Images à vérifier
</a>
<div class="clearfix"></div>
<br/>
<a href="<?= _URL_ADMIN_ ?>lastUpload.php" class="btn btn-success">
<span class="bi-file-earmark-image"></span>
&nbsp;Derniers fichiers envoyés
</a>
<div class="clearfix"></div>
<br/>
<a href="<?= _URL_ADMIN_ ?>nsfwjs.php" class="btn btn-success">
<span class="bi-file-earmark-bar-graph"></span>
&nbsp;IA - Catégorisation nsfwjs
</a>
<div class="clearfix"></div>
<br/>
<a href="<?= _URL_ADMIN_ ?>abuse-network.php" class="btn btn-success">
<span class="bi-cpu"></span>
&nbsp;Réseaux suspects
</a>
<div class="clearfix"></div>
<br/>
<a href="<?= _URL_ADMIN_ ?>validate.php" class="btn btn-success">
<span class="bi-cloud-check"></span>
&nbsp;Vérifier la configuration
</a>
<div class="clearfix"></div>
<br/>
<a href="<?= _URL_ADMIN_ ?>cleanFilesNeverUsed.php" class="btn btn-warning">
<span class="bi-file-earmark-x"></span>
&nbsp;Supprimer les images jamais affichées et envoyées depuis plus de <?= _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ ?> jour<?= (_DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ > 1) ? 's' : '' ?>
</a>
<div class="clearfix"></div>
<br/>
<a href="<?= _URL_ADMIN_ ?>cleanInactiveFiles.php" class="btn btn-warning">
<span class="bi-file-earmark-x-fill"></span>
&nbsp;Supprimer les images non affichées depuis plus de <?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ ?> jour<?= (_DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ > 1) ? 's' : '' ?>
</a>
<div class="clearfix"></div>
<br/>
<a href="<?= _URL_ADMIN_ ?>cleanAccountsNeverUsed.php" class="btn btn-warning">
<span class="bi-person-x"></span>
&nbsp;Supprimer les comptes créés il y a plus de <?= _DELAI_EFFACEMENT_COMPTES_JAMAIS_UTILISES_ ?> jour<?= (_DELAI_EFFACEMENT_COMPTES_JAMAIS_UTILISES_ > 1) ? 's' : '' ?> et sans images associées
</a>
</div>
</div>
<div class="card-body">
<a href="<?= _URL_ADMIN_ ?>validate.php" class="btn btn-success">
<span class="fas fa-list-alt"></span>
&nbsp;
Vérifier la configuration
</a>
<div class="clearfix"></div>
<br />
<a href="<?= _URL_ADMIN_ ?>cleanFilesNeverUsed.php" class="btn btn-warning">
<span class="fas fa-trash"></span>
&nbsp;
Supprimer les images jamais affichées et envoyées depuis
plus de <?= _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ ?>
jour<?= (_DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ > 1 ) ? 's' : '' ?>
</a>
<div class="clearfix"></div>
<br />
<a href="<?= _URL_ADMIN_ ?>cleanInactiveFiles.php" class="btn btn-warning">
<span class="fas fa-trash-alt"></span>
&nbsp;
Supprimer les images non affichées depuis
plus de <?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ ?>
jour<?= (_DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ > 1 ) ? 's' : '' ?>
</a>
<div class="card">
<div class="card-header">
Gestion technique
</div>
<div class="card-body">
<a href="<?= _URL_ADMIN_ ?>listeFichiers.php" class="btn btn-default">
<span class="bi-list-check"></span>
&nbsp;
Lister les fichiers présents sur le disque
</a>
<div class="clearfix"></div>
<br/>
<a href="<?= _URL_ADMIN_ ?>cleanErrors.php" class="btn btn-default">
<span class="bi-database-fill-check"></span>
&nbsp;
Vérifier la cohérence disque et BDD
</a>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
Gestion technique
</div>
<div class="card-body">
<a href="<?= _URL_ADMIN_ ?>listeFichiers.php" class="btn btn-default">
<span class="fas fa-list-alt"></span>
&nbsp;
Lister les fichiers présents sur le disque
</a>
<div class="clearfix"></div>
<br />
<a href="<?= _URL_ADMIN_ ?>cleanErrors.php" class="btn btn-default">
<span class="fas fa-check"></span>
&nbsp;
Vérifier la cohérence disque et BDD
</a>
</div>
</div>
<?php require _TPL_BOTTOM_ ?>
<?php require _TPL_BOTTOM_ ?>

134
admin/lastUpload.php Normal file
View file

@ -0,0 +1,134 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
use ArrayObject;
if (!defined('_PHPUNIT_')) {
require '../config/config.php';
}
// Vérification des droits d'accès
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Derniers fichiers envoyés</small></h1>
<?php
$message = '';
$table = [
'legende' => 'trouvée##',
'values' => new ArrayObject(),
];
// Action à effectuer sur une image
if (isset($_GET['idImage']) && is_numeric($_GET['idImage'])) {
$monImage = new ImageObject($_GET['idImage'], RessourceObject::SEARCH_BY_ID);
if (isset($_GET['bloquer'])) {
// Blocage de l'image
$monImage->bloquer();
$message .= 'Image ' . $monImage->getNomNouveau() . ' bloquée !';
} elseif (isset($_GET['approuver'])) {
// Approbation de l'image
$monImage->approuver();
$message .= 'Image ' . $monImage->getNomNouveau() . ' approuvée !';
}
} else {
/**
* Images à traiter
*/
$idStart = 0;
if (!empty($_GET['nextId']) && preg_match('#^[0-9]+$#', $_GET['nextId'])) {
$idStart = (int)$_GET['nextId'];
}
$req = 'SELECT MAX(new_name) as new_name FROM images' . ($idStart !== 0 ? ' WHERE id < ' . $idStart : '') . ' GROUP BY md5 ORDER BY date_action DESC LIMIT 50';
$table['values'] = HelperAdmin::queryOnNewName($req);
}
$isPlural = false;
if (count($table['values']) > 1) {
$isPlural = true;
}
$lastId = '';
// Charger les objets concernés
$mesImages = ImageObject::chargerMultiple($table['values'], RessourceObject::SEARCH_BY_NAME, false);
?>
<?php if (!empty($message)) : ?>
<div class="alert alert-success">
<?= $message ?>
</div>
<?php endif; ?>
<div class="card">
<div class="card-header">
<?= count($table['values']) ?> image<?= ($isPlural ? 's' : '') . ' ' . str_replace('##', ($isPlural ? 's' : ''), $table['legende']) ?>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Image</th>
<th>Actions</th>
<th class="text-break">Nom originel</th>
<th class="text-break">Date d'envoi</th>
<th class="text-break">IP envoi</th>
<th class="text-break">Nb vues</th>
<th class="text-break">Dernier affichage</th>
<th class="text-break">Utilisateur</th>
</tr>
</thead>
<tbody>
<?php foreach ($mesImages as $uneImage) : ?>
<tr>
<td>
<?= (($uneImage->isSuspecte() || $uneImage->isSignalee()) && !$uneImage->isBloquee() && !$uneImage->isApprouvee() ? '<span class="bi-exclamation-square-fill" style="color:red;"></span>' : '') ?>
<?php if (!$uneImage->isApprouvee()): ?>
<?php if ($uneImage->getNbViewPerDay() >= _ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_): ?>
<span class="bi-exclamation-diamond" style="color: orange"></span>
<?php elseif ($uneImage->getNbViewPerDay() >= _ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_): ?>
<span class="bi-exclamation-circle" style="color: lightblue"></span>
<?php endif; ?>
<?php endif; ?>
<a href="<?= $uneImage->getURL(true) ?>?forceDisplay=1" target="_blank" style="<?= ($uneImage->isBloquee() ? 'text-decoration: line-through double red;' : '') ?><?= ($uneImage->isApprouvee() ? 'text-decoration: underline double green;' : '') ?>"><?= $uneImage->getNomNouveau() ?></a></td>
<td>
<a href="<?= _URL_ADMIN_ ?>lastUpload.php?approuver=1&idImage=<?= $uneImage->getId() . ($idStart !== 0 ? '&nextId=' . $idStart : "") ?>" title="Approuver"><span class="bi-hand-thumbs-up-fill" style="color: green"></span></a>
<a href="<?= _URL_ADMIN_ ?>lastUpload.php?bloquer=1&idImage=<?= $uneImage->getId() . ($idStart !== 0 ? '&nextId=' . $idStart : "") ?>" title="Bloquer"><span class="bi-hand-thumbs-down-fill" style="color: red"></span></a>
</td>
<td class="text-break"><?= $uneImage->getNomOriginalFormate() ?></td>
<td class="text-break"><?= $uneImage->getDateEnvoiFormatee() ?></td>
<td class="text-break"><?= $uneImage->getIpEnvoi() ?></td>
<td><?= $uneImage->getNbViewTotal() ?><small> (<?= $uneImage->getNbViewPerDay() ?>/jour)</small></td>
<td class="text-break"><?= $uneImage->getLastViewFormate() ?></td>
<td class="text-break"><?= $uneImage->getIdProprietaire() ?></td>
</tr>
<?php $lastId = $uneImage->getId() ?>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<th>
<a href="<?= _URL_ADMIN_ ?>lastUpload.php?nextId=<?= $lastId ?>" class="btn btn-primary"><span class="bi-arrow-left"></span> </a>
</th>
</tr>
</tfoot>
</table>
</div>
</div>
<?php require _TPL_BOTTOM_; ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -26,35 +26,36 @@ require '../config/config.php';
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Images présentes sur le disque</small></h1>
<ul>
<?php
<h1 class="mb-3"><small>Images présentes sur le disque</small></h1>
<ul>
<?php
/**
* Scan récursif
* @param string $path
* @return string
*/
function getScandirRecursif($path)
{
$monRetour = '<ul>';
/**
* Scan récursif
* @param string $path
* @return string
*/
function getScandirRecursif(string $path): string
{
$monRetour = '<ul>';
// Scanne le répertoire fourni
$scan_rep = scandir($path);
// Pour chaque item
foreach ($scan_rep as $item) {
if ($item !== '.' && $item !== '..') {
$monRetour .= '<li>' . $path . $item . '</li>';
if (is_dir($path . $item)) {
// Appel récursif
$monRetour .= getScandirRecursif($path . $item . '/');
// Scanne le répertoire fourni
$scan_rep = scandir($path);
// Pour chaque item
foreach ($scan_rep as $item) {
if ($item !== '.' && $item !== '..') {
$monRetour .= '<li>' . $path . $item . '</li>';
if (is_dir($path . $item)) {
// Appel récursif
$monRetour .= getScandirRecursif($path . $item . '/');
}
}
}
$monRetour .= '</ul>';
return $monRetour;
}
$monRetour .= '</ul>';
return $monRetour;
}
echo getScandirRecursif(_PATH_IMAGES_);
?>
</ul>
<?php require _TPL_BOTTOM_ ?>
echo getScandirRecursif(_PATH_IMAGES_);
?>
</ul>
<?php require _TPL_BOTTOM_ ?>

139
admin/nsfwjs.php Normal file
View file

@ -0,0 +1,139 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
if (!defined('_PHPUNIT_')) {
require '../config/config.php';
}
// Vérification des droits d'accès
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>NSFWJS</small></h1>
<?php
$message = '';
// Action à effectuer sur une image
if (isset($_GET['idImage']) && is_numeric($_GET['idImage'])) {
$monImage = new ImageObject($_GET['idImage'], RessourceObject::SEARCH_BY_ID);
if (isset($_GET['bloquer'])) {
// Blocage de l'image
$monImage->bloquer();
$message .= 'Image ' . $monImage->getNomNouveau() . ' bloquée !';
} elseif (isset($_GET['approuver'])) {
// Approbation de l'image
$monImage->approuver();
$message .= 'Image ' . $monImage->getNomNouveau() . ' approuvée !';
}
}
/**
* Images à traiter
*/
$idStart = 0;
if (!empty($_GET['nextId']) && is_numeric($_GET['nextId'])) {
$idStart = trim(str_replace('\'', '_', $_GET['nextId']));
}
$req = 'SELECT new_name FROM images' . ($idStart !== 0 ? ' WHERE id < ' . $idStart : '') . ' ORDER BY id DESC LIMIT 50';
$table = [
'legende' => 'trouvée##',
'values' => HelperAdmin::queryOnNewName($req)
];
// Charger les objets concernés
$mesImages = ImageObject::chargerMultiple($table['values'], RessourceObject::SEARCH_BY_NAME);
?>
<?php if (!empty($message)) : ?>
<div class="alert alert-success">
<?= $message ?>
</div>
<?php endif; ?>
<div class="card">
<div class="card-header">
<?= count($table['values']) ?> image<?= (count($table['values']) > 1 ? 's' : '') . ' ' . str_replace('##', (count($table['values']) > 1 ? 's' : ''), $table['legende']) ?>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>Image</th>
<th>Actions</th>
<th>Date d'envoi</th>
<th>Catégorisation IA nsfwjs</th>
</tr>
</thead>
<tbody id="tbody">
<?php foreach ($mesImages as $uneImage) : ?>
<tr>
<td><a href="<?= $uneImage->getURL(true) ?>?forceDisplay=1" target="_blank" style="<?= ($uneImage->isBloquee() ? 'text-decoration: line-through double red;' : '') . ($uneImage->isApprouvee() ? 'text-decoration: underline double green;' : '') ?>"><?= $uneImage->getNomNouveau() ?></a></td>
<td>
<a href="<?= _URL_ADMIN_ ?>lastUpload.php?approuver=1&idImage=<?= $uneImage->getId() ?>" title="Approuver"><span class="bi-hand-thumbs-up-fill" style="color: green"></span></a>
<a href="<?= _URL_ADMIN_ ?>lastUpload.php?bloquer=1&idImage=<?= $uneImage->getId() ?>" title="Bloquer"><span class="bi-hand-thumbs-down-fill" style="color: red"></span></a>
</td>
<td><?= $uneImage->getDateEnvoiFormatee() ?></td>
<td></td>
</tr>
<?php endforeach; ?>
</tbody>
<tfoot>
<tr>
<th>
<a href="<?= _URL_ADMIN_ ?>nsfwjs.php?nextId=<?= $uneImage->getId() ?>" class="btn btn-primary"><span class="bi-arrow-left"></span> </a>
</th>
</tr>
</tfoot>
</table>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest/dist/tf.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/nsfwjs"></script>
<script type="module">
// Forcer le mode "Production" de tensorflow
tf.enableProdMode();
// Modèle récupéré de https://github.com/infinitered/nsfwjs/tree/master/example/nsfw_demo/public/quant_mid
const model = await nsfwjs.load('<?=_URL_ADMIN_ ?>nsfwjs_model_quant_mid/', {type: 'graph'});
// On itère sur le tableau HTML
let tabTr = Array.from(document.getElementById("tbody").children);
for (const unTr of tabTr) {
let img = new Image();
// Lien vers l'image
img.src = unTr.children[0].children[0].href;
console.log(img.src);
// Attendre que la ressource soit chargée
await img.decode();
// Classify the image -> une seule classification attendue en résultat
const predictions = await model.classify(img, 1);
console.log('Predictions: ', predictions);
// Résultats
let results = '';
for (let unType of predictions) {
results += unType.className + ' -> ' + Math.round(unType.probability * 100) + '%<br />';
}
// Injection de la valeur dans la page
unTr.children[3].innerHTML = results;
}
</script>
<?php require _TPL_BOTTOM_; ?>

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -26,53 +26,58 @@ require '../config/config.php';
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Vérification de la configuration du serveur</small></h1>
<div class="card">
<div class="card-header">
PHP
<h1 class="mb-3"><small>Vérification de la configuration du serveur</small></h1>
<div class="card">
<div class="card-header">
PHP
</div>
<div class="card-body">
<?= HelperSysteme::getPhpVersion() ?>
</div>
</div>
<div class="card-body">
<?= MetaObject::getPhpVersion() ?>
<div class="card">
<div class="card-header">
MySQL
</div>
<div class="card-body">
<?= HelperSysteme::getMysqlVersion() ?>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
MySQL
<div class="card">
<div class="card-header">
Imagick
</div>
<div class="card-body">
<?= HelperSysteme::getImagickVersion() ?>
</div>
</div>
<div class="card-body">
<?= MetaObject::getMysqlVersion() ?>
<div class="card">
<div class="card-header">
Accès aux répertoires protégés par .htaccess .
</div>
<div class="card-body">
Les valeurs doivent être de type "HTTP/*.* 403 Forbidden".
<ul>
<li>Répertoire config : <?= HelperSysteme::getStatusHTTP(_URL_CONFIG_) ?></li>
<li>Répertoire admin : <?= HelperSysteme::getStatusHTTP(_URL_ADMIN_) ?></li>
<li>Répertoire membre : <?= HelperSysteme::getStatusHTTP(_URL_MEMBRE_) ?></li>
</ul>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
Accès aux répertoires protégés par .htaccess .
</div>
<div class="card-body">
Les valeurs doivent être de type "HTTP *.* 403 Forbidden".
<ul>
<li>Répertoire config : <?= MetaObject::getStatusHTTP(_URL_CONFIG_) ?></li>
<li>Répertoire admin : <?= MetaObject::getStatusHTTP(_URL_ADMIN_) ?></li>
<li>Répertoire membre : <?= MetaObject::getStatusHTTP(_URL_MEMBRE_) ?></li>
</ul>
</div>
</div>
<div class="card">
<div class="card-header">
Droits sur tous les dossiers dans files/
</div>
<div class="card-body">
<ul>
<?php
<div class="card">
<div class="card-header">
Droits sur tous les dossiers dans files/
</div>
<div class="card-body">
<ul>
<?php
$lesDroits = MetaObject::isRecursivelyWritable(_PATH_IMAGES_);
foreach ((array) $lesDroits as $unItem) :
?>
<li><?= $unItem ?></li>
<?php endforeach; ?>
</ul>
$lesDroits = HelperSysteme::isRecursivelyWritable(_PATH_IMAGES_);
foreach ((array)$lesDroits as $unItem) :
?>
<li><?= $unItem ?></li>
<?php endforeach; ?>
</ul>
</div>
</div>
</div>
<?php
require _TPL_BOTTOM_;
?>
<?php require _TPL_BOTTOM_; ?>

129
cgu.php
View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -24,65 +24,76 @@ namespace ImageHeberg;
require 'config/config.php';
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Conditions Générales d'Utilisation de <?= _SITE_NAME_ ?></small></h1>
<h1 class="mb-3"><small>Conditions Générales d'Utilisation de <?= _SITE_NAME_ ?></small></h1>
<div class="alert alert-success">Ces CGU sont modifiables sans préavis et à tout moment.</div>
<div class="alert alert-success">Ces CGU sont modifiables sans préavis et à tout moment.</div>
<div class="card">
<div class="card-header">
Contenus autorisés
</div>
<div class="card-body">
<ul>
<li>Toutes images de type JPG, PNG, GIF.</li>
<li>Contenu conforme à la législation française.</li>
<li>Pornographie et érotisme non autorisés.</li>
</ul>
</div>
</div>
<div class="card">
<div class="card-header">
Propriétés de l'hébergement
</div>
<div class="card-body">
<ul>
<li>Gratuit.</li>
<li>Traffic illimité <em>(hors abus)</em>.</li>
<li>
<b>Conservation :</b>
<ul>
<li>
<?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ ?>
jour<?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ > 1 ? 's' : '' ?>
à compter de la dernière utilisation du fichier.
</li>
<li>
A défaut, <?= _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ ?>
jour<?= _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ > 1 ? 's' : '' ?>
après l'envoi <em>(si aucun affichage)</em>.
</li>
</ul>
</li>
<li>
Nombre d'images par compte : illimité.
</li>
<li>
Les fichiers restent votre propriété.
</li>
<li>
Aucune suppression sur demande d'un utilisateur.
<em>(utilisez la fonction de suppression à l'envoi ou utilisez l'espace membre)</em>
</li>
<li>
Toutes les données possédées seront fournies en cas de demande judiciaire.
</li>
<li>
Rappel : l'administrateur (<?= _ADMINISTRATEUR_NOM_ ?>) a accès à toutes les données du service.
</li>
</ul>
<div class="card-footer">
<em>Mises à jour le 29 juin 2018 : réduction des durées de conservation des images inactives</em>
<div class="card">
<div class="card-header">
Contenus autorisés
</div>
<div class="card-body">
<ul>
<li>Toutes images de type <?= strtoupper(implode(', ', _ACCEPTED_EXTENSIONS_)) ?>.</li>
<li>Contenus conformes à la législation française.</li>
<li>L'administrateur du site est seul juge de la conformité d'une image aux présentes CGU. En cas de désaccord, vous pouvez le contacter via <a href="/contact.php">le formulaire de contact</a>.</li>
</ul>
</div>
</div>
</div>
<?php require _TPL_BOTTOM_ ?>
<div class="card">
<div class="card-header">
Contenus non autorisés
</div>
<div class="card-body">
<ul>
<li>Contenus non conformes à la législation française.</li>
<li>Pornographie et érotisme.</li>
<li>Scènes de violence (cadavre, torture, maltraitance, image dégradante, ...) ou d'appel à la violence.</li>
<li>Pour information, les images peuvent être analysées par un composant d'IA dédié au service afin d'aider à leur vérification.</li>
</ul>
</div>
</div>
<div class="card">
<div class="card-header">
Propriétés de l'hébergement
</div>
<div class="card-body">
<ul>
<li>Gratuit.</li>
<li>Traffic illimité <em>(hors abus, auquel cas des limitations peuvent être mises en place)</em>.</li>
<li>
<b>Conservation :</b>
<ul>
<li>
<?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ ?> jour<?= _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ > 1 ? 's' : '' ?> à compter de la dernière utilisation du fichier.
</li>
<li>
A défaut, <?= _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ ?> jour<?= _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ > 1 ? 's' : '' ?> après l'envoi <em>(si aucun affichage)</em>.
</li>
</ul>
</li>
<li>
Nombre d'images par compte : illimité.
</li>
<li>
Les fichiers restent votre propriété.
</li>
<li>
Aucune suppression sur demande d'un utilisateur. <em>(utilisez la fonction de suppression à l'envoi ou utilisez <a href="membre/connexionCompte.php">l'espace membre)</a></em>
</li>
<li>
Toutes les données possédées seront fournies en cas de demande judiciaire ou de dépôt de plainte.
</li>
<li>
Suppression des comptes membres qui n'ont jamais été utilisés au bout de <?= _DELAI_EFFACEMENT_COMPTES_JAMAIS_UTILISES_ ?> jour<?= _DELAI_EFFACEMENT_COMPTES_JAMAIS_UTILISES_ > 1 ? 's' : '' ?>.
</li>
<li>
Rappel : <a href="contact.php">l'administrateur (<?= _ADMINISTRATEUR_NOM_ ?>)</a> a accès à toutes les données du service.
</li>
</ul>
<div class="card-footer">
<em>Mises à jour le 02 janvier 2024 : ajout de précisions sur les images non autorisées + module de catégorisation IA.</em>
</div>
</div>
</div>
<?php require _TPL_BOTTOM_ ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -24,266 +24,332 @@ namespace ImageHeberg;
require 'config/config.php';
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Historique des versions</small></h1>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v22">
v2.2 - A venir
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v22" class="card-collapse">
<div class="card-body">
<ul>
<li>Envoi de plusieurs images simultanément.</li>
<li>Glisser - déposer pour le choix des fichiers.</li>
<li>Membres : Albums photos (création, affichage, partage).</li>
<li>Membres : Permettre de définir une valeur par défaut pour les paramètres des images à l'envoi.</li>
<li>Membres : Afficher les liens de partage dans l'espace membre.</li>
<li>Expliquer les avantages pour les personnes inscrites sur le site.</li>
</ul>
<h1 class="mb-3"><small>Historique des versions</small></h1>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v2x">
v2.x - A venir
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v2x" class="card-collapse">
<div class="card-body">
<ul>
<li>Envoi de plusieurs images simultanément.</li>
<li>Glisser - déposer pour le choix des fichiers.</li>
<li>Membres : Albums photos (création, affichage, partage).</li>
<li>Membres : Permettre de définir une valeur par défaut pour les paramètres des images à l'envoi.</li>
<li>Membres : Afficher les liens de partage dans l'espace membre.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v21">
v21 - Février 2021
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v21" class="card-collapse">
<div class="card-body">
<ul>
<li>Migration bootstrap 4 -> 5.</li>
<li>Suppression de jQuery.</li>
<li>Sélection uniquement des images lors du choix du fichier sur l'ordinateur.</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v25">
v2.5 - Janvier 2024
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v25" class="card-collapse">
<div class="card-body">
<ul>
<li>Prise en charge du format WEBP.</li>
<li>Blocage d'accès au site pour les personnes ayant envoyé trop d'images qui ont été bloquées.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v205">
v2.0.5 - 2020
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v205" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Migration bootstrap 3 -> 4.</li>
<li>Utilisation de FontAwesome.</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v24">
v2.4 - Décembre 2023
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v24" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Espace admin : renforcement des systèmes anti-abus.</li>
<li>Meilleure gestion des GIF animés.</li>
<li>Classification des images via IA avec nsfwjs.</li>
<li>Amélioration des performances des requêtes dans la base de données.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v204">
v2.0.4 - Octobre 2019
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v204" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Améliorations techniques de l'outil pour les maintenances futures.</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v23">
v2.3 - Août 2023
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v23" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Espace admin : ajout d'une interface pour gérer les abus.</li>
<li>Renforcement des systèmes anti-abus.</li>
<li>Migration bootstrap 5.1 -> 5.3</li>
<li>Migration fontawesome -> bootstrap icons</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v203">
v2.0.3 - Octobre 2019
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v203" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Améliorations techniques pour MySQL 5.7.</li>
<li>Améliorations techniques pour PHP7.</li>
<li>Ajout d'un outil de validation de l'installation.</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v22">
v2.2 - Décembre 2021
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v22" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Espace membre : ajout des miniatures des images possédées.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v202">
v2.0.2 - Janvier 2019
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v202" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Correction d'une erreur à l'effacement des images.</li>
<li>Migration jQuery 2 => 3</li>
<li>Suppression de la liste des referer <em>(optimisation des affichages d'images)</em>.</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v21">
v2.1 - Février 2021
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v21" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Migration bootstrap 4 -> 5.</li>
<li>Suppression de jQuery.</li>
<li>Sélection uniquement des images lors du choix du fichier sur l'ordinateur.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v201">
v2.0.1 - Juin 2018
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v201" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Ajout d'une liste des referer</li>
<li>Gestion dynamique des délais avant suppression des fichiers inactifs</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v205">
v2.0.5 - 2020
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v205" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Migration bootstrap 3 -> 4.</li>
<li>Utilisation de FontAwesome.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v20">
v2.0 - Novembre 2016
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v20" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Réactivation de l'ensemble des fonctionnalités du site (miniatures, retournement, ...)</li>
<li>Modèle orienté objet pour une meilleure évolutivité sur le long terme</li>
<li>Nouveau thème graphique</li>
<li>Admin : Refonte de l'administration</li>
<li>Admin : création de fonctions pour nettoyer les images obsolètes</li>
<li>Reprise du schéma de la BDD</li>
<li>Mise en place de tests automatisés sur l'ensemble des fonctionnalités du site</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v204">
v2.0.4 - Octobre 2019
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v204" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Améliorations techniques de l'outil pour les maintenances futures.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v19">
v1.9 - Janvier 2014
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v19" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Passage à la programation objet</li>
<li>Reprise, factorisation et optimisation globale du site</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v203">
v2.0.3 - Octobre 2019
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v203" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Améliorations techniques pour MySQL 5.7.</li>
<li>Améliorations techniques pour PHP7.</li>
<li>Ajout d'un outil de validation de l'installation.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v127">
v1.2.7 - 26 avril 2012
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v127" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Remise en place des options miniatures, rotation et redimensionnement</li>
<li>Corrections de charset sur des messages d'erreur</li>
<li>Amélioration de la protection anti-flood</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v202">
v2.0.2 - Janvier 2019
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v202" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Correction d'une erreur à l'effacement des images.</li>
<li>Migration jQuery 2 => 3</li>
<li>Suppression de la liste des referer <em>(optimisation des affichages d'images)</em>.</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v126">
v1.2.6 - 9 janvier 2012
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v126" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Changement de serveur internet (la vitesse d'affichage est meilleure + connectivité IPv6 !)</li>
<li>Changement d'encodage par défaut pour les pages et le code HTML <em>(passage en UTF-8)</em></li>
<li>Gestion de l'IPv6 dans la BDD et les statistiques</li>
<li>Sélection du contenu des champs contenants l'url de l'image au clic</li>
<li>Amélioration du code HTML</li>
<li>Refonte des CSS</li>
<li>Préparation d'un système de templates</li>
<li>Utilisation de jQuery : box en haut de page, options cachées par défaut à l'envoi d'un fichier</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v201">
v2.0.1 - Juin 2018
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v201" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Ajout d'une liste des referer</li>
<li>Gestion dynamique des délais avant suppression des fichiers inactifs</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v125">
v1.2.5 - 30 octobre 2011
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v125" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Suppression de la limite des dimensions d'images pour les utilisateurs enregistrés</li>
<li>Ajout d'une fonction de blocage d'images + affichage d'une image d'information sur le blocage</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v20">
v2.0 - Novembre 2016
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v20" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Réactivation de l'ensemble des fonctionnalités du site (miniatures, retournement, ...)</li>
<li>Modèle orienté objet pour une meilleure évolutivité sur le long terme</li>
<li>Nouveau thème graphique</li>
<li>Admin : Refonte de l'administration</li>
<li>Admin : création de fonctions pour nettoyer les images obsolètes</li>
<li>Reprise du schéma de la BDD</li>
<li>Mise en place de tests automatisés sur l'ensemble des fonctionnalités du site</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v124">
v1.2.4 - 08 septembre 2011
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v124" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Meilleure gestion des images inexistantes (erreurs 404)</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v19">
v1.9 - Janvier 2014
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v19" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Passage à la programation objet</li>
<li>Reprise, factorisation et optimisation globale du site</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v123e">
v1.2.3.e - 14 août 2011
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v123e" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Administration : suivi des requêtes SQL amélioré</li>
<li>Finalisation de l'encodage des caractères spéciaux conformément à la norme HTML.</li>
<li>Optimisation de la lisibilité du code source.</li>
<li>Optimisation du temps d'éxecution du code PHP.</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v127">
v1.2.7 - 26 avril 2012
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v127" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Remise en place des options miniatures, rotation et redimensionnement</li>
<li>Corrections de charset sur des messages d'erreur</li>
<li>Amélioration de la protection anti-flood</li>
</ul>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v123d">
v1.2.3.d - 10 août 2011
&nbsp;<span class="fas fa-caret-down"></span>
</a>
</div>
<div id="v123d" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Création du changelog.</li>
<li>Encodage conforme à la norme HTML des caractères spéciaux.</li>
<li>Correction d'une erreur PHP en cas d'envoi 'hack' de fichier.</li>
<li>Amélioration de la portée des variables de language.</li>
</ul>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v126">
v1.2.6 - 9 janvier 2012
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v126" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Changement de serveur internet (la vitesse d'affichage est meilleure + connectivité IPv6 !)</li>
<li>Changement d'encodage par défaut pour les pages et le code HTML <em>(passage en UTF-8)</em></li>
<li>Gestion de l'IPv6 dans la BDD et les statistiques</li>
<li>Sélection du contenu des champs contenants l'url de l'image au clic</li>
<li>Amélioration du code HTML</li>
<li>Refonte des CSS</li>
<li>Préparation d'un système de templates</li>
<li>Utilisation de jQuery : box en haut de page, options cachées par défaut à l'envoi d'un fichier</li>
</ul>
</div>
</div>
</div>
</div>
<?php require _TPL_BOTTOM_ ?>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v125">
v1.2.5 - 30 octobre 2011
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v125" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Suppression de la limite des dimensions d'images pour les utilisateurs enregistrés</li>
<li>Ajout d'une fonction de blocage d'images + affichage d'une image d'information sur le blocage</li>
</ul>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v124">
v1.2.4 - 08 septembre 2011
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v124" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Meilleure gestion des images inexistantes (erreurs 404)</li>
</ul>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v123e">
v1.2.3.e - 14 août 2011
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v123e" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Administration : suivi des requêtes SQL amélioré</li>
<li>Finalisation de l'encodage des caractères spéciaux conformément à la norme HTML.</li>
<li>Optimisation de la lisibilité du code source.</li>
<li>Optimisation du temps d'éxecution du code PHP.</li>
</ul>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<a data-bs-toggle="collapse" href="#v123d">
v1.2.3.d - 10 août 2011
&nbsp;<span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="v123d" class="card-collapse collapse">
<div class="card-body">
<ul>
<li>Création du changelog.</li>
<li>Encodage conforme à la norme HTML des caractères spéciaux.</li>
<li>Correction d'une erreur PHP en cas d'envoi 'hack' de fichier.</li>
<li>Amélioration de la portée des variables de language.</li>
</ul>
</div>
</div>
</div>
<?php require _TPL_BOTTOM_ ?>

54
classes/HelperAbuse.php Normal file
View file

@ -0,0 +1,54 @@
<?php
namespace ImageHeberg;
class HelperAbuse
{
/**
* Retourne le nombre d'images bloquées issues du même réseau IP
* @param string $remote_addr Adresse IP à tester
* @return int
*/
public static function checkIpReputation(string $remote_addr): int
{
$monRetour = 0;
// IPv4 - Filtrer sur un /24 || IPv6 - Filtrer sur un /56
$req = MaBDD::getInstance()->prepare(
'SELECT COUNT(*) AS nb
FROM images
WHERE isBloquee = 1
AND abuse_network = (
IF(LOCATE(\'.\', :remote_addr) != 0,
SUBSTRING(:remote_addr, 1, (LENGTH(:remote_addr) - LOCATE(\'.\', REVERSE(:remote_addr)))),
SUBSTRING(HEX(INET6_ATON(:remote_addr)), 1, 14)
)
)'
);
$req->bindValue(':remote_addr', $remote_addr);
$req->execute();
$resultat = $req->fetch();
if ($resultat !== false) {
$monRetour = (int)$resultat->nb;
}
return $monRetour;
}
/**
* Mettre à jour la réputation des adresses IP en base
* @return void
*/
public static function updateIpReputation(): void
{
// Compléter les données "abuse_network" (normalement déjà fait dans ImageObject::creer())
// IPv4 - Filtrer sur un /24 || IPv6 - Filtrer sur un /56
$req = 'UPDATE images SET abuse_network =
IF(LOCATE(\'.\', remote_addr) != 0,
SUBSTRING(remote_addr, 1, (LENGTH(remote_addr) - LOCATE(\'.\', REVERSE(remote_addr)))),
SUBSTRING(HEX(INET6_ATON(remote_addr)), 1, 14)
)
WHERE abuse_network = \'\'';
MaBDD::getInstance()->query($req);
}
}

404
classes/HelperAdmin.php Normal file
View file

@ -0,0 +1,404 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
use ArrayObject;
/**
* Bibliothèque d'outils pour la gestion du site en tant qu'admin
*/
abstract class HelperAdmin
{
/**
* Liste des images n'ayant jamais été affichées et présentes sur le serveur depuis xx temps
* @return ArrayObject
*/
public static function getNeverUsedFiles(): ArrayObject
{
// Toutes les images jamais affichées & envoyées il y a plus de xx jours
$req = 'SELECT im.new_name
FROM images im
WHERE im.last_view = \'0000-00-00\'
AND im.date_action < DATE_SUB(CURRENT_DATE(), INTERVAL ' . _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ . ' DAY)
/* Préservation des fichiers des membres */
AND 0 = (
SELECT COUNT(*)
FROM possede po
WHERE po.images_id = im.id
)
/* Préservation si miniature affichée */
AND 0 = (
SELECT COUNT(*)
FROM thumbnails th
WHERE th.images_id = im.id
AND th.last_view <> \'0000-00-00\'
)';
return self::queryOnNewName($req);
}
/**
* Liste des images plus utilisées depuis au moins xx jours
* @return ArrayObject
*/
public static function getUnusedFiles(): ArrayObject
{
// Toutes les images non affichées depuis xx jours
$req = 'SELECT im.new_name
FROM images im
WHERE im.last_view < DATE_SUB(CURRENT_DATE(), INTERVAL ' . _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ . ' DAY)
/* Non prise en compte des images jamais affichées */
AND im.last_view <> \'0000-00-00\'
/* Préservation des images membres */
AND 0 = (
SELECT COUNT(*)
FROM possede po
WHERE po.images_id = im.id
)
/* Préservation si miniature affichée */
AND 0 = (
SELECT COUNT(*)
FROM thumbnails th
WHERE th.images_id = im.id
AND th.last_view > DATE_SUB(CURRENT_DATE(), INTERVAL ' . _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ . ' DAY)
)';
return self::queryOnNewName($req);
}
/**
* Liste des comptes sans images et créés depuis au moins xx jours
* @return ArrayObject
*/
public static function getUnusedAccounts(): ArrayObject
{
// Effacer les logs de connexion > 1 an
$req = 'DELETE
FROM login
WHERE date_action < (NOW() - INTERVAL 1 YEAR)';
MaBDD::getInstance()->query($req);
// Toutes les comptes créés et jamais utilisés depuis xx jours
$req = 'SELECT m.id
FROM membres m
WHERE m.date_action < DATE_SUB(CURRENT_DATE(), INTERVAL ' . _DELAI_EFFACEMENT_COMPTES_JAMAIS_UTILISES_ . ' DAY)
/* Préservation des comptes possédant des images */
AND 0 = (
SELECT COUNT(*)
FROM possede po
WHERE po.membres_id = m.id
)';
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
$monRetour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($resultat->fetchAll() as $value) {
// J'ajoute l'ID du compte
$monRetour->append($value->id);
}
return $monRetour;
}
/**
* Liste de l'ensemble des images en BDD
* @return ArrayObject
*/
public static function getAllImagesNameBDD(): ArrayObject
{
// Toutes les images (sauf 404 & banned)
$req = 'SELECT md5 FROM images WHERE id > 2';
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
$monRetour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($resultat->fetchAll() as $value) {
// J'ajoute le nom de l'image
$monRetour->append($value->md5);
}
return $monRetour;
}
/**
* Liste de l'ensemble des images en HDD
* @param string $path path à analyser
* @return ArrayObject
*/
public static function getAllImagesNameHDD(string $path): ArrayObject
{
$monRetour = new ArrayObject();
// Scanne le répertoire des images
$scan_rep = scandir($path);
// Pour chaque item
foreach ($scan_rep as $item) {
if (!in_array($item, ['.', '..', '_dummy', 'z_cache'])) {
if (is_dir($path . $item)) {
// Appel récursif
if ($path . $item . '/' !== _PATH_MINIATURES_) {
$monRetourTmp = self::getAllImagesNameHDD($path . $item . '/');
// Parsage et récupération des sous fichiers...
foreach ($monRetourTmp as $fichier) {
$monRetour->append($fichier);
}
}
} elseif ($item !== _IMAGE_404_ && $item !== _IMAGE_BAN_) {
$monRetour->append($item);
}
}
}
return $monRetour;
}
/**
* Liste de l'ensemble des miniatures en BDD
*/
public static function getAllMiniaturesNameBDD(): ArrayObject
{
// Toutes les images
$req = 'SELECT md5 FROM thumbnails';
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
$monRetour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($resultat->fetchAll() as $value) {
// J'ajoute le nom de l'image
$monRetour->append($value->md5);
}
return $monRetour;
}
/**
* Toutes les images avec un même MD5
* @param string $unMd5
* @return ArrayObject
*/
public static function getImageByMd5(string $unMd5): ArrayObject
{
// Images avec le même MD5
$req = MaBDD::getInstance()->prepare('SELECT new_name FROM images WHERE md5 = :md5');
$req->bindValue(':md5', $unMd5);
$req->execute();
$monRetour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($req->fetchAll() as $value) {
// J'ajoute le nom de l'image
$monRetour->append($value->new_name);
}
return $monRetour;
}
/**
* Toutes les images signalées
* @return ArrayObject
*/
public static function getImagesSignalees(): ArrayObject
{
// Images signalées
$req = 'SELECT new_name FROM images WHERE isSignalee = 1 and isBloquee = 0';
return self::queryOnNewName($req);
}
/**
* Images dont les statistiques d'affichage sont incohérentes
* @param int $nbMax Nb affichage / jour à partir duquel on veut les images
* @param bool $onlyOnImagesSuspectes Filtrer sur les images suspectes ?
* @param bool $useProjection Projeter l'utilisation actuelle de l'image (potentialité de dépassement de limite ultérieur)
* @param bool $includeApproved Inclure les images approuvées (ou signalées) ?
* @return ArrayObject
*/
public static function getImagesTropAffichees(int $nbMax, bool $onlyOnImagesSuspectes = false, bool $useProjection = false, bool $includeApproved = false): ArrayObject
{
if ($onlyOnImagesSuspectes) {
$tabNewName = [];
foreach ((array)self::getImagesPotentiellementIndesirables() as $newName) {
$tabNewName[] = '\'' . str_replace('\'', '', $newName) . '\'';
}
if (empty($tabNewName)) {
// Mettre un placeholder vide
$listeImagesForIn = '\'\'';
} else {
$listeImagesForIn = implode(',', $tabNewName);
}
}
// Images avec trop d'affichages
if ($useProjection) {
// Ne prendre que les images qui sont présentes depuis plus d'une heure pour limiter les faux positifs
$req = 'SELECT im.new_name, ( ( im.nb_view_v4 + im.nb_view_v6 + (SELECT IFNULL(SUM(th.nb_view_v4 + th.nb_view_v6), 0) FROM thumbnails th where th.images_id = im.id) ) / IF(im.date_action < (NOW() - INTERVAL 1 HOUR), ( UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(im.date_action) ), -1) ) * (60 * 60 * 24 ) as nbViewPerDay';
} else {
$req = 'SELECT im.new_name, ( im.nb_view_v4 + im.nb_view_v6 + (SELECT IFNULL(SUM(th.nb_view_v4 + th.nb_view_v6), 0) FROM thumbnails th where th.images_id = im.id) ) / IF(DATEDIFF(NOW(), im.date_action) > 0, DATEDIFF(NOW(), im.date_action), 1) as nbViewPerDay';
}
$req .= ' FROM images im
WHERE im.isBloquee = 0';
if (!$includeApproved) {
$req .= ' AND im.isSignalee = 0
AND im.isApprouvee = 0';
}
// Filter sur certaines images
if ($onlyOnImagesSuspectes) {
$req .= ' AND im.new_name IN (' . $listeImagesForIn . ')';
}
$req .= ' HAVING nbViewPerDay > ' . $nbMax . '
ORDER BY nbViewPerDay DESC';
return self::queryOnNewName($req);
}
/**
* Images dont les données sont proches d'images déjà bloquées
* @return ArrayObject
*/
public static function getImagesPotentiellementIndesirables(): ArrayObject
{
// Compléter les données "abuse_network" (normalement déjà fait dans ImageObject::creer())
HelperAbuse::updateIpReputation();
// Images potentiellement indésirables
$req = 'SELECT new_name FROM (
SELECT im.new_name, ((nb_view_v4 + nb_view_v6) / DATEDIFF(NOW(), im.date_action)) AS nbAff
FROM images im
LEFT JOIN possede po ON po.images_id = im.id
WHERE im.isBloquee = 0
AND im.isApprouvee = 0
AND im.isSignalee = 0
AND (
/* Même réseau IP */
im.abuse_network IN (SELECT DISTINCT abuse_network FROM images WHERE isBloquee = 1)
OR (
/* Même propriétaire */
po.membres_id IS NOT NULL
AND
po.membres_id IN (SELECT DISTINCT membres_id FROM possede WHERE images_id IN (SELECT id FROM images WHERE isBloquee = 1))
)
OR (
/* Même MD5 */
im.md5 IN (SELECT DISTINCT md5 FROM images WHERE isBloquee = 1)
)
)
ORDER BY nbAff DESC, im.id DESC
) tableTmp
LIMIT 0, 100';
return self::queryOnNewName($req);
}
/**
* Images dont les données sont proches d'images déjà approuvées
* @return ArrayObject
*/
public static function getImagesPotentiellementApprouvables(): ArrayObject
{
// Compléter les données "abuse_network" (normalement déjà fait dans ImageObject::creer())
HelperAbuse::updateIpReputation();
// Images potentiellement approuvables
$req = 'SELECT new_name FROM (
SELECT im.new_name, ((nb_view_v4 + nb_view_v6) / DATEDIFF(NOW(), im.date_action)) AS nbAff
FROM images im
LEFT JOIN possede po ON po.images_id = im.id
WHERE im.isBloquee = 0
AND im.isApprouvee = 0
AND im.isSignalee = 0
AND (
/* Même réseau IP */
im.abuse_network IN (SELECT DISTINCT abuse_network FROM images WHERE isApprouvee = 1)
OR (
/* Même propriétaire */
po.membres_id IS NOT NULL
AND
po.membres_id IN (SELECT DISTINCT membres_id FROM possede WHERE images_id IN (SELECT id FROM images WHERE isApprouvee = 1))
)
OR (
/* Même MD5 */
im.md5 IN (SELECT DISTINCT md5 FROM images WHERE isApprouvee = 1)
)
/*OR (
/* Même nom originel * /
im.old_name IN (SELECT DISTINCT old_name FROM images WHERE isApprouvee = 1)
)*/
)
HAVING nbAff > ' . (_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ / 2) . '
ORDER BY nbAff DESC, im.id DESC
) tableTmp
LIMIT 0, 25';
return self::queryOnNewName($req);
}
/**
* Liste des réseaux avec mauvaise réputation
* @return ArrayObject ["IP" => "count()"]
*/
public static function getBadNetworks(): ArrayObject
{
$monRetour = new ArrayObject();
$req = 'SELECT COUNT(*) AS nb, abuse_network FROM images WHERE isBloquee = 1 GROUP BY abuse_network';
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
// Pour chaque résultat retourné
foreach ($resultat->fetchAll() as $value) {
$ip = $value->abuse_network;
// Formatter les IPv6
if (!str_contains($ip, '.')) {
$ip = implode(':', str_split($ip, 4));
$ip .= '::/56';
} else {
$ip .= '.0/24';
}
$monRetour->offsetSet($ip, $value->nb);
}
// Tri "humain"
$monRetour->ksort(SORT_NATURAL);
return $monRetour;
}
/**
* Joue une requête SQL et retourne un tableau "new_name"
* @param string $req
* @return ArrayObject
*/
public static function queryOnNewName(string $req): ArrayObject
{
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
$monRetour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($resultat->fetchAll() as $value) {
// J'ajoute le nom de l'image
$monRetour->append($value->new_name);
}
return $monRetour;
}
}

214
classes/HelperImage.php Normal file
View file

@ -0,0 +1,214 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
use Imagick;
use ImagickException;
/**
* Bibliothèque d'outils pour la gestion des images
*/
abstract class HelperImage
{
/**
* Type de l'image
* @param string $path chemin sur le filesystem
* @return false|int
*/
public static function getType(string $path): bool|int
{
return exif_imagetype($path);
}
/**
* MIME type de l'image
* @param string $path chemin sur le filesystem
* @return string
*/
public static function getMimeType(string $path): string
{
return image_type_to_mime_type(self::getType($path));
}
/**
* Chargement ressource PHP image
* @param string $path
* @return Imagick
* @throws ImagickException
*/
public static function getImage(string $path): Imagick
{
$monImage = new Imagick();
$monImage->readImage($path);
// Pour les images animeés (GIF), générer une image pour chaque frame la composant
if (self::getType($path) === IMAGETYPE_GIF) {
$monImage = $monImage->coalesceImages();
}
return $monImage;
}
/**
* Enregistrement d'une ressource PHP image
* @param Imagick $uneImage Image à enregistrer
* @param int $imageType type PHP de l'image
* @param string $path chemin du fichier
* @return bool Succès ?
* @throws ImagickException
*/
public static function setImage(Imagick $uneImage, int $imageType, string $path): bool
{
$monRetour = false;
// Image animée (GIF)
if ($imageType === IMAGETYPE_GIF) {
$uneImage->setInterlaceScheme(Imagick::INTERLACE_GIF);
// Pour la génération du GIF, on ne veut que les différences entre les images
$uneImage = $uneImage->deconstructImages();
// Suppression des commentaires & co
$uneImage->stripImage();
// Enregistrement de l'ensemble des images
$monRetour = $uneImage->writeImages($path, true);
} else {
// Image non animée
if ($imageType === IMAGETYPE_JPEG) {
$uneImage->setInterlaceScheme(Imagick::INTERLACE_JPEG);
// Pas de destruction de l'image
$uneImage->setImageCompression(Imagick::COMPRESSION_JPEG);
$uneImage->setImageCompressionQuality(100);
} elseif ($imageType === IMAGETYPE_PNG) {
$uneImage->setInterlaceScheme(Imagick::INTERLACE_PNG);
$uneImage->setImageCompression(Imagick::COMPRESSION_LZW);
$uneImage->setImageCompressionQuality(9);
} elseif ($imageType === IMAGETYPE_WEBP) {
$uneImage->setImageFormat('webp');
$uneImage->setImageCompression(Imagick::COMPRESSION_LZW);
$uneImage->setImageCompressionQuality(100);
}
// Suppression des commentaires & co
$uneImage->stripImage();
$monRetour = $uneImage->writeImage($path);
}
return $monRetour;
}
/**
* Fourni l'extension officielle d'une ressource
* @param string $path chemin sur le filesystem
* @return string
*/
public static function getExtension(string $path): string
{
$ext = strtolower(image_type_to_extension(self::getType($path), false));
if ($ext === 'jpeg') {
// Préférence pour .jpg [filename.ext]
$ext = 'jpg';
}
return $ext;
}
/**
* Est-il possible de modifier l'image (mémoire suffisante ?)
* @param string $path
* @return bool Possible ?
* @see http://www.dotsamazing.com/en/labs/phpmemorylimit
*/
public static function isModifiableEnMemoire(string $path): bool
{
$monRetour = false;
// Nombre de canaux d'information de l'image
$nbCanaux = 4;
/**
* Information sur les canaux ?
*/
$imageinfo = getimagesize($path);
// Si information sur les canaux de l'image...
if (isset($imageinfo['channels']) && is_int($imageinfo['channels'])) {
$nbCanaux = $imageinfo['channels'];
}
/**
* Mémoire requise :
* (hauteur x largeur x profondeur)
* => x 2 [imageSource + imageDest]
* => x 1.8 [fudge factor]
*/
$memReq = $imageinfo[1] * $imageinfo[0] * $nbCanaux;
$memReq *= 2;
$memReq *= _FUDGE_FACTOR_;
// Est-ce possible ?
if ($memReq < HelperSysteme::getMemoireAllouee()) {
$monRetour = true;
}
return $monRetour;
}
/**
* Dimension maximale acceptable en mémoire pour les images
* <br />Suppose que l'image est carrée (donc indicatif !)
* <br /> Suppose 4 canaux dans l'image
* @return int
* @see isModifiableEnMemoire
*/
public static function getMaxDimension(): int
{
$memDispo = HelperSysteme::getMemoireAllouee();
/**
* Mémoire requise :
* (hauteur x largeur x profondeur)
* => x 2 [imageSource + imageDest]
* => x 1.8 [fudge factor]
*/
$dimMax = round(sqrt($memDispo / 4 / 2 / _FUDGE_FACTOR_));
return (int)$dimMax;
}
/**
* Est-ce une image WEBP animée ?
* @param string $path fichier à tester
* @return bool
*/
public static function isAnimatedWebp(string $path): bool
{
$monRetour = false;
// "Note that animated WebP files cannot be read."
// https://www.php.net/manual/en/function.imagecreatefromwebp.php
if (self::getType($path) === IMAGETYPE_WEBP) {
// "An animation is controlled by 'ANIM' and 'ANMF' Chunks."
// https://developers.google.com/speed/webp/docs/riff_container?hl=en
$headerFile = file_get_contents($path, false, null, 0, 100);
if (stripos($headerFile, 'ANIM') !== false || stripos($headerFile, 'ANMF') !== false) {
$monRetour = true;
}
}
return $monRetour;
}
}

170
classes/HelperSysteme.php Normal file
View file

@ -0,0 +1,170 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
use ArrayObject;
use Imagick;
use PDO;
/**
* Bibliothèque d'outils pour la gestion du système
*/
abstract class HelperSysteme
{
/**
* Taille mémoire maximale autorisée
* @see http://php.net/manual/fr/function.ini-get.php
* @return int
*/
public static function getMemoireAllouee(): int
{
// Récupération de la valeur du php.ini
$valBrute = trim(ini_get('memory_limit'));
// memory_limit=0 / -1 est possible
if ($valBrute <= 0) {
// Arbitrairement limite à 2Go
$valBrute = '2G';
}
// Gestion de l'unité multiplicatrice...
$unite = strtolower(substr($valBrute, -1));
$monRetour = (int)substr($valBrute, 0, -1);
switch ($unite) {
case 'g':
$monRetour *= 1024;
// no break
case 'm':
$monRetour *= 1024;
// no break
case 'k':
$monRetour *= 1024;
// no break
}
return $monRetour;
}
/**
* Version de PHP
* @return string
*/
public static function getPhpVersion(): string
{
return PHP_VERSION . ' - ' . PHP_OS;
}
/**
* Version de Imagick
* @return string
*/
public static function getImagickVersion(): string
{
return Imagick::getVersion()['versionString'];
}
/**
* Version de MySQL
* @return string
*/
public static function getMysqlVersion(): string
{
// Exécution de la requête
return MaBDD::getInstance()->getAttribute(PDO::ATTR_SERVER_VERSION);
}
/**
* Headers HTTP status code
* @param string $url URL à tester
* @return string retour HTTP
*/
public static function getStatusHTTP(string $url): string
{
$classe = 'danger';
$fa = 'exclamation-circle';
// On regarde ce que ça donne
$resultat = get_headers($url);
// Est-ce le résultat attendu ?
if (stripos($resultat[0], 'Forbidden')) {
$classe = 'success';
$fa = 'check';
}
// Mise en forme du résultat
return '<span class="bi-' . $fa . ' text-' . $classe . '">&nbsp;' . $resultat[0] . '</span>';
}
/**
* Vérifie de manière récursive l'écriture dans un dossier
* @param string $folder Path du dossier parent
* @return ArrayObject
*/
public static function isRecursivelyWritable(string $folder): ArrayObject
{
// On évite le // dans le path... (estéthique)
if (str_ends_with($folder, '/')) {
$folder = substr($folder, 0, -1);
}
$monRetour = new ArrayObject();
if (is_writable($folder)) {
$monRetour->append('<span class="bi-check text-success">&nbsp;' . $folder . '</span>');
} else {
$monRetour->append('<span class="bi-exclamation-circle text-danger">&nbsp;' . $folder . '</span>');
}
// Dossiers enfants
$objects = glob($folder . '/*', GLOB_ONLYDIR);
foreach ($objects as $object) {
// Je vérifie si les dossiers enfants sont écrivables
$sousRetour = self::isRecursivelyWritable($object);
// Gestion de l'itération...
foreach ($sousRetour as $unRetour) {
$monRetour->append($unRetour);
}
}
return $monRetour;
}
/**
* Volume des images
* @return float
*/
public static function getHDDUsage(): float
{
// Poids de l'ensemble des images
$req = 'SELECT SUM(im.size) AS images, (
SELECT SUM(th.size)
FROM thumbnails th
) AS miniatures
FROM images im';
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
// Récupération de la valeur
$value = $resultat->fetch();
return round(($value->images + $value->miniatures) / (1024 * 1024 * 1024));
}
}

View file

@ -0,0 +1,47 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
use Exception;
/**
* Exception custom à l'application
*/
class ImageHebergException extends Exception
{
/**
* Définir les champs custom de l'exception
*
* @param string $message Message d'erreur
* @param int $code Code de l'exception
* @param string $file Fichier l'erreur à eu lieu
* @param int $line Line concernée
* @return void
*/
public function define(string $message, int $code, string $file, int $line): void
{
$this->message = $message;
$this->code = $code;
$this->file = $file;
$this->line = $line;
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -21,6 +21,9 @@
namespace ImageHeberg;
use ArrayObject;
use Exception;
use ImagickException;
use PDO;
/**
@ -28,150 +31,195 @@ use PDO;
*/
class ImageObject extends RessourceObject implements RessourceInterface
{
/**
* Constructeur
* @param string $newName nom de l'image
* @param string $value Identifiant image-heberg
* @param string $fromField Champ à utiliser en BDD
* @throws ImageHebergException
*/
public function __construct($newName = false)
public function __construct(string $value = '', string $fromField = RessourceObject::SEARCH_BY_NAME)
{
// Définition du type pour le RessourceObject
$this->setType(RessourceObject::TYPE_IMAGE);
// Si on me donne un ID d'image, je charge l'objet
if ($newName) {
if (!$this->charger($newName)) {
// Envoi d'une exception si l'image n'existe pas
throw new Exception('Image ' . $newName . ' inexistante');
}
// Faut-il charger l'objet ?
if ($value !== '' && !$this->charger($value, $fromField)) {
// Envoi d'une exception si l'image n'existe pas
throw new ImageHebergException('Image ' . $value . ' inexistante (' . $fromField . ')');
}
}
/**
* {@inheritdoc}
*/
public function charger($newName)
public function charger(string $value, string $fromField = RessourceObject::SEARCH_BY_NAME): bool
{
// Retour
$monRetour = false;
// Charger les informations depuis la BDD
$this->chargerFromBdd([$value], $fromField);
// Je vais chercher les infos en BDD
$req = MaBDD::getInstance()->prepare("SELECT * FROM images WHERE new_name = :newName");
$req->bindValue(':newName', $newName, PDO::PARAM_STR);
$req->execute();
// Gestion du retour
return ($this->getId() !== 0);
}
// J'éclate les informations
$resultat = $req->fetch();
if ($resultat !== false) {
$this->setId($resultat->id);
$this->setIpEnvoi($resultat->ip_envoi);
$this->setDateEnvoi($resultat->date_envoi);
$this->setNomOriginal($resultat->old_name);
// Permet l'effacement des fichiers non enregistrés en BDD
$this->setNomNouveau($newName);
$this->setPoids($resultat->size);
$this->setHauteur($resultat->height);
$this->setLargeur($resultat->width);
$this->setLastView($resultat->last_view);
$this->setNbViewIPv4($resultat->nb_view_v4);
$this->setNbViewIPv6($resultat->nb_view_v6);
$this->setMd5($resultat->md5);
$this->setBloquee($resultat->isBloquee);
$this->setSignalee($resultat->isSignalee);
/**
* Charger des images en masse
* @param ArrayObject $values Valeur du champ $fromField
* @param string $fromField Nom du champ à utiliser en BDD pour identifier les images
* @param bool $orderByIdAsc Trier les résultats par ID ASC ?
* @return array
*/
public static function chargerMultiple(ArrayObject $values, string $fromField, bool $orderByIdAsc = true): array
{
$monRetour = [];
// Gestion du retour
$monRetour = true;
if (count($values) > 0) {
$monRetour = (new ImageObject())->chargerFromBdd((array)$values, $fromField, false, $orderByIdAsc);
}
return $monRetour;
}
/**
* {@inheritdoc}
* Charger des images depuis la BDD
* @param array $values Valeurs du champ $fromField
* @param string $fromField Nom du champ à utiliser en BDD pour identifier les images
* @param bool $saveOnCurrentObject Enregistrer les résultats dans l'objet courant ou dans un tableau ?
* @param bool $orderByIdAsc Trier les résultats par ID ASC ?
* @return array
*/
public function sauver()
private function chargerFromBdd(array $values, string $fromField, bool $saveOnCurrentObject = true, bool $orderByIdAsc = true): array
{
$monRetour = [];
// Génération des placeholders
$placeHolders = str_repeat('?,', count($values) - 1) . '?';
// Je vais chercher les infos en BDD
$req = MaBDD::getInstance()->prepare('SELECT *, (SELECT COUNT(*) FROM images im2 WHERE im2.isBloquee = 1 AND im2.abuse_network = images.abuse_network) AS reputation FROM images LEFT JOIN possede on images.id = possede.images_id WHERE ' . $fromField . ' IN (' . $placeHolders . ') ORDER BY images.id ' . ($orderByIdAsc ? 'ASC' : 'DESC'));
$req->execute($values);
// Traitement des résultats
foreach ($req->fetchAll() as $resultat) {
if ($saveOnCurrentObject) {
$varName = 'this';
} else {
$varName = 'uneImage';
unset(${$varName});
${$varName} = new ImageObject();
}
${$varName}->setId($resultat->id);
${$varName}->setIpEnvoi($resultat->remote_addr);
${$varName}->setDateEnvoi($resultat->date_action);
${$varName}->setNomOriginal($resultat->old_name);
${$varName}->setNomNouveau($resultat->new_name);
${$varName}->setPoids($resultat->size);
${$varName}->setHauteur($resultat->height);
${$varName}->setLargeur($resultat->width);
${$varName}->setLastView($resultat->last_view);
${$varName}->setNbViewIPv4($resultat->nb_view_v4);
${$varName}->setNbViewIPv6($resultat->nb_view_v6);
${$varName}->setMd5($resultat->md5);
${$varName}->setBloquee($resultat->isBloquee);
${$varName}->setSignalee($resultat->isSignalee);
${$varName}->setApprouvee($resultat->isApprouvee);
${$varName}->setIdProprietaire($resultat->membres_id);
${$varName}->setSuspecte(($resultat->reputation > 0));
if (!$saveOnCurrentObject) {
// Gestion du retour
$monRetour[] = ${$varName};
}
}
return $monRetour;
}
public function sauver(): void
{
// J'enregistre les infos en BDD
$req = MaBDD::getInstance()->prepare("UPDATE images SET ip_envoi = :ipEnvoi, date_envoi = :dateEnvoi, old_name = :oldName, new_name = :newName, size = :size, height = :height, width = :width, last_view = :lastView, nb_view_v4 = :nbViewV4, nb_view_v6 = :nbViewV6, md5 = :md5, isBloquee = :isBloquee, isSignalee = :isSignalee WHERE id = :id");
$req->bindValue(':ipEnvoi', $this->getIpEnvoi(), PDO::PARAM_STR);
$req = MaBDD::getInstance()->prepare('UPDATE images SET remote_addr = :ipEnvoi, date_action = :dateEnvoi, old_name = :oldName, new_name = :newName, size = :size, height = :height, width = :width, last_view = :lastView, nb_view_v4 = :nbViewV4, nb_view_v6 = :nbViewV6, md5 = :md5, isBloquee = :isBloquee, isSignalee = :isSignalee, isApprouvee = :isApprouvee WHERE id = :id');
$req->bindValue(':ipEnvoi', $this->getIpEnvoi());
$req->bindValue(':dateEnvoi', $this->getDateEnvoiBrute());
$req->bindValue(':oldName', $this->getNomOriginal(), PDO::PARAM_STR);
$req->bindValue(':newName', $this->getNomNouveau(), PDO::PARAM_STR);
$req->bindValue(':oldName', $this->getNomOriginal());
$req->bindValue(':newName', $this->getNomNouveau());
$req->bindValue(':size', $this->getPoids(), PDO::PARAM_INT);
$req->bindValue(':height', $this->getHauteur(), PDO::PARAM_INT);
$req->bindValue(':width', $this->getLargeur(), PDO::PARAM_INT);
$req->bindValue(':lastView', $this->getLastView());
$req->bindValue(':nbViewV4', $this->getNbViewIPv4(), PDO::PARAM_INT);
$req->bindValue(':nbViewV6', $this->getNbViewIPv6(), PDO::PARAM_INT);
$req->bindValue(':md5', $this->getMd5(), PDO::PARAM_STR);
$req->bindValue(':md5', $this->getMd5());
$req->bindValue(':isBloquee', $this->isBloquee(), PDO::PARAM_INT);
$req->bindValue(':isSignalee', $this->isSignalee(), PDO::PARAM_INT);
$req->bindValue(':isApprouvee', $this->isApprouvee(), PDO::PARAM_INT);
$req->bindValue(':id', $this->getId(), PDO::PARAM_INT);
$req->execute();
}
/**
* {@inheritdoc}
* Récupérer les ID des images des miniatures associées
* @param bool $onlyPreview Uniquement les miniatures d'aperçu dans l'espace membre ?
* @return ArrayObject new_name en BDD des miniatures
*/
public function supprimer()
public function getMiniatures(bool $onlyPreview = false): ArrayObject
{
$monRetour = true;
$monRetour = new ArrayObject();
/**
* Suppression de la ou les miniatures
*/
// Chargement des miniatures
$req = MaBDD::getInstance()->prepare("SELECT new_name FROM thumbnails where id_image = :idImage");
$req->bindValue(':idImage', $this->getId(), PDO::PARAM_INT);
$query = 'SELECT new_name FROM thumbnails WHERE images_id = :imagesId';
if ($onlyPreview) {
$query .= ' AND is_preview = 1';
}
$req = MaBDD::getInstance()->prepare($query);
$req->bindValue(':imagesId', $this->getId(), PDO::PARAM_INT);
$req->execute();
// Je passe toutes les lignes de résultat
foreach ($req->fetchAll() as $value) {
// Chargement de la miniature
$maMiniature = new MiniatureObject($value->new_name);
// Suppression
$maMiniature->supprimer();
}
/**
* Suppression de l'affectation
*/
if ($monRetour) {
$req = MaBDD::getInstance()->prepare("DELETE FROM possede WHERE image_id = :imageId");
/* @var $req PDOStatement */
$req->bindValue(':imageId', $this->getId(), PDO::PARAM_INT);
$monRetour = $req->execute();
}
/**
* Suppression de l'image en BDD
*/
if ($monRetour) {
$req = MaBDD::getInstance()->prepare("DELETE FROM images WHERE id = :id");
/* @var $req PDOStatement */
$req->bindValue(':id', $this->getId(), PDO::PARAM_INT);
$monRetour = $req->execute();
}
/**
* Suppression du HDD
*/
if ($monRetour) {
// Plus aucune image n'utilise le fichier (BDD déjà mise à jour !)
if ($this->getNbDoublons() == 0) {
// Je supprime l'image sur le HDD
$monRetour = unlink($this->getPathMd5());
}
// Nom du fichier
$monRetour->append($value->new_name);
}
return $monRetour;
}
/**
* {@inheritdoc}
* @throws ImageHebergException
*/
public function creer()
public function supprimer(): void
{
/**
* Suppression de la ou les miniatures
*/
// Je passe toutes les lignes de résultat
foreach ($this->getMiniatures() as $new_name) {
// Chargement de la miniature
$maMiniature = new MiniatureObject($new_name);
// Suppression
$maMiniature->supprimer();
}
// Suppresion de l'affectation en BDD
$req = MaBDD::getInstance()->prepare('DELETE FROM possede WHERE images_id = :imagesId');
$req->bindValue(':imagesId', $this->getId(), PDO::PARAM_INT);
if ($req->execute()) {
// Suppresion de l'image en BDD
$req = MaBDD::getInstance()->prepare('DELETE FROM images WHERE id = :id');
$req->bindValue(':id', $this->getId(), PDO::PARAM_INT);
// Si plus aucune image n'utilise le fichier => supprimer l'image sur le HDD
if (
$req->execute()
&& $this->getNbUsages() === 0
&& is_file($this->getPathMd5())
) {
unlink($this->getPathMd5());
}
}
}
/**
* @throws ImagickException
* @throws Exception
*/
public function creer(): bool
{
// Retour
$monRetour = true;
@ -194,15 +242,29 @@ class ImageObject extends RessourceObject implements RessourceInterface
// On enregistre le nom
$this->setNomNouveau($new_name);
// PHP ne gère pas les images WebP animée -> ne pas faire de traitements
if (!HelperImage::isAnimatedWebp($this->getPathTemp())) {
// Optimiser l'image (permettra de comparer son hash avec celles déjà stockées)
HelperImage::setImage(HelperImage::getImage($this->getPathTemp()), HelperImage::getType($this->getPathTemp()), $this->getPathTemp());
}
/**
* Déplacement du fichier
*/
// Vérification de la non existence du fichier
if ($this->getNbDoublons() == 0) {
// Image inconnue : optimisation de sa taille
$monRetour = Outils::setImage(Outils::getImage($this->getPathTemp()), Outils::getType($this->getPathTemp()), $this->getPathTemp());
if ($this->getNbUsages() === 0) {
// Copie du fichier vers l'emplacement de stockage
copy($this->getPathTemp(), $this->getPathMd5());
// Ne peut pas être fait avant car le MD5 n'est pas encore connu
$monRetour = copy($this->getPathTemp(), $this->getPathMd5());
} else {
// Ce MD5 est-il déjà bloqué pour une autre image ?
$req = MaBDD::getInstance()->prepare('SELECT MAX(isBloquee) AS isBloquee FROM images WHERE md5 = :md5');
$req->bindValue(':md5', $this->getMd5());
$req->execute();
$values = $req->fetch();
if ($values !== false) {
$this->setBloquee((bool)$values->isBloquee);
}
}
// Ssi copie du fichier réussie
@ -224,15 +286,16 @@ class ImageObject extends RessourceObject implements RessourceInterface
/**
* Création en BDD
*/
$req = MaBDD::getInstance()->prepare("INSERT INTO images (ip_envoi, date_envoi, old_name, new_name, size, height, width, md5) VALUES (:ipEnvoi, NOW(), :oldName, :newName, :size, :height, :width, :md5)");
$req->bindValue(':ipEnvoi', $this->getIpEnvoi(), PDO::PARAM_STR);
$req = MaBDD::getInstance()->prepare('INSERT INTO images (remote_addr, date_action, old_name, new_name, size, height, width, md5, isBloquee) VALUES (:ipEnvoi, NOW(), :oldName, :newName, :size, :height, :width, :md5, :isBloquee)');
$req->bindValue(':ipEnvoi', $this->getIpEnvoi());
// Date : NOW()
$req->bindValue(':oldName', $this->getNomOriginal(), PDO::PARAM_STR);
$req->bindValue(':newName', $this->getNomNouveau(), PDO::PARAM_STR);
$req->bindValue(':oldName', $this->getNomOriginal());
$req->bindValue(':newName', $this->getNomNouveau());
$req->bindValue(':size', $this->getPoids(), PDO::PARAM_INT);
$req->bindValue(':height', $this->getHauteur(), PDO::PARAM_INT);
$req->bindValue(':width', $this->getLargeur(), PDO::PARAM_INT);
$req->bindValue(':md5', $this->getMd5(), PDO::PARAM_STR);
$req->bindValue(':md5', $this->getMd5());
$req->bindValue(':isBloquee', $this->isBloquee());
if (!$req->execute()) {
// Gestion de l'erreur d'insertion en BDD
@ -243,6 +306,67 @@ class ImageObject extends RessourceObject implements RessourceInterface
*/
$idEnregistrement = MaBDD::getInstance()->lastInsertId();
$this->setId($idEnregistrement);
// Définir les informations relatives au réseau utilisé (anti abus)
HelperAbuse::updateIpReputation();
}
}
return $monRetour;
}
/**
* Bloquer une image en BDD
* Effet contaminant sur les autres images partagant le même MD5
*/
public function bloquer(): void
{
// J'enregistre les infos en BDD
$req = MaBDD::getInstance()->prepare('UPDATE images SET isBloquee = 1, isApprouvee = 0 WHERE md5 = :md5');
$req->bindValue(':md5', $this->getMd5());
$req->execute();
}
/**
* Approuver une image en BDD
* Effet contaminant sur les autres images partagant le même MD5
*/
public function approuver(): void
{
// J'enregistre les infos en BDD
$req = MaBDD::getInstance()->prepare('UPDATE images SET isBloquee = 0, isSignalee = 0, isApprouvee = 1 WHERE md5 = :md5');
$req->bindValue(':md5', $this->getMd5());
$req->execute();
}
/**
* Nombre d'appels IPv4 & IPv6
* @return int
*/
public function getNbViewTotal(): int
{
return parent::getNbViewTotal() + $this->getNbViewMiniatures();
}
/**
* Récupérer le total d'affichage des miniatures
* @return int
*/
public function getNbViewMiniatures(): int
{
$monRetour = 0;
// Chargement des miniatures
$query = 'SELECT SUM(nb_view_v4 + nb_view_v6) as total FROM thumbnails WHERE images_id = :imagesId';
$req = MaBDD::getInstance()->prepare($query);
$req->bindValue(':imagesId', $this->getId(), PDO::PARAM_INT);
$req->execute();
// Je passe toutes les lignes de résultat
foreach ($req->fetchAll() as $value) {
if (!is_null($value->total)) {
$monRetour = $value->total;
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -22,6 +22,7 @@
namespace ImageHeberg;
use PDO;
use PDOStatement;
/**
* Lien vers la BDD
@ -31,9 +32,9 @@ use PDO;
class MaBDD
{
// PDO
private $maBDD = null;
private PDO $maBDD;
// Instance de la classe
private static $monInstance = null;
private static ?MaBDD $monInstance = null;
/**
* Constructeur
@ -47,9 +48,9 @@ class MaBDD
/**
* Crée & renvoi l'objet d'instance
* @return PDO
* @return MaBDD
*/
public static function getInstance()
public static function getInstance(): MaBDD
{
// Si pas de connexion active, en crée une
if (is_null(self::$monInstance)) {
@ -61,9 +62,9 @@ class MaBDD
/**
* PDO::query
* @param string $query
* @return type
* @return false|PDOStatement
*/
public function query($query)
public function query(string $query): bool|PDOStatement
{
return $this->maBDD->query($query);
}
@ -71,18 +72,18 @@ class MaBDD
/**
* PDO::prepare
* @param string $query
* @return type
* @return false|PDOStatement
*/
public function prepare($query)
public function prepare(string $query): bool|PDOStatement
{
return $this->maBDD->prepare($query);
}
/**
* PDO::lastInsertId
* @return type
* @return string
*/
public function lastInsertId()
public function lastInsertId(): string
{
return $this->maBDD->lastInsertId();
}
@ -90,7 +91,7 @@ class MaBDD
/**
* Fermeture du PDO
*/
public static function close()
public static function close(): void
{
self::$monInstance = null;
}
@ -98,9 +99,9 @@ class MaBDD
/**
* PDO::getAttribute
* @param int $attribute
* @return type
* @return mixed
*/
public function getAttribute($attribute)
public function getAttribute(int $attribute): mixed
{
return $this->maBDD->getAttribute($attribute);
}

View file

@ -1,309 +0,0 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
use PDO;
use ArrayObject;
/**
* Les méthodes "génériques"
*
* @author anael
*/
class MetaObject
{
/**
* Liste des images n'ayant jamais été affichées et présentes sur le serveur depuis xx temps
* @return \ArrayObject
*/
public static function getNeverUsedFiles()
{
// Toutes les images jamais affichées & envoyées il y a plus de xx jours
$req = "SELECT im.new_name
FROM images im
WHERE im.last_view IS NULL
AND im.date_envoi < DATE_SUB(CURRENT_DATE(), INTERVAL " . _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ . " DAY)
/* Préservation des fichiers des membres */
AND 0 = (
SELECT COUNT(*)
FROM possede po
WHERE po.image_id = im.id
)
/* Préservation si miniature affichée */
AND 0 = (
SELECT COUNT(*)
FROM thumbnails th
WHERE th.id_image = im.id
AND th.last_view IS NOT NULL
)
";
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
$retour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($resultat->fetchAll() as $value) {
// J'ajoute le nom de l'image
$retour->append($value->new_name);
}
return $retour;
}
/**
* Liste des images plus utilisées depuis au moins xx jours
* @return \ArrayObject
*/
public static function getUnusedFiles()
{
// Toutes les images non affichées depuis xx jours
$req = "SELECT im.new_name
FROM images im
WHERE im.last_view < DATE_SUB(CURRENT_DATE(), INTERVAL " . _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ . " DAY)
/* Non prise en compte des images jamais affichées */
AND im.last_view IS NOT NULL
/* Préservation des images membres */
AND 0 = (
SELECT COUNT(*)
FROM possede po
WHERE po.image_id = im.id
)
/* Préservation si miniature affichée */
AND 0 = (
SELECT COUNT(*)
FROM thumbnails th
WHERE th.id_image = im.id
AND th.last_view > DATE_SUB(CURRENT_DATE(), INTERVAL " . _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ . " DAY)
)";
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
$retour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($resultat->fetchAll() as $value) {
// J'ajoute le nom de l'image
$retour->append($value->new_name);
}
return $retour;
}
/**
* Liste de l'ensemble des images en BDD
* @return \ArrayObject
*/
public static function getAllImagesNameBDD()
{
// Toutes les images (sauf 404 & banned)
$req = "SELECT md5 FROM images WHERE id > 2";
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
$retour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($resultat->fetchAll() as $value) {
// J'ajoute le nom de l'image
$retour->append($value->md5);
}
return $retour;
}
/**
* Liste de l'ensemble des images en HDD
* @param type $path path à analyser
* @return \ArrayObject
*/
public static function getAllImagesNameHDD($path)
{
$monRetour = new ArrayObject();
// Scanne le répertoire des images
$scan_rep = scandir($path);
// Pour chaque item
foreach ($scan_rep as $item) {
if ($item !== '.' && $item !== '..' && $item !== '_dummy') {
if (is_dir($path . $item)) {
// Appel récursif
if ($path . $item . '/' !== _PATH_MINIATURES_) {
$monRetourTmp = self::getAllImagesNameHDD($path . $item . '/');
// Parsage et récupération des sous fichiers...
foreach ($monRetourTmp as $fichier) {
$monRetour->append($fichier);
}
}
} elseif ($item !== _IMAGE_404_ && $item !== _IMAGE_BAN_) {
$monRetour->append($item);
}
}
}
return $monRetour;
}
/**
* Liste de l'ensemble des miniatures en BDD
*/
public static function getAllMiniaturesNameBDD()
{
// Toutes les images
$req = "SELECT thumbnails.md5 FROM images, thumbnails WHERE images.id = thumbnails.id";
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
$retour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($resultat->fetchAll() as $value) {
// J'ajoute le nom de l'image
$retour->append($value->md5);
}
return $retour;
}
/**
* Volume des images
* @return int
*/
public static function getHDDUsage()
{
// Poids de l'ensemble des images
$req = "SELECT SUM(im.size) AS images, (
SELECT SUM(th.size)
FROM thumbnails th
) AS miniatures
FROM images im";
// Exécution de la requête
$resultat = MaBDD::getInstance()->query($req);
// Récupération de la valeur
$value = $resultat->fetch();
$retour = round(($value->images + $value->miniatures) / (1024 * 1024 * 1024));
return $retour;
}
/**
* Version de PHP
* @return string
*/
public static function getPhpVersion()
{
$retour = PHP_VERSION . " - " . PHP_OS;
return $retour;
}
/**
* Version de MySQL
* @return string
*/
public static function getMysqlVersion()
{
// Exécution de la requête
$retour = MaBDD::getInstance()->getAttribute(PDO::ATTR_SERVER_VERSION);
return $retour;
}
/**
* Headers HTTP status code
* @param string $url URL à tester
* @return string retour HTTP
*/
public static function getStatusHTTP($url)
{
$retour = get_headers($url);
return $retour[0];
}
/**
* Vérifie de manière récursive l'écriture dans un dossier
* @param string $folder Path du dossier parent
* @return \ArrayObject
*/
public static function isRecursivelyWritable($folder)
{
// On évite le // dans le path... (estéthique)
if (substr($folder, -1) === "/") {
$folder = substr($folder, 0, -1);
}
$monRetour = new ArrayObject();
if (is_writable($folder)) {
$monRetour->append("OK - $folder");
} else {
$monRetour->append("KO - $folder");
}
// Enfants...
$objects = scandir($folder);
foreach ($objects as $object) {
// Perfs : élimination de tous les noms contenant un . (fichier.ext)
if (strpos($object, ".") === false) {
$pathObject = $folder . "/" . $object;
// . & .. n'arrivent pas ici...
if (is_dir($pathObject)) {
$sousRetour = self::isRecursivelyWritable($pathObject);
// Gestion de l'itération...
foreach ($sousRetour as $unRetour) {
$monRetour->append($unRetour);
}
}
}
}
return $monRetour;
}
/**
* Toutes les images avec un même MD5
* @param string $unMd5
* @return \ArrayObject
*/
public static function getImageByMd5($unMd5)
{
// Images avec le même MD5
$req = MaBDD::getInstance()->prepare("SELECT new_name FROM images WHERE md5 = :md5");
$req->bindValue(':md5', $unMd5, PDO::PARAM_STR);
$req->execute();
$retour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($req->fetchAll() as $value) {
// J'ajoute le nom de l'image
$retour->append($value->new_name);
}
return $retour;
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -21,6 +21,7 @@
namespace ImageHeberg;
use Exception;
use PDO;
/**
@ -28,37 +29,34 @@ use PDO;
*/
class MiniatureObject extends RessourceObject implements RessourceInterface
{
private $idImage;
private int $idImage;
private bool $isPreview = false;
/**
* Constructeur
* @param string $newName newName de l'image maître
* @param string $value Identifiant image-heberg
* @param string $fromField Champ à utiliser en BDD
* @throws ImageHebergException
*/
public function __construct($newName = false)
public function __construct(string $value = '', string $fromField = RessourceObject::SEARCH_BY_NAME)
{
// Définition du type pour le RessourceObject
$this->setType(RessourceObject::TYPE_MINIATURE);
// Si on me donne un newName d'image, je charge l'objet
if ($newName) {
if (!$this->charger($newName)) {
// Envoi d'une exception si l'image n'existe pas
throw new Exception('Miniature ' . $newName . ' inexistante');
}
// Faut-il charger l'objet ?
if ($value !== '' && !$this->charger($value, $fromField)) {
// Envoi d'une exception si l'image n'existe pas
throw new ImageHebergException('Miniature ' . $value . ' inexistante - ' . $fromField);
}
}
/**
* {@inheritdoc}
*/
public function charger($newName)
public function charger(string $value, string $fromField = RessourceObject::SEARCH_BY_NAME): bool
{
$monRetour = false;
// Je vais chercher les infos en BDD
$req = MaBDD::getInstance()->prepare("SELECT * FROM thumbnails WHERE new_name = :newName");
/* @var $req PDOStatement */
$req->bindValue(':newName', $newName, PDO::PARAM_STR);
$req = MaBDD::getInstance()->prepare('SELECT * FROM thumbnails WHERE ' . $fromField . ' = :value');
$req->bindValue(':value', $value);
$req->execute();
// J'éclate les informations
@ -72,17 +70,21 @@ class MiniatureObject extends RessourceObject implements RessourceInterface
$this->setNbViewIPv6($resultat->nb_view_v6);
$this->setMd5($resultat->md5);
$this->setId($resultat->id);
$this->setDateEnvoi($resultat->date_creation);
$this->setNomNouveau($newName);
$this->setIdImage($resultat->id_image);
$this->setDateEnvoi($resultat->date_action);
$this->setNomNouveau($resultat->new_name);
$this->setIdImage($resultat->images_id);
$this->setIsPreview($resultat->is_preview);
// Reprise des informations de l'image maitresse
$imageMaitre = new ImageObject();
$imageMaitre->charger($newName);
$this->setBloquee($imageMaitre->isBloquee());
$this->setSignalee($imageMaitre->isSignalee());
$this->setNomOriginal($imageMaitre->getNomOriginal());
$this->setIpEnvoi($imageMaitre->getIpEnvoi());
$imageParente = new ImageObject();
$imageParente->charger($this->getIdImage(), RessourceObject::SEARCH_BY_ID);
$this->setBloquee($imageParente->isBloquee());
$this->setSignalee($imageParente->isSignalee());
$this->setApprouvee($imageParente->isApprouvee());
$this->setNomOriginal($imageParente->getNomOriginal());
$this->setIpEnvoi($imageParente->getIpEnvoi());
$this->setIdProprietaire($imageParente->getIdProprietaire());
$this->setSuspecte($imageParente->isSuspecte());
// Notification du chargement réussi
$monRetour = true;
@ -90,15 +92,13 @@ class MiniatureObject extends RessourceObject implements RessourceInterface
return $monRetour;
}
/**
* {@inheritdoc}
*/
public function sauver()
public function sauver(): void
{
// J'enregistre les infos en BDD
$req = MaBDD::getInstance()->prepare("UPDATE thumbnails SET id_image = :idImage, date_creation = :dateCreation, new_name = :newName, size = :size, height = :height, width = :width, last_view = :lastView, nb_view_v4 = :nbViewV4, nb_view_v6 = :nbViewV6, md5 = :md5 WHERE id = :id");
$req = MaBDD::getInstance()->prepare('UPDATE thumbnails SET images_id = :imagesId, is_preview = :isPreview, date_action = :dateCreation, new_name = :newName, size = :size, height = :height, width = :width, last_view = :lastView, nb_view_v4 = :nbViewV4, nb_view_v6 = :nbViewV6, md5 = :md5 WHERE id = :id');
$req->bindValue(':idImage', $this->getIdImage(), PDO::PARAM_INT);
$req->bindValue(':imagesId', $this->getIdImage(), PDO::PARAM_INT);
$req->bindValue(':isPreview', $this->getIsPreview(), PDO::PARAM_INT);
$req->bindValue(':dateCreation', $this->getDateEnvoiBrute());
$req->bindValue(':newName', $this->getNomNouveau(), PDO::PARAM_STMT);
$req->bindValue(':size', $this->getPoids(), PDO::PARAM_INT);
@ -107,44 +107,34 @@ class MiniatureObject extends RessourceObject implements RessourceInterface
$req->bindValue(':lastView', $this->getLastView());
$req->bindValue(':nbViewV4', $this->getNbViewIPv4(), PDO::PARAM_INT);
$req->bindValue(':nbViewV6', $this->getNbViewIPv6(), PDO::PARAM_INT);
$req->bindValue(':md5', $this->getMd5(), PDO::PARAM_STR);
$req->bindValue(':md5', $this->getMd5());
$req->bindValue(':id', $this->getId(), PDO::PARAM_INT);
$req->execute();
}
/**
* {@inheritdoc}
*/
public function supprimer()
public function supprimer(): void
{
$monRetour = true;
/**
* Suppression de l'image en BDD
*/
$req = MaBDD::getInstance()->prepare("DELETE FROM thumbnails WHERE id = :id");
/* @var $req PDOStatement */
// Suppresion de l'image en BDD
$req = MaBDD::getInstance()->prepare('DELETE FROM thumbnails WHERE id = :id');
$req->bindValue(':id', $this->getId(), PDO::PARAM_INT);
$monRetour = $req->execute();
/**
* Suppression du HDD
*/
if ($monRetour) {
// Plus aucune miniature n'utilise le fichier (BDD déjà mise à jour !)
if ($this->getNbDoublons() == 0) {
// Je supprime l'image sur le HDD
$monRetour = unlink($this->getPathMd5());
}
// Si plus aucune image n'utilise le fichier => supprimer l'image sur le HDD
if (
$req->execute()
&& $this->getNbUsages() === 0
&& file_exists($this->getPathMd5())
) {
unlink($this->getPathMd5());
}
return $monRetour;
}
/**
* {@inheritdoc}
* @throws Exception
*/
public function creer()
public function creer(): bool
{
// Retour
$monRetour = true;
@ -171,7 +161,7 @@ class MiniatureObject extends RessourceObject implements RessourceInterface
* Déplacement du fichier
*/
// Vérification de la non existence du fichier
if ($this->getNbDoublons() == 0) {
if ($this->getNbUsages() === 0) {
$monRetour = rename($this->getPathTemp(), $this->getPathMd5());
}
@ -190,14 +180,14 @@ class MiniatureObject extends RessourceObject implements RessourceInterface
/**
* Création en BDD
*/
$req = MaBDD::getInstance()->prepare("INSERT INTO thumbnails (id_image, date_creation, new_name, size, height, width, md5) VALUES (:idImage, NOW(), :newName, :size, :height, :width, :md5)");
$req->bindValue(':idImage', $this->getIdImage(), PDO::PARAM_INT);
$req = MaBDD::getInstance()->prepare('INSERT INTO thumbnails (images_id, date_action, new_name, size, height, width, md5) VALUES (:imagesId, NOW(), :newName, :size, :height, :width, :md5)');
$req->bindValue(':imagesId', $this->getIdImage(), PDO::PARAM_INT);
// Date : NOW()
$req->bindValue(':newName', $this->getNomNouveau(), PDO::PARAM_STR);
$req->bindValue(':newName', $this->getNomNouveau());
$req->bindValue(':size', $this->getPoids(), PDO::PARAM_INT);
$req->bindValue(':height', $this->getHauteur(), PDO::PARAM_INT);
$req->bindValue(':width', $this->getLargeur(), PDO::PARAM_INT);
$req->bindValue(':md5', $this->getMd5(), PDO::PARAM_STR);
$req->bindValue(':md5', $this->getMd5());
if (!$req->execute()) {
// Gestion de l'erreur d'insertion en BDD
@ -221,7 +211,7 @@ class MiniatureObject extends RessourceObject implements RessourceInterface
* ID image parente
* @return int
*/
public function getIdImage()
public function getIdImage(): int
{
return $this->idImage;
}
@ -230,8 +220,24 @@ class MiniatureObject extends RessourceObject implements RessourceInterface
* ID image parente
* @param int $idImage
*/
public function setIdImage($idImage)
public function setIdImage(int $idImage): void
{
$this->idImage = $idImage;
}
/**
* @return bool
*/
public function getIsPreview(): bool
{
return $this->isPreview;
}
/**
* @param bool $isPreview
*/
public function setIsPreview(bool $isPreview): void
{
$this->isPreview = $isPreview;
}
}

View file

@ -1,215 +0,0 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
/**
* Bibliothèque d'outils pour la gestion des images
*/
class Outils
{
/**
* Type de l'image
* @param string $path chemin sur le filesystem
* @return string
*/
public static function getType($path)
{
return exif_imagetype($path);
}
/**
* MIME type de l'image
* @param string $path chemin sur le filesystem
* @return string
*/
public static function getMimeType($path)
{
return image_type_to_mime_type(self::getType($path));
}
/**
* Chargement ressource PHP image
* @return resource
*/
public static function getImage($path)
{
$monImage = null;
// Je charge l'image en mémoire en fonction de son type
switch (self::getType($path)) {
case IMAGETYPE_GIF:
$monImage = imagecreatefromgif($path);
break;
case IMAGETYPE_JPEG:
$monImage = imagecreatefromjpeg($path);
// Activation de l'entrelacement (image progressive)
imageinterlace($monImage, true);
break;
case IMAGETYPE_PNG:
$monImage = imagecreatefrompng($path);
// Gestion de la transparence
imagealphablending($monImage, true);
imagesavealpha($monImage, true);
break;
}
return $monImage;
}
/**
* Enregistrement d'une ressource PHP image
* @param ressource $uneImage Image a enregistrer
* @param int $imageType type PHP de l'image
* @param string $path chemin du fichier
* @return boolean Succès ?
*/
public static function setImage($uneImage, $imageType, $path)
{
$monRetour = false;
// Je charge l'image en mémoire en fonction de son type
switch ($imageType) {
case IMAGETYPE_GIF:
$monRetour = imagegif($uneImage, $path);
break;
case IMAGETYPE_JPEG:
// Activation de l'entrelacement (image progressive)
imageinterlace($uneImage, true);
$monRetour = imagejpeg($uneImage, $path, 100);
break;
case IMAGETYPE_PNG:
// Gestion de la transparence
imagealphablending($uneImage, true);
imagesavealpha($uneImage, true);
$monRetour = imagepng($uneImage, $path, 9);
break;
}
return $monRetour;
}
/**
* Fourni l'extension officielle d'une ressource
* @param string $path chemin sur le filesystem
* @return string
*/
public static function getExtension($path)
{
$ext = image_type_to_extension(self::getType($path), false);
if ($ext === 'jpeg') {
// Préférence pour .jpg [filenmae.ext]
$ext = 'jpg';
}
return $ext;
}
/**
* Taille mémoire maximale autorisée
* @see http://php.net/manual/fr/function.ini-get.php
* @return int
*/
public static function getMemoireAllouee()
{
// Récupération de la valeur du php.ini
$valBrute = trim(ini_get('memory_limit'));
// Gestion de l'unité multiplicatrice...
$unite = strtolower(substr($valBrute, -1));
$val = (int) substr($valBrute, 0, -1);
switch ($unite) {
case 'g':
$val *= 1024;
// no break
case 'm':
$val *= 1024;
// no break
case 'k':
$val *= 1024;
// no break
}
return $val;
}
/**
* Est-il possible de modifier l'image (mémoire suffisante ?)
* @param string $path
* @return boolean Possible ?
* @see http://www.dotsamazing.com/en/labs/phpmemorylimit
*/
public static function isModifiableEnMemoire($path)
{
$monRetour = false;
// Nombre de canaux d'information de l'image
$nbCanaux = 4;
/**
* Information sur les canaux ?
*/
$imageinfo = getimagesize($path);
// Si information sur les canaux de l'image...
if (isset($imageinfo['channels']) && is_int($imageinfo['channels'])) {
$nbCanaux = $imageinfo['channels'];
}
/**
* Mémoire requise :
* (hauteur x largeur x profondeur)
* => x 2 [imageSource + imageDest]
* => x 1.8 [fudge factor]
*/
$memReq = $imageinfo[1] * $imageinfo[0] * $nbCanaux;
$memReq *= 2;
$memReq *= _FUDGE_FACTOR_;
// Est-ce possible ?
if ($memReq < self::getMemoireAllouee()) {
$monRetour = true;
}
return $monRetour;
}
/**
* Dimension maximale acceptable en mémoire pour les images
* <br />Suppose que l'image est carrée (donc indicatif !)
* <br /> Suppose 4 canaux dans l'image
* @return int
* @see isModifiableEnMemoire
*/
public static function getMaxDimension()
{
$memDispo = self::getMemoireAllouee();
/**
* Mémoire requise :
* (hauteur x largeur x profondeur)
* => x 2 [imageSource + imageDest]
* => x 1.8 [fudge factor]
*/
$dimMax = round(sqrt($memDispo / 4 / 2 / _FUDGE_FACTOR_), 0);
return (int) $dimMax;
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -26,29 +26,27 @@ namespace ImageHeberg;
*/
interface RessourceInterface
{
/**
* Crée sur le HDD et dans la BDD la ressource
* @return boolean Résultat ?
* @return bool Résultat ?
*/
public function creer();
public function creer(): bool;
/**
* Charge unn objet ressource depuis la BDD
* @param string $nom Identifiant image-heberg
* @return boolean Résultat ?
* Charge un objet ressource depuis la BDD
* @param string $value Identifiant image-heberg
* @param string $fromField Champ à utiliser en BDD
* @return bool Résultat ?
*/
public function charger($nom);
public function charger(string $value, string $fromField = RessourceObject::SEARCH_BY_NAME): bool;
/**
* Enregistre en BDD un objet ressource
* @return boolean Résultat ?
*/
public function sauver();
public function sauver(): void;
/**
* Supprime sur le HDD et dans la BDD la ressource
* @return boolean Résultat ?
*/
public function supprimer();
public function supprimer(): void;
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -21,6 +21,8 @@
namespace ImageHeberg;
use Exception;
use ImagickException;
use PDO;
/**
@ -28,52 +30,67 @@ use PDO;
*/
abstract class RessourceObject
{
public const TYPE_IMAGE = 1;
public const TYPE_MINIATURE = 2;
// Types de ressources
final public const int TYPE_IMAGE = 1;
final public const int TYPE_MINIATURE = 2;
private $id;
private $nomOriginal;
private $nomNouveau;
private $largeur;
private $hauteur;
private $poids;
private $lastView;
private $nbViewIPv4;
private $nbViewIPv6;
private $dateEnvoi;
private $md5;
private $ipEnvoi;
private $isBloquee;
private $isSignalee;
private $pathTemp;
private $type;
private $nomTemp;
// Champ à utiliser en BDD pour charger la ressource
final public const string SEARCH_BY_MD5 = 'md5';
final public const string SEARCH_BY_NAME = 'new_name';
final public const string SEARCH_BY_ID = 'id';
// Attributs de la classe
private int $id = 0;
private string $nomOriginal = '';
private string $nomNouveau = '';
private int $largeur = 0;
private int $hauteur = 0;
private int $poids = 0;
private string $lastView = '0000-00-00';
private int $nbViewIPv4 = 0;
private int $nbViewIPv6 = 0;
private string $dateEnvoi = '';
private ?string $md5 = null;
private string $ipEnvoi = '';
private bool $isBloquee = false;
private bool $isSignalee = false;
private bool $isApprouvee = false;
private bool $isSuspecte = false;
private string $pathTemp = '';
private int $type = self::TYPE_IMAGE;
private string $nomTemp = '';
private ?int $idProprietaire = null;
/**
* Génère le nom d'une nouvelle image
* @param int $nb nombre de chiffres à rajouter à la fin du nom
* @return string nom de l'image
* @throws Exception
*/
protected function genererNom($nb = 0)
protected function genererNom(int $nb = 0): string
{
// Random pour unicité + cassage lien nom <-> @IP
$random = rand(100, 999);
$random = random_int(100, 999);
// @IP expéditeur
$adresseIP = abs(crc32($_SERVER['REMOTE_ADDR'] . $random));
// Timestamp d'envoi
$timestamp = $_SERVER['REQUEST_TIME'];
// Calcul du nom de l'image
$new_name = $timestamp . $adresseIP . substr($random, 0, $nb) . '.' . Outils::getExtension($this->getPathTemp());
// Taille max : 28 chr
// - Timestamp : 1699048241 : 10 caractères jusqu'en 2286...
// - $adresseIP -> CRC32 : 8 bits en hexa, soit 16^8 valeurs => 4 294 967 296 : 10 caractères
// - Suffixe anti-doublon : hypothèse très haute 10k doublons : 4 caractères
// - Extension : 1+3 caractères
return $new_name;
// Calcul du nom de l'image
return $timestamp . $adresseIP . substr($random, 0, $nb) . '.' . HelperImage::getExtension($this->getPathTemp());
}
/**
* MD5 de la ressource
* @return string
*/
public function getMd5()
public function getMd5(): string
{
// Création d'une image => Utilisation du fichier temporaire
if (is_null($this->md5) && $this->getPathTemp()) {
@ -88,13 +105,9 @@ abstract class RessourceObject
* Path sur le HDD
* @return string
*/
public function getPathMd5()
public function getPathMd5(): string
{
// Path final
$pathFinal = '';
// Path du type d'image
$pathDuType = '';
if ($this->getType() === self::TYPE_IMAGE) {
// Image
$pathDuType = _PATH_IMAGES_;
@ -103,8 +116,9 @@ abstract class RessourceObject
$pathDuType = _PATH_MINIATURES_;
}
// Path final
if ($this->getType() === self::TYPE_IMAGE && ($this->getId() === 1 || $this->getId() === 2)) {
// Gestion des images spécificques 404 / ban
// Gestion des images spécifiques 404 / ban
$pathFinal = $pathDuType . $this->getNomNouveau();
} else {
// Cas par défaut
@ -117,39 +131,40 @@ abstract class RessourceObject
/**
* Nombre d'images ayant le même MD5 (Normalement 1 à minima, l'image courante...)
* @return int nombre d'images ayant ce MD5 (-1 en cas d'erreur)
* @return int nombre d'images ayant ce MD5 (0 par défaut)
*/
public function getNbDoublons()
public function getNbUsages(): int
{
// Retour - -1 par défaut pour marquer l'erreur
$monRetour = -1;
$monRetour = 0;
// Existe-t-il d'autres occurences de cette image ?
if ($this->getType() === self::TYPE_IMAGE) {
// Image
$req = MaBDD::getInstance()->prepare("SELECT COUNT(*) AS nb FROM images WHERE md5 = :md5");
} else {
// Miniature
$req = MaBDD::getInstance()->prepare("SELECT COUNT(*) AS nb FROM thumbnails WHERE md5 = :md5");
}
/* @var $req PDOStatement */
$req->bindValue(':md5', $this->getMd5(), PDO::PARAM_STR);
$req->execute();
$values = $req->fetch();
if ($values !== false) {
$monRetour = (int) $values->nb;
// Si l'image n'existe pas et qu'on est pas en train de l'envoyer, ne pas tenter de la charger
if (!is_null($this->md5) || $this->getPathTemp()) {
// Existe-t-il d'autres occurences de cette image ?
if ($this->getType() === self::TYPE_IMAGE) {
// Image
$req = MaBDD::getInstance()->prepare('SELECT COUNT(*) AS nb FROM images WHERE md5 = :md5');
} else {
// Miniature
$req = MaBDD::getInstance()->prepare('SELECT COUNT(*) AS nb FROM thumbnails WHERE md5 = :md5');
}
$req->bindValue(':md5', $this->getMd5());
$req->execute();
$resultat = $req->fetch();
if ($resultat !== false) {
$monRetour = (int)$resultat->nb;
}
}
return $monRetour;
}
/**
* URL de la ressource
* @param bool $forceHttps Forcer le HTTPS ?
* @return string
*/
public function getURL()
public function getURL(bool $forceHttps = false): string
{
// Path du type d'image
$urlDuType = '';
if ($this->getType() === self::TYPE_IMAGE) {
// Image
$urlDuType = _URL_IMAGES_;
@ -158,54 +173,41 @@ abstract class RessourceObject
$urlDuType = _URL_MINIATURES_;
}
// Forcer le HTTPS ?
if ($forceHttps) {
$urlDuType = str_replace('http:/', 'https:/', $urlDuType);
}
return $urlDuType . $this->getNomNouveau();
}
/**
* Rotation d'une ressource <br />
* Inclus mise à jour largeur / hauteur / poids de l'image
* @param int $angle xxx° de rotation GAUCHE
* @param int $angle xxx° de rotation horaire
* @param string $pathSrc chemin de la ressource d'origine
* @param string $pathDst chemin de la ressource de destination
* @return boolean succès ?
* @return bool succès ?
* @throws ImagickException
*/
public function rotation($angle, $pathSrc, $pathDst)
public function rotation(int $angle, string $pathSrc, string $pathDst): bool
{
// Je charge l'image en mémoire
$resImg = Outils::getImage($pathSrc);
// Je vérifie que tout va bien
if ($resImg === false) {
return false;
$resImg = HelperImage::getImage($pathSrc);
// Rotation (Imagick est dans le sens horaire, imagerotate dans le sens anti-horaire)
if (HelperImage::getType($pathSrc) === IMAGETYPE_GIF) {
// Image animée (GIF) : c'est une succession d'images !
foreach ($resImg as $frame) {
$frame->rotateImage('rgb(0,0,0)', $angle);
}
} else {
// Cas standard (image non animée)
$resImg->rotateImage('rgb(0,0,0)', $angle);
}
// J'effectue la rotation
$imgRotate = imagerotate($resImg, $angle, 0);
// Je vérifie que tout va bien
if ($imgRotate === false) {
return false;
}
// Nettoyage mémoire (image d'origine)
imagedestroy($resImg);
// J'enregistre l'image
$retour = Outils::setImage($imgRotate, Outils::getType($pathSrc), $pathDst);
// La création du fichier s'est bien passé ?
if ($retour === false) {
return false;
}
// Mise à jour des propriétés de l'image
// Dimensions
$this->setLargeur(imagesx($imgRotate));
$this->setHauteur(imagesy($imgRotate));
// Poids de l'image
$this->setPoids(filesize($pathDst));
return $retour;
return HelperImage::setImage($resImg, HelperImage::getType($pathSrc), $pathDst);
}
/**
@ -214,81 +216,53 @@ abstract class RessourceObject
* @param string $pathDst chemin de la ressource de destination
* @param int $largeurDemandee largeur souhaitée
* @param int $hauteurDemandee hauteur souhaitée
* @return boolean réussi ?
* @return bool réussi ?
* @throws ImagickException
*/
public function redimensionner($pathSrc, $pathDst, $largeurDemandee, $hauteurDemandee)
public function redimensionner(string $pathSrc, string $pathDst, int $largeurDemandee, int $hauteurDemandee): bool
{
// Chargement de l'image
$monImage = Outils::getImage($pathSrc);
$monImage = HelperImage::getImage($pathSrc);
// Récupération de ses dimensions
$largeurImage = imagesx($monImage);
$hauteurImage = imagesy($monImage);
$largeurImage = $monImage->getImageWidth();
$hauteurImage = $monImage->getImageHeight();
// Dimension nulle : on arrête
// Dimensions incohérentes : on arrête
if ($hauteurImage <= 0 || $hauteurDemandee <= 0 || $largeurImage <= 0 || $largeurDemandee <= 0 || $largeurImage <= $largeurDemandee || $hauteurImage <= $hauteurDemandee) {
return false;
}
/**
* @author Nicolas
*/
if ($largeurImage > $hauteurImage) {
// Format paysage
$largeurMax = max(array($largeurDemandee, $hauteurDemandee));
$hauteurMax = min(array($largeurDemandee, $hauteurDemandee));
// Redimensionnement par Imagick
if (HelperImage::getType($pathSrc) === IMAGETYPE_GIF) {
// Cas image animée (GIF) : c'est une succession d'images !
foreach ($monImage as $frame) {
$frame->thumbnailImage($largeurDemandee, $hauteurDemandee, true);
}
} else {
// Format portrait ou carré
$largeurMax = min(array($largeurDemandee, $hauteurDemandee));
$hauteurMax = max(array($largeurDemandee, $hauteurDemandee));
// Cas standard (image non animée)
$monImage->thumbnailImage($largeurDemandee, $hauteurDemandee, true);
}
// Calcul du ratio
$monRatio = min(array($largeurMax / $largeurImage, $hauteurMax / $hauteurImage));
// Dimensions finales
$largeurFinale = round($largeurImage * $monRatio);
$hauteurFinale = round($hauteurImage * $monRatio);
// Debug
if (_PHPUNIT_) {
echo "Initial : " . $largeurImage . " x " . $hauteurImage . "\r\n";
echo "Demandé : " . $largeurDemandee . " x " . $hauteurDemandee . "\r\n";
echo "Fourni : " . $largeurFinale . " x " . $hauteurFinale . "\r\n";
}
// Redimensionnement (en mémoire)
$newImage = imagescale($monImage, $largeurFinale, $hauteurFinale);
// Ecriture de l'image
$monRetour = Outils::setImage($newImage, Outils::getType($pathSrc), $pathDst);
return $monRetour;
return HelperImage::setImage($monImage, HelperImage::getType($pathSrc), $pathDst);
}
/**
* Cet utilisateur est-il propriétaire de l'image ?
* @return boolean
* @return bool
*/
public function isProprietaire()
public function isProprietaire(): bool
{
$monRetour = false;
// Je vais chercher les infos en BDD
$req = MaBDD::getInstance()->prepare("SELECT * FROM possede WHERE image_id = :imageId");
/* @var $req PDOStatement */
$req->bindValue(':imageId', $this->getId(), PDO::PARAM_INT);
$req->execute();
// Je récupère les potentielles valeurs
$values = $req->fetch();
// Si l'image à un propriétaire...
if ($values !== false) {
if ($this->getIdProprietaire() !== null) {
// Le propriétaire est-il connecté ?
$uneSession = new SessionObject();
// Est-ce le propriétaire de l'image ?
if ((int) $values->pk_membres === $uneSession->getId()) {
if ($this->getIdProprietaire() === $uneSession->getId()) {
// Si oui... on confirme !
$monRetour = true;
}
@ -301,62 +275,96 @@ abstract class RessourceObject
* Date d'envoi formatée
* @return string
*/
public function getDateEnvoiFormatee()
public function getDateEnvoiFormatee(): string
{
$phpdate = strtotime($this->getDateEnvoiBrute());
return date("d/m/Y H:i:s", $phpdate);
return date('d/m/Y H:i:s', $phpdate);
}
/**
* Date de dernier affichage formaté
* @return string
*/
public function getLastViewFormate()
public function getLastViewFormate(): string
{
$phpdate = strtotime($this->getLastView());
$monRetour = '?';
if ($this->getLastView() !== '0000-00-00') {
$phpdate = strtotime($this->getLastView());
// Gestion du cas de non affichage
if ($phpdate === 0) {
return "-";
// Gestion du cas de non affichage
if ($phpdate !== 0 && $phpdate !== false) {
$monRetour = date('d/m/Y', $phpdate);
}
}
return date("d/m/Y", $phpdate);
return $monRetour;
}
/**
* Nombre d'appels IPv4 & IPv6
* @return int
*/
public function getNbViewTotal()
public function getNbViewTotal(): int
{
return (int) $this->getNbViewIPv4() + $this->getNbViewIPv6();
return $this->getNbViewIPv4() + $this->getNbViewIPv6();
}
/**
* Nombre d'affichage par jour
* @return int
*/
public function getNbViewPerDay(): int
{
// date_diff ne comptabilise que les journées entières, alors qu'en SQL, on compare des dates
// date_diff('2023-09-03 23:38:42', '2023-09-05 22:53:03') => 1
// date_diff('2023-09-03', '2023-09-05 22:53:03') => 2
// => substr() de la date d'envoi pour aligner sur les valeurs du SQL
$nbJours = (int)date_diff(date_create(substr($this->getDateEnvoiBrute(), 0, 10)), date_create())->format('%r%a');
// Le premier jour, autoriser les xxx vues de la journée
if ($nbJours === 0) {
$nbJours = 1;
}
return (int)($this->getNbViewTotal() / $nbJours);
}
/**
* Nom original de la ressource
* @return string
*/
public function getNomOriginalFormate()
public function getNomOriginalFormate(): string
{
return htmlentities($this->nomOriginal);
return htmlentities($this->nomOriginal, ENT_SUBSTITUTE);
}
/**
* Incrémente le nombre d'affichage IPv4
* Met à jour les statistiques (nb d'affichage et date) en BDD
*/
public function setNbViewIpv4PlusUn()
public function updateStatsAffichage(string $remoteAddr): void
{
$this->nbViewIPv4 = $this->getNbViewIPv4() + 1;
$this->setLastView(date("Y-m-d"));
// Prendre la bonne table
if ($this->getType() === self::TYPE_IMAGE) {
// Image
$table = 'images';
} else {
// Miniature
$table = 'thumbnails';
}
// Prendre le bon type d'@ IP
if (filter_var($remoteAddr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
// IPv4
$typeAcces = 'nb_view_v4';
} else {
// IPv6
$typeAcces = 'nb_view_v6';
}
$req = MaBDD::getInstance()->prepare('UPDATE ' . $table . ' SET last_view = NOW(), ' . $typeAcces . ' = ' . $typeAcces . ' + 1 WHERE id = :id');
$req->bindValue(':id', $this->getId(), PDO::PARAM_INT);
$req->execute();
}
/**
* Incrémente le nombre d'affichage IPv6
*/
public function setNbViewIpv6PlusUn()
{
$this->nbViewIPv6 = $this->getNbViewIPv6() + 1;
$this->setLastView(date("Y-m-d"));
}
/**
* GETTERS ET SETTERS
*/
@ -365,16 +373,16 @@ abstract class RessourceObject
* ID de la ressource
* @return int
*/
public function getId()
public function getId(): int
{
return (int) $this->id;
return $this->id;
}
/**
* Nom original de la ressource
* @return string
*/
protected function getNomOriginal()
protected function getNomOriginal(): string
{
return $this->nomOriginal;
}
@ -383,7 +391,7 @@ abstract class RessourceObject
* Nom image-heberg
* @return string
*/
public function getNomNouveau()
public function getNomNouveau(): string
{
return $this->nomNouveau;
}
@ -392,42 +400,43 @@ abstract class RessourceObject
* Largeur en px
* @return int
*/
public function getLargeur()
public function getLargeur(): int
{
return (int) $this->largeur;
return $this->largeur;
}
/**
* Hauteur en px
* @return int
*/
public function getHauteur()
public function getHauteur(): int
{
return (int) $this->hauteur;
return $this->hauteur;
}
/**
* Poids de la ressource
* @return int
*/
public function getPoids()
public function getPoids(): int
{
return (int) $this->poids;
return $this->poids;
}
/**
* Poids de la ressource en Mo
* @return float
*/
public function getPoidsMo() {
return round($this->getPoids() /1024/1024, 1);
public function getPoidsMo(): float
{
return round($this->getPoids() / 1024 / 1024, 1);
}
/**
* Date de dernier affichage
* @return type
* @return string
*/
protected function getLastView()
protected function getLastView(): string
{
return $this->lastView;
}
@ -436,25 +445,25 @@ abstract class RessourceObject
* Nb d'affichage en IPv4
* @return int
*/
protected function getNbViewIPv4()
protected function getNbViewIPv4(): int
{
return (int) $this->nbViewIPv4;
return $this->nbViewIPv4;
}
/**
* Nb d'affichage en IPv6
* @return int
*/
protected function getNbViewIPv6()
protected function getNbViewIPv6(): int
{
return (int) $this->nbViewIPv6;
return $this->nbViewIPv6;
}
/**
* Date d'envoi du fichier
* @return type
* @return string
*/
public function getDateEnvoiBrute()
public function getDateEnvoiBrute(): string
{
return $this->dateEnvoi;
}
@ -463,43 +472,61 @@ abstract class RessourceObject
* @ IP d'envoi
* @return string
*/
public function getIpEnvoi()
public function getIpEnvoi(): string
{
return $this->ipEnvoi;
}
/**
* Image bloquée ?
* @return boolean
* @return bool
*/
public function isBloquee()
public function isBloquee(): bool
{
return $this->isBloquee;
}
/**
* Image signalée ?
* @return boolean
* @return bool
*/
public function isSignalee()
public function isSignalee(): bool
{
return $this->isSignalee;
}
/**
* Image approuvée (marquée comme valide)
* @return bool
*/
public function isApprouvee(): bool
{
return $this->isApprouvee;
}
/**
* Image envoyée depuis un réseau suspect
* @return bool
*/
public function isSuspecte(): bool
{
return $this->isSuspecte;
}
/**
* Path temporaire (upload d'image)
* @return string
*/
public function getPathTemp()
public function getPathTemp(): string
{
return $this->pathTemp;
}
/**
* Type d'image
* @return int ressoruceObject const
* @return int ressourceObject const
*/
public function getType()
public function getType(): int
{
return $this->type;
}
@ -508,7 +535,7 @@ abstract class RessourceObject
* Nom temporaire (PC utilisateur - upload d'image)
* @return string
*/
public function getNomTemp()
public function getNomTemp(): string
{
return $this->nomTemp;
}
@ -517,7 +544,7 @@ abstract class RessourceObject
* Nom temporaire (PC utilisateur - upload d'image)
* @param string $nomTemp
*/
public function setNomTemp($nomTemp)
public function setNomTemp(string $nomTemp): void
{
$this->nomTemp = $nomTemp;
}
@ -526,7 +553,7 @@ abstract class RessourceObject
* Type d'image
* @param int $type RessourceObject const
*/
public function setType($type)
public function setType(int $type): void
{
$this->type = $type;
}
@ -535,34 +562,52 @@ abstract class RessourceObject
* Path temporaire (upload d'image)
* @param string $pathTemp
*/
public function setPathTemp($pathTemp)
public function setPathTemp(string $pathTemp): void
{
$this->pathTemp = $pathTemp;
}
/**
* Image bloquée ?
* @param boolean $bloquee
* @param bool $bloquee
*/
public function setBloquee($bloquee)
public function setBloquee(bool $bloquee): void
{
$this->isBloquee = $bloquee;
}
/**
* Image signalée ?
* @param boolean $isSignalee
* @param bool $isSignalee
*/
public function setSignalee($isSignalee)
public function setSignalee(bool $isSignalee): void
{
$this->isSignalee = $isSignalee;
}
/**
* Image approuvée (marquée comme valide) ?
* @param bool $isApprouvee
*/
public function setApprouvee(bool $isApprouvee): void
{
$this->isApprouvee = $isApprouvee;
}
/**
* Image envoyée depuis un réseau suspect
* @param bool $isSuspecte
*/
public function setSuspecte(bool $isSuspecte): void
{
$this->isSuspecte = $isSuspecte;
}
/**
* ID de l'image
* @param int $id
*/
protected function setId($id)
protected function setId(int $id): void
{
$this->id = $id;
}
@ -571,7 +616,7 @@ abstract class RessourceObject
* Nom original de la ressource
* @param string $nomOriginal
*/
protected function setNomOriginal($nomOriginal)
protected function setNomOriginal(string $nomOriginal): void
{
$this->nomOriginal = $nomOriginal;
}
@ -580,7 +625,7 @@ abstract class RessourceObject
* Nom image-heberg
* @param string $nomNouveau
*/
protected function setNomNouveau($nomNouveau)
protected function setNomNouveau(string $nomNouveau): void
{
$this->nomNouveau = $nomNouveau;
}
@ -589,7 +634,7 @@ abstract class RessourceObject
* Largeur en px
* @param int $largeur
*/
protected function setLargeur($largeur)
protected function setLargeur(int $largeur): void
{
$this->largeur = $largeur;
}
@ -598,7 +643,7 @@ abstract class RessourceObject
* Hauteur en px
* @param int $hauteur
*/
protected function setHauteur($hauteur)
protected function setHauteur(int $hauteur): void
{
$this->hauteur = $hauteur;
}
@ -607,17 +652,21 @@ abstract class RessourceObject
* Poids de la ressource
* @param int $poids
*/
protected function setPoids($poids)
protected function setPoids(int $poids): void
{
$this->poids = $poids;
}
/**
* Date de dernier affichage
* @param type $lastView
* @param ?string $lastView
*/
protected function setLastView($lastView)
protected function setLastView(?string $lastView): void
{
if (is_null($lastView)) {
// Si l'image n'a jamais été affichée, elle est à NULL en BDD
$lastView = '';
}
$this->lastView = $lastView;
}
@ -625,7 +674,7 @@ abstract class RessourceObject
* Nb d'affichage en IPv4
* @param int $nbViewIPv4
*/
protected function setNbViewIPv4($nbViewIPv4)
protected function setNbViewIPv4(int $nbViewIPv4): void
{
$this->nbViewIPv4 = $nbViewIPv4;
}
@ -634,16 +683,16 @@ abstract class RessourceObject
* Nb d'affichage en IPv6
* @param int $nbViewIPv6
*/
protected function setNbViewIPv6($nbViewIPv6)
protected function setNbViewIPv6(int $nbViewIPv6): void
{
$this->nbViewIPv6 = $nbViewIPv6;
}
/**
* Date d'envoi du fichier
* @param type $dateEnvoi
* @param string $dateEnvoi
*/
protected function setDateEnvoi($dateEnvoi)
protected function setDateEnvoi(string $dateEnvoi): void
{
$this->dateEnvoi = $dateEnvoi;
}
@ -652,7 +701,7 @@ abstract class RessourceObject
* MD5 de la ressource
* @param string $md5
*/
protected function setMd5($md5)
protected function setMd5(string $md5): void
{
$this->md5 = $md5;
}
@ -661,8 +710,26 @@ abstract class RessourceObject
* @ IP d'envoi
* @param string $ipEnvoi
*/
protected function setIpEnvoi($ipEnvoi)
protected function setIpEnvoi(string $ipEnvoi): void
{
$this->ipEnvoi = $ipEnvoi;
}
/**
* ID du compte propriétaire de l'image
* @return ?int
*/
public function getIdProprietaire(): ?int
{
return $this->idProprietaire;
}
/**
* @param ?int $idProprietaire ID du compte propriétaire de l'image
* @return void
*/
protected function setIdProprietaire(?int $idProprietaire): void
{
$this->idProprietaire = $idProprietaire;
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -27,26 +27,23 @@ namespace ImageHeberg;
class SessionObject
{
// @ IP de l'utilisateur
private $IP;
private string $IP = '';
// Objet utilisateur
private $userObject;
private UtilisateurObject $userObject;
public function __construct()
{
// Je vérifie qu'une session n'est pas déjà lancée & que pas tests travis (session_start déjà effectué)
if (session_status() === PHP_SESSION_NONE && !_PHPUNIT_) {
if (!_PHPUNIT_ && session_status() === PHP_SESSION_NONE) {
// Je lance la session côté PHP
session_start();
}
// Si j'ai déjà une session existante
if (isset($_SESSION['userObject'])) {
// Si l'@ IP correspond
if ($_SESSION['IP'] === $_SERVER['REMOTE_ADDR']) {
// On recharge les informations
$this->setIP($_SESSION['IP']);
$this->setUserObject($_SESSION['userObject']);
}
// Si j'ai déjà une session existante && que l'@ IP correspond
if (isset($_SESSION['userObject']) && $_SESSION['IP'] === $_SERVER['REMOTE_ADDR']) {
// On recharge les informations
$this->setIP($_SESSION['IP']);
$this->setUserObject($_SESSION['userObject']);
}
}
@ -54,20 +51,16 @@ class SessionObject
* Mon utilisateur
* @return UtilisateurObject
*/
private function getUserObject()
private function getUserObject(): UtilisateurObject
{
if (isset($this->userObject)) {
return $this->userObject;
} else {
return new UtilisateurObject();
}
return $this->userObject ?? new UtilisateurObject();
}
/**
* Mon utilisateur
* @param UtilisateurObject $userObject Objet utilisateur
*/
public function setUserObject($userObject)
public function setUserObject(UtilisateurObject $userObject): void
{
$this->userObject = $userObject;
$_SESSION['userObject'] = $userObject;
@ -77,7 +70,7 @@ class SessionObject
* Nom d'utilisateur
* @return string
*/
public function getUserName()
public function getUserName(): string
{
return $this->getUserObject()->getUserName();
}
@ -86,16 +79,16 @@ class SessionObject
* @ IP
* @return string
*/
public function getIP()
public function getIP(): string
{
return $this->IP;
}
/**
* Niveau de droits
* @return type
* @return int
*/
public function getLevel()
public function getLevel(): int
{
return $this->getUserObject()->getLevel();
}
@ -104,16 +97,16 @@ class SessionObject
* ID en BDD
* @return int
*/
public function getId()
public function getId(): int
{
return (int) $this->getUserObject()->getId();
return $this->getUserObject()->getId();
}
/**
* IP
* @param string $IP
*/
public function setIP($IP)
public function setIP(string $IP): void
{
$this->IP = $IP;
// On enregistre dans la session
@ -122,22 +115,22 @@ class SessionObject
/**
* Vérification des droits de l'utilisateur pour la page
* @param type $levelRequis
* @return boolean
* @param int $levelRequis
* @return bool
*/
public function verifierDroits($levelRequis)
public function verifierDroits(int $levelRequis): bool
{
$monRetour = false;
if ($this->getLevel() >= $levelRequis) {
return true;
} else {
return false;
$monRetour = true;
}
return $monRetour;
}
/**
* Déconnexion d'un utilisateur
*/
public function deconnexion()
public function deconnexion(): void
{
// Destruction de l'objet utilisateur
unset($_SESSION['userObject']);
@ -151,25 +144,30 @@ class SessionObject
/**
* Active le flag de suivi (vérification d'affichage de page avant envoi)
*/
public function setFlag()
public function setFlag(): void
{
$_SESSION['flag'] = true;
$_SESSION['flag'] = time();
}
/**
* Supprime le flag de suivi
*/
public function removeFlag()
public function removeFlag(): void
{
unset($_SESSION['flag']);
}
/**
* Vérifie le flag de suivi
* @return boolean Suivi OK ?
* Vérifie le flag de suivi (a été activé il y a plus d'une seconde)
* @return bool Suivi OK ?
*/
public function checkFlag()
public function checkFlag(): bool
{
return isset($_SESSION['flag']);
$monRetour = false;
// Au moins une seconde pour remplir le formulaire
if (isset($_SESSION['flag']) && (time() - $_SESSION['flag']) > 1) {
$monRetour = true;
}
return $monRetour;
}
}

182
classes/Tor.class.php Normal file
View file

@ -0,0 +1,182 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
use JsonException;
/**
* Fonctions relative à Tor
*/
class Tor
{
private const string IPV4 = 'IPv4';
private const string IPV6 = 'IPv6';
/**
* Mettre à jour la liste des adresses IP des noeuds de sortie Tor
* @throws JsonException
*/
public function updateListeExitNodes(): void
{
$torNodeList = file_get_contents(_TOR_EXIT_NODE_LIST_URL_);
// Ne mettre à jour que si on a des données
if (!empty($torNodeList)) {
// Récupération du dernier fichier
$objJson = json_decode($torNodeList, false, 512, JSON_THROW_ON_ERROR);
$tabIP = [];
$tabIP[self::IPV4] = [];
$tabIP[self::IPV6] = [];
foreach ($objJson->relays as $unRelay) {
// Adresse IP de sortie (IPv4 uniquement)
// https://metrics.torproject.org/onionoo.html#details_relay_exit_addresses
if (isset($unRelay->exit_addresses)) {
foreach ($unRelay->exit_addresses as $uneIp) {
$this->addToTab($uneIp, $tabIP);
}
}
// Adresse IP sur lequel le noeud écoute (IPv4 + IPv6)
// Lorsque exit_addresses incluera les IPv6, plus besoin de cette partie qui surbloque...
if (isset($unRelay->or_addresses)) {
foreach ($unRelay->or_addresses as $uneIp) {
$this->addToTab($uneIp, $tabIP, true);
}
}
}
// Enregister le résultat sur le disque
$retour = file_put_contents(_TOR_LISTE_IPV4_, json_encode($tabIP[self::IPV4], JSON_THROW_ON_ERROR));
echo 'IPv4 : ' . $retour;
$retour = file_put_contents(_TOR_LISTE_IPV6_, json_encode($tabIP[self::IPV6], JSON_THROW_ON_ERROR));
echo '<br />IPv6 : ' . $retour;
} else {
// Envoyer un mail d'avertissement
mail(_ADMINISTRATEUR_EMAIL_, '[' . _SITE_NAME_ . '] - Actualisation des noeuds Tor en erreur', 'Liste de noeuds récupérée : ' . var_export($torNodeList, true), 'From: ' . _ADMINISTRATEUR_EMAIL_);
die();
}
}
/**
* Nettoyer et ajouter une IP dans le tableau des adresses connues
* @param string $ip @ IP à ajouter
* @param string[] $tabIp Liste des addresses IP déjà connues
* @param bool $withPort Le port est précisé (1.2.3.4:1234)
*/
private function addToTab(string $ip, array &$tabIp, bool $withPort = false): void
{
if (substr_count($ip, ':') > 1) {
// C'est une IPv6
// Supprimer le port
if ($withPort) {
$ip = substr($ip, 0, strrpos($ip, ':'));
}
// Supprimer les crochets de la notation [1234:5678::]
$ip = str_replace(['[', ']'], '', $ip);
// Forcer la réécriture de l'IP
$ip = inet_ntop(inet_pton($ip));
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$this->saveInTab($ip, $tabIp, self::IPV6);
}
} else {
// C'est une IPv4
// Supprimer le port
if ($withPort) {
$ip = substr($ip, 0, strrpos($ip, ':'));
}
// Valider l'IP et l'enregistrer si inconnue
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
$this->saveInTab($ip, $tabIp, self::IPV4);
}
}
}
/**
* Insérer dans le tableau une adresse sans faire de doublon
* @param string $ip @ IP à ajouter
* @param array $tabIp Liste des addresses IP déjà connues
* @param string $typeIp IPv4 ou IPv6
*/
private function saveInTab(string $ip, array &$tabIp, string $typeIp): void
{
if (!in_array($ip, $tabIp[$typeIp], true)) {
$tabIp[$typeIp][] = self::formatIp($ip);
}
}
/**
* Formatter une adresse IP
* @param string $ip adresse IP à formatter
* @return string adresse IP formattée
*/
private static function formatIp(string $ip): string
{
return inet_ntop(inet_pton($ip));
}
/**
* Vérifie si une IP correspond à un noeud de sortie Tor
* @param string $ip
* @return bool
*/
public static function checkIp(string $ip): bool
{
$monRetour = true;
$ip = self::formatIp($ip);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
if (file_exists(_TOR_LISTE_IPV6_) && filesize(_TOR_LISTE_IPV6_) > 0) {
try {
$tabIp = json_decode(file_get_contents(_TOR_LISTE_IPV6_), true, 512, JSON_THROW_ON_ERROR);
if (!in_array($ip, $tabIp, true)) {
$monRetour = false;
}
} catch (JsonException $e) {
// En cas d'erreur, par défaut, faire confiance à l'IP
$monRetour = false;
}
}
} elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
if (file_exists(_TOR_LISTE_IPV4_) && filesize(_TOR_LISTE_IPV4_) > 0) {
try {
$tabIp = json_decode(file_get_contents(_TOR_LISTE_IPV4_), true, 512, JSON_THROW_ON_ERROR);
if (!in_array($ip, $tabIp, true)) {
$monRetour = false;
}
} catch (JsonException $e) {
// En cas d'erreur, par défaut, faire confiance à l'IP
$monRetour = false;
}
}
}
return $monRetour;
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -29,29 +29,30 @@ use ArrayObject;
*/
class UtilisateurObject
{
private $userName;
private $password;
private $email;
private $dateInscription;
private $ipInscription;
private $level = self::LEVEL_GUEST;
private $id = 0;
private $isActif = 1;
private $token;
private string $userName = '';
private string $password = '';
private string $email = '';
private string $dateInscription = '';
private string $ipInscription = '';
private int $level = self::LEVEL_GUEST;
private int $id = 0;
private bool $isActif = true;
private string $token = '';
// Niveaux de droits
public const LEVEL_GUEST = 0;
public const LEVEL_USER = 1;
public const LEVEL_ADMIN = 2;
final public const int LEVEL_GUEST = 0;
final public const int LEVEL_USER = 1;
final public const int LEVEL_ADMIN = 2;
/**
* @throws ImageHebergException
*/
public function __construct($userID = false)
{
// Utilisateur à charger
if ($userID) {
if (!$this->charger($userID)) {
// Envoi d'une exception si l'utilisateur n'existe pas
throw new Exception('Utilisateur ' . $userID . ' inexistant.');
}
if ($userID && !$this->charger($userID)) {
// Envoi d'une exception si l'utilisateur n'existe pas
throw new ImageHebergException('Utilisateur ' . $userID . ' inexistant.');
}
}
@ -59,7 +60,7 @@ class UtilisateurObject
* Nom d'utilisateur avec htmlentities
* @return string
*/
public function getUserName()
public function getUserName(): string
{
return htmlentities($this->userName);
}
@ -68,7 +69,7 @@ class UtilisateurObject
* BDD - Nom d'utilisateur non htmlentities
* @return string
*/
private function getUserNameBDD()
private function getUserNameBDD(): string
{
return $this->userName;
}
@ -77,7 +78,7 @@ class UtilisateurObject
* Mot de passe
* @return string
*/
private function getPassword()
private function getPassword(): string
{
return $this->password;
}
@ -86,35 +87,35 @@ class UtilisateurObject
* Email
* @return string
*/
public function getEmail()
public function getEmail(): string
{
return $this->email;
}
/**
* Date d'inscription
* @return type
* @return string
*/
private function getDateInscription()
private function getDateInscription(): string
{
return $this->dateInscription;
}
/**
* Date d'inscription formatée
* @return type
* @return string
*/
public function getDateInscriptionFormate()
public function getDateInscriptionFormate(): string
{
$phpdate = strtotime($this->getDateInscription());
return date("d/m/Y", $phpdate);
return date('d/m/Y', $phpdate);
}
/**
* @ IP d'inscription
* @return string
*/
public function getIpInscription()
public function getIpInscription(): string
{
return $this->ipInscription;
}
@ -123,52 +124,52 @@ class UtilisateurObject
* Niveau de droits
* @return int
*/
public function getLevel()
public function getLevel(): int
{
return (int) $this->level;
return $this->level;
}
/**
* ID en BDD
* @return int
*/
public function getId()
public function getId(): int
{
return (int) $this->id;
return $this->id;
}
/**
* Utilisateur est actif ?
* @return boolean
* @return bool
*/
public function getIsActif()
public function getIsActif(): bool
{
return $this->isActif;
}
/**
* Token associé au compte utilisateur
* @return string|null
* @return string
*/
public function getToken()
public function getToken(): string
{
return $this->token;
}
/**
* Utilisateur est actif ?
* @param boolean $isActif
* @param bool $isActif
*/
public function setIsActif($isActif)
public function setIsActif(bool $isActif): void
{
$this->isActif = $isActif;
}
/**
* Token lié à l'utilisateur
* @param string|null $token
* @param string $token
*/
public function setToken($token)
public function setToken(string $token): void
{
$this->token = $token;
}
@ -177,7 +178,7 @@ class UtilisateurObject
* Nom d'utilisateur
* @param string $userName
*/
public function setUserName($userName)
public function setUserName(string $userName): void
{
$this->userName = $userName;
}
@ -186,7 +187,7 @@ class UtilisateurObject
* Mot de passe
* @param string $password
*/
private function setPassword($password)
private function setPassword(string $password): void
{
$this->password = $password;
}
@ -195,7 +196,7 @@ class UtilisateurObject
* Mot de passe à crypter
* @param string $password
*/
public function setPasswordToCrypt($password)
public function setPasswordToCrypt(string $password): void
{
$this->password = password_hash($password, PASSWORD_DEFAULT);
}
@ -204,16 +205,16 @@ class UtilisateurObject
* Email
* @param string $email
*/
public function setEmail($email)
public function setEmail(string $email): void
{
$this->email = $email;
}
/**
* Date d'inscription
* @param type $dateInscription
* @param string $dateInscription
*/
private function setDateInscription($dateInscription)
private function setDateInscription(string $dateInscription): void
{
$this->dateInscription = $dateInscription;
}
@ -222,7 +223,7 @@ class UtilisateurObject
* @ IP d'inscription
* @param string $ipInscription
*/
private function setIpInscription($ipInscription)
private function setIpInscription(string $ipInscription): void
{
$this->ipInscription = $ipInscription;
}
@ -231,7 +232,7 @@ class UtilisateurObject
* Niveau de droits
* @param int $level
*/
public function setLevel($level)
public function setLevel(int $level): void
{
$this->level = $level;
}
@ -240,7 +241,7 @@ class UtilisateurObject
* ID en BDD
* @param int $id
*/
private function setId($id)
private function setId(int $id): void
{
$this->id = $id;
}
@ -251,51 +252,48 @@ class UtilisateurObject
* @param string $pwd Mot de passe associé
* @return int ID de l'utilisateur (0 si identifiants invalides)
*/
private function verifierIdentifiants($user, $pwd)
private function verifierIdentifiants(string $user, string $pwd): int
{
// Identifiants KO par défaut
$monRetour = 0;
// Vérification de l'existance du login
$req = MaBDD::getInstance()->prepare("SELECT * FROM membres WHERE login = :login");
/* @var $req PDOStatement */
$req->bindValue(':login', $user, PDO::PARAM_STR);
$req = MaBDD::getInstance()->prepare('SELECT * FROM membres WHERE login = :login');
$req->bindValue(':login', $user);
$req->execute();
// Je récupère les potentielles valeurs
$values = $req->fetch();
$resultat = $req->fetch();
// Si l'utilisateur existe
if ($values !== false) {
if ($resultat !== false) {
// Faut-il mettre à jour le hash du mot de passe ?
$updateHash = false;
// Est-ce un cas de compatibilité avec les anciens mots de passe ?
if (substr($values->password, 0, 1) !== '$') {
if (!str_starts_with($resultat->password, '$')) {
// Les hash générés par crypt possédent un schème spécifique avec $ en premier chr
// https://en.wikipedia.org/wiki/Crypt_(C)#Key_derivation_functions_supported_by_crypt
if (hash_equals($values->password, hash('sha256', _GRAIN_DE_SEL_ . $pwd))) {
if (hash_equals($resultat->password, hash('sha256', _GRAIN_DE_SEL_ . $pwd))) {
// Ancien mot de passe => update hash du password ;-)
$updateHash = true;
// Identifiants matchent !
$monRetour = $values->id;
$monRetour = $resultat->id;
}
} else {
} elseif (password_verify($pwd, $resultat->password)) {
// Cas standard : comparaison du hash du mot de passe fourni avec celui stocké en base
if (password_verify($pwd, $values->password)) {
// => Faut-il mettre à jour le cryptage utilisé ?
if (password_needs_rehash($values->password, PASSWORD_DEFAULT)) {
$updateHash = true;
}
// Identifiants matchent !
$monRetour = $values->id;
// => Faut-il mettre à jour le chiffrement utilisé ?
if (password_needs_rehash($resultat->password, PASSWORD_DEFAULT)) {
$updateHash = true;
}
// Identifiants matchent !
$monRetour = $resultat->id;
}
// Mise à jour du hash si requis
if ($updateHash) {
$monUtilisateur = new UtilisateurObject();
$monUtilisateur->charger($values->id);
$monUtilisateur->charger($resultat->id);
$monUtilisateur->setPasswordToCrypt($pwd);
$monUtilisateur->modifier();
}
@ -307,9 +305,9 @@ class UtilisateurObject
* Connexion d'un utilisateur : vérification & création de la session
* @param string $user Utilisateur
* @param string $pwd Mot de passe
* @return boolean
* @return bool
*/
public function connexion($user, $pwd)
public function connexion(string $user, string $pwd): bool
{
// Protection contre une attaque : on délaie un peu l'action
usleep(500000);
@ -331,9 +329,9 @@ class UtilisateurObject
$maSession->setUserObject($this);
// J'enregistre en BDD la connexion réussie
$req = MaBDD::getInstance()->prepare("INSERT INTO login (ip_login, date_login, pk_membres) VALUES (:ipLogin, NOW(), :pkMembres)");
$req->bindValue(':ipLogin', $_SERVER['REMOTE_ADDR'], PDO::PARAM_STR);
$req->bindValue(':pkMembres', $userID, PDO::PARAM_INT);
$req = MaBDD::getInstance()->prepare('INSERT INTO login (remote_addr, date_action, membres_id) VALUES (:ipLogin, NOW(), :membresId)');
$req->bindValue(':ipLogin', $_SERVER['REMOTE_ADDR']);
$req->bindValue(':membresId', $userID, PDO::PARAM_INT);
$req->execute();
}
@ -345,33 +343,32 @@ class UtilisateurObject
/**
* Charge un utilisateur depuis la BDD
* @param int $userID ID en BDD
* @return boolean Utilisateur existant ?
* @return bool Utilisateur existant ?
*/
private function charger($userID)
private function charger(int $userID): bool
{
$monRetour = false;
// Je récupère les données en BDD
$req = MaBDD::getInstance()->prepare("SELECT * FROM membres WHERE id = :id");
/* @var $req PDOStatement */
$req = MaBDD::getInstance()->prepare('SELECT * FROM membres WHERE id = :id');
$req->bindValue(':id', $userID, PDO::PARAM_INT);
$req->execute();
// Je récupère les potentielles valeurs
$values = $req->fetch();
$resultat = $req->fetch();
// Si l'utilisateur n'existe pas... on retourne un UtilisateurObject vide
if ($values !== false) {
if ($resultat !== false) {
// Je charge les informations de l'utilisateur (sauf password)
$this->setId($userID);
$this->setEmail($values->email);
$this->setUserName($values->login);
$this->setDateInscription($values->date_inscription);
$this->setIpInscription($values->ip_inscription);
$this->setLevel($values->lvl);
$this->setPassword($values->password);
$this->setIsActif($values->isActif);
$this->setToken($values->token);
$this->setEmail($resultat->email);
$this->setUserName($resultat->login);
$this->setDateInscription($resultat->date_action);
$this->setIpInscription($resultat->remote_addr);
$this->setLevel($resultat->lvl);
$this->setPassword($resultat->password);
$this->setIsActif($resultat->isActif);
$this->setToken($resultat->token);
// Gestion du retour
$monRetour = true;
@ -383,17 +380,17 @@ class UtilisateurObject
/**
* Enregistrement (BDD) d'un utilisateur
*/
public function enregistrer()
public function enregistrer(): void
{
$req = MaBDD::getInstance()->prepare("INSERT INTO membres (email, login, password, date_inscription, ip_inscription, lvl, isActif, token) VALUES (:email, :login, :password, NOW(), :ipInscription, :lvl, :isActif, :token)");
$req->bindValue(':email', $this->getEmail(), PDO::PARAM_STR);
$req->bindValue(':login', $this->getUserNameBDD(), PDO::PARAM_STR);
$req->bindValue(':password', $this->getPassword(), PDO::PARAM_STR);
$req = MaBDD::getInstance()->prepare('INSERT INTO membres (email, login, password, date_action, remote_addr, lvl, isActif, token) VALUES (:email, :login, :password, NOW(), :ipInscription, :lvl, :isActif, :token)');
$req->bindValue(':email', $this->getEmail());
$req->bindValue(':login', $this->getUserNameBDD());
$req->bindValue(':password', $this->getPassword());
// Date est définie par NOW()
$req->bindValue(':ipInscription', $_SERVER['REMOTE_ADDR'], PDO::PARAM_STR);
$req->bindValue(':ipInscription', $_SERVER['REMOTE_ADDR']);
$req->bindValue(':lvl', $this->getLevel(), PDO::PARAM_INT);
$req->bindValue(':isActif', $this->getIsActif(), PDO::PARAM_BOOL);
$req->bindValue(':token', $this->getToken(), PDO::PARAM_STR);
$req->bindValue(':token', $this->getToken());
$req->execute();
}
@ -401,15 +398,15 @@ class UtilisateurObject
/**
* Modifier (BDD) un utilisateur déjà existant
*/
public function modifier()
public function modifier(): void
{
$req = MaBDD::getInstance()->prepare("UPDATE membres SET email = :email, login = :login, password = :password, lvl = :lvl, isActif = :isActif, token = :token WHERE id = :id");
$req->bindValue(':email', $this->getEmail(), PDO::PARAM_STR);
$req->bindValue(':login', $this->getUserNameBDD(), PDO::PARAM_STR);
$req->bindValue(':password', $this->getPassword(), PDO::PARAM_STR);
$req = MaBDD::getInstance()->prepare('UPDATE membres SET email = :email, login = :login, password = :password, lvl = :lvl, isActif = :isActif, token = :token WHERE id = :id');
$req->bindValue(':email', $this->getEmail());
$req->bindValue(':login', $this->getUserNameBDD());
$req->bindValue(':password', $this->getPassword());
$req->bindValue(':lvl', $this->getLevel(), PDO::PARAM_INT);
$req->bindValue(':isActif', $this->getIsActif(), PDO::PARAM_BOOL);
$req->bindValue(':token', $this->getToken(), PDO::PARAM_STR);
$req->bindValue(':token', $this->getToken());
$req->bindValue(':id', $this->getId(), PDO::PARAM_INT);
$req->execute();
@ -418,20 +415,20 @@ class UtilisateurObject
/**
* Suppression (BDD) d'un utilisateur
*/
public function supprimer()
public function supprimer(): void
{
// Les images possédées
$req = MaBDD::getInstance()->prepare("DELETE FROM possede WHERE pk_membres = :pkMembres");
$req->bindValue(':pkMembres', $this->getId(), PDO::PARAM_INT);
$req = MaBDD::getInstance()->prepare('DELETE FROM possede WHERE membres_id = :membresId');
$req->bindValue(':membresId', $this->getId(), PDO::PARAM_INT);
$req->execute();
// Historique des logins
$req = MaBDD::getInstance()->prepare("DELETE FROM login WHERE pk_membres = :pkMembres");
$req->bindValue(':pkMembres', $this->getId(), PDO::PARAM_INT);
$req = MaBDD::getInstance()->prepare('DELETE FROM login WHERE membres_id = :membresId');
$req->bindValue(':membresId', $this->getId(), PDO::PARAM_INT);
$req->execute();
// Paramètres du compte
$req = MaBDD::getInstance()->prepare("DELETE FROM membres WHERE id = :id");
$req = MaBDD::getInstance()->prepare('DELETE FROM membres WHERE id = :id');
$req->bindValue(':id', $this->getId(), PDO::PARAM_INT);
$req->execute();
}
@ -439,83 +436,115 @@ class UtilisateurObject
/**
* Assigne une image à un utilisateur en BDD
* @param ImageObject $imageObject
* @throws ImageHebergException
*/
public function assignerImage($imageObject)
public function assignerImage(ImageObject $imageObject): void
{
if ($this->getId() === 0) {
throw new Exception("Aucun utilisateur n'est défini !");
throw new ImageHebergException('Aucun utilisateur n\'est défini !');
}
// Les images possédées
$req = MaBDD::getInstance()->prepare("INSERT INTO possede (image_id, pk_membres) VALUES (:imageId, :pkMembres)");
$req->bindValue(':imageId', $imageObject->getId(), PDO::PARAM_INT);
$req->bindValue(':pkMembres', $this->getId(), PDO::PARAM_INT);
$req = MaBDD::getInstance()->prepare('INSERT INTO possede (images_id, membres_id) VALUES (:imagesId, :membresId)');
$req->bindValue(':imagesId', $imageObject->getId(), PDO::PARAM_INT);
$req->bindValue(':membresId', $this->getId(), PDO::PARAM_INT);
$req->execute();
}
/**
* Vérifier si un login est disponible pour enregistrement
* @param type $login
* @return boolean
* @param string $login
* @return bool
*/
public static function verifierLoginDisponible($login)
public static function verifierLoginDisponible(string $login): bool
{
$req = MaBDD::getInstance()->prepare("SELECT * FROM membres WHERE login = :login");
/* @var $req PDOStatement */
$req->bindValue(':login', $login, PDO::PARAM_STR);
$req = MaBDD::getInstance()->prepare('SELECT * FROM membres WHERE login = :login');
$req->bindValue(':login', $login);
$req->execute();
// Par défaut le login est disponible
$retour = true;
$monRetour = true;
// Si j'ai un résultat...
if ($req->fetch()) {
// Le retour est négatif
$retour = false;
$monRetour = false;
}
return $retour;
return $monRetour;
}
/**
* Vérifier si un email est disponible pour enregistrement
* @param string $email
* @return bool
*/
public static function verifierEmailDisponible(string $email): bool
{
$req = MaBDD::getInstance()->prepare('SELECT * FROM membres WHERE email = :email');
$req->bindValue(':email', strtolower($email));
$req->execute();
// Par défaut l'email est disponible
$monRetour = true;
// Si j'ai un résultat...
if ($req->fetch()) {
// Le retour est négatif
$monRetour = false;
}
return $monRetour;
}
/**
* Vérifie que l'utilisateur à le droit d'afficher la page et affiche un EM au cas
* @param type $levelRequis
* @param int $levelRequis Niveau de droit minimum requis
* @param bool $dieIfNotGranted Arrêter le script si le niveau de droit n'est pas atteint
* @return void|bool
*/
public static function checkAccess($levelRequis)
public static function checkAccess(int $levelRequis, bool $dieIfNotGranted = true)
{
$monRetour = false;
$monUser = new SessionObject();
if ($monUser->verifierDroits($levelRequis) === false) {
header("HTTP/1.1 403 Forbidden");
require _TPL_TOP_;
echo "<h1 class=\"mb-3\">Accès refusé</h1>";
echo "<p>Désolé, vous n'avez pas le droit d'accèder à cette page.</p>";
require _TPL_BOTTOM_;
die();
// Arrêter le script...
if ($dieIfNotGranted) {
header('HTTP/2 403 Forbidden');
require _TPL_TOP_;
echo '<h1 class="mb-3">Accès refusé</h1>';
echo '<p>Désolé, vous n\'avez pas le droit d\'accèder à cette page.</p>';
require _TPL_BOTTOM_;
die();
}
} else {
$monRetour = true;
}
return $monRetour;
}
/**
* Toutes les images appartenant à un utilisateur
* @param type $userId ID de l'user en question
* @return \ArrayObject new_name image
* @return ArrayObject new_name image
*/
public function getImages()
public function getImages(): ArrayObject
{
// Toutes les images
$req = MaBDD::getInstance()->prepare("SELECT new_name FROM possede, images WHERE id = image_id AND pk_membres = :pkMembres ");
/* @var $req PDOStatement */
$req->bindValue(':pkMembres', $this->getId(), PDO::PARAM_INT);
$req = MaBDD::getInstance()->prepare('SELECT new_name FROM possede, images WHERE id = images_id AND membres_id = :membresId ');
$req->bindValue(':membresId', $this->getId(), PDO::PARAM_INT);
// Exécution de la requête
$req->execute();
$retour = new ArrayObject();
$monRetour = new ArrayObject();
// Pour chaque résultat retourné
foreach ($req->fetchAll() as $value) {
// J'ajoute le nom de l'image
$retour->append($value->new_name);
$monRetour->append($value->new_name);
}
return $retour;
return $monRetour;
}
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -24,50 +24,75 @@
*/
/* Base de données */
// Serveur de base de données
define('_BDD_HOST_', 'xxx');
const _BDD_HOST_ = 'xxx';
// Utilisateur SQL
define('_BDD_USER_', 'xxx');
const _BDD_USER_ = 'xxx';
// Mot de passe SQL
define('_BDD_PASS_', 'xxx');
const _BDD_PASS_ = 'xxx';
// Nom de la base de données
define('_BDD_NAME_', 'xxx');
const _BDD_NAME_ = 'xxx';
/* Système de fichiers */
// Emplacement de votre site sur le système de fichiers de votre hébergeur
define('_PATH_', '/path/to/example.com/');
const _PATH_ = '/path/to/example.com/';
/* A propos de l'outil */
// Nom affiché du service
define('_SITE_NAME_', 'monSite');
const _SITE_NAME_ = 'monSite';
// URL du site
define('_BASE_URL_', 'www.example.com/');
const _BASE_URL_ = 'www.example.com/';
// Administrateur du site
define('_ADMINISTRATEUR_NOM_', 'John DOE');
const _ADMINISTRATEUR_NOM_ = 'John DOE';
// Site web de l'administrateur
define('_ADMINISTRATEUR_SITE_', '//www.example.com/');
const _ADMINISTRATEUR_SITE_ = '//www.example.com/';
// Mail de l'administrateur (non affiché)
define('_ADMINISTRATEUR_EMAIL_', 'john.doe@example.com');
const _ADMINISTRATEUR_EMAIL_ = 'john.doe@example.com';
/* Informations légales */
// Hébergeur du site
define('_HEBERGEUR_NOM_', 'OVH');
const _HEBERGEUR_NOM_ = 'HOSTING';
// Site web de l'hébergeur
define('_HEBERGEUR_SITE_', '//www.ovh.com');
const _HEBERGEUR_SITE_ = '//www.example.com';
/* Configurations spécifiques de l'outil */
// Poids maximal des fichiers
define('_IMAGE_POIDS_MAX_', 5242880);
const _IMAGE_POIDS_MAX_ = 5242880;
// Délai de conservation d'une image jamais affichée (en jours)
define('_DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_', 7);
const _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ = 7;
// Délai depuis le dernier affichage d'une image avant de la supprimer (en jours)
define('_DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_', 365);
const _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ = 365;
// Volume maximal de stockage d'images (en Go)
define('_QUOTA_MAXIMAL_IMAGES_GO_', 90);
const _QUOTA_MAXIMAL_IMAGES_GO_ = 90;
// Affichage des messages d'erreur
define('_DEBUG_', true);
const _DEBUG_ = false;
// Délai de conservation des comptes jamais utilisés (en jours)
const _DELAI_EFFACEMENT_COMPTES_JAMAIS_UTILISES_ = 30;
/* Gestion des abus */
// Nombre d'affichages par jour à partir duquel une image est suspecte
const _ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ = 1500;
// Nombre d'affichages par jour à partir duquel une image est automatiquement bloquée
const _ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_ = 100000;
// Nombre d'affichages par jour à partir duquel une image est clairement abusive;
const _ABUSE_NB_AFFICHAGES_PAR_JOUR_ABUSIF_ = 10 * _ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_;
// Division des seuils d'abus si une image est considérée comme suspecte
const _ABUSE_DIVISION_SEUILS_SI_SUSPECT_ = 2;
// Désactiver l'envoi d'images depuis un noeud de sortie Tor
const _TOR_DISABLE_UPLOAD_ = true;
// Désactiver l'envoi d'images au bout de x images bloquées (mettre 0 pour ne pas l'activer)
const _ABUSE_DISABLE_UPLOAD_AFTER_X_IMAGES_ = 100;
// User-Agent pour lesquels bloquer les images
const _ABUSE_DISABLE_PICS_WHEN_USERE_AGENT_ = ['someUserAgentNumberOne', 'AnoterUserAgentNumberTwo'];
// Clef pour la récupération des données pour l'entraînement du module d'IA
const _KEY_FOR_IA_TRAINING_ = '';
/**
* FIN DES CHAMPS A CONFIGURER
@ -77,11 +102,11 @@ define('_DEBUG_', true);
*/
// Salt pour les mots de passe
// Legacy - n'est plus requis !!
define('_GRAIN_DE_SEL_', 'xxx');
const _GRAIN_DE_SEL_ = 'xxx';
/**
* FIN DES CHAMPS A COMPLETER UNIQUEMENT SI VOUS AVEZ UNE VERSION ANTERIEURE A v2.0.4
*/
// Désactivation des tests Tests TRAVIS-CI
define('_PHPUNIT_', false);
const _PHPUNIT_ = false;
require _PATH_ . 'config/image-heberg.php';

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -21,25 +21,35 @@
namespace ImageHeberg;
use Exception;
use Throwable;
error_reporting(E_ALL);
// Avoir le détail des paramètres des méthodes dans les stack traces
ini_set('zend.exception_string_param_max_len', 1000000);
if (_DEBUG_) {
error_reporting(E_ALL | E_STRICT);
}
if (!_PHPUNIT_) {
/**
* Supprime des informations sensibles du log d'erreur
* @param string $value
* @return string
*/
function exception_handler_cleaner(string $value): string
{
return str_replace([_BDD_HOST_, _BDD_NAME_, _BDD_USER_, _BDD_PASS_, _PATH_], 'xxx', $value);
}
/**
* Gestion des exceptions de l'application
* @param Exception $exception
* @param Throwable $exception
*/
function exception_handler($exception)
function exception_handler(Throwable $exception): void
{
/* @var $exception Exception */
if (_DEBUG_) {
// Afficher l'erreur en masquant les informations sensibles
echo '<pre>';
print_r($exception->getMessage());
print_r(exception_handler_cleaner($exception->getMessage()));
echo '<br /><br /><hr /><br />';
print_r($exception->getTraceAsString());
print_r(exception_handler_cleaner($exception->getTraceAsString()));
echo '</pre>';
} else {
echo 'Une erreur a été rencontrée';
@ -49,34 +59,55 @@ if (!_PHPUNIT_) {
* Envoi d'un mail avec le détail de l'erreur à l'administrateur
*/
// Adresse expediteur
$headers = 'From: ' . _ADMINISTRATEUR_EMAIL_ . "\n";
$headers = 'From: ' . _ADMINISTRATEUR_EMAIL_;
// Adresse de retour
$headers .= 'Reply-To: ' . _ADMINISTRATEUR_EMAIL_ . "\n";
$headers .= PHP_EOL . 'Reply-To: ' . _ADMINISTRATEUR_EMAIL_;
// Agent mail
$headers .= 'X-Mailer: ' . _SITE_NAME_ . ' script at ' . _URL_ . "\n";
$headers .= PHP_EOL . 'X-Mailer: ' . _SITE_NAME_ . ' script at ' . _URL_;
// Date
$headers .= 'Date: ' . date('D, j M Y H:i:s +0200') . "\n";
$message = $exception->getMessage() . "\r\n" . $exception->getTraceAsString();
$message .= "\r\nURL : " . $_SERVER['REQUEST_URI'];
if (isset($_SERVER['HTTP_REFERER'])) {
$message .= "\r\nHTTP REFERER : " . $_SERVER['HTTP_REFERER'];
}
$message .= "\r\nHTTP USER AGENT : " . $_SERVER['HTTP_USER_AGENT'];
$message .= "\r\nREMOTE ADDR : " . $_SERVER['REMOTE_ADDR'];
$headers .= PHP_EOL . 'Date: ' . date('D, j M Y H:i:s +0200');
$message = PHP_EOL . exception_handler_cleaner($exception->getMessage()) . PHP_EOL . exception_handler_cleaner($exception->getTraceAsString());
$message .= PHP_EOL . 'URL : ' . ($_SERVER['REQUEST_URI'] ?? '');
$message .= PHP_EOL . 'HTTP REFERER : ' . ($_SERVER['HTTP_REFERER'] ?? '');
$message .= PHP_EOL . 'HTTP USER AGENT : ' . ($_SERVER['HTTP_USER_AGENT'] ?? '');
$message .= PHP_EOL . 'REMOTE ADDR : ' . ($_SERVER['REMOTE_ADDR'] ?? '');
$message .= PHP_EOL . 'DATE : ' . date('Y-m-d H:i:s');
mail(_ADMINISTRATEUR_EMAIL_, '[' . _URL_ . '] Erreur rencontrée', $message, $headers);
mail(_ADMINISTRATEUR_EMAIL_, '[' . _SITE_NAME_ . '] - Erreur rencontrée', $message, $headers);
}
/**
* Gestions des erreurs dans l'application
* @param int $errno Niveau d'erreur
* @param string $errstr Message d'erreur
* @param string $errfile Fichier à lieu l'erreur
* @param int $errline Ligne concernée
* @return void
*/
function error_handler(int $errno, string $errstr, string $errfile, int $errline): void
{
$monException = new ImageHebergException();
$monException->define($errstr, $errno, $errfile, $errline);
exception_handler($monException);
}
set_exception_handler('ImageHeberg\exception_handler');
set_error_handler('ImageHeberg\error_handler');
}
// Répertoires
/**
* Répertoires
*/
define('_REPERTOIRE_IMAGE_', 'files/');
define('_REPERTOIRE_MINIATURE_', _REPERTOIRE_IMAGE_ . 'thumbs/');
define('_REPERTOIRE_ADMIN_', 'admin/');
define('_REPERTOIRE_MEMBRE_', 'membre/');
define('_REPERTOIRE_CONFIG_', 'config/');
// URL
/**
* URL
*/
define('_URL_', 'http://' . _BASE_URL_);
define('_URL_HTTPS_', 'https://' . _BASE_URL_);
define('_URL_SANS_SCHEME_', '//' . _BASE_URL_);
@ -86,37 +117,64 @@ define('_URL_IMAGES_', _URL_ . _REPERTOIRE_IMAGE_);
define('_URL_MINIATURES_', _URL_ . _REPERTOIRE_MINIATURE_);
define('_URL_CONFIG_', _URL_HTTPS_ . _REPERTOIRE_CONFIG_);
// Système de fichiers
/**
* Système de fichiers
*/
define('_PATH_IMAGES_', _PATH_ . _REPERTOIRE_IMAGE_);
define('_PATH_MINIATURES_', _PATH_ . _REPERTOIRE_MINIATURE_);
define('_PATH_ADMIN_', _PATH_ . _REPERTOIRE_ADMIN_);
define('_PATH_TESTS_IMAGES_', _PATH_ . '__tests/images/');
define('_PATH_TESTS_IMAGES_', _PATH_ . '__tests/images/'); // Images pour les tests
define('_PATH_TESTS_IMAGES_A_IMPORTER_', _PATH_TESTS_IMAGES_ . 'aImporter/'); // Images devant déjà être importées avant d'exécurer les tests
define('_PATH_TESTS_OUTPUT_', _PATH_ . '__tests/output/');
define('_TPL_TOP_', _PATH_ . 'template/templateV2Top.php');
define('_TPL_BOTTOM_', _PATH_ . 'template/templateV2Bottom.php');
// Fonction de chargement des classes en cas de besoin
spl_autoload_register(function ($class) {
// Suppression du namespace
$class = str_replace('ImageHeberg\\', "", $class);
// Code pour TRAVIS
$charger = true;
// Code spécifique Travis : pas de chargement des classes de PHPUnit
if (_PHPUNIT_ && (strpos($class, "PHPUnit") !== false || strpos($class, "Composer") !== false)) {
$charger = false;
spl_autoload_register(static function ($class) {
// Ne pas faire d'erreur pour les classes gérées par l'autoloader de PHPUnit
if (_PHPUNIT_ && str_starts_with($class, 'PHPUnit')) {
return;
}
if ($charger) {
require _PATH_ . 'classes/' . $class . '.class.php';
// Suppression du namespace
$class = str_replace('ImageHeberg\\', '', $class);
if (str_contains($class, 'Helper')) {
// Helper par exemple
$file = _PATH_ . 'classes/' . $class . '.php';
} else {
// Classe instantiable
$file = _PATH_ . 'classes/' . $class . '.class.php';
}
// Si le fichier existe...
if (file_exists($file)) {
require $file;
} elseif (_PHPUNIT_) {
echo 'spl_autoload_register() - Impossible de charger : ' . $file . ' (' . $class . ')' . PHP_EOL;
}
});
/**
* Paramètres divers pour les images
*/
// Gestion de la mémoire
define('_FUDGE_FACTOR_', 1.8);
define('_IMAGE_DIMENSION_MAX_', Outils::getMaxDimension());
define('_IMAGE_DIMENSION_MAX_', HelperImage::getMaxDimension());
// Images spécifiques
define('_IMAGE_404_', '_image_404.png');
define('_IMAGE_BAN_', '_image_banned.png');
// Dimensions des apeçus dans l'espace membre
define('_SIZE_PREVIEW_', 256);
// Types d'images gérés
define('_ACCEPTED_EXTENSIONS_', ['jpg', 'png', 'gif', 'webp']);
define('_ACCEPTED_MIME_TYPE_', [IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_WEBP]);
/**
* Tor
*/
// URL de l'API Tor
define('_TOR_EXIT_NODE_LIST_URL_', 'https://onionoo.torproject.org/details?flag=exit');
define('_TOR_LISTE_IPV4_', _PATH_ . _REPERTOIRE_IMAGE_ . 'z_cache/ipv4.txt');
define('_TOR_LISTE_IPV6_', _PATH_ . _REPERTOIRE_IMAGE_ . 'z_cache/ipv6.txt');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -22,23 +22,25 @@
namespace ImageHeberg;
require 'config/config.php';
require _TPL_TOP_;
// Anti flood
$maSession = new SessionObject();
require _TPL_TOP_;
// En cas de validation du formulaire
if (isset($_POST['Submit']) && $maSession->checkFlag()) {
// Vérification du bon format de l'adresse mail
if (filter_var($_POST['userMail'], FILTER_VALIDATE_EMAIL) !== false) {
// Je complète le message avec l'IP de mon émeteur
$message = $_POST['userMessage'];
$message .= "\r\n\r\n ---------------------------------------------";
$message .= "\r\n\r\n IP : " . $_SERVER['REMOTE_ADDR'];
$message .= "\r\n\r\n BROWSER : " . $_SERVER['HTTP_USER_AGENT'];
$message .= PHP_EOL . '---------------------------------------------';
$message .= PHP_EOL . 'IP : ' . $_SERVER['REMOTE_ADDR'];
$message .= PHP_EOL . 'BROWSER : ' . $_SERVER['HTTP_USER_AGENT'];
$message .= PHP_EOL . 'DATE : ' . date('Y-m-d H:i:s');
// Tout va bien, on envoit un mail
mail(_ADMINISTRATEUR_EMAIL_, "[" . _SITE_NAME_ . "] - Formulaire de contact", $message, "From: " . $_POST['userMail']);
mail(_ADMINISTRATEUR_EMAIL_, '[' . _SITE_NAME_ . '] - Formulaire de contact', $message, 'From: ' . $_POST['userMail']);
$maSession->removeFlag();
// Retour utilisateur
@ -47,27 +49,24 @@ if (isset($_POST['Submit']) && $maSession->checkFlag()) {
// Adresse mail invalide
echo '<div class = "alert alert-danger">Votre adresse mail n\'est pas valide !<br /><pre>' . $_POST['userMail'] . '</pre></div>';
}
} else {
// Premier affichage de la page
if (!isset($_POST['Submit'])) {
// Activation de la protection robot
$maSession->setFlag();
}
} elseif (!isset($_POST['Submit'])) {
// Premier affichage de la page => activation de la protection robot
$maSession->setFlag();
}
?>
<?php if ($maSession->checkFlag()) : ?>
<?php if (!isset($_POST['Submit']) || $maSession->checkFlag()) : ?>
<h1 class="mb-3"><small>Contacter l'administrateur du service - <?= _ADMINISTRATEUR_NOM_ ?></small></h1>
<form method="post">
<div class="mb-3 form-floating">
<input type="email" class="form-control" name="userMail" id="userMail" required="required" value="<?= (isset($_POST['userMail'])) ? $_POST['userMail'] : '' ?>">
<label for="userMail" >Votre adresse courriel</label>
<input type="email" class="form-control" name="userMail" id="userMail" required="required" value="<?= $_POST['userMail'] ?? '' ?>">
<label for="userMail">Votre adresse courriel</label>
<div class="form-text">Sera utilisée uniquement pour vous apporter une réponse.</div>
</div>
<div class="mb-3 form-floating">
<textarea class="form-control" name="userMessage" id="userMessage" required="required"><?= (isset($_POST['userMessage'])) ? $_POST['userMessage'] : '' ?></textarea>
<textarea class="form-control" name="userMessage" id="userMessage" required="required"><?= $_POST['userMessage'] ?? '' ?></textarea>
<label for="userMessage">Votre message</label>
</div>
<button type="submit" name="Submit" class="btn btn-success">Envoyer</button>
</form>
<?php endif; ?>
<?php require _TPL_BOTTOM_ ?>
<?php endif; ?>
<?php require _TPL_BOTTOM_ ?>

108
cron/abuse.php Normal file
View file

@ -0,0 +1,108 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
use ArrayObject;
use Exception;
/*
* Vérifier les images avec des comportements suspects
*/
// Peut être appelé par displayPics.php -> ne pas recharger la config
require_once __DIR__ . '/../config/config.php';
/**
* Met en forme pour un mail une liste d'images
* @param ArrayObject $listeImages
* @param string $titre Titre de la catégorie à indiquer dans le mail
* @return string
* @throws Exception
*/
function formatageMailListeImages(ArrayObject $listeImages, string $titre): string
{
$monRetour = '';
if (count($listeImages) > 0) {
$monRetour = $titre . ' : (' . count($listeImages) . ')' . PHP_EOL;
foreach ((array)$listeImages as $value) {
$monImage = new ImageObject($value);
$monRetour .= ' -> ' . $monImage->getURL(true) . '?forceDisplay=1 ("' . $monImage->getNomOriginalFormate() . '") : ' . $monImage->getNbViewTotal() . ' affichages (' . $monImage->getNbViewPerDay() . '/jour) - envoyée le ' . $monImage->getDateEnvoiFormatee() . ' par ' . $monImage->getIpEnvoi() . ' - dernier affichage le ' . $monImage->getLastViewFormate() . PHP_EOL;
}
$monRetour .= '...done' . PHP_EOL;
}
return $monRetour;
}
/**
* Bloquer des images
* @param ArrayObject $listeImages liste d'images à bloquer
* @return void
* @throws Exception
*/
function bloquerImage(ArrayObject $listeImages): void
{
foreach ((array)$listeImages as $value) {
$monImage = new ImageObject($value);
$monImage->setSignalee(true);
$monImage->sauver();
}
}
$contenu = '';
// Tracer l'image ayant généré l'appel au cron
if (!empty($_SERVER['REQUEST_URI'])) {
$contenu .= 'Requête ayant généré le cron : ' . $_SERVER['REQUEST_URI'] . PHP_EOL;
}
// Liste des images signalées
$listeImages = HelperAdmin::getImagesSignalees();
$contenu .= formatageMailListeImages($listeImages, 'Images signalées');
// Liste des images avec un ratio d'affichage important
$listeImages = HelperAdmin::getImagesTropAffichees(_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_);
$contenu .= formatageMailListeImages($listeImages, 'Images qui sont beaucoup affichées (>' . _ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ . '/jour)');
// Liste des images SUSPECTES avec un ratio d'affichage important
$listeImages = HelperAdmin::getImagesTropAffichees((_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_), true);
$contenu .= formatageMailListeImages($listeImages, 'Images SUSPECTES qui sont beaucoup affichées (>' . (_ABUSE_NB_AFFICHAGES_PAR_JOUR_WARNING_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_) . '/jour)');
// Liste des images avec un ratio d'affichage abusif
$listeImages = HelperAdmin::getImagesTropAffichees(_ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_);
bloquerImage($listeImages);
$contenu .= formatageMailListeImages($listeImages, 'Blocage des images qui abusent (>' . _ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_ . '/jour)');
// Liste des images SUSPECTES avec un ratio d'affichage abusif
$listeImages = HelperAdmin::getImagesTropAffichees((_ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_), true);
bloquerImage($listeImages);
$contenu .= formatageMailListeImages($listeImages, 'Blocage des images SUSPECTES qui abusent (>' . (_ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_) . '/jour)');
// Liste des images suspectes
$listeImages = HelperAdmin::getImagesPotentiellementIndesirables();
$contenu .= formatageMailListeImages($listeImages, 'Images suspectes');
// Liste des images abusant clairement
$listeImages = HelperAdmin::getImagesTropAffichees(_ABUSE_NB_AFFICHAGES_PAR_JOUR_ABUSIF_, false, true, true);
$contenu .= formatageMailListeImages($listeImages, 'Images qui abusent du service (>' . _ABUSE_NB_AFFICHAGES_PAR_JOUR_ABUSIF_ . '/jour)');
if (!empty($contenu)) {
// Envoyer une notification à l'admin
mail(_ADMINISTRATEUR_EMAIL_, '[' . _SITE_NAME_ . '] - Gestion des abus', $contenu, 'From: ' . _ADMINISTRATEUR_EMAIL_);
}

39
cron/cleanAccounts.php Normal file
View file

@ -0,0 +1,39 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
/*
* Suppression des comptes obsolètes
*/
require __DIR__ . '/../config/config.php';
// Effacer les comptes jamais utilisés
echo 'Suppression des comptes jamais utilisés ' . _DELAI_EFFACEMENT_COMPTES_JAMAIS_UTILISES_ . ' jours après leur création' . PHP_EOL;
$listeComptes = HelperAdmin::getUnusedAccounts();
foreach ((array)$listeComptes as $value) {
// Je crée mon objet et lance la suppression
$monUtilisateur = new UtilisateurObject($value);
echo ' -> ' . $monUtilisateur->getEmail() . ' - créé le ' . $monUtilisateur->getDateInscriptionFormate() . ' via IP ' . $monUtilisateur->getIpInscription() . PHP_EOL;
$monUtilisateur->supprimer();
}
echo '...done' . PHP_EOL;

50
cron/cleanImages.php Normal file
View file

@ -0,0 +1,50 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
/*
* Suppression des images obsolètes
*/
require __DIR__ . '/../config/config.php';
// Effacer les fichiers jamais affichées
echo 'Suppression des images jamais affichées depuis ' . _DELAI_EFFACEMENT_IMAGES_JAMAIS_AFFICHEES_ . ' jours' . PHP_EOL;
$listeImages = HelperAdmin::getNeverUsedFiles();
foreach ((array)$listeImages as $value) {
// Je crée mon objet et lance la suppression
$monImage = new ImageObject($value);
echo ' -> ' . $monImage->getNomNouveau() . ' (envoi le ' . $monImage->getDateEnvoiFormatee() . ')' . PHP_EOL;
$monImage->supprimer();
}
echo '...done' . PHP_EOL;
// Effacer les fichiers inactifs
echo 'Suppression des images non affichées depuis ' . _DELAI_INACTIVITE_AVANT_EFFACEMENT_IMAGES_ . ' jours' . PHP_EOL;
$listeImages = HelperAdmin::getUnusedFiles();
foreach ((array)$listeImages as $value) {
// Je crée mon objet et lance la suppression
$monImage = new ImageObject($value);
echo ' -> ' . $monImage->getNomNouveau() . ' (dernier affichage le ' . $monImage->getLastViewFormate() . ')' . PHP_EOL;
$monImage->supprimer();
}
echo '...done' . PHP_EOL;

34
cron/updateTorIp.php Normal file
View file

@ -0,0 +1,34 @@
<?php
/*
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
* image-heberg.fr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* image-heberg.fr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
namespace ImageHeberg;
/*
* Mise à jour des adresses IP des noeuds de sortie TOR
*/
require __DIR__ . '/../config/config.php';
if (_TOR_DISABLE_UPLOAD_) {
$objTor = new Tor();
$objTor->updateListeExitNodes();
}
echo '...done' . PHP_EOL;

View file

@ -1,5 +1,5 @@
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -21,98 +21,131 @@
-- Structure de la table `images`
--
CREATE TABLE IF NOT EXISTS `images` (
`id` int(20) NOT NULL AUTO_INCREMENT,
`ip_envoi` text NOT NULL,
`date_envoi` datetime NULL DEFAULT NULL,
`old_name` text NOT NULL,
`new_name` text NOT NULL,
`size` int(11) NOT NULL,
`height` int(11) NOT NULL,
`width` int(11) NOT NULL,
`last_view` date NULL DEFAULT NULL,
`nb_view_v4` int(11) NOT NULL DEFAULT '0',
`nb_view_v6` int(11) NOT NULL DEFAULT '0',
`md5` tinytext NOT NULL,
`isBloquee` tinyint(1) NOT NULL DEFAULT '0',
`isSignalee` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `images`
(
`id` int(20) NOT NULL AUTO_INCREMENT,
`remote_addr` varchar(45) NOT NULL,
`date_action` datetime NOT NULL,
`old_name` varchar(255) NOT NULL,
`new_name` varchar(30) NOT NULL,
`size` int(11) NOT NULL,
`height` int(11) NOT NULL,
`width` int(11) NOT NULL,
`last_view` date NOT NULL,
`nb_view_v4` int(11) NOT NULL,
`nb_view_v6` int(11) NOT NULL,
`md5` varchar(32) NOT NULL,
`isBloquee` tinyint(1) NOT NULL,
`isSignalee` tinyint(1) NOT NULL,
`isApprouvee` tinyint(1) NOT NULL,
`abuse_network` varchar(45) NOT NULL,
PRIMARY KEY (`id`),
KEY `statutImage` (`isBloquee`, `isApprouvee`, `isSignalee`) USING BTREE,
KEY `abuse_network_bloquee` (`abuse_network`,`isBloquee`) USING BTREE,
KEY `abuse_network_approuvee` (`abuse_network`,`isApprouvee`),
KEY `isApprouvee` (`isApprouvee`)
) ENGINE = MyISAM
DEFAULT CHARSET = utf8;
-- --------------------------------------------------------
--
-- Structure de la table `login`
--
CREATE TABLE IF NOT EXISTS `login` (
`pk_login` int(11) NOT NULL AUTO_INCREMENT,
`ip_login` text NOT NULL,
`date_login` datetime NOT NULL,
`pk_membres` int(11) NOT NULL,
PRIMARY KEY (`pk_login`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `login`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`remote_addr` varchar(45) NOT NULL,
`date_action` datetime NOT NULL,
`membres_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `membres_id` (`membres_id`)
) ENGINE = MyISAM
DEFAULT CHARSET = utf8;
-- --------------------------------------------------------
--
-- Structure de la table `membres`
--
CREATE TABLE IF NOT EXISTS `membres` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` text NOT NULL,
`login` text NOT NULL,
`password` text NOT NULL,
`date_inscription` date NOT NULL,
`ip_inscription` text NOT NULL,
`lvl` tinyint(4) NOT NULL,
`isActif` tinyint(1) NOT NULL DEFAULT '1',
`token` text,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
CREATE TABLE IF NOT EXISTS `membres`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` text NOT NULL,
`login` text NOT NULL,
`password` text NOT NULL,
`date_action` date NOT NULL,
`remote_addr` varchar(45) NOT NULL,
`lvl` tinyint(1) NOT NULL,
`isActif` tinyint(1) NOT NULL,
`token` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE = MyISAM
DEFAULT CHARSET = utf8;
-- --------------------------------------------------------
--
-- Structure de la table `possede`
--
CREATE TABLE IF NOT EXISTS `possede` (
`image_id` int(11) NOT NULL,
`pk_membres` int(11) NOT NULL,
PRIMARY KEY (`image_id`,`pk_membres`),
KEY `image_id` (`image_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `possede`
(
`images_id` int(11) NOT NULL,
`membres_id` int(11) NOT NULL,
PRIMARY KEY (`images_id`, `membres_id`),
KEY `membres_id` (`membres_id`)
) ENGINE = MyISAM
DEFAULT CHARSET = utf8;
-- --------------------------------------------------------
--
-- Structure de la table `thumbnails`
--
CREATE TABLE IF NOT EXISTS `thumbnails` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_image` int(11) NOT NULL,
`date_creation` date NULL DEFAULT NULL,
`new_name` text NOT NULL,
`size` int(11) NOT NULL,
`height` int(11) NOT NULL,
`width` int(11) NOT NULL,
`last_view` date NULL DEFAULT NULL,
`nb_view_v4` int(11) NOT NULL DEFAULT '0',
`nb_view_v6` int(11) NOT NULL DEFAULT '0',
`md5` tinytext NOT NULL,
PRIMARY KEY (`id`),
KEY `id_image` (`id_image`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `thumbnails`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`images_id` int(11) NOT NULL,
`is_preview` tinyint(1) NOT NULL,
`date_action` date NOT NULL,
`new_name` varchar(30) NOT NULL,
`size` int(11) NOT NULL,
`height` int(11) NOT NULL,
`width` int(11) NOT NULL,
`last_view` date NOT NULL,
`nb_view_v4` int(11) NOT NULL,
`nb_view_v6` int(11) NOT NULL,
`md5` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
KEY `images_id` (`images_id`) USING BTREE
) ENGINE = MyISAM
DEFAULT CHARSET = utf8;
--
-- Création du compte administrateur
--
INSERT INTO `membres` (`id`, `email`, `login`, `password`, `date_inscription`, `ip_inscription`, `lvl`) VALUES
(1, 'john.doe@example.com', 'admin', '$2y$10$2mn2aXq7R2ROZhi9R3H1iO95vSXo0Vd02u3vAdAZSkZhcBq4Vd1bu', DATE(NOW()), '127.0.0.1', 2);
INSERT INTO `membres` (`id`, `email`, `login`, `password`, `date_action`, `remote_addr`, `lvl`, `isActif`,
`token`)
VALUES (1, 'john.doe@example.com', 'admin', '$2y$10$2mn2aXq7R2ROZhi9R3H1iO95vSXo0Vd02u3vAdAZSkZhcBq4Vd1bu', DATE(NOW()),
'127.0.0.1', 2, 1, '');
--
-- Images 404 & bannie
--
INSERT INTO `images` (`id`, `ip_envoi`, `date_envoi`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`, `nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`) VALUES
(1, '127.0.0.1', '2008-01-01 00:00:00', '_image_404.png', '_image_404.png', 30703, 150, 640, NULL, 0, 0, '6858ce6ddc171a0fd9640831a5e74dfd', 0, 0),
(2, '127.0.0.1', '2008-01-01 00:00:00', '_image_banned.png', '_image_banned.png', 28713, 150, 640, NULL, 0, 0, '12c357976276091e7cd42e98debb7fb1', 0, 0);
INSERT INTO `images` (`id`, `remote_addr`, `date_action`, `old_name`, `new_name`, `size`, `height`, `width`, `last_view`,
`nb_view_v4`, `nb_view_v6`, `md5`, `isBloquee`, `isSignalee`, `isApprouvee`, `abuse_network`)
VALUES (1, '127.0.0.1', '2008-01-01 00:00:00', '_image_404.png', '_image_404.png', 30703, 150, 640, '0000-00-00', 0, 0,
'6858ce6ddc171a0fd9640831a5e74dfd', 0, 0, 1, '127.0.0'),
(2, '127.0.0.1', '2008-01-01 00:00:00', '_image_banned.png', '_image_banned.png', 28713, 150, 640, '0000-00-00',
0, 0, '12c357976276091e7cd42e98debb7fb1', 0, 0, 1, '127.0.0');
--
-- Assignation à l'administrateur
--
INSERT INTO `possede` (`image_id`, `pk_membres`) VALUES ('1', '1'), ('2', '1');
INSERT INTO `possede` (`images_id`, `membres_id`)
VALUES ('1', '1'),
('2', '1');

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -24,7 +24,6 @@ namespace ImageHeberg;
if (!defined('_PHPUNIT_')) {
require 'config/config.php';
}
require _TPL_TOP_;
$erreur = false;
$msgErreur = '';
@ -32,7 +31,7 @@ $msgErreur = '';
/**
* Vérification du paramètre
*/
if (!isset($_GET['id']) || !isset($_GET['type'])) {
if (!isset($_GET['id'], $_GET['type'])) {
$erreur = true;
$msgErreur .= 'La page n\'a pas été appelée correctement !<br />';
}
@ -41,7 +40,7 @@ if (!isset($_GET['id']) || !isset($_GET['type'])) {
* Chargement de l'image depuis la BDD
*/
if (!$erreur) {
if ((int) $_GET['type'] === RessourceObject::TYPE_IMAGE) {
if ((int)$_GET['type'] === RessourceObject::TYPE_IMAGE) {
$monImage = new ImageObject();
} else {
$monImage = new MiniatureObject();
@ -62,7 +61,13 @@ if (!$erreur) {
* -> Envoi il y a moins d'une heure par la même @ IP
*/
if (!$erreur) {
if ($monImage->isProprietaire() || ((strtotime($monImage->getDateEnvoiBrute()) + 3600) > strtotime("now") && $monImage->getIpEnvoi() === $_SERVER['REMOTE_ADDR'])) {
if (
$monImage->isProprietaire()
|| (
(strtotime($monImage->getDateEnvoiBrute()) + 3600) > time()
&& $monImage->getIpEnvoi() === $_SERVER['REMOTE_ADDR']
)
) {
// Effacement...
$monImage->supprimer();
} else {
@ -70,22 +75,21 @@ if (!$erreur) {
$msgErreur = 'Vous n\'avez pas le droit de supprimer cette image !<br />';
}
}
?>
<h1 class="mb-3"><small>Suppression du fichier</small></h1>
<?php if (!empty($msgErreur)) : ?>
// Pas d'erreur => Redirection sur la page d'accueil
if (empty($erreur)) {
header('Location: ' . _URL_HTTPS_ . '?delete_success');
} else {
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Suppression du fichier</small></h1>
<div class="alert alert-danger">
<span class="glyphicon glyphicon-remove"></span>
&nbsp;
<b>Une erreur a été rencontrée !</b>
<br />
<?= $msgErreur ?>
<br/>
<?= $msgErreur ?>
</div>
<?php else : ?>
<div class="alert alert-success">
<span class="glyphicon glyphicon-ok"></span>
&nbsp;
<b>L'image a été supprimée avec succès !</b>
</div>
<?php endif; ?>
<?php require _TPL_BOTTOM_; ?>
<?php
require _TPL_BOTTOM_;
}

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -24,18 +24,32 @@ namespace ImageHeberg;
/*
* Affichage d'une image & mise à jour des stats
*/
require 'config/config.php';
if (!defined('_PHPUNIT_')) {
require 'config/config.php';
}
// URL demandée
$url = $_SERVER['REQUEST_URI'];
// Nom du fichier demandé
$fileName = basename($url);
// Nom du fichier demandé - Nettoyer les paramètres
$fileName = basename(parse_url($url, PHP_URL_PATH));
// Faut-il forcer l'affichage (et ne pas enregistrer les stats) ?
$adminForceAffichage = false;
/**
* Gestion du God mode
*/
if (
str_contains($url, 'forceDisplay=1') // Mis en premier pour éviter d'ouvrir des sessions inutiles
&& UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_ADMIN, false)
) {
$adminForceAffichage = true;
}
/**
* Définition du type
*/
$monObjet;
if (preg_match("#/" . _REPERTOIRE_MINIATURE_ . "#", trim($url))) {
if (preg_match('#/' . _REPERTOIRE_MINIATURE_ . '#', trim($url))) {
// Miniature
$monObjet = new MiniatureObject();
} else {
@ -44,35 +58,62 @@ if (preg_match("#/" . _REPERTOIRE_MINIATURE_ . "#", trim($url))) {
}
/**
* Est-ce que le fichier existe ?
* Est-ce que le fichier existe en BDD et sur le système de fichier ?
*/
if (!$monObjet->charger($fileName)) {
if (
!$monObjet->charger($fileName)
|| !file_exists($monObjet->getPathMd5())
) {
// Fichier non trouvé...
$monObjet = new ImageObject();
$monObjet->charger(_IMAGE_404_);
// Envoi d'un header en 404
header("HTTP/1.0 404 Not Found");
header('HTTP/2 404 Not Found');
} elseif (
isset($_SERVER['HTTP_USER_AGENT'])
&& in_array($_SERVER['HTTP_USER_AGENT'], _ABUSE_DISABLE_PICS_WHEN_USERE_AGENT_, true)
) {
// Détection des User-Agent malveillant et blocage des images demandées
$monObjet->setSignalee(true);
$monObjet->sauver();
if (!_PHPUNIT_) {
// Générer un mail d'erreur à l'admin
require 'cron/abuse.php';
}
}
/**
* Le fichier est-il bloqué ?
*/
if ($monObjet->isBloquee() || $monObjet->isSignalee()) {
if (
!$adminForceAffichage
&& ($monObjet->isBloquee() || $monObjet->isSignalee())
) {
$monObjet = new ImageObject();
$monObjet->charger(_IMAGE_BAN_);
// Envoi d'un header en 451 -> Unavailable For Legal Reasons
header('HTTP/2 451 Unavailable For Legal Reasons');
} elseif (
!$adminForceAffichage
&& !$monObjet->isApprouvee()
&& (
// Image non suspecte
(!$monObjet->isSuspecte() && $monObjet->getNbViewPerDay() > _ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_)
// Image suspecte -> seuils réduits
|| ($monObjet->isSuspecte() && $monObjet->getNbViewPerDay() > (_ABUSE_NB_AFFICHAGES_PAR_JOUR_BLOCAGE_AUTO_ / _ABUSE_DIVISION_SEUILS_SI_SUSPECT_))
)
) {
// Lancer un blocage de l'image si trop affichée
require 'cron/abuse.php';
}
/**
* Mise à jour des stats d'affichage
*/
if (filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
// IPv4
$monObjet->setNbViewIpv4PlusUn();
} else {
// IPv6
$monObjet->setNbViewIpv6PlusUn();
if (!$adminForceAffichage) {
$monObjet->updateStatsAffichage($_SERVER['REMOTE_ADDR']);
}
$monObjet->sauver();
/**
* Fermeture du lien sur la BDD
@ -83,7 +124,7 @@ MaBDD::close();
* Envoi du bon entête HTTP
*/
if (!_PHPUNIT_) {
header("Content-type: " . Outils::getMimeType($monObjet->getPathMd5()));
header('Content-type: ' . HelperImage::getMimeType($monObjet->getPathMd5()));
}
/**

0
files/z_cache/_dummy Normal file
View file

180
index.php
View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -22,14 +22,26 @@
namespace ImageHeberg;
require 'config/config.php';
require _TPL_TOP_;
// Anti-flood
$maSession = new SessionObject();
$maSession->setFlag();
// Désactiver l'envoi d'images
$uploadDisabled = false;
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Envoyer une image</small></h1>
<?php if (MetaObject::getHDDUsage() > _QUOTA_MAXIMAL_IMAGES_GO_) : ?>
<h1 class="mb-3"><small>Envoyer une image</small></h1>
<?php if (isset($_GET['delete_success'])) : ?>
<div class="alert alert-success">
<span class="glyphicon glyphicon-ok"></span>
&nbsp;
<b>L'image a été supprimée avec succès !</b>
</div>
<?php endif; ?>
<?php if (HelperSysteme::getHDDUsage() > _QUOTA_MAXIMAL_IMAGES_GO_) : ?>
<?php $uploadDisabled = true; ?>
<div class="alert alert-danger">
<?= _SITE_NAME_ ?> est victime de son succès : trop d'images ont été envoyées
et tout l'espace disque acheté est utilisé !
@ -37,76 +49,92 @@ $maSession->setFlag();
Si vous souhaitez soutenir le projet, merci d'utiliser <a href="contact.php">le formulaire de contact</a>.
</div>
<?php endif; ?>
<div class="alert alert-info">
<?= _SITE_NAME_ ?> est un service gratuit vous permettant d'héberger vos images sur internet.
<ul>
<li>
Image de type JPG, PNG, GIF
</li>
<li>
Taille maximale : <?= round(_IMAGE_POIDS_MAX_ / 1048576, 1) ?> Mo
</li>
<li>
Dimensions maximales : <?= _IMAGE_DIMENSION_MAX_ ?> x <?= _IMAGE_DIMENSION_MAX_ ?> pixels
(hauteur x largeur)
</li>
</ul>
</div>
<div class="card">
<div class="card-body">
<form enctype="multipart/form-data" action="<?= _URL_HTTPS_ ?>upload.php" method="post" class="mb-3">
<div class="mb-3">
<label for="fichier" class="col-mb-3 form-label">Fichier à envoyer</label>
<div class="col-md-9">
<input type="file" accept="image/*" name="fichier" id="fichier" required="required" <?= (MetaObject::getHDDUsage() > _QUOTA_MAXIMAL_IMAGES_GO_) ? 'disabled="disabled"' : '' ?> class="form-control">
</div>
<div class="help-block">
Tout envoi de fichier implique l'acceptation des
<a href="cgu.php">Conditions Générales d'Utilisation</a> du service.
</div>
</div>
<h3>Options</h3>
<span class="help-block"><em>Le ratio de l'image sera conservé.</em></span>
<div class="mb-3">
<label class="col-mb-3 form-label">Rotation de l'image</label>
<div class="col-md-9">
<select name="angleRotation" class="form-select">
<option value="" selected>-- Ne pas effectuer --</option>
<optgroup label="Rotation vers la gauche">
<option value="90">90&deg; (&frac14; tour)</option>
<option value="180">180&deg; (&frac12; tour)</option>
<option value="270">270&deg; (&frac34; tour)</option>
</optgroup>
</select>
</div>
</div>
<div class="mb-3">
<label class="col-mb-3 form-label">Faire une miniature</label>
<div class="col-md-9">
<select name="dimMiniature" class="form-select">
<option value="" selected>-- Ne pas effectuer --</option>
<option value="100x100">Avatar (100x100)</option>
<option value="320x240">Miniature (320x240)</option>
<option value="640x480">Miniature XL (640x480)</option>
<option value="700x700">Miniature Forum (700x700)</option>
</select>
</div>
</div>
<div class="mb-3">
<label class="col-mb-3 form-label">Redimensionner l'image</label>
<div class="col-md-9">
<select name="redimImage" class="form-select">
<option value="" selected>-- Ne pas effectuer --</option>
<option value="320x240">320x240</option>
<option value="640x480">640x480</option>
<option value="800x600">800x600</option>
<option value="1024x768">1024x768</option>
</select>
</div>
</div>
<button class="btn btn-success" name="Submit" type="submit"><span class="fas fa-cloud-upload-alt"></span>&nbsp;Envoyer</button>
</form>
<?php if (_TOR_DISABLE_UPLOAD_ && Tor::checkIp($_SERVER['REMOTE_ADDR'])) : ?>
<?php $uploadDisabled = true; ?>
<div class="alert alert-danger">
Suite à un abus d'utilisation de <?= _SITE_NAME_ ?>, l'envoi d'image est impossible depuis le réseau Tor.
<br>
Si vous le souhaitez contacter l'administrateur du site, merci d'utiliser <a href="contact.php">le formulaire de contact</a>.
</div>
</div>
<?php require _TPL_BOTTOM_; ?>
<?php endif; ?>
<?php if (_ABUSE_DISABLE_UPLOAD_AFTER_X_IMAGES_ > 0 && HelperAbuse::checkIpReputation($_SERVER['REMOTE_ADDR']) >= _ABUSE_DISABLE_UPLOAD_AFTER_X_IMAGES_) : ?>
<?php $uploadDisabled = true; ?>
<div class="alert alert-danger">
Suite à un abus d'utilisation de <?= _SITE_NAME_ ?> depuis votre plage adresse IP (<?= $_SERVER['REMOTE_ADDR'] ?>), l'envoi d'image ne vous est plus accessible.
<br>
Si vous le souhaitez contacter l'administrateur du site, merci d'utiliser <a href="contact.php">le formulaire de contact</a>.
</div>
<?php endif; ?>
<div class="alert alert-info">
<?= _SITE_NAME_ ?> est un service gratuit vous permettant d'héberger vos images sur internet.
<ul>
<li>
Image de type <?= strtoupper(implode(', ', _ACCEPTED_EXTENSIONS_)) ?>
</li>
<li>
Taille maximale : <?= round(_IMAGE_POIDS_MAX_ / 1048576, 1) ?> Mo
</li>
<li>
Dimensions maximales : <?= _IMAGE_DIMENSION_MAX_ ?> x <?= _IMAGE_DIMENSION_MAX_ ?> pixels
(hauteur x largeur)
</li>
</ul>
</div>
<div class="card">
<div class="card-body">
<form enctype="multipart/form-data" action="<?= _URL_HTTPS_ ?>upload.php" method="post" class="mb-3">
<div class="mb-3">
<label for="fichier" class="col-mb-3 form-label">Fichier à envoyer</label>
<div class="col-md-9">
<input type="file" accept="image/*" name="fichier" id="fichier" required="required" <?= ($uploadDisabled ? 'disabled="disabled"' : '') ?> class="form-control">
</div>
<div class="help-block">
Tout envoi de fichier implique l'acceptation des
<a href="cgu.php">Conditions Générales d'Utilisation</a> du service.
</div>
</div>
<h3>Options</h3>
<span class="help-block"><em>Le ratio de l'image sera conservé.</em></span>
<div class="mb-3">
<label for="angleRotation" class="col-mb-3 form-label">Rotation de l'image</label>
<div class="col-md-9">
<select name="angleRotation" id="angleRotation" class="form-select">
<option value="" selected>-- Ne pas effectuer --</option>
<optgroup label="Rotation vers la droite (sens horaire)">
<option value="90">90&deg; (&frac14; tour)</option>
<option value="180">180&deg; (&frac12; tour)</option>
<option value="270">270&deg; (&frac34; tour)</option>
</optgroup>
</select>
</div>
</div>
<div class="mb-3">
<label for="dimMiniature" class="col-mb-3 form-label">Faire une miniature</label>
<div class="col-md-9">
<select name="dimMiniature" id="dimMiniature" class="form-select">
<option value="" selected>-- Ne pas effectuer --</option>
<option value="100x100">Avatar (100x100)</option>
<option value="320x240">Miniature (320x240)</option>
<option value="640x480">Miniature XL (640x480)</option>
<option value="700x700">Miniature Forum (700x700)</option>
</select>
</div>
</div>
<div class="mb-3">
<label for="redimImage" class="col-mb-3 form-label">Redimensionner l'image</label>
<div class="col-md-9">
<select name="redimImage" id="redimImage" class="form-select">
<option value="" selected>-- Ne pas effectuer --</option>
<option value="320x240">320x240</option>
<option value="640x480">640x480</option>
<option value="800x600">800x600</option>
<option value="1024x768">1024x768</option>
</select>
</div>
</div>
<button class="btn btn-success" name="Submit" type="submit" <?= ($uploadDisabled ? 'disabled="disabled"' : '') ?>><span class="bi-cloud-arrow-up-fill"></span>&nbsp;Envoyer</button>
</form>
</div>
</div>
<?php require _TPL_BOTTOM_; ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -25,14 +25,15 @@ namespace ImageHeberg;
* Vérification des prérequis basiques
* Ce fichier peut être supprimé après l'installation
*/
echo 'PHP v' . PHP_VERSION . '<br />';
/* 1 - Existence du fichier de config */
$conf = file_exists(__DIR__ . "/config/config.php");
$conf = file_exists(__DIR__ . '/config/config.php');
if (!$conf) {
$msg = "Le fichier de configuration n'existe pas dans config/config.php !";
$msg = 'Le fichier de configuration n\'existe pas dans config/config.php !';
echo $msg;
if (!_PHPUNIT_) {
die($msg);
} else {
echo $msg;
die();
}
}
@ -40,44 +41,47 @@ if (!$conf) {
if (!defined('_PHPUNIT_')) {
require 'config/config.php';
}
$res = MaBDD::getInstance()->query("SELECT COUNT(*) AS nbImages FROM images");
$res = MaBDD::getInstance()->query('SELECT COUNT(*) AS nbImages FROM images');
if (!$res) {
$msg = "Erreur de connexion à la base de données, vérifiez les identifiants dans le fichier config/config.php !";
$msg = 'Erreur de connexion à la base de données, vérifiez les identifiants dans le fichier config/config.php !';
echo $msg;
if (!_PHPUNIT_) {
die($msg);
} else {
echo $msg;
die();
}
}
$resultat = $res->fetch()->nbImages;
if ($resultat < 2) { // 404 & banned par défaut
$msg = "La base de données n'a pas été initialisée correctement avec le fichier database.sql !";
$msg = 'La base de données n\'a pas été initialisée correctement avec le fichier database.sql !';
echo $msg;
if (!_PHPUNIT_) {
die($msg);
} else {
echo $msg;
die();
}
}
/* 3 - droits sur répertoire des images */
if (!is_writable(_PATH_IMAGES_ . '_image_404.png')) {
$msg = "PHP doit pouvoir écrire dans les répertoires " . _REPERTOIRE_IMAGE_ . "* !";
$msg = 'PHP doit pouvoir écrire dans les répertoires ' . _REPERTOIRE_IMAGE_ . '* !';
echo $msg;
if (!_PHPUNIT_) {
die($msg);
} else {
echo $msg;
die();
}
}
/* 4 - gestion des sessions */
if (headers_sent()) {
$msg = "Les entêtes (sessions) ont déjà été envoyés, corrigez votre configuration serveur !";
$msg = 'Les entêtes (sessions) ont déjà été envoyés, corrigez votre configuration serveur !';
if (!_PHPUNIT_) {
die($msg);
} else {
// Ne rien faire, nous sommes dans l'environnement de tests qui a déjà affiché des données (phpunit...)
//echo $msg;
}
// Sinon, ne rien faire, nous sommes dans l'environnement de tests qui a déjà affiché des données (phpunit...)
}
echo "L'installation est OK !";
/* Configuration */
if (_PATH_ !== __DIR__ . '/') {
$msg = 'La variable _PATH_ (' . _PATH_ . ') n\'est pas cohérente avec l\'emplacement de l\'application (' . __DIR__ . ')';
echo $msg;
if (!_PHPUNIT_) {
die();
}
}
echo 'L\'installation est OK !';

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -33,21 +33,21 @@ if (isset($_POST['valider'])) {
$messageErreur = '';
if (empty($_POST['userName'])) {
$messageErreur .= "<br />Merci de saisir un identifiant.";
$messageErreur .= '<br />Merci de saisir un identifiant.';
}
if (empty($_POST['userPassword'])) {
$messageErreur .= "<br />Merci de saisir un mot de passe.";
$messageErreur .= '<br />Merci de saisir un mot de passe.';
}
// Si tout est bon
if (empty($messageErreur)) {
if ($monUtilisateur->connexion($_POST['userName'], $_POST['userPassword']) === true) {
if (!_PHPUNIT_) {
// Succès -> redirige sur la page d'accueil
header('Location: ' . _URL_);
header('Location: ' . _URL_HTTPS_);
die();
}
} else {
$messageErreur .= "<br />Erreur dans vos identifiants.";
$messageErreur .= '<br />Erreur dans vos identifiants.';
}
}
}
@ -61,19 +61,19 @@ if (isset($messageErreur)) :
<?= $messageErreur ?>
</div>
<?php endif; ?>
<h1 class="mb-3"><small>Connexion à mon compte</small></h1>
<h1 class="mb-3"><small>Connexion à mon compte</small></h1>
<form method="post">
<div class="mb-3 row form-floating">
<input type="text" class="form-control" name="userName" id="userName" placeholder="Identifiant" value="<?= $monUtilisateur->getUserName() ?>" required="required">
<label for="userName">Identifiant</label>
</div>
<div class="mb-3 row form-floating">
<input type="password" class="form-control" name="userPassword" id="userPassword" placeholder="Mot de passe" required="required">
<label for="userPassword">Mot de passe</label>
</div>
<button type="submit" name="valider" class="btn btn-success"><span class="fas fa-sign-in-alt"></span>&nbsp;Se connecter</button>
<a class="btn btn-outline-warning" role="button" disabled>Mot de passe oublié (à venir)</a>
<a class="btn btn-info" href="<?= _URL_MEMBRE_ ?>creerCompte.php" role="button"><span class="fas fa-save"></span>&nbsp;S'inscrire</a>
</form>
<?php require _TPL_BOTTOM_ ?>
<form method="post">
<div class="mb-3 row form-floating">
<input type="text" class="form-control" name="userName" id="userName" placeholder="Identifiant" value="<?= $monUtilisateur->getUserName() ?>" required="required">
<label for="userName">Identifiant</label>
</div>
<div class="mb-3 row form-floating">
<input type="password" class="form-control" name="userPassword" id="userPassword" placeholder="Mot de passe" required="required">
<label for="userPassword">Mot de passe</label>
</div>
<button type="submit" name="valider" class="btn btn-success"><span class="bi-box-arrow-in-right"></span>&nbsp;Se connecter</button>
<a class="btn btn-outline-warning" role="button" disabled>Mot de passe oublié (à venir)</a>
<a class="btn btn-info" href="<?= _URL_MEMBRE_ ?>creerCompte.php" role="button"><span class="bi-person-add"></span>&nbsp;S'inscrire</a>
</form>
<?php require _TPL_BOTTOM_ ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -24,7 +24,6 @@ namespace ImageHeberg;
if (!defined('_PHPUNIT_')) {
require '../config/config.php';
}
require _TPL_TOP_;
// Anti flood
$maSession = new SessionObject();
@ -40,25 +39,30 @@ if (isset($_POST['valider']) && $maSession->checkFlag()) {
if (empty($_POST['userName'])) {
$flagCreation = false;
$messageErreur .= "<br />Merci de saisir un identifiant.";
$messageErreur .= '<br />Merci de saisir un identifiant.';
}
if (empty($_POST['userPassword'])) {
$flagCreation = false;
$messageErreur .= "<br />Merci de saisir un mot de passe.";
$messageErreur .= '<br />Merci de saisir un mot de passe.';
}
if (empty($_POST['userMail'])) {
$flagCreation = false;
$messageErreur .= "<br />Merci de saisir une adresse courriel.";
$messageErreur .= '<br />Merci de saisir une adresse courriel.';
}
// Vérification du bon format de l'adresse mail
if (filter_var($_POST['userMail'], FILTER_VALIDATE_EMAIL) === false) {
$flagCreation = false;
$messageErreur .= "<br />L'adresse courriel saisie n'est pas correcte.";
$messageErreur .= '<br />L\'adresse courriel saisie n\'est pas correcte.';
}
// Disponibilité de l'email
if (UtilisateurObject::verifierEmailDisponible($_POST['userMail']) !== true) {
$flagCreation = false;
$messageErreur .= '<br />Cet email n\'est pas disponible. Merci d\'en choisir un autre.';
}
// Disponibilité du login
if (UtilisateurObject::verifierLoginDisponible($_POST['userName']) !== true) {
$flagCreation = false;
$messageErreur .= "<br />Ce nom d'utilisateur n'est pas possible. Merci d'en choisir un autre.";
$messageErreur .= '<br />Ce nom d\'utilisateur n\'est pas possible. Merci d\'en choisir un autre.';
}
// Données administratives : droits de l'utilisateur
@ -69,7 +73,7 @@ if (isset($_POST['valider']) && $maSession->checkFlag()) {
// Mot de passe - Crypté
$monUtilisateur->setPasswordToCrypt($_POST['userPassword']);
// Adresse mail
$monUtilisateur->setEmail($_POST['userMail']);
$monUtilisateur->setEmail(strtolower($_POST['userMail']));
// Si tout est bon
if ($flagCreation) {
@ -84,33 +88,37 @@ if (isset($_POST['valider']) && $maSession->checkFlag()) {
if (!_PHPUNIT_) {
// Redirection sur la page d'accueil - sauf si mode tests
header('Location: ' . _URL_);
header('Location: ' . _URL_HTTPS_);
die();
}
} else {
echo '<div class="alert alert-danger"><strong>La création de votre compte n\'est pas possible :</strong>';
echo $messageErreur;
echo '</div>';
}
}
require _TPL_TOP_;
$maSession->setFlag();
?>
<h1 class="mb-3"><small>Créer mon compte</small></h1>
<form method="post">
<div class="mb-3 form-floating">
<input type="text" class="form-control" name="userName" id="userName" value="<?= $monUtilisateur->getUserName() ?>" required="required">
<label for="userName">Identifiant</label>
<h1 class="mb-3"><small>Créer mon compte</small></h1>
<?php if (!empty($messageErreur)) : ?>
<div class="alert alert-danger"><strong>La création de votre compte n'est pas possible :</strong>
<?= $messageErreur ?>
</div>
<div class="mb-3 form-floating">
<input type="password" class="form-control" name="userPassword" id="userPassword" required="required">
<label for="userPassword">Mot de passe</label>
</div>
<div class="mb-3 form-floating">
<input type="email" class="form-control" name="userMail" id="userMail" value="<?= $monUtilisateur->getEmail() ?>" required="required">
<label for="userMail">Adresse courriel</label>
<div class="form-text">Utilisée uniquement en cas de réinitialisation de votre mot de passe.</div>
</div>
<button type="submit" name="valider" class="btn btn-success">M'inscrire</button>
</form>
<?php require _TPL_BOTTOM_ ?>
<?php endif; ?>
<form method="post">
<div class="mb-3 form-floating">
<input type="text" class="form-control" name="userName" id="userName" value="<?= $monUtilisateur->getUserName() ?>" required="required">
<label for="userName">Identifiant</label>
</div>
<div class="mb-3 form-floating">
<input type="password" class="form-control" name="userPassword" id="userPassword" required="required">
<label for="userPassword">Mot de passe</label>
</div>
<div class="mb-3 form-floating">
<input type="email" class="form-control" name="userMail" id="userMail" value="<?= $monUtilisateur->getEmail() ?>" required="required">
<label for="userMail">Adresse courriel</label>
<div class="form-text">Utilisée uniquement en cas de réinitialisation de votre mot de passe.</div>
</div>
<button type="submit" name="valider" class="btn btn-success">
<span class="bi-person-add"></span>&nbsp;
M'inscrire
</button>
</form>
<?php require _TPL_BOTTOM_ ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -19,57 +19,80 @@
* along with image-heberg.fr. If not, see <http://www.gnu.org/licenses/>
*/
// TODO : affichage des images dans la page en javascript ?
// TODO : navigation entre les images
namespace ImageHeberg;
require __DIR__ . '/../config/config.php';
// Vérification des droits d'accès
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_USER);
require _TPL_TOP_;
// Je récupère la session de mon utilisateur
$maSession = new SessionObject();
// Et je reprend ses données
$monUtilisateur = new UtilisateurObject($maSession->getId());
?>
<h1 class="mb-3"><small>Mes images</small></h1>
<table class="table table-hover">
<thead>
<tr class="text-center">
<th>Nom de l'image</th>
<th>Date d'envoi</th>
<th>Dernier affichage</th>
<th>Nb. vues</th>
<th>Voir</th>
<th>Supprimer</th>
</tr>
</thead>
<tbody>
<?php
$mesImages = $monUtilisateur->getImages();
foreach ((array) $mesImages as $newName) :
$uneImage = new ImageObject($newName);
require _TPL_TOP_;
?>
<h1 class="mb-3"><small>Mes images</small></h1>
<div class="row">
<?php
// Charger les objets concernés
$mesImages = ImageObject::chargerMultiple($monUtilisateur->getImages(), RessourceObject::SEARCH_BY_NAME);
foreach ($mesImages as $uneImage) :
$miniature = $uneImage->getMiniatures(true);
if ($miniature->count() === 0) {
// Duplication de l'image source
$tmpFile = tempnam(sys_get_temp_dir(), uniqid('', true));
copy($uneImage->getPathMd5(), $tmpFile);
// Génération de la miniature pour l'aperçu
$maMiniature = new MiniatureObject();
$maMiniature->setPathTemp($tmpFile);
$maMiniature->setIdImage($uneImage->getId());
$maMiniature->redimensionner($maMiniature->getPathTemp(), $maMiniature->getPathTemp(), _SIZE_PREVIEW_, _SIZE_PREVIEW_);
$maMiniature->setNomTemp('preview_' . $uneImage->getId());
$maMiniature->creer();
$maMiniature->setIsPreview(true);
$maMiniature->sauver();
} else {
$maMiniature = new MiniatureObject($miniature->offsetGet(0));
}
?>
<tr>
<td><?= $uneImage->getNomOriginalFormate() ?></td>
<td class="text-center"><?= $uneImage->getDateEnvoiFormatee() ?></td>
<td class="text-center"><?= $uneImage->getLastViewFormate() ?></td>
<td class="text-center"><?= $uneImage->getNbViewTotal() ?></td>
<td class="text-center">
<a href='<?= _URL_IMAGES_ ?><?= $uneImage->getNomNouveau() ?>' target="_blank">
<span class="fas fa-share-square"></span>
</a>
</td>
<td class="text-center">
<a href='<?= _URL_ ?>delete.php?id=<?= $uneImage->getNomNouveau() ?>&type=<?= RessourceObject::TYPE_IMAGE ?>'
target="_blank">
<span class="fas fa-trash"></span>
</a>
</td>
</tr>
<div class="col p-1" style="min-width: <?= _SIZE_PREVIEW_ ?>px;">
<div class="card h-100">
<div class="card-header">
<?= $uneImage->getNomOriginalFormate() ?>
</div>
<div class="card-body">
<p class="card-text">
Envoyée le <?= $uneImage->getDateEnvoiFormatee() ?>
<br/>
<?= $uneImage->getNbViewTotal() ?> affichage<?= $uneImage->getNbViewTotal() > 1 ? 's' : '' ?> <span title="Date du dernier affichage" class="fw-light fst-italic">(<?= $uneImage->getLastViewFormate() ?>)</span>
</p>
</div>
<div class="text-center">
<a href="<?= _URL_IMAGES_ ?><?= $uneImage->getNomNouveau() ?>" title="<?= $uneImage->getNomOriginalFormate() ?>" target="_blank">
<img src="<?= $maMiniature->getURL() ?>" class="card-img-bottom rounded-3" alt="<?= $uneImage->getNomOriginalFormate() ?>" style="<?= ($maMiniature->getLargeur() < 256 ? 'max-height: ' . _SIZE_PREVIEW_ . 'px; width: auto;' : 'height: auto; max-width: ' . _SIZE_PREVIEW_ . 'px;') ?>" loading="lazy">
</a>
</div>
<div class="card-footer text-muted text-end">
<a href="<?= _URL_IMAGES_ ?><?= $uneImage->getNomNouveau() ?>" title="Afficher" target="_blank">
<button type="button" class="btn btn-sm btn-outline-primary">
<span class="bi-box-arrow-up-right"></span>
</button>
</a>
<button type="button" class="btn btn-sm btn-outline-secondary" data-bs-toggle="modal" data-bs-target="#linksModal">
<span class="bi-link-45deg"></span>
</button>
<button type="button" class="btn btn-sm btn-outline-warning" data-bs-toggle="modal" data-bs-target="#renameModal">
<span class="bi-pencil"></span>
</button>
<a href="<?= _URL_HTTPS_ ?>delete.php?id=<?= $uneImage->getNomNouveau() ?>&type=<?= RessourceObject::TYPE_IMAGE ?>" title="Effacer" target="_blank">
<button type="button" class="btn btn-sm btn-outline-danger">
<span class="bi-trash"></span>
</button>
</a>
</div>
</div>
</div>
<?php endforeach; ?>
</tbody>
</table>
<?php require _TPL_BOTTOM_ ?>
</div>
<?php require _TPL_BOTTOM_; ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -25,13 +25,13 @@ if (!defined('_PHPUNIT_')) {
require '../config/config.php';
}
UtilisateurObject::checkAccess(UtilisateurObject::LEVEL_USER);
require _TPL_TOP_;
// Je récupère la session de mon utilisateur
$maSession = new SessionObject();
// Et je reprend ses données
$monUtilisateur = new UtilisateurObject($maSession->getId());
require _TPL_TOP_;
if (isset($_POST['modifierPwd'])) {
// Je vérifie qu'on me donne le bon mot de passe
if ($monUtilisateur->connexion($maSession->getUserName(), $_POST['oldUserPassword'])) {
@ -74,11 +74,11 @@ if (isset($_POST['modifierPwd'])) {
?>
<div class="alert alert-success">
Votre compte a été supprimé !
<br />
<br/>
Les images liées à votre compte n'ont pas été supprimées.
<br />
<br/>
Cette action est irrévocable !
<br />
<br/>
Merci d'avoir utilisé <?= _SITE_NAME_ ?>.
</div>
<?php
@ -95,106 +95,100 @@ if (isset($_POST['modifierPwd'])) {
}
}
?>
<h1 class="mb-3"><small>Mon compte <?= _SITE_NAME_ ?></small></h1>
<h1 class="mb-3"><small>Mon compte <?= _SITE_NAME_ ?></small></h1>
<div class="card">
<div class="card-header">
<?= $monUtilisateur->getUserName() ?>
</div>
<div class="card-body">
Membre depuis le : <?= $monUtilisateur->getDateInscriptionFormate() ?>
<br />
Adresse courriel : <?= $monUtilisateur->getEmail() ?>
<br />
Images possédées : <?= count($monUtilisateur->getImages()) ?>
</div>
</div>
<!-- Modification du mot de passe -->
<div class="card card-default">
<div class="card-header">
<a data-bs-toggle="collapse" href="#collapsePwd">
Changer de mot de passe <span class="fas fa-caret-down"></span>
</a>
</div>
<div id="collapsePwd" class="card-collapse collapse">
<div class="card">
<div class="card-header">
<?= $monUtilisateur->getUserName() ?>
</div>
<div class="card-body">
<form method="post">
<div class="mb-3">
<label for="oldUserPassword" class="form-label">Mot de passe actuel</label>
<input type="password" class="form-control" name="oldUserPassword" id="oldUserPassword"
placeholder="Mot de passe actuel" required="required">
</div>
<div class="mb-3">
<label for="newUserPassword" class="form-label">Nouveau mot de passe</label>
<input type="password" class="form-control" name="newUserPassword" id="newUserPassword"
placeholder="Nouveau mot de passe" required="required">
</div>
<button type="submit" name="modifierPwd" class="btn btn-success">Modifier le mot de passe</button>
</form>
Membre depuis le : <?= $monUtilisateur->getDateInscriptionFormate() ?>
<br/>
Adresse courriel : <?= $monUtilisateur->getEmail() ?>
<br/>
Images possédées : <?= count($monUtilisateur->getImages()) ?>
</div>
</div>
</div>
<!-- Changer l'adresse mail -->
<div class="card card-default">
<div class="card-header">
<a data-bs-toggle="collapse" href="#collapseMail">
Changer d'adresse courriel <span class="fas fa-caret-down"></span>
</a>
</div>
<div id="collapseMail" class="card-collapse collapse">
<div class="card-body">
<form method="post">
<div class="mb-3">
<label for="userMail" class="form-label">Nouvelle adresse courriel</label>
<input type="email" class="form-control" name="userMail" id="userMail"
placeholder="Nouvelle adresse courriel" required="required">
</div>
<div class="mb-3">
<label for="userPasswordMail" class="form-label">Mot de passe</label>
<input type="password" class="form-control" name="userPasswordMail" id="userPasswordMail"
placeholder="Mot de passe" required="required">
</div>
<button type="submit" name="modifierMail" class="btn btn-success">Modifier l'adresse courriel</button>
</form>
<!-- Modification du mot de passe -->
<div class="card card-default">
<div class="card-header">
<a data-bs-toggle="collapse" href="#collapsePwd">
Changer de mot de passe <span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="collapsePwd" class="card-collapse collapse">
<div class="card-body">
<form method="post">
<div class="mb-3">
<label for="oldUserPassword" class="form-label">Mot de passe actuel</label>
<input type="password" class="form-control" name="oldUserPassword" id="oldUserPassword" placeholder="Mot de passe actuel" required="required">
</div>
<div class="mb-3">
<label for="newUserPassword" class="form-label">Nouveau mot de passe</label>
<input type="password" class="form-control" name="newUserPassword" id="newUserPassword" placeholder="Nouveau mot de passe" required="required">
</div>
<button type="submit" name="modifierPwd" class="btn btn-success">Modifier le mot de passe</button>
</form>
</div>
</div>
</div>
</div>
<!-- Supprimer le compte -->
<div class="card card-default">
<div class="card-header">
<a data-bs-toggle="collapse" href="#collapseDelete">
Supprimer mon compte <span class="fas fa-caret-down"></span>
</a>
</div>
<div id="collapseDelete" class="card-collapse collapse">
<div class="card-body">
<form method="post">
<label class="text-danger form-label">
<input type="checkbox" value="" name="confirmeDelete">
<span class="fas fa-warning-sign"></span>
Je confirme souhaiter supprimer mon compte <?= _SITE_NAME_ ?>.
<br />
<b>
Les images rattachées à mon compte ne seront pas supprimées
et ne seront plus supprimables avant leur expiration !
</b>
<br />
Cette action est irrévocable !
</label>
<!-- Changer l'adresse mail -->
<div class="card card-default">
<div class="card-header">
<a data-bs-toggle="collapse" href="#collapseMail">
Changer d'adresse courriel <span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="collapseMail" class="card-collapse collapse">
<div class="card-body">
<form method="post">
<div class="mb-3">
<label for="userMail" class="form-label">Nouvelle adresse courriel</label>
<input type="email" class="form-control" name="userMail" id="userMail"
placeholder="Nouvelle adresse courriel" required="required">
</div>
<div class="mb-3">
<label for="userPasswordMail" class="form-label">Mot de passe</label>
<input type="password" class="form-control" name="userPasswordMail" id="userPasswordMail"
placeholder="Mot de passe" required="required">
</div>
<div class="mb-3">
<label for="userPasswordDelete" class="form-label">Mot de passe</label>
<input type="password" class="form-control" name="userPasswordDelete" id="userPasswordDelete"
placeholder="Mot de passe" required="required">
</div>
<button type="submit" name="supprimerCompte" class="btn btn-danger">Supprimer mon compte</button>
</form>
<button type="submit" name="modifierMail" class="btn btn-success">Modifier l'adresse courriel</button>
</form>
</div>
</div>
</div>
</div>
<?php require _TPL_BOTTOM_ ?>
<!-- Supprimer le compte -->
<div class="card card-default">
<div class="card-header">
<a data-bs-toggle="collapse" href="#collapseDelete">
Supprimer mon compte <span class="bi-caret-down-fill"></span>
</a>
</div>
<div id="collapseDelete" class="card-collapse collapse">
<div class="card-body">
<form method="post">
<label class="text-danger form-label">
<input type="checkbox" value="" name="confirmeDelete">
<span class="bi-exclamation-triangle-fill"></span>
Je confirme souhaiter supprimer mon compte <?= _SITE_NAME_ ?>.
<br/>
<b>Les images rattachées à mon compte ne seront pas supprimées et ne seront plus supprimables avant leur expiration !</b>
<br/>
Cette action est irrévocable !
</label>
<div class="mb-3">
<label for="userPasswordDelete" class="form-label">Mot de passe</label>
<input type="password" class="form-control" name="userPasswordDelete" id="userPasswordDelete" placeholder="Mot de passe" required="required">
</div>
<button type="submit" name="supprimerCompte" class="btn btn-danger">Supprimer mon compte</button>
</form>
</div>
</div>
</div>
<?php require _TPL_BOTTOM_ ?>

View file

@ -1,7 +1,7 @@
<?php
/*
* Copyright 2008-2021 Anael MOBILIA
* Copyright 2008-2024 Anael MOBILIA
*
* This file is part of image-heberg.fr.
*
@ -25,35 +25,30 @@ require 'config/config.php';
require _TPL_TOP_;
// Stats Images
$reqImage = MaBDD::getInstance()->query("SELECT COUNT(*) AS nb, SUM(nb_view_v4 * size) AS bpv4, SUM(nb_view_v6 * size) AS bpv6, SUM(nb_view_v4 + nb_view_v6) AS nbAff, SUM(size) as totSize FROM images");
/* @var $reqImage PDOStatement */
$reqImage = MaBDD::getInstance()->query('SELECT COUNT(*) AS nb, SUM(nb_view_v4 * size) AS bpv4, SUM(nb_view_v6 * size) AS bpv6, SUM(nb_view_v4 + nb_view_v6) AS nbAff, SUM(size) as totSize, MAX(id) as nbTot FROM images');
// Je récupère les valeurs
$valImage = $reqImage->fetch();
// Stats Miniatures
$reqMiniature = MaBDD::getInstance()->query("SELECT COUNT(*) AS nb, SUM(nb_view_v4 * size) AS bpv4, SUM(nb_view_v6 * size) AS bpv6, SUM(nb_view_v4 + nb_view_v6) AS nbAff, SUM(size) as totSize FROM thumbnails");
/* @var $reqMiniature PDOStatement */
$reqMiniature = MaBDD::getInstance()->query('SELECT COUNT(*) AS nb, SUM(nb_view_v4 * size) AS bpv4, SUM(nb_view_v6 * size) AS bpv6, SUM(nb_view_v4 + nb_view_v6) AS nbAff, SUM(size) as totSize FROM thumbnails');
// Je récupère les valeurs
$valMiniature = $reqMiniature->fetch();
// Stats membres
$reqMembre = MaBDD::getInstance()->query("SELECT COUNT(*) AS nb FROM membres");
/* @var $reqMembre PDOStatement */
$reqMembre = MaBDD::getInstance()->query('SELECT COUNT(*) AS nb FROM membres');
// Je récupère les valeurs
$valMembre = $reqMembre->fetch();
// Stats membres -> possède
$reqPossede = MaBDD::getInstance()->query("SELECT COUNT(*) AS nb FROM possede");
/* @var $reqPossede PDOStatement */
$reqPossede = MaBDD::getInstance()->query('SELECT COUNT(*) AS nb FROM possede');
// Je récupère les valeurs
$valPossede = $reqPossede->fetch();
// Bande passante
$bp_v4 = $valImage->bpv4 + $valMiniature->bpv4;
$bp_all = 1;
$bp_all += $valImage->bpv4 + $valMiniature->bpv4;
$bp_v6 = $valImage->bpv6 + $valMiniature->bpv6;
$bp_all = $bp_v4 + $bp_v6;
// Fix pour l'affichage des stats sans utilisation du service
$bp_all = ($bp_all) ? $bp_all : 1;
$bp_all += $bp_v6;
// Nombre d'affichages
$nb_view_all = $valImage->nbAff + $valMiniature->nbAff;
// Taille totale
@ -65,24 +60,27 @@ $size_all = $valImage->totSize + $valMiniature->totSize;
<div class="card-body">
<ul>
<li>
<?= number_format($valImage->nb, 0, ',', ' ') ?> images et
<?= number_format($valMiniature->nb, 0, ',', ' ') ?> miniatures actuellement hébergées
<?= number_format($valImage->nbTot, 0, ',', ' ') ?> images hébergées au total.
</li>
<li>
<?= number_format($size_all / 1073741824, 1, ',', ' ') ?> Go de fichiers stockés
<?= number_format($valImage->nb, 0, ',', ' ') ?> images et
<?= number_format($valMiniature->nb, 0, ',', ' ') ?> miniatures actuellement hébergées.
</li>
<li>
<?= number_format($size_all / 1073741824, 1, ',', ' ') ?> Go de fichiers actuellement stockés.
</li>
<li>
<?= number_format($bp_all / 1073741824, 1, ',', ' ') ?> Go de trafic - dont
<?= number_format(($bp_v6 / $bp_all) * 100, 2) ?>%
en <a href="http://fr.wikipedia.org/wiki/Ipv6">IPv6</a>
en <a href="https://fr.wikipedia.org/wiki/Ipv6">IPv6</a>.
</li>
<li>
<?= number_format($nb_view_all, 0, ',', ' ') ?> affichages d'images
<em>(<?= number_format($valImage->nbAff, 0, ',', ' ') ?> images
+ <?= number_format($valMiniature->nbAff, 0, ',', ' ') ?> miniatures)</em>
+ <?= number_format($valMiniature->nbAff, 0, ',', ' ') ?> miniatures)</em>.
</li>
<li>
<?= $valMembre->nb ?> membres possèdant au total <?= $valPossede->nb ?> images
<?= $valMembre->nb ?> membres possèdant au total <?= $valPossede->nb ?> images.
</li>
</ul>
</div>

6
template/css/bootstrap-5.3.3.min.css vendored Normal file

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show more