Convertir des marque-pages JSON en HTML Netscape

Lorsque je formate ou que je change d'ordinateur, je fais une sauvegarde de mes marque-pages. C'est normal. Depuis plus de cinq ans, j'ai donc des fichiers de sauvegarde qui s'entassent. Certains sont au format HTML Netscape, un standard. Mais d'autres sont au format JSON. Peu commodes à utiliser, je souhaitais les convertir en HTML. Malheur à moi !

Le nouvel Opera n'importe que des fichiers HTML. Firefox m'explique que le fichier n'est pas bon. Bien, tant pis, je me lance dans la conception d'un petit script pour convertir ces JSON en HTML. Et quoi de mieux que Javascript pour faire le job ?


<script type="text/javascript">
function parseObject(obj, result) {
	result += "\n";

	if(obj.constructor == Object) { 
		// Folders
		if(obj.hasOwnProperty("children")) {
			result += "<DT><H3 ";
			var tmp = new Object();

			for(var p in obj) {
				if(p === "name" || p === "title") {
					tmp["value"] = obj[p];
				} else if(p === "dateAdded" || p === "date_added") {
					tmp["date"] = obj[p];
				} else if(p === "lastModified" || p === "date_modified") {
					tmp["modified"] = obj[p];
				}
			}

			// WebKit doesn't know how to use POSIX time è.é
			// tmp["date"] = (tmp["date"]/1000000-11644473600);
			// tmp["modified"] = (tmp["modified"]/1000000-11644473600);

			result += 'ADD_DATE="' + String(tmp["date"]).substring(0,10) + '" ';
			result += 'LAST_MODIFIED="' + String(tmp["modified"]).substring(0,10) + '">';
			result += tmp["value"] + "</H3>\n";
			result += parseObject(obj["children"], "\n");

		}	
		// Links
		else if(obj.hasOwnProperty("url") || obj.hasOwnProperty("uri")){
			result += '<DT><A HREF="';
			var tmp = new Object();

			for(var p in obj) {
				if(p === "uri" || p === "url") {
					tmp["url"] = obj[p];
				} else if(p === "name" || p === "title") {
					tmp["value"] = obj[p];
				} else if(p === "dateAdded" || p === "date_added") {
					tmp["date"] = obj[p];
				} else if(p === "lastModified" || p === "date_modified") {
					tmp["modified"] = obj[p];
				} else if(p === "charset") {
					tmp["charset"] = obj[p];
				} else if(p === "annos") {
					tmp["description"] = obj[p][0].value;
				} 
			}

			// WebKit doesn't know how to use POSIX time è.é
			// tmp["date"] = (tmp["date"]/1000000-11644473600);
			// tmp["modified"] = (tmp["modified"]/1000000-11644473600);

			result += encodeURI(tmp["url"]) + '" ADD_DATE="' + String(tmp["date"]).substring(0,10) + '" LAST_MODIFIED="';
			result += String(tmp["modified"]).substring(0,10) + '" LAST_CHARSET="' + tmp["charset"] + '" >'; 
			result += tmp["value"]+ "</A>\n";
			if(tmp.hasOwnProperty("description"))
				result += "<DD>" + tmp["description"] + "\n";
		}
		// Others
		else {
			for(var p in obj) {
				result += parseObject(obj[p], "\n");
			}
		}
	} else if(obj.constructor == Array) {
		for(var p in obj) {
			result += parseObject(obj[p], "\n");
		}	
	} 

	return result;
}


function jsonToHTML(input){
	var output = "<!DOCTYPE NETSCAPE-Bookmark-file-1>\n";
	output += '<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">\n';
	output += "<TITLE>Bookmarks</TITLE>\n"; 
	output += "<H1>Bookmarks</H1>\n";
	output += "<DL>\n";
	
	// We construct a valid JSON, thanks to Firefox's shitty code è.é
	input = input.replace(",]", "]");
	input = input.replace(",}", "}");
	input = input.replace("[,", "[");
	input = input.replace("{,", "{");
	
	var data = JSON.parse(input);
	console.log(data);
	output += parseObject(data, "\n");
	
	output += "</DL>\n";
	output += "</DL>";
	return output;
}
	
	// Usage : jsonToHTML(someInputFromATextarea);
</script>

Voir une démo - Voir le dépôt GitHub

Il s'avère que j'ai trouvé pourquoi Firefox me retourne une erreur avec ces fichiers. Au départ il les exportait avec un léger défaut : en fin de fichier, il inscrivait ,]}, ce qui, bien entendu, n'est pas valide. Je ne sais pas quand le tir a été corrigé, mais dorénavant les vieux fichiers de sauvegarde sont incompatibles…

Oh, et tant que j'y suis… Chrome et dérivés (Opera, Chromium…) tendent à ne pas savoir calculer un timestamp Unix correct. J'ai d'abord pensé qu'ils ajoutaient systématiquement un 0 après les deux premiers chiffres. Un simple ajout javascript aurait alors fait l'affaire :


tmp["date"] = String(tmp["date"]).slice(0,2).concat(String(tmp["date"]).slice(3));
tmp["modified"] = String(tmp["modified"]).slice(0,2).concat(String(tmp["modified"]).slice(3));

Mais en réalité ils vont plus loin dans leur incompétence : les trois premiers chiffres sont toujours 130 !

Intrigué (l'export en HTML donnant un timestamp correct), j'ai voulu creuser un peu plus. Il s'avère que Chrome n'utilise pas un timestamp Unix Epoch mais ce qui est communément appelé webkit timestamp, un format 64 bit qu'il partage avec Windows. Au lieu de compter les secondes depuis 1970, il compte les microsecondes depuis 1601. Pourquoi 1601 ? Oh, sans doute le fameux Ballmer Peak

Tout ce qu'il y a à faire, c'est de diviser par un million pour avoir les secondes, puis de soustraire le nombre de secondes entre 1970 et 1601 (soit 11644473600, ne me remerciez pas). Soit le code :


tmp["date"] = (tmp["date"]/1000000-11644473600);
tmp["modified"] = (tmp["modified"]/1000000-11644473600);

PluXML : faire un plan de site

La page du wiki officiel ayant été effacée, et parce que c'est tout de même une fonction bien pratique à insérer sur un site, voici comment faire un plan de site sous PluXML 5+.

Il suffit de créer une nouvelle page statique et d'y insérer le contenu ci-dessous :


<?php
# Page statique Pluxml : liste de tous les articles par catégories par stephane@pluxml.org
# révision 2.0 par Yomli :
#	- compatibilité pluxml 5+ 

global $plxShow;
#=======PARAMÈTRES ========
# format de la date
$format_date = '#num_day/#num_month/#num_year(4)';
 
# liste des catégories à afficher
# exemple: $catList = ''; = articles de toutes les catégories, 
# exemple: $catList = '001|003'; = articles des catégories 001 et 003 uniquement
$catList = ''; 
# nombre d'articles à afficher par catégorie, mettre 0 pour lister tous les articles
$artsByCategory = 0; 
#==========================
?>

<h4>Pages statiques</h4>
<ul class="alt">
	<?php $plxShow->staticList('Accueil','<li id="#static_id"><a href="#static_url" class="#static_status" title="#static_name">#static_name</a></li>'); ?>
</ul>

<h4>Catégories</h4>
<ul class="alt">
	<?php $plxShow->catList('','<li id="#cat_id" class="#cat_status"><a href="#cat_url" title="#cat_name">#cat_name</a></li>'); ?>
</ul>

<h4>Articles</h4>
<?php
$plx_arts = array();
$plxGlob_arts = plxGlob::getInstance(PLX_ROOT.$plxShow->plxMotor->aConf['racine_articles']);
$aFiles = $plxGlob_arts->query('/[0-9]{4}.[home|0-9,]*'.$catList.'[0-9,]*.[0-9]{3}.[0-9]{12}.[a-z0-9-]+.xml$/','art','rsort',0,false,'before');
$aCatList = explode('|', $catList);
if(is_array($aFiles)) { # On a des fichiers
    while(list($k,$v) = each($aFiles)) { # On parcourt tous les fichiers
        $temp = $plxShow->plxMotor->parseArticle(PLX_ROOT.$plxShow->plxMotor->aConf['racine_articles'].$v);
		$cats = explode(',', $temp['categorie']);
		foreach($cats as $cat) {
			if($catList=='' OR in_array($cat, $aCatList)) {
				if(!isset($plx_arts[$cat]))
					$plx_arts[$cat][] = $temp;
				elseif(intval(sizeof($plx_arts[$cat])) < $artsByCategory OR $artsByCategory == 0)
					$plx_arts[$cat][] = $temp;
			}
		}
    }
    if($plx_arts) { # On a des articles
		
		# tri en fonction de l'ordre d'affiche des catégories
		uksort($plx_arts, create_function('$a, $b', 'global $plxShow; return strcmp(array_search($a, array_keys($plxShow->plxMotor->aCats)), array_search($b, array_keys($plxShow->plxMotor->aCats)));'));
			
        # On boucle sur nos articles
		foreach ($plx_arts as $k => $v) {

			$cat_num = $k;
		
			# on trie en fonction de l'ordre d'affichage des articles dans la catégorie
			if($cat_num=='home') {
				if ($plxShow->plxMotor->aConf['tri']=='asc') 
					usort($v, create_function('$a, $b', 'return strcmp($a["date"], $b["date"]);'));
				else
					usort($v, create_function('$a, $b', 'return strcmp($b["date"], $a["date"]);'));
				echo '<h5><a href="'.$plxShow->plxMotor->aConf['racine'].'">Accueil</a></h5>';
			}
			elseif(!isset($plxShow->plxMotor->aCats[$cat_num])) {
				if ($plxShow->plxMotor->aConf['tri']=='asc') 
					usort($v, create_function('$a, $b', 'return strcmp($a["date"], $b["date"]);'));
				else
					usort($v, create_function('$a, $b', 'return strcmp($b["date"], $a["date"]);'));
				echo '<h5>Non classé</h5>';
			} 
			else {
				if ($plxShow->plxMotor->aCats[$cat_num]['tri'] == 'asc')
					usort($v, create_function('$a, $b', 'return strcmp($a["date"], $b["date"]);'));
				else
					usort($v, create_function('$a, $b', 'return strcmp($b["date"], $a["date"]);'));
				$cat_name = plxUtils::strCheck($plxShow->plxMotor->aCats[ $cat_num ]['name']);
				$cat_url = $plxShow->plxMotor->aCats[ $cat_num ]['url'];
				echo '';				
			}
			echo "<h5>".$cat_name."</h5><ul class='alt'>";
			# On boucle sur les articles de la categories
			while(list($null, $art) = each($v)) {
				$art_num = intval($art['numero']);
				$art_url = plxUtils::strCheck(($art['url']));
				$art_title = plxUtils::strCheck(($art['title']));
				$art_date = plxDate::formatDate($art['date'], $format_date);
				echo '<li>'.$art_date.' : <a href="'.$plxShow->plxMotor->aConf['racine'].'?article'.$art_num.'/'.$art_url.'">'.$art_title.'</a></li>'; 
			}
			echo "</ul>";
        }

    }
}
?>

µBlock Origin vs AdBlock Plus

Utilisateurs/utilisatrices d'AdBlock Plus, je vous enjoins à essayer µBlock Origin, la même chose en plus léger (et si vous activez les listes Fanboy, ça peut même remplacer Ghostery dans la foulée).
Comparaison de la mémoire utilisée, 46-48% (environ 2,6 GiO) avant, 31-33% (environ 1,7 GiO) après, mêmes onglets ouverts, c'est pas rien.

Avant Après

Fragilités dans le format OpenPGP

OpenPGP, une fragilité découverte par l'ANSSI. Ça ne touche que les systèmes automatisés qui affichent des messages d'erreur évocateurs, mais bon, il est toujours intéressant de rappeler pourquoi il ne faut pas faire confiance à des webmail chiffrées automatiquement. Si vous utilisez PGP, faites-le manuellement. C'est chiant, mais c'est sécurisé.

Cryptanalyse de messages OpenPGP par l'exploitation de fragilités cryptographiques du format

CipherSaber.js

Monsieur Urvoas souhaite pouvoir empêcher le chiffrement, à ce qu'il en ressort des débats pour le ‪#‎PJLRenseignement‬. Le problème, c'est qu'il est impossible d'arrêter le chiffrement de données.

CipherSaber en est l'exemple parfait. Cet algorithme est si simple que n'importe quel développeur amateur peut en faire une implémentation de tête. Il se base sur l'idée qu'un Jedi devait fabriquer son propre sabre laser avant d'être admis comme chevalier, le rendant indépendant de tout Empire totalitaire. Voici une implémentation en javascript de mon cru, très basique, que vous pouvez tester sur JSFiddle ou bien en suivant le lien de la démo.

Dépôt github : https://github.com/yomli/ciphersaber.js/

Loi sur le renseignement : au sortir de la démocratie

Ah bah, je suis pas le seul à le dire, en sous-titre, que cette loi officialise la sortie de la démocratie. ‪#‎PJLRenseignement‬ L'article ne parle que des conséquences, je donne donc une info technique : l'équipement qui sera utilisé pour nous surveiller sera le même que celui qu'Amesys, Area, Qosmos, Alcatel, Nokia, etc. installèrent dans les dictatures maghrebines et en Iran. Ces équipements furent considérés comme des armes violant les droits de l'Homme par le Parlement Européen en 2011, qui en interdit la vente à des dictatures (source : https://reflets.info/deep-packet-inspection-la-vente-darme…/).

Donc quand des armes sont pointées sur des citoyens par un gouvernement, je ne sais pas comment vous appelez ça, mais moi ce n'est pas le terme « démocratie » qui me vient en premier.

http://moreas.blog.lemonde.fr/2015/04/12/loi-sur-le-renseignement-les-petits-les-gros-les-bons-et-les-mechants/


À propos de ce blog

Certains ont des carnets de notes, les développeurs web ont tendance à avoir des blogs. Ça leur permet de partager leurs découvertes, de garder une trace de leurs hacks beaucoup plus simplement. Voici le mien.

Vous trouverez ici pas mal de trucs de geek, principalement des bouts de code en développement web, mais également quelques scripts bash pour faciliter l'utilisation quotidienne de GNU/Linux (je tourne sous Linux Mint 17.1 actuellement). La catégorie Veille comporte quelques articles d'actualité, mais rien de bien folichon.

Si l'envie vous prend de me contacter, vous trouverez toutes les informations utiles dans le footer (le bas de page, pour les burnes en anglais), ou bien vous pouvez utiliser la page de contact dédiée.

Enfin, si vous trouvez ici des informations utiles, n'hésitez pas à m'offrir un café en cliquant sur le bouton :

Offrir un café  
-----BEGIN GEEK CODE BLOCK-----
  Version: 3.20
  GCS dx s:- a- c+ C++ B+++ 7-- w E- O- L++ M- u++ W+++$ 
  H++++ Z+++ F--- PS+++ PE-- Y+ PGP++ T++ S J+++ t@ X- 
  R+++ m+ !tv b+++@ DI? D++ G e* h+ A- r y++
------END GEEK CODE BLOCK------
					
Haut de page