[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / ecrire / inc / admin.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2017 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 /**
14 * Gestion d'administration d'un SPIP
15 *
16 * @param SPIP\Core\Admin
17 **/
18
19 if (!defined('_ECRIRE_INC_VERSION')) {
20 return;
21 }
22
23 /**
24 * Teste qu'un utilisateur a des droits sur les fichiers du site et
25 * exécute l'action (en base) demandée si c'est le cas.
26 *
27 * Demande / vérifie le droit de création de répertoire par le demandeur;
28 * Mémorise dans les meta que ce script est en cours d'exécution.
29 * Si elle y est déjà c'est qu'il y a eu suspension du script, on reprend.
30 *
31 * @uses debut_admin()
32 * @uses admin_verifie_session()
33 * @uses fin_admin()
34 *
35 * @param string $script
36 * Script d'action (en base) à exécuter si on a des droits d'accès aux fichiers
37 * @param string $titre
38 * Titre de l'action demandée
39 * @param string $comment
40 * Commentaire supplémentaire
41 * @param bool $anonymous
42 * ?
43 * @return string
44 * Code HTML de la page (pour vérifier les droits),
45 * sinon code HTML de la page après le traitement effectué.
46 **/
47 function inc_admin_dist($script, $titre, $comment = '', $anonymous = false) {
48 $reprise = true;
49 if (!isset($GLOBALS['meta'][$script])
50 or !isset($GLOBALS['meta']['admin'])
51 ) {
52 $reprise = false;
53 $res = debut_admin($script, $titre, $comment);
54 if ($res) {
55 return $res;
56 }
57 spip_log("meta: $script " . join(',', $_POST));
58 ecrire_meta($script, serialize($_POST));
59 }
60
61 $res = admin_verifie_session($script, $anonymous);
62 if ($res) {
63 return $res;
64 }
65 $base = charger_fonction($script, 'base');
66 $base($titre, $reprise);
67 fin_admin($script);
68
69 return '';
70 }
71
72 /**
73 * Gestion dans la meta "admin" du script d'administation demandé,
74 * pour éviter des exécutions en parallèle, notamment après Time-Out.
75 *
76 * Cette meta contient le nom du script et, à un hachage près, du demandeur.
77 * Le code de ecrire/index.php dévie toute demande d'exécution d'un script
78 * vers le script d'administration indiqué par cette meta si elle est là.
79 *
80 * Au niveau de la fonction inc_admin, on controle la meta 'admin'.
81 *
82 * - Si la meta n'est pas là, c'est le début on la crée.
83 * - Sinon, si le hachage actuel est le même que celui en base,
84 * c'est une reprise, on continue.
85 * - Sinon, si le hachage diffère à cause du connect, c'est une arrivée
86 * inoppotune, on refuse sa connexion.
87 * - Enfin, si hachage diffère pour une autre raison, c'est que l'operation
88 * se passe mal, on la stoppe
89 *
90 * @uses fichier_admin()
91 *
92 * @param string $script
93 * Script d'action (en base)
94 * @param bool $anonymous
95 * ?
96 * @return string
97 * Code HTML si message d'erreur, '' sinon;
98 */
99 function admin_verifie_session($script, $anonymous = false) {
100 include_spip('base/abstract_sql');
101 $pref = sprintf('_%d_', $GLOBALS['visiteur_session']['id_auteur']);
102 $signal = fichier_admin($script, "$script$pref");
103 $valeur = sql_getfetsel('valeur', 'spip_meta', "nom='admin'");
104 if ($valeur === null) {
105 ecrire_meta('admin', $signal, 'non');
106 } else {
107 if (!$anonymous and ($valeur != $signal)) {
108 if (!preg_match('/^(.*)_(\d+)_/', $GLOBALS['meta']['admin'], $l)
109 or intval($l[2]) != $GLOBALS['visiteur_session']['id_auteur']
110 ) {
111 include_spip('inc/minipres');
112 spip_log("refus de lancer $script, priorite a $valeur");
113
114 return minipres(_T('info_travaux_texte'), '', array('status' => 503));
115 }
116 }
117 }
118 $journal = 'spip';
119 if (autoriser('configurer')) {
120 // c'est une action webmestre, soit par ftp soit par statut webmestre
121 $journal = 'webmestre';
122 }
123 // on pourrait statuer automatiquement les webmestres a l'init d'une action auth par ftp ... ?
124
125 spip_log("admin $pref" . ($valeur ? ' (reprise)' : ' (init)'), $journal);
126
127 return '';
128 }
129
130 /**
131 * Retourne l'emplacement du répertoire où sera testé l'accès utilisateur
132 *
133 * Dans le répertoire temporaire si on est admin, sinon dans le répertoire
134 * de transfert des admins restreints
135 *
136 * @return string
137 * Chemin du répertoire.
138 **/
139 function dir_admin() {
140 if (autoriser('configurer')) {
141 return _DIR_TMP;
142 } else {
143 return _DIR_TRANSFERT . $GLOBALS['visiteur_session']['login'] . '/';
144 }
145 }
146
147 /**
148 * Retourne le nom d'un fichier de teste d'authentification par accès
149 * aux fichiers
150 *
151 * Le nom calculé est un hash basé sur l’heure, l’action et l’auteur.
152 *
153 * @param string $action
154 * Nom du script d'action (en base)
155 * @param string $pref
156 * Préfixe au nom du fichier calculé
157 * @return string
158 * Nom du fichier
159 **/
160 function fichier_admin($action, $pref = 'admin_') {
161 return $pref .
162 substr(md5($action . (time() & ~2047) . $GLOBALS['visiteur_session']['login']), 0, 10);
163 }
164
165 /**
166 * Demande la création d'un répertoire (pour tester l'accès de l'utilisateur)
167 * et sort ou quitte sans rien faire si le répertoire est déjà là.
168 *
169 * Si l'on est webmestre, la plupart des actions n'ont pas besoin
170 * de tester la création du répertoire (toutes sauf repair ou delete_all).
171 * On considère qu'un webmestre a déjà du prouver ses droits sur les fichiers.
172 * Dans ce cas, on quitte sans rien faire également.
173 *
174 * @uses dir_admin()
175 * @uses fichier_admin()
176 *
177 * @param string $script
178 * Script d'action (en base) à exécuter ensuite
179 * @param string $action
180 * Titre de l'action demandée
181 * @param string $corps
182 * Commentaire supplémentaire
183 * @return string
184 * Code HTML de la page (pour vérifier les droits),
185 * sinon chaîne vide si déjà fait.
186 **/
187 function debut_admin($script, $action = '', $corps = '') {
188 if ((!$action) || !(autoriser('webmestre') or autoriser('chargerftp'))) {
189 include_spip('inc/minipres');
190
191 return minipres();
192 } else {
193 $dir = dir_admin();
194 $signal = fichier_admin($script);
195 if (@file_exists($dir . $signal)) {
196 spip_log("Action admin: $action");
197
198 return '';
199 }
200 include_spip('inc/minipres');
201
202 // Si on est un super-admin, un bouton de validation suffit
203 // sauf dans les cas destroy
204 if ((autoriser('webmestre') or $script === 'repair')
205 and $script != 'delete_all'
206 ) {
207 if (_request('validation_admin') == $signal) {
208 spip_log("Action super-admin: $action");
209
210 return '';
211 }
212 $corps .= '<input type="hidden" name="validation_admin" value="' . $signal . '" />';
213 $suivant = _T('bouton_valider');
214 $js = '';
215 } else {
216 // cet appel permet d'assurer un copier-coller du nom du repertoire a creer dans tmp (esj)
217 // l'insertion du script a cet endroit n'est pas xhtml licite
218 // mais evite de l'embarquer dans toutes les pages minipres
219 $corps .= http_script('', 'spip_barre.js');
220
221 $corps .= '<fieldset><legend>'
222 . _T('info_authentification_ftp')
223 . aider('ftp_auth')
224 . "</legend>\n<label for='fichier'>"
225 . _T('info_creer_repertoire')
226 . "</label>\n"
227 . "<span id='signal' class='formo'>" . $signal . '</span>'
228 . "<input type='hidden' id='fichier' name='fichier' value='"
229 . $signal
230 . "' />"
231 . _T('info_creer_repertoire_2', array('repertoire' => joli_repertoire($dir)))
232 . '</fieldset>';
233
234 $suivant = _T('bouton_recharger_page');
235
236 // code volontairement tordu:
237 // provoquer la copie dans le presse papier du nom du repertoire
238 // en remettant a vide le champ pour que ca marche aussi en cas
239 // de JavaScript inactif.
240 $js = " onload='var range=document.createRange(); var signal = document.getElementById(\"signal\"); var userSelection = window.getSelection(); range.setStart(signal,0); range.setEnd(signal,1); userSelection.addRange(range);'";
241 }
242
243 // admin/xxx correspond
244 // a exec/base_xxx de preference
245 // et exec/xxx sinon (compat)
246 if (tester_url_ecrire("base_$script")) {
247 $script = "base_$script";
248 }
249 $form = copy_request($script, $corps, $suivant);
250 $info_action = _T('info_action', array('action' => "$action"));
251
252 return minipres($info_action, $form, $js);
253 }
254 }
255
256 /**
257 * Clôture la phase d'administration en supprimant le répertoire
258 * testant l'accès au fichiers ainsi que les metas d'exécution
259 *
260 * @param string $action
261 * Nom de l'action (en base) qui a été exécutée
262 **/
263 function fin_admin($action) {
264 $signal = dir_admin() . fichier_admin($action);
265 spip_unlink($signal);
266 if ($action != 'delete_all') {
267 effacer_meta($action);
268 effacer_meta('admin');
269 spip_log("efface les meta admin et $action ");
270 }
271 }
272
273 /**
274 * Génère un formulaire avec les données postées
275 *
276 * Chaque donnée est mise en input hidden pour
277 * les soumettre avec la validation du formulaire.
278 *
279 * @param string $script
280 * Nom du script (pour l'espace privé) de destination
281 * @param string $suite
282 * Corps du formulaire
283 * @param string $submit
284 * Texte du bouton de validation
285 * @return string
286 * Code HTML du formulaire
287 **/
288 function copy_request($script, $suite, $submit = '') {
289 include_spip('inc/filtres');
290 foreach (array_merge($_POST, $_GET) as $n => $c) {
291 if (!in_array($n, array('fichier', 'exec', 'validation_admin')) and !is_array($c)) {
292 $suite .= "\n<input type='hidden' name='" . spip_htmlspecialchars($n) . "' value='" .
293 entites_html($c) .
294 "' />";
295 }
296 }
297
298 return generer_form_ecrire($script, $suite, '', $submit);
299 }