Le blog de Bajazet

Celui qui a échappé à la foudre en parle volontiers

Restaurer Shaarli

icon 18/06/2013

Introduction

Répéter 100 fois "Je ne me moquerai plus des personnes qui perdent leurs données et qui ne font pas de sauvegardes" car ça vient de m'arriver comme vous avez pu le voir.

Aujourd'hui j'ai eu la brillante idée de tester SparkleShare, un outil de stockage basé sur Git + SSH ? Et bien il marche plutôt bien, mais vous savez quoi ? Vérifiez que vous avez de la place quand vous copiez 3Gio de données. Sparkle s'installe par défaut dans le /home, et comme je suis entrain de tester, je me dis que ce n'est pas grave et que je dois avoir de la place, sans vérifier. Et bien non, je ne fais pas attention, je publie un lien dans Shaarli pendant qu'il synchronise et là, plus rien, plus de liens et pourtant mes services fonctionnent,
Je consulte rapidement sa base locale et elle n'est pas vide. Je regarde l'en-tête et je vois "< ?php" mais en fin de fichier pas de "? >", un df -k me confirme que mon /home est plein. Shaarli n'a pas réussi à tout écrire dans ce fichier, il est corrompu et HS.

Vous êtes peut-être paranos, donc vous sauvegardez souvent. Pour ma part c'est de temps en temps, faut vraiment que je prenne le temps de faire ça correctement. Ma dernière sauvegarde date du 31 mai. Et depuis, j'ai publié plus d'une centaine de liens, donc j'ai vraiment envie de restaurer mes liens et surtout conserver les URL.

Étape 1: Trouver une sauvegarde sur la toile

Heureusement que Bronco est là, il "m'autoblog" http://autoblogs.warriordudimanche.net/30e895128ead04e02291610dee7d1e90a7145e2a/, mais malheureusement, il s'arrête au dimanche 16 juin 2013 à 11:55 et nous sommes déjà mardi. Je récupère donc le fichier .db qui est une base SQLite.

Et là, par le plus grand des hasards, je tombe sur shaarli.fr ! C'est un site a priori récent car dans les archives, je ne remonte pas très loin, mais juste assez pour voir mes liens depuis le 16. En tout, une bonne dizaine, plutôt qu'essayer un truc bourrin à tenter de jouer avec le XML, je préfère éditer le fichier SQLite avec l'excellent SQLite Browser (disponible dans les dépôts Debian et même sous Win). J'ai entré manuellement mes données, mais les dates sur shaarli.fr ne sont pas exactes et approximatives. Alors j'ai donné une date fixe au 17 juin à midi.

Étape 2: Comprendre le mécanisme de Shaarli

Tout content je commence par essayer des requêtes SQL, récupérer ça en PHP, puis je m'attaque au code de Shaarli. Pour le coup j'avais déjà joué avec et je savais "à peu près" comment ça fonctionnait. Mais j'avais oublié une chose : les "smallhash", vous savez ce hash à côté des URL ?
Et bien ça correspond exactement à la date du lien hashé, et là, c'est le drame. Shaarli regarde ce hash, puis hash toutes les dates des liens pour savoir à qui il correspond. Avec mes dates fixes, je l'ai dans l'os.

Alors j'ai écrit un bout de code qui lit le fichier SQLite, récupère l'URL "feed_id, extrait le hash et si le hash de la date est différent du hash de l'URL, je stock ce hash et je lance une boucle de bourrin pour trouver la date de ce hash.

<?php
$starttime = 1370011383; // correspond au timestamp du dernier lien en commun entre l'autoblog et ma sauvegarde

// format 20120502_125821
function shaarlidate($timestamp) {
	return date('Ymd_His', $timestamp);
}

// hasher la date - code shaarli
function smallHash($text)
{
    $t = rtrim(base64_encode(hash('crc32',$text,true)),'=');
    $t = str_replace('+','-',$t); // Get rid of characters which need encoding in URLs.
    $t = str_replace('/','_',$t);
    $t = str_replace('=','@',$t);
    return $t;
}

// lire le fichier sqlite
$database="/var/www/shaarli/articles.db";
$dbHandle = new PDO("sqlite:".$database);
$q = $dbHandle->query("SELECT feed_id, title, url, date, content FROM articles WHERE date > ".$starttime." order by date asc");
$result = $q->fetchAll();
$hashs=array();

foreach($result as $tot_table) {
	// récupère tous les hashs pétés
	if(strpos($tot_table['feed_id'], smallHash(shaarlidate($tot_table['date']))) === false) {
		$exp=explode('?', $tot_table['feed_id']);
		$hashs[]=$exp[1];
	}
}

//mode bourrin, retrouver la bonne date, obligatoire
$now = time();
for($i=$starttime; $i<$now; $i++) {
	$hash=smallHash(shaarlidate($i));
	if (in_array($hash, $hashs)) {
		echo $i." for ".$hash."<br>";
	}
}

var_dump($hashs); //si vide tout est ok.

?>

Avec ces correspondances, je mets à jour mes dates dans ma base SQLite. J'ai relancé et cette fois, le var_dump() est bien vide.

Étape 3: Construire les liens et concaténer dans le fichier shaarli

Je vais donc maintenant récupérer le fichier de données de ma sauvegarde, il se situe dans le répertoire data fichier datastore.php. J'ai simplement ajouté mes données dans ce fichier, en me basant toujours sur le code de Shaarli.

<?php
define('PHPPREFIX','<?php /* '); // Prefix to encapsulate data in php code. - code shaarli
define('PHPSUFFIX',' */ ?>'); // Suffix to encapsulate data in php code. - code shaarli
$starttime = 1370011383;

// 20120502_125821
function shaarlidate($timestamp) {
	return date('Ymd_His', $timestamp);
}

function clean($text) {
	$t = html_entity_decode($text);
	$t = str_replace("<br>", "\n", $t);
	$t = str_replace("<br />", "", $t);
	$t = substr($t, 0, -60); //(<a href="http://bajazet.fr/shaarli/?NVmi_w">Permalink</a>;) en fin de chaque lien
	$t = preg_replace('/<a href="(.+?)">(.+?)<\/a>/', '$2', $t); // depuis autoblog il affiche les balises
	return $t;
}

/* read shaarli datastore */
$links=(file_exists("datastore.php.31mai") ? unserialize(gzinflate(base64_decode(substr(file_get_contents("datastore.php.31mai"),strlen(PHPPREFIX),-strlen(PHPSUFFIX))))) : array() );

/* read the sqlite file */
$database="/var/www/shaarli/articles.db";
$dbHandle = new PDO("sqlite:".$database);
$q = $dbHandle->query("SELECT feed_id, title, url, date, content FROM articles WHERE date > ".$starttime." order by date asc");
$result = $q->fetchAll();

foreach($result as $tot_table) {
        // linkdate correspond à la clef de la base de shaarli
	$linkdate = shaarlidate($tot_table['date']);
	$links[$linkdate] = array('linkdate'=>$linkdate,'title'=>html_entity_decode($tot_table['title']),'url'=>$tot_table['url'],'description'=>clean($tot_table['content']),'tags'=>"",'private'=>0);
}

file_put_contents("/var/www/shaarli/data.php", PHPPREFIX.base64_encode(gzdeflate(serialize($links))).PHPSUFFIX);

?>

Et on récupère le fichier data.php et on le copie dans le data du vrai Shaarli en le nommant datastore.php.

Conclusion
J'ai passé ma soirée à faire ça, alors qu'une simple restauration de la veille aurait été plus simple. Dans la foulée, j'ai perdu mes liens privés et les tags depuis le 31 mai.
Merci à E-loquens qui m'a envoyé un dump de son Leed, mais ma base n'a pas aimé son export :)

icon Tags de l'article :

5 commentaires

naxos - 19/06/2013 à 01:57:20

Bonsoir

Je ne sais pas si vous avez encore besoin de récupérer quelque chose, mais au cas où je suis votre flux rss depuis plusieurs mois. Via le module Brief pour Firefox.

Le hic c'est que j'ai quelques dizaines de flux et que je peux pas extraire juste le votre. J'ai utilisé l'outil dont vous faites référence dans votre article, mais je n'ai pas sû/pû faire le boulot. Mes connaissances de SQL remontent aux années 90 et je n'y ai pas touché depuis ...

Je vous propose donc le chargement de la base sqlite complète de mes flux. Le tout est compressé avec 7z mais ça fait quand même 50 Mo.
C'est récupérable ici : http://naxos.fr.free.fr/divers/brief.7z

@répondre #lien

bajazet - 19/06/2013 à 05:39:49

@naxos : Oh merci Naxos
---
select entries.providedID, entries.entryURL, entries.date, entries_text_content.c0title, entries_text_content.c1content from entries_text_content,entries, feeds where entries_text_content.docid=entries.id and entries.feedID=feeds.feedID and feeds.feedURL='http://bajazet.fr/shaarli/index.php?do=rss'

et hop ! (on peut se tutoyer) A priori j'ai tout :)

@répondre #lien

Knah Tsaeb - 19/06/2013 à 06:37:23

Idem, je suit tes flux RSS avec Tiny Tiny RSS et il a la bonne idée de conserver les article pendant quelques années. Donc si il te manque quelque chose.

Sinon pour en revenir au nerf de la guerre, oui c'est sauvegarde c'est contraignant et chiant à faire, enfin au début parce dés qu'on automatise un peu la chose c'est vraiment transparent. Mais effectivement ça sauve la vie en cas de problème.

@répondre #lien

qwerty - 26/06/2013 à 09:50:19

Perso, mon shaarli étant hébergé chez un serveur de mon fai (page perso), sauvegarde quotidienne ! Pour mon blog, dès que j'écris un article. Oui. je suis parano, mais ayant perdu une fois les données de ma clé usb, la leçon est retenu !

@répondre #lien

Bronco - 16/09/2013 à 17:58:39

Je viens de tomber sur ce billet et je reconnais bien les galères qui te poussent plus tard à sauvegarder de façon compulsive ^^
Au passage, heureux que les autoblogs aient pu servir ;)

Tchuss mon pote ^^

@répondre #lien

icon Flux RSS des commentaires de cet article