Petite visite du guérisseur

Monsieur Soleil peut tout faire. Retour de l'être aimé, prédiction des résultats du loto, choix infaillible de la couleur de la cravatte, conjuration des problèmes d'impuissance, etc... Bon plus sérieusement je ne vais pas parler de ce type de guérisseur là, rassurez-vous. Je vais vous parler de Jason, figure de la mythologie grecque connu sous le surnom "le guérisseur"...bon non plus, ça c'était juste pour le jeu de mot (bien pourri). En fait je vais vous parler de JSON et, plus précisément, des JSON-RPC que j'ai eu la surprise de découvrir accessibles sur mon serveur...

Marabout - Based on a Creative Common photo published by "Ludovic Hirlimann" on Flickr
Pour poser le cadre donc : j'ai un serveur multimédia (allumé H24[1], comme tout bon serveur qui se respecte) et l'autre jour alors que je faisais le petit nmap routinier de mon réseau local apparait une nouveauté sur ce serveur :

PORT     STATE SERVICE     VERSION
9090/tcp open  zeus-admin?

Autant vous dire que, sur le moment, j'ai un peu fait la gueule :-D Après enquête rapide il s'avère que c'est en fait le XBMC qui tourne sur mon serveur qui expose là l'une de ses nouvelles interfaces de commande... Dans les anciennes versions, XBMC exposait uniquement (pour autant que je sache) une interface de commande HTTP accessible après authentification et qui permettait ainsi de controller les lectures multimédia via le réseau (super pratique pour transformer votre téléphone android en télécommande wifi). Dans les dernières version l'interface HTTP est deprecated, et c'est l'interface JSON-RPC qui devrait la remplacer.

Pour ceux qui l'ignorent (comme moi il y a quelques semaines), le JSON-RPC c'est une méthode de RPC qui utilise la syntaxe JSON (on ne s'en douterai pas hein ?). Et pour la conclure l'explication : le JSON c'est une méthode de formatage de données dont l'esprit est de se rapprocher de la syntaxe....javascript. Je ne plaisante pas, JSON c'est même l'acronyme de "JavaScript Object Notation" si vous voulez tout savoir. Du coup une requête JSON-RPC ça ressemble à ça :

{"jsonrpc": "2.0", "method": "subtract", "params": {42, 23}, "id": 1}

Bon...c'est pas très joli mais ça reste compréhensible :) Du coup je me suis dit "voyons ce que l'on peut faire avec ça", mais avant d'exposer les résultats je dois faire un disclaimer : "j'ai peut-être été trop permissif dans ma configuration d'xbmc (même si j'en doute), donc ne blamons pas trop les réglages qu'on peut/doit faire pour utiliser une télécommande, c'est peut être ma faute". Maintenant répondons à "que peut on faire avec ça ?" :

Première chose : survoler la liste des procédures accessibles. La première qui a une tête sympa c'est "JSONRPC.Ping". Aussitôt identifiée aussitôt testée :

echo '{"jsonrpc": "2.0", "method": "JSONRPC.Ping", "id":"5"}' | nc serveurmultimedia 9090

Et réponse immédiate :

{
   "id" : "5",
   "jsonrpc" : "2.0",
   "result" : "pong"
}

Cool ça marche :-D !!!...Pas cool : j'ai pas eu besoin de m'identifier :-/ La paranoïa aidant la seconde procédure que je teste c'est "Files.GetDirectory"...forcément.

echo '{"jsonrpc": "2.0", "method": "Files.GetDirectory", "id":"peur", "params":{"directory":"/"}}' | nc serveurmultimedia 9090

Ah bah....ça marche :

{
   "id" : "peur",
   "jsonrpc" : "2.0",
   "result" : {
      "directories" : [
         {
            "fanart" : "special://masterprofile/Thumbnails/Video/Fanart/cfb24076.tbn",
            "file" : "/var/",
            "label" : "var"
         },
         {
            "fanart" : "special://masterprofile/Thumbnails/Video/Fanart/b1fe6366.tbn",
            "file" : "/usr/",
            "label" : "usr"
         },
         {
            "fanart" : "special://masterprofile/Thumbnails/Video/Fanart/dafd2084.tbn",
            "file" : "/tmp/",
...ETC...

Et vous savez le mieux ? Y'a une procédure Files.Download :-D !...Après l'avoir testé dans tout les sens il semblerait qu'en fait cette procédure ne soit, pour l'instant, pas implémentée (ouf !) et que de toute façon elle ne permette de télécharger que des ressources générées par XBMC (vignettes, etc.). M'enfin déjà le simple Files.GetDirectory c'est pas très joli[2]...Du coup une seule chose à dire : faites attentions aux permissions que vous mettez sur votre XBMC si vous voulez utiliser une télécommande réseau, il se peut que vous ouvriez des portes à n'importe qui ayant accès au port TCP.

Ceci étant dit, on va considérer que ça cloture la partie "sécu" de ce billet, et on va voir si on peut jouer avec ça sans vilaines pensées, par exemple essayons de faire les requêtes en python :

>>> import json, socket, random
>>> req = {'jsonrpc':'2.0', 'id':str(random.randint(0,100)), 'method':'JSONRPC.Ping'}
>>> s =  socket.socket(socket.AF_INET, socket.SOCK_STREAM)
>>> s.connect(("serveurmultimedia",9090))
>>> s.send(json.dumps(req))
>>> reponse = s.recv(1024)
>>> print reponse
{
   "id" : "82",
   "jsonrpc" : "2.0",
   "result" : "pong"
}

Intéressant...Du coup si jamais un script python sur android qui est en train d'exécuter time.sleep() ne consomme pas de CPU (donc de batterie) je pourrai m'écrire un script, utilisable sur mon téléphone, qui me réveille à l'heure de mon choix en jouant le média de mon choix sur mon serveur !!! Intéressant non ?

Pour que ça soit intéressant j'ai envie de lire un mp3 aléatoire. La logique de XBMC étant ce qu'elle est[3] je dois d'abord récupérer les "sources" de music :

>>> req = {'jsonrpc':'2.0', 'id':str(random.randint(0,100))}
>>> req['method'] = "Files.GetSources"
>>> req['params'] = {'media':'Music'}
>>> s.send(json.dumps(req))
>>> reponse = s.recv(10*1024)
>>> shares = json.loads(reponse)['result']['shares']
>>> for s in shares:
>>>    print s['file']
/mnt/raid0/musiquelegalementachetee/
...

Une fois les sources récupérées (et surtout les répertoires associées, dans l'attribu bien nommé "file"...) on peut utiliser Files.GetDirectory dont on parlait plus haut afin de récupérer l'ensemble des morceaux directement à la racine de notre source (j'ai la flemme de coder le récursif pour l'instant) :

>>> req = {'jsonrpc':'2.0', 'id':str(random.randint(0,100))}
>>> req['method'] = "Files.GetDirectory"
>>> req['params'] = {'directory':shares[0]['file']}
>>> s.send(json.dumps(req))
>>> reponse = s.recv(100*1024)
>>> musics = json.loads(reponse)
>>> random.shuffle(musics['result']['files'])
>>> musique = musics['result']['files'][0]["file"]
>>> print musique
/mnt/raid0/musiquelegalementachetee/rickroll.mp3

Maintenant j'aurai adoré utilisé "AudioPlaylist.Insert" pour insérer la chanson à l'endroit de mon choix dans la playlist courante puis en lancer la lecture, sauf que quand je fais appel à "AudioPlaylist.Insert" j'ai une réponse "Method not found."...On va donc supposer que ce n'est pas encore implémenté :-( Résultat il ne me reste plus qu'à me rabattre sur AudioPlaylist.Add puis prier le guérisseur pour que lors de l'appel à AudioPlaylist.Play ce soit le bon morceau qui soit joué...Je l'ai donc fait :

>>> req = {'jsonrpc':'2.0', 'id':str(random.randint(0,100))}
>>> req['method'] = "AudioPlaylist.Add"
>>> req['params'] = {"item": {"file":musique}}
>>> s.send(json.dumps(req))
>>> reponse = s.recv(1024)
>>> print reponse
{
   "id" : "2",
   "jsonrpc" : "2.0",
   "result" : "OK"
}
>>> req = {'jsonrpc':'2.0', 'id':str(random.randint(0,100))}
>>> req['method'] = "AudioPlaylist.Play"
>>> s.send(json.dumps(req))

Et là ça ne joue pas le bon morceau :-S ... Vérification de la playlist : mon morceau n'a même pas été ajouté en fait... Heureusement la communauté des dev de XBMC est plutôt très active et j'ai rapidement trouvé le problème :

The current playlist implementation/representation in json rpc has many (design) flaws and there are lots of unsupported things and it will hopefully be completely rewritten for the next release (if there's enough time). Until then it might be the safest approach not to use the Playlist methods (except SkipPrevious, SkipNext, GetItems, Clear, Shuffle and UnShuffle which should work as expected).

We're sorry for the inconvenience and hope to provide a much better solution in the next version

Donc voilà : actuellement il est impossible de faire ce que je voulais :-( ..Snif. La suite peut-être à un prochain numéro :) !

Notes

[1] Que les amis de la planète se rassurent : c'est une machine à base d'atom qui consomme moins en H24 qu'une seule heure de jeu sur les dernières cartes graphiques à la mode

[2] Même si, comme moi, vous faites tourner votre XBMC avec des droits minimalistes

[3] et comme je n'utilise pas les fonctionnalitées "library" d'XBMC

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

La discussion continue ailleurs

URL de rétrolien : http://www.ozwald.fr/index.php?trackback/39

Fil des commentaires de ce billet