Le protocole JNTP

Spécification du protocole JNTP
Julien Arlandis
Juillet 2014

Protocole JNTP : « Json News Transfer Protocol »

Statut du présent mémoire

Ce document définit un nouveau protocole pour la diffusion d’articles sur les newsgroups, et appelle à discussion et suggestions en vue de son amélioration. Il est distribué comme une RFC pour rendre ces informations facilement accessibles aux internautes. La distribution de ce document est illimitée.

Sommaire

Conventions utilisées dans ce document
Résumé
Généralités sur le protocole
Format des articles
Champs d’un paquet JNTP
Accès aux ressources
Les balises et les plugins
Les commandes du protocole JNTP
Exemple d’une communication entre un client et un serveur
Exemple d’une communication entre deux serveurs
L’algorithme de propagation
Différences avec le protocole NNTP
Description de la passerelle JNTP<=>NNTP

Conventions utilisées dans ce document

Les types de données utilisés par le protocole JNTP sont :

– String désigne une chaîne de caractères,
– Number désigne un nombre entier positif,
– Array désigne un tableau,
– Object désigne un objet,
– Bollean désigne la valeur true ou false.
– Sha1 désigne une empreinte numérique SHA-1

Dans la suite de ce document, nous utiliserons la notation xxx(Type yyy) pour désigner une fonction xxx qui admet en paramètre la valeur yyy de type Type.

Résumé

Le protocole JNTP « Json News Transfer Protocol » permet à un client de naviguer sur un réseau de forums décentralisés, il permet aux différents serveurs qui constituent les noeuds de ce réseau d’échanger les articles et d’assurer la gestion des forums regroupés en hiérarchies.
Ce document décrit le protocole JNTP, et également le format des articles diffusés par les clients et les serveurs JNTP.

Généralités sur le protocole

Le protocole JNTP s’appuie sur un mécanisme transactionnel par le biais de requêtes. Une requête JNTP ainsi que sa réponse fournie sont toutes deux constituées d’une chaîne de caractères définie par le format de données JSON. Le protocole JNTP suppose un protocole de requêtes de la couche applicative pour encapsuler la requête JNTP et sa réponse, il s’agira généralement de HTTP.
Toutes les commandes transmises par les clients et les serveurs JNTP sont des objets JSON. Le protocole JNTP est sensible à la casse, le protocole reconnait les types de données : String, Object, Number, Array,  Boolean.

Format des articles

Tout article se présente sous la forme d’un objet JSON nommé Paquet qui comprend au minimum une liste de 4 éléments : le Jid, la Route, l’ID et la Data.

{
	"Jid": Sha1 "@" ServerOrigin,
	"Route": Array,
	"ID": Number,
	"Data": Object,
	"ServerSign": String
}

Voici un exemple d’article JNTP :

{
	"Jid": "baedf3bbade59518b5a3abec50a4cee886c9d1a2@news.nemoweb.net",
	"Route": [
		"news.nemoweb.net"
	],
	"Data": {
		"DataType": "Article",
		"FromName": "Julien Arlandis",
		"FromMail": "julien.arlandis@laposte.net",
		"Subject": "Un article de test",
		"References": [],
		"Newsgroups": [
			"fr.test"
		],
		"UserAgent": "Nemo/0.99z99c",
		"Body": "test",
		"Media": [],
		"FollowupTo": [],
		"HashClient": "a676e47276b84ae3c287d2bf2e81bd9e700c1196",
		"ThreadID": "1ac620ae7f6288275119ff8a813a738c82a86189",
		"Uri": "http://news.nemoweb.net/?Jid=74799397f656276d357944bdc0ea306748ec4b24@news.nemoweb.net",
		"InjectionDate": "2014-07-02T10:37:08Z",
		"OriginServer": "news.nemoweb.net",
		"Organization": "Nemoweb",
		"Browser": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/28.0.1500.71 Chrome/28.0.1500.71 Safari/537.36",
		"PostingHost": "4f47bc8d8d32d104b6d8dec6257e44ecbf407eec",
		"ComplaintsTo": "julien.arlandis@laposte.net",
		"Protocol": "JNTP-Strict",
		"ProtocolVersion": "0.63",
		"Server": "PhpNemoServer/0.73",
		"ServerPublicKey": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCN5EHDx1w9Dynd+vMWy+vWmXKb\n4D5a1qEr4kGBFXj8+xurONX87Zryc+3aMxjRo2V1S48XcQtPhBm4u9TjKhPAfPpf\n49XbpedVnnw/GzFbTiHIL59HHryXqppHKriNVUteXTrNka2x8t9zguC4G5R/ICzX\nEAEgZnfPh9nQzSzMdQIDAQAB\n-----END PUBLIC KEY-----",
		"UserID": "1@news.nemoweb.net"
	},
	"ServerSign": "ONS2+ugbZPwzpZXLVlgANplwEHIlOlFhQcCvg1DxDQrE8igG85aoguEQb/UikMYiKoj1H2iU1M7BYToKMep8jVINUsXuXUI0gX+BG2vbXGKCxy62mTvi0aGSowrO9BpK0SW80auQlbtLKs258sgyTt4weXMIkO2tmCk2I8Tf6hM=",
	"ID": 454608,
}

Champs d’un paquet JNTP

1. Jid (String, obligatoire)

Le Jid est la chaîne de caractères qui identifie une Data sur le réseau.

Jid = SHA-1(Json2txt(Data)) "@" Nom-du-serveur
SHA-1 = fonction qui retourne l’empreinte numérique d’une chaîne de caractère passée en paramètres
Json2txt = fonction qui transforme un object JSON en String.
Nom-du-Serveur désigne le nom de domaine du serveur émetteur du paquet, sa valeur doit être identique à Data/ServerOrigin.

Le Jid est entièrement calculé par le serveur.

Algorithme de la fonction Json2txt :

On définit l’identifiant d’une valeur contenue dans un objet JSON par une chaîne de caractère qui contient la hiérarchie de toutes les clés parentes séparées entre elles par le caractère « / ». Lorsque la valeur est incluse dans un tableau l’identifiant est suffixé du caractère « : » suivi d’un entier qui identifie la position de la valeur dans le tableau, le premier élément étant indicé à 1.

1 – On établit la liste de toutes les valeurs de l’objet JSON et de leurs identifiants associés
2 – On trie les clés par ordre alphabétique,
3 – On ajoute à chaque clé sa valeur précédée par le caractère « = »
4 – Les éléments de cette liste ainsi formée sont concaténés et séparés entre eux par le caractère « & ».

Attention : Lorsqu’un paquet contient des médias (objet Data/Media) les identifiants Data/Media:Number/data doivent être ignorés pour le calcul du Jid si la clé Data/Media:Number/hash est présente.

Exemple avec le paquet suivant :

Paquet = {
	"Firstname": "Julien",
	"Name" : "Arlandis",
	"Projet" : {
		"name" : "Nemo",
		"date" : 2014,
		"contributeurs" : ["Joe", "Jim", "Jack"]
	}
}

étape 1 :

"FirstName" "Julien"
"Name" "Arlandis"
"Projet/name" "Nemo"
"Projet/date" "2014"
"Projets/contributeurs:1" "Joe"
"Projets/contributeurs:2" "Jim"
"Projets/contributeurs:3" "Jack"

étape 2-3 :

FirstName=Julien
Name=Arlandis
Projets/contributeurs:1=Joe
Projets/contributeurs:2=Jim
Projets/contributeurs:3=Jack
Projet/date=2014
Projet/name=Nemo

étape 4 :

FirstName=Julien&Name=Arlandis&Projets/contributeurs:1=Joe&Projets/contributeurs:2=Jim&Projets/contributeurs:3=Jack&Projet/date=2014&Projet/name=Nemo

Json2txt(Paquet) = "FirstName=Julien&Name=Arlandis&Projets/contributeurs:1=Joe&Projets/contributeurs:2=Jim&Projets/contributeurs:3=Jack&Projet/date=2014&Projet/name=Nemo"

2. Route (Array, obligatoire)

La route liste dans un tableau de la gauche vers la droite les différents noeuds empruntés par l’article. Ce champ est calculé par le serveur.

"Route": ["nemo.pasdenom.info", "news.nemoweb.net"] signifie que le paquet est passé successivement par le serveur nemo.pasdenom.info puis news.nemoweb.net.

3. ID (Number, obligatoire?)

L’ID est un nombre entier qui identifie localement un article sur un serveur donné, l’ID est incrémenté chaque fois que le serveur reçoit un nouvel article par le biais des autres serveurs JNTP ou des clients JNTP.
L’ID est calculé par le serveur.

4. ServerSign (String, obligatoire)

Ce champ constitue la signature numérique qui garantit que le paquet a bien été émis par le serveur mentionné dans la partie droite du Jid, réservée au nom de domaine du serveur.
ServerSign = RSA(Jid, PrivateKey) où PrivateKey désigne la clé RSA privée du serveur.
On peut donc vérifier que Jid = RSA(ServerSign, PublicKey).
Ce champ est calculé par le serveur.

5. Data (Object, obligatoire)

La Data est l’objet qui contient toutes les informations relatives au contenu publié.

5.1. Data/DataType (String, obligatoire)

Ce champ identifie la nature de la donnée publiée. Pour l’instant on distingue deux DataType distincts "Article" et "ListGroup".
Ce champ est renseigné par le client.

5.2. Data/FromName (String, obligatoire)

Contient le nom de l’auteur de la publication.
Ce champ est renseigné par le client.

5.3. Data/FromMail (String, obligatoire)

Contient le mail de l’auteur de la publication.
Ce champ est renseigné par le client.

5.4. Data/Subject (String, obligatoire)

Contient le Sujet de l’article.
Ce champ est renseigné par le client.

5.5. Data/References (Array, obligatoire)

Contient la liste des Jid référents de l’article, empilés du plus ancien au plus récent article et dans la limite de 10.
Ce champ est renseigné par le client.

5.6. Data/Newsgroups (Array, obligatoire)

Contient la liste des Newsgroups dans lesquels l’article est publié. Le nom d’un groupe ne peut contenir que des caractères alpha-numériques, et les caractères « . », « _ », « -« . Le nom d’un groupe peut également être préfixé par le caractère « @ » ou « # ». Un groupe commençant par « @ » désigne un groupe technique à l’usage exclusif des serveurs JNTP, par exemple le groupe @newsgroups accueille les articles de définition des hiérarchies. Le Data/DataType de ces articles vaut « ListGroup ». Les groupes commençant par le caractère « # » sont appelés NemoTags et sont crées automatiquement à la publication d’un article à destination d’un NemoTag donné.
Ce champ est renseigné par le client.

5.7. Data/UserAgent (String, facultatif)

Désigne le nom du client JNTP utilisé pour publier l’article.
Ce champ est renseigné par le client.

5.8. Data/Body (String, obligatoire)

Contient le texte de l’article et des balises BBCode pour enrichir le contenu.
La séquence de caractères « # » « # » présente dans Data/Body sera automatiquement remplacée par le client par le Jid de l’article courant.

5.9. Data/Media (Array, obligatoire)

Contient les fichiers inclus dans l’article. Chaque fichier est un objet contenant 3 clés parmi data, hash et filename (les deux dernières clés étant facultatives).

Exemple :

"Media": [
	{
		"hash": "d96a654f83fea5ae30da697525f63786d5b8d229",
		"filename": "favicon.png",
		"data": "data:image\/png;base64,iVBORw0KGgo..."
	}
]

Afin de limiter la bande passante lors du chargement d’un article, le serveur peut supprimer les valeurs Data/Media:Number/data dans sa réponse si le fichier est trop volumineux, cela n’affectera pas le calcul du Jid effectué par le client car cette valeur est exclue dans l’algorithme utilisé pour le calcul du Jid.
Ce champ est renseigné par le client.

5.9.1 Data/Media:Number/hash (String, obligatoire)

Contient l’empreinte SHA-1 du fichier.
Data/Media:Number/hash = sha1(Data/Media:Number/data)
Ce champ est renseigné par le client.

5.9.2 Data/Media:Number/filename (String, facultatif)

Contient le nom du fichier.
Ce champ est renseigné par le client.

5.9.3 Data/Media:Number/data

Contient la donnée du fichier au format data URI scheme.
Ce champ est renseigné par le client.

5.10. Data/FollowupTo (Array, obligatoire)

Contient la liste des newsgroups dans lesquels les réponses au présent article devront être publiées.
Ce champ est renseigné par le client.

5.11. Data/HashClient (String, obligatoire)

Il s’agit d’une empreinte calculée par l’auteur d’un article au moment de sa publication afin de contrôler les modifications et suppressions ultérieures. Chaque client conserve dans ses paramètres locaux une chaîne de caractères PrivateChecksum connue de lui seul.
Ce champ est renseigné par le client.

Algorithme pour calculer Data/HashClient :

1 – On dresse la liste des valeurs pour les identifiants suivant :

On considère l’objet JSON suivant :

Objet_Json = {
	"DataType": valueof(Data/DataType),
	"FromName": valueof(Data/FromName),
	"FromMail": valueof(Data/FromName),
	"Subject": valueof(Data/FromMail),
	"References": valueof(Data/References),
	"Newsgroups": valueof(Data/Newsgroups),
	"UserAgent": valueof(Data/UserAgent),
	"Body": valueof(Data/Body),
	"Media": valueof(Data/Media),
	"FollowupTo": valueof(Data/FollowupTo),
	"HashClient": PrivateChecksum
}

Data/Media ne doit pas contenir les données binaires des fichiers.

2- On modifie la valeur de HashClient de l’objet Objet_Json de la manière suivante :
HashClient = sha1(Json2txt((Objet_Json))

3- On obtient ainsi un nouvel objet Json Objet_Json2 sur lequel on va calculer la valeur définitive de Data/HashClient :
Data/HashClient = sha1(Json2txt((Objet_Json2))

5.12. Data/ThreadID (String, obligatoire)

Ce champ identifie un fil de discussion, il est renseigné par le client en reprenant le ThreadID de l’article auquel est adressée une réponse, sinon il est généré par le serveur pour toute nouvelle discussion.

5.13. Data/Uri (String, facultatif)

Spécifie une URL pour faciliter la lecture de l’article.
Ce champ est renseigné par le client.

5.14. Data/InjectionDate (String, obligatoire)

Contient la date au format UTC à laquelle l’article a été émis sur le réseau JNTP.
Ce champ est renseigné par le serveur.

5.15. Data/OriginServer (String, obligatoire)

Contient le nom de domaine du serveur, ce champ doit être identique au nom de domaine spécifié dans la partie droite du Jid. Dans le cas contraire l’article sera considéré comme corrompu dans la mesure où rien ne permet plus de garantir que la partie droite du Jid n’a pas été altérée.
Ce champ est renseigné par le serveur.

5.16. Data/Organization (String, facultatif)

Indique l’organisation qui gère le serveur depuis lequel l’article a été émis.
Ce champ est renseigné par le serveur.

5.17. Data/Browser (String, facultatif)

Indique le browser utilisé par le client pour rédiger l’article.
Ce champ est renseigné par le serveur.

5.18. Data/PostingHost (String, facultatif)

Indique l’adresse IP du posteur, cette chaîne peut être hashée ou présenter l’IP en clair.
Ce champ est renseigné par le serveur.

5.19. Data/ComplaintsTo (String, obligatoire)

Adresse email pour signaler un abus.
Ce champ est renseigné par le serveur.

5.20. Data/Protocol (String, obligatoire)

Indique le protocole utilisé pour lire l’article parmi "JNTP-Strict" ou "JNTP-Transitional".
Ce champ est renseigné par le serveur. Le cas JNTP-Transitional concerne les articles publiés sur un serveur NNTP qui ne sont pas soumis à toutes les contraintes rédigées dans ce document.
Ce champ est renseigné par le serveur.

5.21. Data/ProtocolVersion (String, facultatif)

Indique la version du protocole JNTP implémentée par le serveur au moment de la diffusion de l’article.
Ce champ est renseigné par le serveur.

5.22. Data/Server (String, facultatif)

Indique le nom du logiciel du serveur JNTP qui a publié l’article.
Ce champ est renseigné par le serveur.

5.23. Data/ServerPublicKey (String, facultatif)

Indique la clé publique du serveur à l’origine de l’émission de l’article.
Ce champ est renseigné par le serveur.

5.24. Data/UserID (String, obligatoire)

Indique l’identifiant sur le serveur JNTP de l’auteur de l’article.
Ce champ est renseigné par le serveur.

5.25. Data/ReferenceUserID (String, facultatif)

Indique l’identifiant sur le serveur JNTP de l’auteur de l’article. Ce champ est utile pour retrouver les réponses qui nous ont été adressées.
Ce champ est renseigné par le client.

5.26. Data/Supersedes (String, facultatif)

Contient le Jid initial de l’article que l’on vient de modifier.
Ce champ est renseigné par le client.

5.27. Data/Control (Array, facultatif)

Permet de notifier l’annulation d’un article.

Data/Control:1 contient l’action à effectuer, par exemple « cancel ».
Data/Control:2 contient le Jid de l’article concerné.
Data/Control:3 contient sha1(Json2txt((Objet_Json))
où :

Objet_Json = {
	"DataType": valueof(Data/DataType),
	"FromName": valueof(Data/FromName),
	"FromMail": valueof(Data/FromName),
	"Subject": valueof(Data/FromMail),
	"References": valueof(Data/References),
	"Newsgroups": valueof(Data/Newsgroups),
	"UserAgent": valueof(Data/UserAgent),
	"Body": valueof(Data/Body),
	"Media": valueof(Data/Media),
	"FollowupTo": valueof(Data/FollowupTo),
	"HashClient": PrivateChecksum
}

et où Data/Media a été vidé de Data/Media:Number/data.

Exemple :

"Control": [
	"cancel",
	"63ee66297d5d40477a92d909ac0bba76b5a86d54@news.nemoweb.net",
	"6ceed1b8d1a7b2f2a82cc560e411761c775b8a70"
],

Ce champ est renseigné par le client.

5.28. NNTPHeaders (Object, facultatif)

Utile seulement si Data/Protocol = "JNTP-Transitional", cet objet contient tous les champs de l’article NNTP.
Ce champ est renseigné par la passerelle au moment de la conversion NNTP/JNTP.

5.29. Data/ListGroup (Object, facultatif)

Contient la liste, le nom et les statuts des newsgroups d’une hiérarchie. Ce champ est obligatoire lorsque Data/DataType="Listgroup".

5.30. Hierarchy (String, facultatif)

Définit la hiérarchie concernée par le présent article. Ce champ est obligatoire lorsque Data/DataType="Listgroup".

Accès aux ressources

Toute valeur d’un paquet JNTP est accessible via la ressource suivante :

http://serverJNTP/jntp//

Exemple :
Le mail de l’auteur de l’article baedf3bbade59518b5a3abec50a4cee886c9d1a2@news.nemoweb.net est accessible via :
http://news.nemoweb.net/jntp/baedf3bbade59518b5a3abec50a4cee886c9d1a2@news.nemoweb.net/Data/FromMail

Les balises et les plugins

Les balises actuellement reconnues dans un article :
b, i, u, s, a, img, file, pdf, spoil, tex, code, cite, signature, abc, pgn, map, audio, youtube, dailymotion, table, td, tr

Les commandes du protocole JNTP
Syntaxe générale d’une commande JNTP :

[
"commande", Object|Array|String
]

get : Récupère un ou plusieurs articles
diffuse : Diffuse un article sur le réseau
getNewsgroup : Permet d’obtenir la description et le rwm (read – write – moderated) d’un ou plusieurs newsgroups
auth : Permet de s’authentifier sur le serveur de newsgroup
whoami : Informe le client de son identité, utilie pour savoir si on est encore connecté
quit : Ferme la connexion avec le serveur
ihave : Permet à un serveur d’informer un autre serveur qu’il est en possession d’une sélection d’articles
getPublicKey : Fournit la clef publique du serveur
help : Fournit de l’aide sur la liste des instructions disponibles

Commande Fonction
  • get
  • diffuse
  • getNewsgroup
  • auth
  • whoami
  • quit
  • ihave
  • getPublicKey
  • help
  • Récupère un ou plusieurs articles
  • Diffuse un article sur le réseau
  • Permet d’obtenir la description et le rwm (read – write – moderated) d’un ou plusieurs newsgroups
  • Permet de s’authentifier sur le serveur de newsgroup
  • Informe le client de son identité, utilie pour savoir si on est encore connecté
  • Ferme la connexion avec le serveur
  • Permet à un serveur d’informer un autre serveur qu’il est en possession d’une sélection d’articles
  • Fournit la clef publique du serveur
  • Fournit de l’aide sur la liste des instructions disponibles

Exemple d’une communication entre un client et un serveur

Diffusion d’un article par un client :

[
	"diffuse",
	{
		"Data": Object
	}
]

Diffusion d’un article par un serveur :

[
	"diffuse",
	{
		"Packet": Object
	}
]

Exemple d’une communication entre deux serveurs
L’algorithme de propagation
Différences avec le protocole NNTP
Description de la passerelle JNTPNNTP