93b01283f828e5b5987bf3bcd77f2bacdae8ded6
[lhc/web/www.git] / www / plugins-dist / dump / inc / dump.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2020 *
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 * API permettant la manipulation des sauvegardes
15 *
16 * @package SPIP\Dump\API
17 */
18
19 if (!defined('_ECRIRE_INC_VERSION')) {
20 return;
21 }
22
23
24 /**
25 * Répertoire de sauvegarde
26 *
27 * Crée et retourne le chemin du répertoire de sauvegarde
28 *
29 * @see _DIR_DUMP
30 *
31 * @return string
32 */
33 function dump_repertoire() {
34 $repertoire = _DIR_DUMP;
35 if (!@file_exists($repertoire)
36 and !$repertoire = sous_repertoire(_DIR_DUMP, '', false, true)
37 ) {
38 $repertoire = preg_replace(',' . _DIR_TMP . ',', '', _DIR_DUMP);
39 $repertoire = sous_repertoire(_DIR_TMP, $repertoire);
40 }
41
42 return $repertoire;
43 }
44
45
46 /**
47 * Nom du fichier de sauvegarde
48 *
49 * la fourniture de l'extension permet de vérifier que le nom n'existe pas déjà.
50 *
51 * @param string $dir Chemin de stockage du fichier
52 * @param string $extension Extension du fichier de sauvegarde
53 * @return string
54 */
55 function dump_nom_fichier($dir, $extension = 'sqlite') {
56 include_spip('inc/texte');
57 $site = 'spip';
58 if (isset($GLOBALS['meta']['nom_site'])) {
59 $site = typo($GLOBALS['meta']['nom_site']); // extraire_multi
60 $site = couper(translitteration(trim($site)), 30, '');
61 $site = preg_replace(array(',\W,is', ',_(?=_),', ',_$,'), array('_', '', ''), $site);
62 }
63
64 $site .= '_' . date('Ymd');
65
66 $nom = $site;
67 $cpt = 0;
68 while (file_exists($dir . $nom . ".$extension")) {
69 $nom = $site . sprintf('_%03d', ++$cpt);
70 }
71
72 return $nom . ".$extension";
73 }
74
75 /**
76 * Détermine le type de serveur de sauvegarde,
77 * sqlite2 ou sqlite3
78 *
79 * @return string
80 */
81 function dump_type_serveur() {
82 // chercher si sqlite2 ou 3 est disponible
83 include_spip('req/sqlite3');
84 if (function_exists('spip_versions_sqlite3') and spip_versions_sqlite3()) {
85 return 'sqlite3';
86 }
87 if (function_exists('spip_versions_sqlite3')) {
88 spip_log("ERREUR sqlite3 n'est pas correctement installé : "
89 ."extension_loaded('pdo')=".extension_loaded('pdo')
90 ." extension_loaded('pdo_sqlite')=".extension_loaded('pdo_sqlite'),
91 _LOG_ERREUR);
92 }
93
94 include_spip('req/sqlite2');
95 if (function_exists('spip_versions_sqlite2') and spip_versions_sqlite2()) {
96 return 'sqlite2';
97 }
98
99 return '';
100 }
101
102 /**
103 * Conteneur pour les arguments de la connexion
104 *
105 * Si on passe $args, les arguments de la connexion sont memorisés.
106 * Renvoie toujours les derniers arguments memorisés.
107 *
108 * @staticvar array $connect_args Pour stocker le premier conteneur
109 * @param array $args
110 * @return array
111 */
112 function dump_serveur($args = null) {
113 static $connect_args = null;
114 if ($args) {
115 $connect_args = $args;
116 }
117
118 return $connect_args;
119 }
120
121 function dump_connect_args($archive) {
122 if (!$type_serveur = dump_type_serveur()) {
123 return null;
124 }
125
126 return array(dirname($archive), '', '', '', basename($archive, '.sqlite'), $type_serveur, 'spip');
127 }
128
129 /**
130 * Initialiser un dump
131 *
132 * @param string $status_file Fichier contenant les informations serialisees sur le statut de l'export
133 * @param string $archive Nom de l'archive (stockee dans le fichier de statut)
134 * @param array $tables Liste des tables a exporter (autocaculee par defaut)
135 * @param array $where Condition sur l'export
136 * @param string $action Pour differencier la sauvegarde de l'import
137 * @return bool/string
138 */
139 function dump_init($status_file, $archive, $tables = null, $where = array(), $action = 'sauvegarde') {
140 $status_file = _DIR_TMP . basename($status_file) . '.txt';
141
142 if (lire_fichier($status_file, $status)
143 and $status = unserialize($status)
144 and $status['etape'] !== 'fini'
145 and filemtime($status_file) >= time() - 120
146 ) {
147 // si le fichier status est trop vieux c'est un abandon
148 return _T('dump:erreur_' . $action . '_deja_en_cours');
149 }
150
151 if (!$type_serveur = dump_type_serveur()) {
152 return _T('dump:erreur_sqlite_indisponible');
153 }
154
155 if (!$tables) {
156 list($tables, ) = base_liste_table_for_dump(lister_tables_noexport());
157 }
158 $status = array('tables' => $tables, 'where' => $where, 'archive' => $archive);
159
160 $status['connect'] = dump_connect_args($archive);
161 dump_serveur($status['connect']);
162 if (!spip_connect('dump')) {
163 return _T('dump:erreur_creation_base_sqlite');
164 }
165
166 // la constante sert a verifier qu'on utilise bien le connect/dump du plugin,
167 // et pas une base externe homonyme
168 if (!defined('_DUMP_SERVEUR_OK')) {
169 return _T('erreur_connect_dump', array('dump' => 'dump'));
170 }
171
172 $status['etape'] = 'init';
173
174 if (!ecrire_fichier($status_file, serialize($status))) {
175 return _T('dump:avis_probleme_ecriture_fichier', array('fichier' => $status_file));
176 }
177
178 return true;
179 }
180
181 /**
182 * Afficher l'avancement de la copie
183 *
184 * @staticvar int $etape Nombre de fois ou on est passe dans cette foncion
185 * @param <type> $courant Flag pour indiquer si c'est la table sur laquelle on travaille actuellement
186 * @param <type> $total Nombre total de tables
187 * @param <type> $table Nom de la table
188 */
189 function dump_afficher_progres($courant, $total, $table) {
190 static $etape = 1;
191 if (unique($table)) {
192 if ($total < 0 or !is_numeric($total)) {
193 echo '<br /><strong>' . $etape . '. ' . "</strong>$table ";
194 } else {
195 echo '<br /><strong>' . $etape . '. ' . "$table</strong> " . ($courant ? " <i>($courant)</i> " : '');
196 }
197 $etape++;
198 }
199 if (is_numeric($total) and $total >= 0) {
200 echo '. ';
201 } else {
202 echo '(' . (-intval($total)) . ')';
203 }
204 flush();
205 }
206
207 /**
208 * Écrire le js pour relancer la procédure de dump
209 *
210 * @param string $redirect URL de la prochaine etape du dump
211 * @return string Code HTML de redirection
212 */
213 function dump_relance($redirect) {
214 // si Javascript est dispo, anticiper le Time-out
215 return "<script language=\"JavaScript\" type=\"text/javascript\">window.setTimeout('location.href=\"$redirect\";',300);</script>\n";
216 }
217
218
219 /**
220 * Marquer la procédure de dump comme finie
221 *
222 * @param string $status_file
223 * Fichier qui mémorise les infos utiles concernant la sauvegarde en cours
224 * @param string $action
225 * Type d'action supplémentaire à réaliser :
226 *
227 * - restaurer : supprimer la structure qui était stockée dans le dump
228 * - 'auvegarder : stocker dans le dump la structure de la base source
229 */
230 function dump_end($status_file, $action = '') {
231 $status_file = _DIR_TMP . basename($status_file) . '.txt';
232 if (!lire_fichier($status_file, $status)
233 or !$status = unserialize($status)
234 ) {
235 return;
236 }
237
238 switch ($action) {
239 case 'restaurer':
240 // supprimer la structure qui etait stockee dans le dump
241 sql_delete('spip_meta', "nom='dump_structure_temp'");
242 break;
243 case 'sauvegarder':
244 // stocker dans le dump la structure de la base source
245 $structure = array();
246 foreach ($status['tables_copiees'] as $t => $n) {
247 $structure[$t] = sql_showtable($t, true);
248 }
249 dump_serveur($status['connect']);
250 spip_connect('dump');
251 // si spip_meta n'a pas ete backup elle n'est pas dans le dump, il faut la creer pour y stocker cette meta
252 if (!sql_showtable('spip_meta', true, 'dump')) {
253 $desc = sql_showtable('spip_meta', true);
254 sql_create('spip_meta', $desc['field'], $desc['key'], false, false, 'dump');
255 }
256 sql_delete('spip_meta', "nom='dump_structure_temp'", 'dump'); #enlever une vieille structure deja la, au cas ou
257 sql_insertq(
258 'spip_meta',
259 array('nom' => 'dump_structure_temp', 'valeur' => serialize($structure), 'impt' => 'non'),
260 array(),
261 'dump'
262 );
263 break;
264 }
265
266 $status['etape'] = 'fini';
267 ecrire_fichier($status_file, serialize($status));
268 }
269
270 /**
271 * Lister les fichiers de sauvegarde existant dans un repertoire
272 * trie par nom, date ou taille
273 *
274 * @param string $dir Repertoire de sauvegarde
275 * @param string $tri Tri pour recuperer les fichiers
276 * @param string $extension Extension des fichiers de sauvegarde
277 * @param int $limit Nombre max de fichiers listes
278 * @return array
279 */
280 function dump_lister_sauvegardes($dir, $tri = 'nom', $extension = 'sqlite', $limit = 100) {
281 $liste_dump = preg_files($dir, '\.' . $extension . '$', $limit, false);
282
283 $n = strlen($dir);
284 $tn = $tl = $tt = $td = array();
285 foreach ($liste_dump as $fichier) {
286 $d = filemtime($fichier);
287 $t = filesize($fichier);
288 $fichier = substr($fichier, $n);
289 $tl[] = array('fichier' => $fichier, 'taille' => $t, 'date' => $d);
290 $td[] = $d;
291 $tt[] = $t;
292 $tn[] = $fichier;
293 }
294 if ($tri == 'taille') {
295 array_multisort($tt, SORT_ASC, $tl);
296 } elseif ($tri == 'date') {
297 array_multisort($td, SORT_ASC, $tl);
298 } else {
299 array_multisort($tn, SORT_ASC, $tl);
300 }
301
302 return $tl;
303 }
304
305 /**
306 * Extraire le statut contenu dans un fichier
307 *
308 * @param $status_file Nom du fichier stocke dans _DIR_TMP
309 * @return array
310 */
311 function dump_lire_status($status_file) {
312 $status_file = _DIR_TMP . basename($status_file) . '.txt';
313 if (!lire_fichier($status_file, $status)
314 or !$status = unserialize($status)
315 ) {
316 return '';
317 }
318
319 return $status;
320 }
321
322 /**
323 * Verifier qu'un sauvegarde est finie
324 *
325 * @param $status_file Nom du fichier stocke dans _DIR_TMP
326 * @return string Chaine non vide s'il reste des choses a faire
327 */
328 function dump_verifie_sauvegarde_finie($status_file) {
329 if (!$status = dump_lire_status($status_file)
330 or $status['etape'] !== 'fini'
331 ) {
332 return '';
333 }
334
335 return ' ';
336 }
337
338 /**
339 * Recuperer le nom du fichier d'archivage qui est memorise dans le fichier de statut
340 *
341 * @param $status_file Nom du fichier stocke dans _DIR_TMP
342 * @return string Nom ou chaine vide si on a un probleme
343 */
344 function dump_nom_sauvegarde($status_file) {
345 if (!$status = dump_lire_status($status_file)
346 or !file_exists($f = $status['archive'] . '.sqlite')
347 ) {
348 return '';
349 }
350
351 return $f;
352 }
353
354 /**
355 * Recuperer la taille du fichier de sauvegarde
356 *
357 * @param $status_file Nom du fichier stocke dans _DIR_TMP
358 * @return string/int Taille ou Chaine vide en cas de probleme
359 */
360 function dump_taille_sauvegarde($status_file) {
361 if (!$f = dump_nom_sauvegarde($status_file)
362 or !$s = filesize($f)
363 ) {
364 return '';
365 }
366
367 return $s;
368 }
369
370 /**
371 * Recuperer la date de derniere modification du fichier de sauvegarde
372 *
373 * @param $status_file Nom du fichier stocke dans _DIR_TMP
374 * @return string/int Date ou Chaine vide en cas de probleme
375 */
376 function dump_date_sauvegarde($status_file) {
377 if (!$f = dump_nom_sauvegarde($status_file)
378 or !$d = filemtime($f)
379 ) {
380 return '';
381 }
382
383 return date('Y-m-d', $d);
384 }