[SPIP] ~2.1.12 -->2.1.25
[velocampus/web/www.git] / www / ecrire / inc / import.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2014 *
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 if (!defined('_ECRIRE_INC_VERSION')) return;
14
15 include_spip('inc/presentation');
16 include_spip('inc/acces');
17 include_spip('base/abstract_sql');
18
19 // Retourne la premiere balise XML figurant dans le buffet de la sauvegarde
20 // et avance dans ce buffet jusqu'au '>' de cette balise.
21 // Si le 2e argument (passe par reference) est non vide
22 // ce qui precede cette balise y est mis.
23 // Les balises commencant par <! sont ignorees
24 // $abs_pos est globale pour pouvoir etre reinitialisee a la meta
25 // restauration_status en cas d'interruption sur TimeOut.
26 // Evite au maximum les recopies
27
28 // http://doc.spip.org/@xml_fetch_tag
29 function xml_fetch_tag($f, &$before, $_fread='fread', $skip='!') {
30 global $abs_pos;
31 static $buf='';
32 static $ent = array('&gt;','&lt;','&amp;');
33 static $brut = array('>','<','&');
34
35 while (($b=strpos($buf,'<'))===false) {
36 if (!($x = $_fread($f, 1024))) return '';
37 if ($before)
38 $buf .= $x;
39 else {
40 if (_DEBUG_IMPORT)
41 $GLOBALS['debug_import_avant'] .= $buf;
42 $abs_pos += strlen($buf);
43 $buf = $x;
44 }
45 }
46 if ($before) $before = str_replace($ent,$brut,substr($buf,0,$b));
47 # else { spip_log("position: $abs_pos" . substr($buf,0,12));flush();}
48
49 // $b pour ignorer un > de raccourci Spip avant un < de balise XML
50
51 while (($e=strpos($buf,'>', $b))===false) {
52 if (!($x = $_fread($f, 1024))) return '';
53 $buf .= $x;
54 }
55
56 if ($buf[++$b]!=$skip) {
57 if (_DEBUG_IMPORT){
58 $GLOBALS['debug_import_avant'] .= substr($buf,0,$e+1);
59 $GLOBALS['debug_import_avant'] = substr($GLOBALS['debug_import_avant'],-1024);
60 }
61 $tag = substr($buf, $b, $e-$b);
62 $buf = substr($buf,++$e);
63 if (_DEBUG_IMPORT)
64 $GLOBALS['debug_import_apres'] = $buf;
65 $abs_pos += $e;
66 return $tag;
67 }
68 if (_DEBUG_IMPORT)
69 $GLOBALS['debug_import_avant'] .= substr($buf,0,$e+1);
70 $buf = substr($buf,++$e);
71 if (_DEBUG_IMPORT)
72 $GLOBALS['debug_import_apres'] = $buf;
73 $abs_pos += $e;
74 return xml_fetch_tag($f,$before,$_fread,$skip);
75 }
76
77 // http://doc.spip.org/@xml_parse_tag
78 function xml_parse_tag($t) {
79
80 preg_match(',^([\w[?!%.;:-]*),s', $t, $res);
81 $t = substr($t,strlen($res[0]));
82 $res[1] = array();
83
84 // pourquoi on ne peut pas mettre \3 entre crochets ?
85 if (preg_match_all(',\s*(--.*?--)?\s*([^=]*)\s*=\s*([\'"])([^"]*)\3,sS', $t, $m, PREG_SET_ORDER)) {
86 foreach($m as $r) $res[1][$r[2]] = $r[4];
87 }
88 return $res;
89 }
90
91 /**
92 * Lire l'entete du fichier importe
93 * Balise ouvrante:
94 * 'SPIP' si fait par spip, nom de la base source si fait par phpmyadmin
95 *
96 * @param resource $f
97 * @param string $gz
98 * @return array
99 */
100 function import_debut($f, $gz='fread') {
101
102 // Pour les anciennes archives, indiquer le charset par defaut:
103 $charset = 'iso-8859-1';
104 // les + recentes l'ont en debut de ce fichier
105 $flag_phpmyadmin = false;
106 $b = false;
107 while ($t = xml_fetch_tag($f, $b, $gz, '')) {
108 $r = xml_parse_tag($t);
109 if ($r[0] == '?xml' AND $r[1]['encoding'])
110 $charset = strtolower($r[1]['encoding']);
111 elseif ($r[0] == "SPIP") {$r[2] = $charset; return $r;}
112 if (($r[0] == "!--") && (preg_match(",phpmyadmin\sxml\sdump,is",$r[1]))){
113 // c'est un dump xml phpmyadmin
114 // on interprete le commentaire pour recuperer la version de phpmydadmin
115 $version = preg_replace(",(.*?)version\s*([0-9a-z\.\-]*)\s(.*),is","\\2",$r[1]);
116 $flag_phpmyadmin = true;
117 }
118 if (($r[0] != "!--") && ($flag_phpmyadmin == true)){
119 $r[1] = array('version_archive'=>"phpmyadmin::$version");
120 $r[2] = $charset;
121 return $r;
122 }
123 }
124 }
125
126 // on conserve ce tableau pour faire des translations
127 // de table eventuelles
128 $tables_trans = array(
129 );
130
131
132 // http://doc.spip.org/@import_init_tables
133 function import_init_tables($request){
134 global $connect_id_auteur;
135
136 // commencer par verifier les meta et le champ impt=non
137 $config = charger_fonction('config','inc');
138 $config();
139
140
141 // grand menage
142 // on vide toutes les tables dont la restauration est demandee
143 list($tables,) = base_liste_table_for_dump(lister_tables_noerase());
144 spip_log(count($tables) . " tables effacees " . join(', ', $tables),'import');
145
146 foreach($tables as $table){
147 // regarder si il y a au moins un champ impt='non'
148 if (($table!='spip_auteurs')){
149 $desc = description_table($table);
150 if (isset($desc['field']['impt']))
151 sql_delete($table, "impt='oui'");
152 else
153 sql_delete($table);
154 }
155 }
156
157 // Bidouille pour garder l'acces admin actuel pendant toute la restauration
158 sql_delete("spip_auteurs", "id_auteur=0");
159 sql_updateq('spip_auteurs', array('id_auteur'=>0, 'extra'=>$connect_id_auteur), "id_auteur=$connect_id_auteur");
160 sql_delete("spip_auteurs", "id_auteur!=0");
161
162 // retourner la liste des tables a importer, pas celle des tables videes !
163 return import_table_choix($request);
164 }
165
166 // Effacement de la bidouille ci-dessus
167 // Toutefois si la table des auteurs ne contient plus qu'elle
168 // c'est que la sauvegarde etait incomplete et on restaure le compte
169 // pour garder la connection au site (mais il doit pas etre bien beau)
170
171 // http://doc.spip.org/@detruit_restaurateur
172 function detruit_restaurateur()
173 {
174 if (sql_countsel("spip_auteurs", "id_auteur<>0"))
175 sql_delete("spip_auteurs", "id_auteur=0");
176 else {
177 sql_update('spip_auteurs', array('id_auteur'=>'extra'), "id_auteur=0");
178 }
179 }
180
181 // http://doc.spip.org/@import_tables
182 function import_tables($request, $archive) {
183 global $import_ok, $abs_pos, $affiche_progression_pourcent;
184
185 // regarder si on est pas en train d'importer dans une copie des tables
186 if (isset($GLOBALS['meta']['restauration_table_prefix'])) {
187 $charger = charger_fonction('charger','maj/vieille_base');
188 $charger($GLOBALS['meta']['vieille_version_installee']);
189 $GLOBALS['serveur_vieille_base'] = 0;
190 $prefix = $GLOBALS['connexions'][$GLOBALS['serveur_vieille_base']]['prefixe'];
191 $GLOBALS['connexions'][$GLOBALS['serveur_vieille_base']]['prefixe'] = $GLOBALS['meta']['restauration_table_prefix'];
192 // verifier qu'une table meta existe bien
193 // sinon c'est une restauration anterieure echouee
194 if (!sql_getfetsel('valeur','spip_meta','','','','0,1')){
195 $GLOBALS['connexions'][$GLOBALS['serveur_vieille_base']]['prefixe'] = $prefix;
196 return;
197 }
198 // recharger les metas
199 lire_metas();
200 }
201
202 $abs_pos = (!isset($GLOBALS['meta']["restauration_status"])) ? 0 :
203 $GLOBALS['meta']["restauration_status"];
204
205 // au premier appel destruction des tables a restaurer
206 // ou initialisation de la table des translations,
207 // mais pas lors d'une reprise.
208
209 if ($request['insertion']=='on') {
210 include_spip('inc/import_insere');
211 $request['init'] = (!$abs_pos) ? 'insere_1_init' : 'insere_1bis_init';
212 $request['boucle'] = 'import_insere';
213 } elseif ($request['insertion']=='passe2') {
214 $request['init'] = 'insere_2_init';
215 $request['boucle'] = 'import_translate';
216 } else {
217 $request['init'] = (!$abs_pos) ? 'import_init_tables' : 'import_table_choix';
218 $request['boucle'] = 'import_replace';
219 }
220
221 if (strncmp(".gz", substr($archive,-3),3)==0) {
222 $size = false;
223 $taille = taille_en_octets($abs_pos);
224 $file = gzopen($archive, 'rb');
225 $gz = 'gzread';
226 } else {
227 $size = @filesize($archive);
228 $taille = @floor(100 * $abs_pos / $size)." %";
229 $file = fopen($archive, 'rb');
230 $gz = 'fread';
231 }
232
233
234 if ($abs_pos==0) {
235 list($tag, $atts, $charset) = import_debut($file, $gz);
236 // improbable: fichier correct avant debut_admin et plus apres
237 if (!$tag) return !($import_ok = true);
238 $version_archive = import_init_meta($tag, $atts, $charset, $request);
239 } else {
240 $version_archive = $GLOBALS['meta']['restauration_version_archive'];
241 $atts = unserialize($GLOBALS['meta']['restauration_attributs_archive']);
242 spip_log("Reprise de l'importation interrompue en $abs_pos");
243 $_fseek = ($gz=='gzread') ? 'gzseek' : 'fseek';
244 $_fseek($file, $abs_pos);
245 }
246
247 // placer la connexion sql dans le bon charset
248
249 if (isset($GLOBALS['meta']['restauration_charset_sql_connexion']))
250 sql_set_charset($GLOBALS['meta']['restauration_charset_sql_connexion']);
251
252 if (!defined('_DEBUG_IMPORT')) define('_DEBUG_IMPORT', false);
253 if (_DEBUG_IMPORT)
254 ecrire_fichier(_DIR_TMP."debug_import.log","#####".date('Y-m-d H:i:s')."\n",false,false);
255 $fimport = import_charge_version($version_archive);
256
257 if ($request['insertion'] !== 'passe2')
258 import_affiche_javascript($taille);
259
260 if (function_exists('ob_flush')) @ob_flush();
261 flush();
262 $oldtable ='';
263 $cpt = 0;
264 $pos = $abs_pos;
265
266 // BOUCLE principale qui tourne en rond jusqu'a le fin du fichier
267 while ($table = $fimport($file, $request, $gz, $atts)) {
268 // memoriser pour pouvoir reprendre en cas d'interrupt,
269 // mais pas d'ecriture sur fichier, ca ralentit trop
270 ecrire_meta("restauration_status", "$abs_pos",'non');
271 if ($oldtable != $table) {
272 if (_DEBUG_IMPORT){
273 ecrire_fichier(_DIR_TMP."debug_import.log","----\n".$GLOBALS['debug_import_avant']."\n<<<<\n$table\n>>>>\n".$GLOBALS['debug_import_apres']."\n----\n",false,false);
274 }
275 if ($oldtable) spip_log("$cpt entrees","import");
276 spip_log("Analyse de $table (commence en $pos)","import");
277 affiche_progression_javascript($abs_pos,$size,$table);
278 $oldtable = $table;
279 $cpt = 0;
280 $pos = $abs_pos;
281 }
282 $cpt++;
283 }
284 spip_log("$cpt entrees","import");
285 spip_log("fin de l'archive, statut: " .($import_ok ? 'ok' : 'alert'),"import");
286
287 if (!$import_ok)
288 return _T('avis_archive_invalide') . ' ' .
289 _T('taille_octets', array('taille' => $pos)) ;
290
291 if ($GLOBALS['spip_version_base'] != (str_replace(',','.',$GLOBALS['meta']['version_installee']))){
292 // il FAUT recharger les bonnes desc serial/aux avant ...
293 include_spip('base/serial');
294 $GLOBALS['tables_principales']=array();
295 base_serial($GLOBALS['tables_principales']);
296 include_spip('base/auxiliaires');
297 $GLOBALS['tables_auxiliaires']=array();
298 base_auxiliaires($GLOBALS['tables_auxiliaires']);
299 $GLOBALS['tables_jointures']=array();
300 include_spip('public/interfaces');
301 declarer_interfaces();
302 include_spip('base/upgrade');
303 maj_base(); // upgrade jusqu'a la version courante
304 }
305 // regarder si on est pas en train d'importer dans une copie des tables
306 if (isset($GLOBALS['meta']['restauration_table_prefix_source'])){
307 $prefixe_source = $GLOBALS['meta']['restauration_table_prefix_source'];
308
309 $GLOBALS['connexions']['-1'] = $GLOBALS['connexions'][0];
310 // rebasculer le serveur sur les bonnes tables pour finir proprement
311 $GLOBALS['connexions'][0]['prefixe'] = $prefixe_source;
312 // et relire les meta de la bonne base
313 lire_metas();
314
315
316 $tables_recopiees = isset($GLOBALS['meta']['restauration_recopie_tables'])?unserialize($GLOBALS['meta']['restauration_recopie_tables']):array();
317 spip_log("charge tables_recopiees ".serialize($tables_recopiees),'dbdump');
318
319 // recopier les tables l'une sur l'autre
320 // il FAUT recharger les bonnes desc serial/aux avant ...
321 include_spip('base/serial');
322 $GLOBALS['tables_principales']=array();
323 base_serial($GLOBALS['tables_principales']);
324 include_spip('base/auxiliaires');
325 $GLOBALS['tables_auxiliaires']=array();
326 base_auxiliaires($GLOBALS['tables_auxiliaires']);
327 $GLOBALS['tables_jointures']=array();
328 include_spip('public/interfaces');
329 declarer_interfaces();
330
331 // puis relister les tables a importer
332 // et les vider si besoin, au moment du premier passage ici
333 // (et seulement si ce n'est pas une fusion, comment le dit-on ?)
334 $initialisation_copie = (!isset($GLOBALS['meta']["restauration_status_copie"])) ? 0 :
335 $GLOBALS['meta']["restauration_status_copie"];
336
337 if (!$initialisation_copie) {
338 // vide les tables qui le necessitent
339 $tables = import_init_tables($request);
340 ecrire_meta("restauration_status_copie", "ok",'non');
341 }
342 else
343 // la liste des tables a recopier
344 $tables = import_table_choix($request);
345 # var_dump($tables);die();
346 spip_log("tables a copier :".implode(", ",$tables),'dbdump');
347 if (in_array('spip_auteurs',$tables)){
348 $tables = array_diff($tables,array('spip_auteurs'));
349 $tables[] = 'spip_auteurs';
350 }
351 if (in_array('spip_meta',$tables)){
352 $tables = array_diff($tables,array('spip_meta'));
353 $tables[] = 'spip_meta';
354 }
355 sql_drop_table('spip_test','','-1');
356 foreach ($tables as $table){
357 if (sql_showtable($table,true,-1)){
358 if (!isset($tables_recopiees[$table])) $tables_recopiees[$table] = 0;
359 if ($tables_recopiees[$table]!==-1){
360 affiche_progression_javascript(0,0,$table);
361 while (true) {
362 $n = intval($tables_recopiees[$table]);
363 $res = sql_select('*',$table,'','','',"$n,400",'','-1');
364 while ($row = sql_fetch($res,'-1')){
365 array_walk($row,'sql_quote');
366 sql_replace($table,$row);
367 $tables_recopiees[$table]++;
368 }
369 if ($n == $tables_recopiees[$table])
370 break;
371 spip_log("recopie $table ".$tables_recopiees[$table],'dbdump');
372 affiche_progression_javascript($tables_recopiees[$table],0,$table);
373 ecrire_meta('restauration_recopie_tables',serialize($tables_recopiees));
374 }
375 sql_drop_table($table,'','-1');
376 spip_log("drop $table",'dbdump');
377 $tables_recopiees[$table]=-1;
378 ecrire_meta('restauration_recopie_tables',serialize($tables_recopiees));
379 spip_log("tables_recopiees ".serialize($tables_recopiees),'dbdump');
380 }
381 }
382 }
383 }
384
385 // recharger les metas
386 lire_metas();
387 #die();
388 return '' ;
389 }
390
391 // http://doc.spip.org/@import_init_meta
392 function import_init_meta($tag, $atts, $charset, $request)
393 {
394 $version_archive = $atts['version_archive'];
395 $version_base = $atts['version_base'];
396 $insert = $request['insertion'] ;
397
398 $old = (!$insert
399 && version_compare($version_base,$GLOBALS['spip_version_base'],'<')
400 && !isset($GLOBALS['meta']['restauration_table_prefix']));
401
402 if ($old) {
403 // creer une base avec les tables dans l'ancienne version
404 // et changer de contexte
405 $creer_base_anterieure = charger_fonction('create','maj/vieille_base');
406 $creer_base_anterieure($version_base);
407 }
408 if ($old OR $insert) {
409 $init = $request['init'];
410 spip_log("import_init_meta lance $init","import");
411 $init($request);
412 }
413
414 ecrire_meta('restauration_attributs_archive', serialize($atts),'non');
415 ecrire_meta('restauration_version_archive', $version_archive,'non');
416 ecrire_meta('restauration_tag_archive', $tag,'non');
417
418 // trouver le charset de la connexion sql qu'il faut utiliser pour la restauration
419 // ou si le charset de la base est iso-xx
420 // (on ne peut garder une connexion utf dans ce cas)
421 // on laisse sql gerer la conversion de charset !
422
423 if (isset($GLOBALS['meta']['charset_sql_connexion'])
424 OR (strncmp($charset,'iso-',4)==0)
425 ){
426 include_spip('base/abstract_sql');
427 if ($sql_char = sql_get_charset($charset)){
428 $sql_char = $sql_char['charset'];
429 ecrire_meta('restauration_charset_sql_connexion',$sql_char);
430 }
431 else {
432 // faire la conversion de charset en php :(
433 effacer_meta('restauration_charset_sql_connexion'); # precaution
434 spip_log("charset de restauration inconnu de sql : $charset");
435 if ($insert)
436 ecrire_meta('charset_insertion', $charset,'non');
437 else ecrire_meta('charset_restauration', $charset,'non');
438 }
439 }
440
441 $i = $insert ? ("insertion $insert") : '';
442 spip_log("Debut de l'importation (charset: $charset, format: $version_archive) $i");
443 return $version_archive;
444 }
445
446 // http://doc.spip.org/@import_affiche_javascript
447 function import_affiche_javascript($taille)
448 {
449 $max_time = ini_get('max_execution_time')*1000;
450 $t = _T('info_recharger_page');
451 $t = "
452 <input type='text' size='10' name='taille' id='taille' value='$taille' />
453 <input type='text' class='forml' name='recharge' id='recharge' value='$t' />";
454 echo debut_boite_alerte(),
455 "<span style='color: black;' class='verdana1 spip_large'><b>", _T('info_base_restauration'), "</b></span>",
456 generer_form_ecrire('', $t, " style='text-align: center' name='progression' id='progression' method='get' "),
457 fin_boite_alerte();
458 }
459
460
461
462 // http://doc.spip.org/@affiche_progression_javascript
463 function affiche_progression_javascript($abs_pos,$size, $table="", $retour='') {
464
465 include_spip('inc/charsets');
466 echo "\n<script type='text/javascript'><!--\n";
467
468 if ($abs_pos == '100 %') {
469
470 echo "document.progression.taille.value='$abs_pos';\n";
471 if ($x = $GLOBALS['erreur_restauration']) {
472 echo "document.progression.recharge.value='".str_replace("'", "\\'", unicode_to_javascript(html2unicode(_T('avis_erreur').": $x")))." ';\n";
473 }
474 else {
475 if (!$retour) $retour = self();
476 echo "document.progression.recharge.value='".str_replace("'", "\\'", unicode_to_javascript(html2unicode(_T('info_fini'))))."';\n";
477 echo "window.setTimeout('location.href=\"$retour\";',0);";
478 }
479 }
480 else {
481 if (trim($table))
482 echo "document.progression.recharge.value='$table';\n";
483 if (!$size)
484 $taille = preg_replace("/&nbsp;/", " ", taille_en_octets($abs_pos));
485 else
486 $taille = floor(100 * $abs_pos / $size)." %";
487 echo "document.progression.taille.value='$taille';\n";
488 }
489 echo "\n--></script>\n";
490 if (function_exists('ob_flush')) @ob_flush();
491 flush();
492 }
493
494
495 // http://doc.spip.org/@import_table_choix
496 function import_table_choix($request){
497 spip_log("noimport:".implode(',',lister_tables_noimport()),'noimport');
498 list($tables,) = base_liste_table_for_dump(lister_tables_noimport());
499 spip_log("liste:".implode(',',$tables),'noimport');
500 return $tables;
501 }
502 ?>