From fa40856e942a7e1bd1cb2729645182c1fa717468 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 29 Aug 2019 23:43:47 +0200
Subject: Defined a new way to launch updates share servers.

---
 .gitignore                                    |    1 +
 plugins/pychrysalide/analysis/db/Makefile.am  |    5 +-
 plugins/pychrysalide/analysis/db/certs.c      |   20 +-
 plugins/pychrysalide/analysis/db/client.c     |  360 +++++++++
 plugins/pychrysalide/analysis/db/client.h     |   45 ++
 plugins/pychrysalide/analysis/db/collection.c |   45 ++
 plugins/pychrysalide/analysis/db/collection.h |    5 +-
 plugins/pychrysalide/analysis/db/constants.c  |   70 ++
 plugins/pychrysalide/analysis/db/constants.h  |   39 +
 plugins/pychrysalide/analysis/db/module.c     |    4 +
 plugins/pychrysalide/analysis/db/server.c     |  331 ++++++++
 plugins/pychrysalide/analysis/db/server.h     |   45 ++
 src/Makefile.am                               |   16 +-
 src/analysis/binary.c                         |   34 +-
 src/analysis/binary.h                         |    2 +-
 src/analysis/contents/file.c                  |    2 +-
 src/analysis/db/Makefile.am                   |    1 +
 src/analysis/db/auth.c                        |  705 +++++++++++++++++
 src/analysis/db/auth.h                        |   59 ++
 src/analysis/db/cdb.c                         |   84 +-
 src/analysis/db/cdb.h                         |    4 +-
 src/analysis/db/certs.c                       |    4 +-
 src/analysis/db/certs.h                       |    4 +-
 src/analysis/db/client.c                      |  373 +++++----
 src/analysis/db/client.h                      |   34 +-
 src/analysis/db/keymgn.c                      |    6 +-
 src/analysis/db/keymgn.h                      |    2 +
 src/analysis/db/protocol.h                    |    2 +-
 src/analysis/db/server.c                      |  810 ++++++++++++-------
 src/analysis/db/server.h                      |   38 +-
 src/common/io.c                               |   78 +-
 src/common/io.h                               |    6 +-
 src/core/logs.h                               |   14 +
 src/gui/panels/history.c                      |    8 +-
 src/hub.c                                     | 1036 +++++++++++++++++++++++++
 src/main.c                                    |   45 +-
 36 files changed, 3736 insertions(+), 601 deletions(-)
 create mode 100644 plugins/pychrysalide/analysis/db/client.c
 create mode 100644 plugins/pychrysalide/analysis/db/client.h
 create mode 100644 plugins/pychrysalide/analysis/db/constants.c
 create mode 100644 plugins/pychrysalide/analysis/db/constants.h
 create mode 100644 plugins/pychrysalide/analysis/db/server.c
 create mode 100644 plugins/pychrysalide/analysis/db/server.h
 create mode 100644 src/analysis/db/auth.c
 create mode 100644 src/analysis/db/auth.h
 create mode 100644 src/hub.c

diff --git a/.gitignore b/.gitignore
index d67d2d2..9f7a214 100644
--- a/.gitignore
+++ b/.gitignore
@@ -68,6 +68,7 @@ resources.[ch]
 
 # Binaries
 src/chrysalide
+src/chrysalide-hub
 src/csrvmng
 tools/d2c/d2c
 
diff --git a/plugins/pychrysalide/analysis/db/Makefile.am b/plugins/pychrysalide/analysis/db/Makefile.am
index 709f153..4bd8353 100644
--- a/plugins/pychrysalide/analysis/db/Makefile.am
+++ b/plugins/pychrysalide/analysis/db/Makefile.am
@@ -3,9 +3,12 @@ noinst_LTLIBRARIES = libpychrysaanalysisdb.la
 
 libpychrysaanalysisdb_la_SOURCES =		\
 	certs.h certs.c						\
+	client.h client.c					\
 	collection.h collection.c			\
+	constants.h constants.c				\
 	item.h item.c						\
-	module.h module.c
+	module.h module.c					\
+	server.h server.c
 
 libpychrysaanalysisdb_la_LIBADD = 		\
 	items/libpychrysaanalysisdbitems.la
diff --git a/plugins/pychrysalide/analysis/db/certs.c b/plugins/pychrysalide/analysis/db/certs.c
index 3da3849..3d9fcc6 100644
--- a/plugins/pychrysalide/analysis/db/certs.c
+++ b/plugins/pychrysalide/analysis/db/certs.c
@@ -42,10 +42,10 @@
 static bool py_certs_fill_x509_entries(PyObject *, x509_entries *);
 
 /* Crée un certificat de signature racine. */
-static PyObject *py_certs_make_ca(PyObject *, PyObject *);
+static PyObject *py_certs_build_keys_and_ca(PyObject *, PyObject *);
 
 /* Crée un certificat pour application. */
-static PyObject *py_certs_make_request(PyObject *, PyObject *);
+static PyObject *py_certs_build_keys_and_request(PyObject *, PyObject *);
 
 /* Signe un certificat pour application. */
 static PyObject *py_certs_sign_cert(PyObject *, PyObject *);
@@ -127,7 +127,7 @@ static bool py_certs_fill_x509_entries(PyObject *dict, x509_entries *out)
 *                                                                             *
 ******************************************************************************/
 
-static PyObject *py_certs_make_ca(PyObject *self, PyObject *args)
+static PyObject *py_certs_build_keys_and_ca(PyObject *self, PyObject *args)
 {
     PyObject *result;                       /* Désignation à retourner     */
     const char *dir;                        /* Répertoire de sortie        */
@@ -144,7 +144,7 @@ static PyObject *py_certs_make_ca(PyObject *self, PyObject *args)
     status = py_certs_fill_x509_entries(dict, &entries);
     if (!status) return NULL;
 
-    status = make_ca(dir, label, valid, &entries);
+    status = build_keys_and_ca(dir, label, valid, &entries);
 
     free_x509_entries(&entries);
 
@@ -170,7 +170,7 @@ static PyObject *py_certs_make_ca(PyObject *self, PyObject *args)
 *                                                                             *
 ******************************************************************************/
 
-static PyObject *py_certs_make_request(PyObject *self, PyObject *args)
+static PyObject *py_certs_build_keys_and_request(PyObject *self, PyObject *args)
 {
     PyObject *result;                       /* Désignation à retourner     */
     const char *dir;                        /* Répertoire de sortie        */
@@ -186,7 +186,7 @@ static PyObject *py_certs_make_request(PyObject *self, PyObject *args)
     status = py_certs_fill_x509_entries(dict, &entries);
     if (!status) return NULL;
 
-    status = make_request(dir, label, &entries);
+    status = build_keys_and_request(dir, label, &entries);
 
     free_x509_entries(&entries);
 
@@ -253,13 +253,13 @@ PyTypeObject *get_python_certs_type(void)
 {
     static PyMethodDef py_certs_methods[] = {
 
-        { "make_ca", py_certs_make_ca,
+        { "build_keys_and_ca", py_certs_build_keys_and_ca,
           METH_VARARGS | METH_STATIC,
-          "make_ca(dir, label, valid, entries, /)\n--\n\nCreate a certificate authority."
+          "build_keys_and_ca(dir, label, valid, entries, /)\n--\n\nCreate a certificate authority."
         },
-        { "make_request", py_certs_make_request,
+        { "build_keys_and_request", py_certs_build_keys_and_request,
           METH_VARARGS | METH_STATIC,
-          "make_request(dir, label, entries, /)\n--\n\nCreate a certificate sign request."
+          "build_keys_and_request(dir, label, entries, /)\n--\n\nCreate a certificate sign request."
         },
         { "sign_cert", py_certs_sign_cert,
           METH_VARARGS | METH_STATIC,
diff --git a/plugins/pychrysalide/analysis/db/client.c b/plugins/pychrysalide/analysis/db/client.c
new file mode 100644
index 0000000..a9ad930
--- /dev/null
+++ b/plugins/pychrysalide/analysis/db/client.c
@@ -0,0 +1,360 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * client.c - équivalent Python du fichier "analysis/db/client.c"
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "client.h"
+
+
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <analysis/db/client.h>
+#include <core/collections.h>
+
+
+#include "collection.h"
+#include "../../access.h"
+#include "../../helpers.h"
+
+
+
+/* Crée un nouvel objet Python de type 'HubClient'. */
+static PyObject *py_hub_client_new(PyTypeObject *, PyObject *, PyObject *);
+
+/* Démarre la connexion à la base de données. */
+static PyObject *py_hub_client_start(PyObject *, PyObject *);
+
+/* Arrête la connexion à la base de données. */
+static PyObject *py_hub_client_stop(PyObject *, PyObject *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : type = type de l'objet à instancier.                         *
+*                args = arguments fournis à l'appel.                          *
+*                kwds = arguments de type key=val fournis.                    *
+*                                                                             *
+*  Description : Crée un nouvel objet Python de type 'HubClient'.             *
+*                                                                             *
+*  Retour      : Instance Python mise en place.                               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_hub_client_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyObject *result;                       /* Instance à retourner        */
+    const char *hash;                       /* Empreinte du binaire visé   */
+    PyObject *list;                         /* Liste Python de collections */
+    int ret;                                /* Bilan de lecture des args.  */
+    Py_ssize_t length;                      /* Nombre d'éléments collectés */
+    GList *collections;                     /* Liste native de collections */
+    Py_ssize_t i;                           /* Boucle de parcours          */
+    PyObject *item;                         /* Elément de la liste Python  */
+    GDbCollection *collec;                  /* Version équivalente native  */
+    GHubClient *client;                     /* Serveur mis en place        */
+
+#define HUB_CLIENT_DOC                                                                  \
+    "HubClient provides binary updates to a given server.\n"                            \
+    "\n"                                                                                \
+    "Such clients must be authenticated and communications are encrypted using TLS.\n"  \
+    "\n"                                                                                \
+    "Instances can be created using the following constructor:\n"                       \
+    "\n"                                                                                \
+    "    HubClient(hash, list)"                                                         \
+    "\n"                                                                                \
+    "Where hash is a SHA256 fingerprint of the studied binary and list is a list of"    \
+    " pychrysalide.analysis.db.DbCollection instances.\n"                               \
+    "\n"                                                                                \
+    "This kind of list can be retrived with the"                                        \
+    " pychrysalide.analysis.LoadedBinary.collections attribute."
+
+    ret = PyArg_ParseTuple(args, "sO", &hash, &list);
+    if (!ret) return NULL;
+
+    if (!PySequence_Check(list))
+    {
+        PyErr_SetString(PyExc_TypeError, _("The second argument must be a collection list"));
+        return NULL;
+    }
+
+    length = PySequence_Length(list);
+
+    collections = NULL;
+
+    for (i = 0; i < length; i++)
+    {
+        item = PySequence_GetItem(list, i);
+
+        ret = convert_to_db_collection(item, &collec);
+
+        Py_DECREF(item);
+
+        if (ret != 1)
+        {
+            delete_collections_list(&collections);
+            result = NULL;
+            goto exit;
+        }
+
+        g_object_ref(G_OBJECT(collec));
+        collections = g_list_append(collections, collec);
+
+    }
+
+    client = g_hub_client_new(hash, collections);
+
+    if (client != NULL)
+    {
+        result = pygobject_new(G_OBJECT(client));
+        g_object_unref(client);
+    }
+    else result = NULL;
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = serveur à manipuler.                                  *
+*                args = paramètres à transmettre à l'appel natif.             *
+*                                                                             *
+*  Description : Démarre la connexion à la base de données.                   *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_hub_client_start(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Bilan à retourner           */
+    const char *host;                       /* Désignation de serveur      */
+    const char *port;                       /* Port d'écoute associé       */
+    bool ipv6;                              /* Préférence pour IPv6 ?      */
+    int ret;                                /* Bilan de lecture des args.  */
+    GHubClient *client;                     /* Version native du serveur   */
+    bool status;                            /* Bilan de l'opération        */
+
+#define HUB_CLIENT_START_METHOD PYTHON_METHOD_DEF                   \
+(                                                                   \
+    start, "$self, /, host=None, port='1337', ipv6=True",           \
+    METH_VARARGS, py_hub_client,                                    \
+    "Connect to a server for binary updates.\n"                     \
+    "\n"                                                            \
+    "host and port define the properties of the server, and ipv6"   \
+    " tries to establish IPv6 connections first."                   \
+)
+
+    host = NULL;
+    port = "1337";
+    ipv6 = true;
+
+    ret = PyArg_ParseTuple(args, "|ssp", &host, &port, &ipv6);
+    if (!ret) return NULL;
+
+    client = G_HUB_CLIENT(pygobject_get(self));
+
+    if (host == NULL)
+        status = g_hub_client_start_internal(client);
+    else
+        status = g_hub_client_start_remote(client, host, port, ipv6);
+
+    result = status ? Py_True : Py_False;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = serveur à manipuler.                                  *
+*                args = arguments d'appel non utilisés ici.                   *
+*                                                                             *
+*  Description : Arrête la connexion à la base de données.                    *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_hub_client_stop(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Bilan à retourner           */
+    GHubClient *client;                     /* Version native du serveur   */
+
+#define HUB_CLIENT_STOP_METHOD PYTHON_METHOD_DEF    \
+(                                                   \
+    stop, "$self, /",                               \
+    METH_NOARGS, py_hub_client,                     \
+    "Stop the client."                              \
+)
+
+    client = G_HUB_CLIENT(pygobject_get(self));
+
+    g_hub_client_stop(client);
+
+    result = Py_None;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Fournit un accès à une définition de type à diffuser.        *
+*                                                                             *
+*  Retour      : Définition d'objet pour Python.                              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+PyTypeObject *get_python_hub_client_type(void)
+{
+    static PyMethodDef py_hub_client_methods[] = {
+        HUB_CLIENT_START_METHOD,
+        HUB_CLIENT_STOP_METHOD,
+        { NULL }
+    };
+
+    static PyGetSetDef py_hub_client_getseters[] = {
+        { NULL }
+    };
+
+    static PyTypeObject py_hub_client_type = {
+
+        PyVarObject_HEAD_INIT(NULL, 0)
+
+        .tp_name        = "pychrysalide.analysis.db.HubClient",
+        .tp_basicsize   = sizeof(PyGObject),
+
+        .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+        .tp_doc         = HUB_CLIENT_DOC,
+
+        .tp_methods     = py_hub_client_methods,
+        .tp_getset      = py_hub_client_getseters,
+        .tp_new         = py_hub_client_new,
+
+    };
+
+    return &py_hub_client_type;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : module = module dont la définition est à compléter.          *
+*                                                                             *
+*  Description : Prend en charge l'objet 'pychrysalide....db.HubClient'.      *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool ensure_python_hub_client_is_registered(void)
+{
+    PyTypeObject *type;                     /* Type Python 'HubClient'     */
+    PyObject *module;                       /* Module à recompléter        */
+    PyObject *dict;                         /* Dictionnaire du module      */
+
+    type = get_python_hub_client_type();
+
+    if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+    {
+        module = get_access_to_python_module("pychrysalide.analysis.db");
+
+        dict = PyModule_GetDict(module);
+
+        if (!register_class_for_pygobject(dict, G_TYPE_HUB_CLIENT, type, &PyGObject_Type))
+            return false;
+
+    }
+
+    return true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en client de base de données.             *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_hub_client(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)get_python_hub_client_type());
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to hub client");
+            break;
+
+        case 1:
+            *((GHubClient **)dst) = G_HUB_CLIENT(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/analysis/db/client.h b/plugins/pychrysalide/analysis/db/client.h
new file mode 100644
index 0000000..dbeb91f
--- /dev/null
+++ b/plugins/pychrysalide/analysis/db/client.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * client.h - prototypes pour l'équivalent Python du fichier "analysis/db/client.h"
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_CLIENT_H
+#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_CLIENT_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_hub_client_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.analysis.db.HubClient'. */
+bool ensure_python_hub_client_is_registered(void);
+
+/* Tente de convertir en client de base de données. */
+int convert_to_hub_client(PyObject *, void *);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_CLIENT_H */
diff --git a/plugins/pychrysalide/analysis/db/collection.c b/plugins/pychrysalide/analysis/db/collection.c
index 699c272..b7e3e0b 100644
--- a/plugins/pychrysalide/analysis/db/collection.c
+++ b/plugins/pychrysalide/analysis/db/collection.c
@@ -116,3 +116,48 @@ bool ensure_python_db_collection_is_registered(void)
     return true;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en collection de traitements sur binaire. *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_db_collection(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)get_python_db_collection_type());
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to DB collection");
+            break;
+
+        case 1:
+            *((GDbCollection **)dst) = G_DB_COLLECTION(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/analysis/db/collection.h b/plugins/pychrysalide/analysis/db/collection.h
index 9fa3c8a..13ca273 100644
--- a/plugins/pychrysalide/analysis/db/collection.h
+++ b/plugins/pychrysalide/analysis/db/collection.h
@@ -37,6 +37,9 @@ PyTypeObject *get_python_db_collection_type(void);
 /* Prend en charge l'objet 'pychrysalide.analysis.db.DbCollection'. */
 bool ensure_python_db_collection_is_registered(void);
 
+/* Tente de convertir en collection de traitements sur binaire. */
+int convert_to_db_collection(PyObject *, void *);
 
 
-#endif  /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_COLLECTIONS_H */
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_COLLECTION_H */
diff --git a/plugins/pychrysalide/analysis/db/constants.c b/plugins/pychrysalide/analysis/db/constants.c
new file mode 100644
index 0000000..9685ba6
--- /dev/null
+++ b/plugins/pychrysalide/analysis/db/constants.c
@@ -0,0 +1,70 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * constants.c - équivalent Python partiel du fichier "plugins/dex/dex_def.h"
+ *
+ * Copyright (C) 2018 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "constants.h"
+
+
+#include <analysis/db/server.h>
+
+
+#include "../../helpers.h"
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : type = type dont le dictionnaire est à compléter.            *
+*                                                                             *
+*  Description : Définit les constantes pour les serveurs de données.         *
+*                                                                             *
+*  Retour      : true en cas de succès de l'opération, false sinon.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool define_hub_server_constants(PyTypeObject *type)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *values;                       /* Groupe de valeurs à établir */
+
+    values = PyDict_New();
+
+    result = add_const_to_group(values, "FAILURE", SSS_FAILURE);
+    if (result) result = add_const_to_group(values, "SUCCESS", SSS_SUCCESS);
+    if (result) result = add_const_to_group(values, "ALREADY_RUNNING", SSS_ALREADY_RUNNING);
+
+    if (!result)
+    {
+        Py_DECREF(values);
+        goto exit;
+    }
+
+    result = attach_constants_group(type, false, "ServerStartStatus", values, "Status of a server start.");
+
+ exit:
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/analysis/db/constants.h b/plugins/pychrysalide/analysis/db/constants.h
new file mode 100644
index 0000000..a2fc49e
--- /dev/null
+++ b/plugins/pychrysalide/analysis/db/constants.h
@@ -0,0 +1,39 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * constants.h - prototypes pour l'ajout des constantes liées aux bases de données
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_CONSTANTS_H
+#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_CONSTANTS_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Définit les constantes pour les serveurs de données. */
+bool define_hub_server_constants(PyTypeObject *);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_CONSTANTS_H */
diff --git a/plugins/pychrysalide/analysis/db/module.c b/plugins/pychrysalide/analysis/db/module.c
index 33af35f..efe780f 100644
--- a/plugins/pychrysalide/analysis/db/module.c
+++ b/plugins/pychrysalide/analysis/db/module.c
@@ -29,8 +29,10 @@
 
 
 #include "certs.h"
+#include "client.h"
 #include "collection.h"
 #include "item.h"
+#include "server.h"
 #include "items/module.h"
 #include "../../helpers.h"
 
@@ -97,8 +99,10 @@ bool populate_analysis_db_module(void)
     result = true;
 
     if (result) result = ensure_python_certs_is_registered();
+    if (result) result = ensure_python_hub_client_is_registered();
     if (result) result = ensure_python_db_collection_is_registered();
     if (result) result = ensure_python_db_item_is_registered();
+    if (result) result = ensure_python_hub_server_is_registered();
 
     if (result) result = populate_analysis_db_items_module();
 
diff --git a/plugins/pychrysalide/analysis/db/server.c b/plugins/pychrysalide/analysis/db/server.c
new file mode 100644
index 0000000..cee6bf6
--- /dev/null
+++ b/plugins/pychrysalide/analysis/db/server.c
@@ -0,0 +1,331 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * server.c - équivalent Python du fichier "analysis/db/server.c"
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "server.h"
+
+
+#include <pygobject.h>
+
+
+#include <i18n.h>
+#include <analysis/db/server.h>
+
+
+#include "constants.h"
+#include "../../access.h"
+#include "../../helpers.h"
+
+
+
+/* Crée un nouvel objet Python de type 'HubServer'. */
+static PyObject *py_hub_server_new(PyTypeObject *, PyObject *, PyObject *);
+
+/* Démarre le serveur de base de données. */
+static PyObject *py_hub_server_start(PyObject *, PyObject *);
+
+/* Arrête le serveur de base de données. */
+static PyObject *py_hub_server_stop(PyObject *, PyObject *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : type = type de l'objet à instancier.                         *
+*                args = arguments fournis à l'appel.                          *
+*                kwds = arguments de type key=val fournis.                    *
+*                                                                             *
+*  Description : Crée un nouvel objet Python de type 'HubServer'.             *
+*                                                                             *
+*  Retour      : Instance Python mise en place.                               *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_hub_server_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+    PyObject *result;                       /* Instance à retourner        */
+    const char *host;                       /* Désignation de serveur      */
+    const char *port;                       /* Port d'écoute associé       */
+    bool ipv6;                              /* Préférence pour IPv6 ?      */
+    int ret;                                /* Bilan de lecture des args.  */
+    GHubServer *server;                     /* Serveur mis en place        */
+
+#define HUB_SERVER_DOC                                                                  \
+    "HubServer creates a server listening for binary updates from clients.\n"           \
+    "\n"                                                                                \
+    "Such clients are authenticated and communications are encrypted using TLS.\n"      \
+    "\n"                                                                                \
+    "There are two kinds of servers:\n"                                                 \
+    "* one \"local\", which aims to server one given local user account;\n"             \
+    "* one \"remote\", which may target several different users at the same time.\n"    \
+    "\n"                                                                                \
+    "Instances can be created using the following constructor:\n"                       \
+    "\n"                                                                                \
+    "    HubServer(host=None, port='1337', ipv6=True)"                                  \
+    "\n"                                                                                \
+    "Where host and port define the listening properties of the server, and ipv6"       \
+    " tries to establish IPv6 connections first."                                       \
+    "\n"                                                                                \
+    "Without any parameters, a local server is created."
+
+    host = NULL;
+    port = "1337";
+    ipv6 = true;
+
+    ret = PyArg_ParseTuple(args, "|ssp", &host, &port, &ipv6);
+    if (!ret) return NULL;
+
+    if (host == NULL)
+        server = g_hub_server_new_internal();
+    else
+        server = g_hub_server_new_remote(host, port, ipv6);
+
+    if (server != NULL)
+    {
+        result = pygobject_new(G_OBJECT(server));
+        g_object_unref(server);
+    }
+    else result = NULL;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = serveur à manipuler.                                  *
+*                args = arguments d'appel non utilisés ici.                   *
+*                                                                             *
+*  Description : Démarre le serveur de base de données.                       *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_hub_server_start(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Bilan à retourner           */
+    int backlog;                            /* Nombre de connexions        */
+    int ret;                                /* Bilan de lecture des args.  */
+    GHubServer *server;                     /* Version native du serveur   */
+    bool status;                            /* Bilan de l'opération        */
+
+#define HUB_SERVER_START_METHOD PYTHON_METHOD_DEF               \
+(                                                               \
+    start, "$self, /, backlog=10",                              \
+    METH_VARARGS, py_hub_server,                                \
+    "Run a listening server waiting for client connections."    \
+    "\n"                                                        \
+    "The backlog argument defines the maximum length to which"  \
+    " the queue of pending connections may grow."               \
+    "\n"                                                        \
+    "The returned value is a status of type"                    \
+    " pychrysalide.analysis.db.HubServer.ServerStartStatus."    \
+)
+
+    backlog = 10;
+
+    ret = PyArg_ParseTuple(args, "|i", &backlog);
+    if (!ret) return NULL;
+
+    server = G_HUB_SERVER(pygobject_get(self));
+
+    status = g_hub_server_start(server, backlog, true);
+
+    result = cast_with_constants_group(get_python_hub_server_type(), "ServerStartStatus", status);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = serveur à manipuler.                                  *
+*                args = arguments d'appel non utilisés ici.                   *
+*                                                                             *
+*  Description : Arrête le serveur de base de données.                        *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_hub_server_stop(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Bilan à retourner           */
+    GHubServer *server;                     /* Version native du serveur   */
+
+#define HUB_SERVER_STOP_METHOD PYTHON_METHOD_DEF    \
+(                                                   \
+    stop, "$self, /",                               \
+    METH_NOARGS, py_hub_server,                     \
+    "Stop the listening server."                    \
+)
+
+    server = G_HUB_SERVER(pygobject_get(self));
+
+    g_hub_server_stop(server);
+
+    result = Py_None;
+    Py_INCREF(result);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Fournit un accès à une définition de type à diffuser.        *
+*                                                                             *
+*  Retour      : Définition d'objet pour Python.                              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+PyTypeObject *get_python_hub_server_type(void)
+{
+    static PyMethodDef py_hub_server_methods[] = {
+        HUB_SERVER_START_METHOD,
+        HUB_SERVER_STOP_METHOD,
+        { NULL }
+    };
+
+    static PyGetSetDef py_hub_server_getseters[] = {
+        { NULL }
+    };
+
+    static PyTypeObject py_hub_server_type = {
+
+        PyVarObject_HEAD_INIT(NULL, 0)
+
+        .tp_name        = "pychrysalide.analysis.db.HubServer",
+        .tp_basicsize   = sizeof(PyGObject),
+
+        .tp_flags       = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+
+        .tp_doc         = HUB_SERVER_DOC,
+
+        .tp_methods     = py_hub_server_methods,
+        .tp_getset      = py_hub_server_getseters,
+        .tp_new         = py_hub_server_new,
+
+    };
+
+    return &py_hub_server_type;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : module = module dont la définition est à compléter.          *
+*                                                                             *
+*  Description : Prend en charge l'objet 'pychrysalide....db.HubServer'.      *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool ensure_python_hub_server_is_registered(void)
+{
+    PyTypeObject *type;                     /* Type Python 'HubServer'     */
+    PyObject *module;                       /* Module à recompléter        */
+    PyObject *dict;                         /* Dictionnaire du module      */
+
+    type = get_python_hub_server_type();
+
+    if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+    {
+        module = get_access_to_python_module("pychrysalide.analysis.db");
+
+        dict = PyModule_GetDict(module);
+
+        if (!register_class_for_pygobject(dict, G_TYPE_HUB_SERVER, type, &PyGObject_Type))
+            return false;
+
+        if (!define_hub_server_constants(type))
+            return false;
+
+    }
+
+    return true;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en serveur de base de données.            *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_hub_server(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)get_python_hub_server_type());
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to hub server");
+            break;
+
+        case 1:
+            *((GHubServer **)dst) = G_HUB_SERVER(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/analysis/db/server.h b/plugins/pychrysalide/analysis/db/server.h
new file mode 100644
index 0000000..a5dbcde
--- /dev/null
+++ b/plugins/pychrysalide/analysis/db/server.h
@@ -0,0 +1,45 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * server.h - prototypes pour l'équivalent Python du fichier "analysis/db/server.h"
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_SERVER_H
+#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_SERVER_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Fournit un accès à une définition de type à diffuser. */
+PyTypeObject *get_python_hub_server_type(void);
+
+/* Prend en charge l'objet 'pychrysalide.analysis.db.HubServer'. */
+bool ensure_python_hub_server_is_registered(void);
+
+/* Tente de convertir en serveur de base de données. */
+int convert_to_hub_server(PyObject *, void *);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_DB_SERVER_H */
diff --git a/src/Makefile.am b/src/Makefile.am
index a547084..f1fc25c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,7 +1,7 @@
 
 lib_LTLIBRARIES = libchrysacore.la
 
-bin_PROGRAMS = chrysalide csrvmng
+bin_PROGRAMS = chrysalide chrysalide-hub csrvmng
 
 
 
@@ -64,6 +64,20 @@ chrysalide_LDADD = $(LIBINTL)
 # Gestionnaire de serveurs distants
 ############################################################
 
+EXTRA_chrysalide_hub_DEPENDENCIES = $(lib_LTLIBRARIES)
+
+chrysalide_hub_SOURCES =				\
+	hub.c
+
+
+chrysalide_hub_LDFLAGS = $(LIBGTK_LIBS) -L.libs -lchrysacore
+
+
+
+############################################################
+# Gestionnaire de serveurs distants
+############################################################
+
 EXTRA_csrvmng_DEPENDENCIES = $(lib_LTLIBRARIES)
 
 csrvmng_SOURCES =						\
diff --git a/src/analysis/binary.c b/src/analysis/binary.c
index 80d12d3..5ac8e04 100644
--- a/src/analysis/binary.c
+++ b/src/analysis/binary.c
@@ -72,8 +72,8 @@ struct _GLoadedBinary
     char *remote_host;                      /* Nom du serveur distant      */
     unsigned short remote_port;             /* Port du serveur distant     */
 
-    GDbClient *local;                       /* Enregistrements locaux      */
-    GDbClient *remote;                      /* Enregistrements distants    */
+    GHubClient *local;                      /* Enregistrements locaux      */
+    GHubClient *remote;                     /* Enregistrements distants    */
 
     DBStorage storages[DBF_COUNT];          /* Lieux d'enregistrement      */
     GList *collections;                     /* Ensemble de modifications   */
@@ -862,12 +862,12 @@ static bool g_loaded_binary_connect_internal(GLoadedBinary *binary)
 
     /* Tentative de connexion */
 
-    binary->local = g_db_client_new(author, priv,
+    binary->local = g_hub_client_new(/*author, priv,
                                     g_loaded_binary_get_name(binary, false),
-                                    checksum,
+                                     */checksum,
                                     binary->collections);
 
-    result = g_db_client_start_internal(binary->local);
+    result = g_hub_client_start_internal(binary->local);
 
  glbcl_exit:
 
@@ -920,12 +920,12 @@ static bool g_loaded_binary_connect_remote(GLoadedBinary *binary)
 
     /* Tentative de connexion */
 
-    binary->remote = g_db_client_new(author, priv,
+    binary->remote = g_hub_client_new(/*author, priv,
                                      g_loaded_binary_get_name(binary, false),
-                                     checksum,
+                                     */checksum,
                                      binary->collections);
 
-    result = g_db_client_start_remote(binary->local, binary->remote_host, binary->remote_port);
+    result = g_hub_client_start_remote(binary->local, binary->remote_host, "1337", true);//binary->remote_port);
 
     if (!result)
     {
@@ -1008,9 +1008,9 @@ bool g_loaded_binary_save_cache(const GLoadedBinary *binary)
 *                                                                             *
 ******************************************************************************/
 
-GDbClient *g_loaded_binary_get_db_client(const GLoadedBinary *binary)
+GHubClient *g_loaded_binary_get_db_client(const GLoadedBinary *binary)
 {
-    GDbClient *result;                      /* Instance à retourner        */
+    GHubClient *result;                     /* Instance à retourner        */
 
     result = binary->local;
 
@@ -1106,7 +1106,7 @@ bool _g_loaded_binary_add_to_collection(GLoadedBinary *binary, GDbItem *item, bo
     DBFeatures feature;                     /* Domaine de fonctionnalité   */
     GDbCollection *collec;                  /* Collection visée au final   */
     DBStorage storage;                      /* Forme d'enregistrement      */
-    GDbClient *client;                      /* Liaison à utiliser          */
+    GHubClient *client;                     /* Liaison à utiliser          */
     packed_buffer out_pbuf;                 /* Tampon d'émission           */
     int fd;                                 /* Identifiant du canal de com.*/
 
@@ -1134,11 +1134,11 @@ bool _g_loaded_binary_add_to_collection(GLoadedBinary *binary, GDbItem *item, bo
 
         init_packed_buffer(&out_pbuf);
 
-        fd = g_db_client_get_fd(client);
+        fd = g_hub_client_get_fd(client);
 
         result = g_db_collection_pack(collec, &out_pbuf, DBA_ADD_ITEM, item);
 
-        g_db_client_put_fd(client);
+        g_hub_client_put_fd(client);
 
         if (result)
             result = send_packed_buffer(&out_pbuf, fd);
@@ -1175,7 +1175,7 @@ bool _g_loaded_binary_remove_from_collection(GLoadedBinary *binary, DBFeatures f
     bool result;                            /* Bilan à faire remonter      */
     GDbCollection *collec;                  /* Collection visée au final   */
     DBStorage storage;                      /* Forme d'enregistrement      */
-    GDbClient *client;                      /* Liaison à utiliser          */
+    GHubClient *client;                     /* Liaison à utiliser          */
     packed_buffer out_pbuf;                 /* Tampon d'émission           */
     int fd;                                 /* Identifiant du canal de com.*/
 
@@ -1201,11 +1201,11 @@ bool _g_loaded_binary_remove_from_collection(GLoadedBinary *binary, DBFeatures f
 
         init_packed_buffer(&out_pbuf);
 
-        fd = g_db_client_get_fd(client);
+        fd = g_hub_client_get_fd(client);
 
         result = g_db_collection_pack(collec, &out_pbuf, DBA_REM_ITEM, item);
 
-        g_db_client_put_fd(client);
+        g_hub_client_put_fd(client);
 
         if (result)
             result = send_packed_buffer(&out_pbuf, fd);
@@ -1629,7 +1629,7 @@ static bool g_loaded_binary_save(GLoadedBinary *binary, xmlDoc *xdoc, xmlXPathCo
     /* Sauvegarde côté serveur */
 
     if (result)
-        g_db_client_save(binary->local);
+        g_hub_client_save(binary->local);
 
     return result;
 
diff --git a/src/analysis/binary.h b/src/analysis/binary.h
index ad4f568..2c57283 100644
--- a/src/analysis/binary.h
+++ b/src/analysis/binary.h
@@ -117,7 +117,7 @@ bool g_loaded_binary_save_cache(const GLoadedBinary *);
 
 
 /* Fournit le client assurant la liaison avec un serveur. */
-GDbClient *g_loaded_binary_get_db_client(const GLoadedBinary *);
+GHubClient *g_loaded_binary_get_db_client(const GLoadedBinary *);
 
 /* Fournit l'ensemble des collections utilisées par un binaire. */
 GDbCollection **g_loaded_binary_get_all_collections(const GLoadedBinary *, size_t *);
diff --git a/src/analysis/contents/file.c b/src/analysis/contents/file.c
index 951828f..5509455 100644
--- a/src/analysis/contents/file.c
+++ b/src/analysis/contents/file.c
@@ -294,7 +294,7 @@ static void g_file_content_finalize(GFileContent *content)
 
 GBinContent *g_file_content_new(const char *filename)
 {
-    GFileContent *result;                   /* Structure à retourner      */
+    GFileContent *result;                   /* Structure à retourner       */
     int fd;                                 /* Descripteur du fichier      */
     struct stat info;                       /* Informations sur le fichier */
     int ret;                                /* Bilan d'un appel            */
diff --git a/src/analysis/db/Makefile.am b/src/analysis/db/Makefile.am
index 53d7c36..2f7cddd 100644
--- a/src/analysis/db/Makefile.am
+++ b/src/analysis/db/Makefile.am
@@ -3,6 +3,7 @@ noinst_LTLIBRARIES  = libanalysisdb.la libanalysiskeys.la
 
 
 libanalysisdb_la_SOURCES =				\
+	auth.h auth.c						\
 	cdb.h cdb.c							\
 	certs.h certs.c						\
 	client.h client.c					\
diff --git a/src/analysis/db/auth.c b/src/analysis/db/auth.c
new file mode 100644
index 0000000..af51af6
--- /dev/null
+++ b/src/analysis/db/auth.c
@@ -0,0 +1,705 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * auth.c - mise en place et gestion des autorisations pour les partages
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "auth.h"
+
+
+#include <fcntl.h>
+#include <glib.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+
+#include "../../common/extstr.h"
+#include "../../common/io.h"
+#include "../../common/pathname.h"
+#include "../../common/xdg.h"
+#include "../../core/logs.h"
+
+
+
+/* Fournit le répertoire d'enregistrement des certificats. */
+static char *get_cert_storage_directory(const char *, const char *, const char *);
+
+/* Calcule l'empreinte d'un fichier de demande de signature. */
+static char *compute_csr_fingerprint(const char *);
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : addr = adresse UNIX constituée. [OUT]                        *
+*                                                                             *
+*  Description : Met en place un canal UNIX pour un serveur interne.          *
+*                                                                             *
+*  Retour      : Bilan de la définition.                                      *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool build_internal_server_socket(struct sockaddr_un *addr)
+{
+    bool result;                            /* Bilan à retourner           */
+    char *suffix;                           /* Fin de la destination       */
+    char *path;                             /* Chemin d'accès au canal     */
+    int ret;                                /* Bilan intermédiaire         */
+    size_t length;                          /* Taille du chemin complet    */
+
+    suffix = strdup("chrysalide");
+    suffix = stradd(suffix, G_DIR_SEPARATOR_S);
+    suffix = stradd(suffix, "internal-server");
+
+    path = get_xdg_config_dir(suffix);
+
+    free(suffix);
+
+    ret = ensure_path_exists(path);
+    if (ret != 0) goto mts_exit;
+
+    length = strlen(path) + 1;
+
+#ifndef UNIX_PATH_MAX
+#   define UNIX_PATH_MAX 108
+#endif
+
+    if (length > UNIX_PATH_MAX)
+    {
+        log_variadic_message(LMT_ERROR,
+                             _("Impossible to use '%s' as UNIX socket path: string is too long ! (%zu vs %u)\n"),
+                             path, length, UNIX_PATH_MAX);
+        goto mts_exit;
+    }
+
+    memset(addr, 0, sizeof(struct sockaddr_un));
+
+    addr->sun_family = AF_UNIX;
+    strncpy(addr->sun_path, path, UNIX_PATH_MAX - 1);
+
+    result = true;
+
+ mts_exit:
+
+    free(path);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : type = type de certificat à gérer.                           *
+*                host = dénomination du serveur visé ou NULL.                 *
+*                port = port d'écoute ou NULL.                                *
+*                sub  = éventuelle sous-partie ou NULL.                       *
+*                                                                             *
+*  Description : Fournit le répertoire de travail pour les données d'analyse. *
+*                                                                             *
+*  Retour      : Définition d'emplacement à libérer de la mémoire après usage.*
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+char *get_db_working_directory(const char *type, const char *host, const char *port, const char *sub)
+{
+    char *result;                           /* Chemin à retourner          */
+    char *suffix;                           /* Fin de la destination       */
+
+    suffix = strdup("chrysalide");
+    suffix = stradd(suffix, G_DIR_SEPARATOR_S);
+    suffix = stradd(suffix, type);
+    suffix = stradd(suffix, G_DIR_SEPARATOR_S);
+
+    if (host != NULL)
+    {
+        suffix = stradd(suffix, host);
+
+        if (port == NULL)
+            suffix = stradd(suffix, G_DIR_SEPARATOR_S);
+
+    }
+
+    if (port != NULL)
+    {
+        suffix = stradd(suffix, "-");
+        suffix = stradd(suffix, port);
+        suffix = stradd(suffix, G_DIR_SEPARATOR_S);
+    }
+
+    if (sub != NULL)
+    {
+        suffix = stradd(suffix, sub);
+        suffix = stradd(suffix, G_DIR_SEPARATOR_S);
+    }
+
+    result = get_xdg_config_dir(suffix);
+
+    free(suffix);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : outdir = répertoire de sortie pour les nouveaux fichiers.    *
+*                host   = dénomination du serveur visé.                       *
+*                port   = port d'écoute ou NULL.                              *
+*                                                                             *
+*  Description : Fournit le répertoire d'enregistrement des certificats.      *
+*                                                                             *
+*  Retour      : Définition d'emplacement à libérer de la mémoire après usage.*
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *get_cert_storage_directory(const char *outdir, const char *host, const char *port)
+{
+    char *result;                           /* Chemin à retourner          */
+
+    result = strdup(outdir);
+
+    if (!endswith(result, G_DIR_SEPARATOR_S))
+        result = stradd(result, G_DIR_SEPARATOR_S);
+
+    result = stradd(result, host);
+
+    if (port == NULL)
+        result = stradd(result, G_DIR_SEPARATOR_S);
+
+    else
+    {
+        result = stradd(result, "-");
+        result = stradd(result, port);
+        result = stradd(result, G_DIR_SEPARATOR_S);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : valid   = durée de validité des certificats.                 *
+*                entries = éléments d'identité à utiliser pour l'opération.   *
+*                                                                             *
+*  Description : Etablit une base pour l'identité de l'utilisateur.           *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool setup_client_identity(unsigned long valid, x509_entries *entries)
+{
+    bool result;                            /* Bilan de l'opération        */
+    char *working;                          /* Répertoire pour le client   */
+    uid_t uid;                              /* Identifiant d'utilisateur   */
+    struct passwd *pw;                      /* Indications sur l'usager    */
+
+    working = get_db_working_directory("clients", NULL, NULL, NULL);
+
+    result = mkpath(working);
+
+    if (result)
+    {
+        if (entries->common_name == NULL)
+        {
+            uid = geteuid();
+            pw = getpwuid(uid);
+
+            if (pw != NULL)
+            {
+                log_variadic_message(LMT_WARNING,
+                                     _("Replaced the empty identity common name with '%s'"),
+                                     pw->pw_name);
+
+                entries->common_name = strdup(pw->pw_name);
+
+            }
+
+        }
+
+        result = build_keys_and_request(working, "client", entries);
+
+    }
+
+    free(working);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : host    = désignation du serveur à contacter.                *
+*                port    = port d'écoute correspondant.                       *
+*                valid   = durée de validité des certificats.                 *
+*                entries = éléments d'identité à utiliser pour l'opération.   *
+*                                                                             *
+*  Description : Etablit une base pour l'identité d'un serveur.               *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool setup_server_identity(const char *host, const char *port, unsigned long valid, x509_entries *entries)
+{
+    bool result;                            /* Bilan de l'opération        */
+    char *working;                          /* Répertoire pour le serveur  */
+    char *csr;                              /* Requête de signature        */
+    char *old;                              /* Conservation de l'origine   */
+    char *new;                              /* Nouvelle désignation        */
+    char *cacert;                           /* Certificat d'autorité       */
+    char *cakey;                            /* Clef de cette autorité      */
+    char *cert;                             /* Certificat signé en sortie  */
+
+    if (host == NULL)
+    {
+        host = "standalone";
+        port = NULL;
+    }
+
+    else if (strcmp(host, "standalone") != 0 && port == NULL)
+        port = "1337";
+
+    working = get_db_working_directory("servers", host, port, NULL);
+
+    result = mkpath(working);
+
+    if (result)
+    {
+        if (entries->common_name == NULL)
+        {
+            log_variadic_message(LMT_WARNING,
+                                 _("Replaced the empty identity common name with '%s'"),
+                                 host);
+
+            entries->common_name = strdup(host);
+
+        }
+
+        old = entries->common_name;
+
+        new = strdup(old);
+        new = stradd(new, " CA");
+
+        entries->common_name = new;
+
+        result = build_keys_and_ca(working, "ca", valid, entries);
+
+        entries->common_name = old;
+
+        free(new);
+
+        if (result)
+            result = build_keys_and_request(working, "server", entries);
+
+        if (result)
+        {
+            csr = build_absolute_filename(working, "server-csr.pem");
+            cacert = build_absolute_filename(working, "ca-cert.pem");
+            cakey = build_absolute_filename(working, "ca-key.pem");
+            cert = build_absolute_filename(working, "server-cert.pem");
+
+            result = sign_cert(csr, cacert, cakey, cert, valid);
+
+            free(csr);
+            free(cacert);
+            free(cakey);
+            free(cert);
+
+        }
+
+    }
+
+    free(working);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : csr = fichier contenant le certificat à signer.              *
+*                                                                             *
+*  Description : Calcule l'empreinte d'un fichier de demande de signature.    *
+*                                                                             *
+*  Retour      : Empreinte calculée ou NULL en cas d'erreur.                  *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *compute_csr_fingerprint(const char *csr)
+{
+    char *result;                           /* Empreinte à retourner       */
+    int fd;                                 /* Descripteur du fichier      */
+    struct stat info;                       /* Informations sur le fichier */
+    int ret;                                /* Bilan d'un appel            */
+    void *data;                             /* Quantité de données traitées*/
+    bool status;                            /* Bilan de la lecture         */
+
+    result = NULL;
+
+    fd = open(csr, O_RDONLY);
+    if (fd == -1)
+    {
+        LOG_ERROR_N("open");
+        goto exit;
+    }
+
+    ret = fstat(fd, &info);
+    if (ret == -1)
+    {
+        LOG_ERROR_N("fstat");
+        goto done;
+    }
+
+    data = malloc(info.st_size);
+
+    status = safe_read(fd, data, info.st_size);
+
+    if (status)
+        result = g_compute_checksum_for_data(G_CHECKSUM_SHA256, data, info.st_size);
+
+    free(data);
+
+ done:
+
+    close(fd);
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : host   = désignation du serveur à contacter.                 *
+*                port   = port d'écoute correspondant.                        *
+*                valid  = durée de validité des certificats.                  *
+*                csr    = fichier contenant le certificat à signer.           *
+*                outdir = répertoire de sortie pour les nouveaux fichiers.    *
+*                                                                             *
+*  Description : Ajoute un certificat dans les utilisateurs d'un serveur.     *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool add_client_to_server(const char *host, const char *port, unsigned long valid, const char *csr, const char *outdir)
+{
+    bool result;                            /* Bilan de l'opération        */
+    char *hash;                             /* Empreinte de la requête     */
+    char *working;                          /* Répertoire pour le serveur  */
+    char *cacert;                           /* Certificat d'autorité       */
+    char *cakey;                            /* Clef de cette autorité      */
+    char *storage;                          /* Répertoire de stockage      */
+    char *dest;                             /* Destination d'une copie     */
+
+    result = false;
+
+    if (host == NULL)
+    {
+        host = "standalone";
+        port = NULL;
+    }
+
+    else if (strcmp(host, "standalone") != 0 && port == NULL)
+        port = "1337";
+
+    hash = compute_csr_fingerprint(csr);
+    if (hash == NULL) goto exit;
+
+    working = get_db_working_directory("servers", host, port, "authorized");
+
+    result = mkpath(working);
+
+    if (result)
+    {
+        hash = strprep(hash, working);
+        hash = stradd(hash, "-cert.pem");
+
+        free(working);
+
+        working = get_db_working_directory("servers", host, port, NULL);
+
+        cacert = build_absolute_filename(working, "ca-cert.pem");
+        cakey = build_absolute_filename(working, "ca-key.pem");
+
+        result = sign_cert(csr, cacert, cakey, hash, valid);
+
+        if (result)
+        {
+            storage = get_cert_storage_directory(outdir, host, port);
+
+            result = mkpath(storage);
+
+            if (result)
+            {
+                dest = build_absolute_filename(storage, "ca-cert.pem");
+
+                result = copy_file(dest, cacert);
+
+                free(dest);
+
+            }
+
+            if (result)
+            {
+                dest = build_absolute_filename(storage, "client-cert.pem");
+
+                result = copy_file(dest, hash);
+
+                free(dest);
+
+            }
+
+            free(storage);
+
+        }
+
+        free(cacert);
+        free(cakey);
+
+        free(hash);
+
+    }
+
+    free(working);
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Assure la présence d'unenvironnement pour serveur interne.   *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool ensure_internal_connections_setup(void)
+{
+    bool result;                            /* Bilan à retourner           */
+    unsigned long valid;                    /* Durée de validité           */
+    char *filename;                         /* Fichier devant être présent */
+    int ret;                                /* Bilan d'une validation      */
+    x509_entries identity;                  /* Nouvelle identité à pousser */
+    bool status;                            /* Bilan intermédiaire         */
+    char *csr;                              /* Fichier de requête          */
+    char *outdir;                           /* Répertoire de sortie        */
+
+    result = false;
+
+    valid = 3 * 365 * 24 * 60 * 60;
+
+    /* Teste la présence d'une identitié pour le client */
+
+    filename = get_db_working_directory("clients", NULL, NULL, NULL);
+    filename = stradd(filename, "client-csr.pem");
+
+    ret = access(filename, R_OK);
+
+    if (ret != 0)
+    {
+        memset(&identity, 0, sizeof(identity));
+
+        status = setup_client_identity(valid, &identity);
+
+        free_x509_entries(&identity);
+
+        if (status)
+            ret = access(filename, R_OK);
+        else
+            ret = -1;
+
+    }
+
+    free(filename);
+
+    if (ret != 0)
+        goto done;
+
+    /* Teste la présence d'une identitié pour le serveur interne */
+
+    filename = get_db_working_directory("servers", "standalone", NULL, NULL);
+    filename = stradd(filename, "server-csr.pem");
+
+    ret = access(filename, R_OK);
+
+    if (ret != 0)
+    {
+        memset(&identity, 0, sizeof(identity));
+
+        status = setup_server_identity("standalone", NULL, valid, &identity);
+
+        free_x509_entries(&identity);
+
+        if (status)
+            ret = access(filename, R_OK);
+        else
+            ret = -1;
+
+    }
+
+    free(filename);
+
+    if (ret != 0)
+        goto done;
+
+    /* Teste la présence d'une autorisation pour l'accès à ce serveur */
+
+    filename = get_db_working_directory("clients", "standalone", NULL, NULL);
+    filename = stradd(filename, "client-cert.pem");
+
+    ret = access(filename, R_OK);
+
+    if (ret != 0)
+    {
+        csr = get_db_working_directory("clients", NULL, NULL, NULL);
+        csr = stradd(csr, "client-csr.pem");
+
+        outdir = get_db_working_directory("clients", NULL, NULL, NULL);
+
+        status = add_client_to_server("standalone", NULL, valid, csr, outdir);
+
+        free(outdir);
+        free(csr);
+
+        if (status)
+            ret = access(filename, R_OK);
+        else
+            ret = -1;
+
+    }
+
+    free(filename);
+
+    if (ret != 0)
+        goto done;
+
+    result = true;
+
+ done:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Lance un serveur interne si besoin est.                      *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool launch_internal_server(void)
+{
+    bool result;                            /* Bilan à retourner           */
+    pid_t child;                            /* Identifiant de processus    */
+    const char *prgm;                       /* Programme à exécuter        */
+    int wstatus;                            /* Etat du serveur lancé       */
+    pid_t ret;                              /* Bilan d'un appel            */
+
+    char * const argv[] = {
+        "chrysalide-hub",
+        "run",
+        NULL
+    };
+
+    child = fork();
+
+    switch (child)
+    {
+        case -1:
+            result = false;
+            LOG_ERROR_N("fork");
+            break;
+
+        case 0:
+#ifndef DISCARD_LOCAL
+            prgm = PACKAGE_SOURCE_DIR "/src/chrysalide-hub";
+#else
+            prgm = "chrysalide-hub";
+#endif
+
+            execvp(prgm, argv);
+
+            LOG_ERROR_N("execvp");
+            exit(EXIT_FAILURE);
+            break;
+
+        default:
+
+            ret = waitpid(child, &wstatus, 0);
+
+            if (ret == -1)
+            {
+                result = false;
+                LOG_ERROR_N("waitpid");
+            }
+
+            else
+                result = (WEXITSTATUS(wstatus) == EXIT_SUCCESS);
+
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/src/analysis/db/auth.h b/src/analysis/db/auth.h
new file mode 100644
index 0000000..4db7af7
--- /dev/null
+++ b/src/analysis/db/auth.h
@@ -0,0 +1,59 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * auth.h - prototypes pour la mise en place et gestion des autorisations pour les partages
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _ANALYSIS_DB_AUTH_H
+#define _ANALYSIS_DB_AUTH_H
+
+
+#include <stdbool.h>
+#include <sys/un.h>
+
+
+#include "certs.h"
+
+
+
+/* Met en place un canal UNIX pour un serveur interne. */
+bool build_internal_server_socket(struct sockaddr_un *);
+
+/* Fournit le répertoire de travail pour les données d'analyse. */
+char *get_db_working_directory(const char *, const char *, const char *, const char *);
+
+/* Etablit une base pour l'identité de l'utilisateur. */
+bool setup_client_identity(unsigned long, x509_entries *);
+
+/* Etablit une base pour l'identité d'un serveur. */
+bool setup_server_identity(const char *, const char *, unsigned long, x509_entries *);
+
+/* Ajoute un certificat dans les utilisateurs d'un serveur. */
+bool add_client_to_server(const char *, const char *, unsigned long, const char *, const char *);
+
+/* Assure la présence d'unenvironnement pour serveur interne. */
+bool ensure_internal_connections_setup(void);
+
+/* Lance un serveur interne si besoin est. */
+bool launch_internal_server(void);
+
+
+
+#endif  /* _ANALYSIS_DB_AUTH_H */
diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c
index c2855f7..74e8501 100644
--- a/src/analysis/db/cdb.c
+++ b/src/analysis/db/cdb.c
@@ -51,7 +51,6 @@
 #include "../../common/xml.h"
 #include "../../core/collections.h"
 #include "../../core/logs.h"
-#include "../../core/params.h"
 
 
 
@@ -59,7 +58,7 @@
 typedef struct _cdb_client
 {
     SSL *ssl_fd;                            /* Canal de communication      */
-    rle_string user;                        /* Utilisateur à l'autre bout  */
+    char *user;                             /* Utilisateur à l'autre bout  */
 
     uint64_t last_time;                     /* Date de dernier envoi       */
 
@@ -125,7 +124,7 @@ static bool g_cdb_archive_read(GCdbArchive *);
 
 
 /* Crée la description XML correspondant à l'archive. */
-static bool g_cdb_archive_create_xml_desc(GCdbArchive *, const rle_string *);
+static bool g_cdb_archive_create_xml_desc(GCdbArchive *);
 
 /* Vérifie la conformité d'une description XML avec le serveur. */
 static bool g_cdb_archive_check_xml_version(const GCdbArchive *);
@@ -263,7 +262,7 @@ static void g_cdb_archive_finalize(GCdbArchive *archive)
     if (archive->xml_desc != NULL)
     {
         ret = unlink(archive->xml_desc);
-        if (ret != 0) perror("unlink");
+        if (ret != 0) LOG_ERROR_N("unlink");
 
         free(archive->xml_desc);
 
@@ -272,7 +271,7 @@ static void g_cdb_archive_finalize(GCdbArchive *archive)
     if (archive->sql_db != NULL)
     {
         ret = unlink(archive->sql_db);
-        if (ret != 0) perror("unlink");
+        if (ret != 0) LOG_ERROR_N("unlink");
 
         free(archive->sql_db);
 
@@ -290,8 +289,8 @@ static void g_cdb_archive_finalize(GCdbArchive *archive)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : basedir = répertoire de stockage des enregistrements.        *
+*                tmpdir  = répertoire de travail temporaire.                  *
 *                hash    = empreinte du binaire à représenter.                *
-*                user    = désignation d'un éventuel nouveau créateur.        *
 *                error   = indication éventuelle en cas d'échec. [OUT]        *
 *                                                                             *
 *  Description : Définit ou ouvre une archive d'éléments utilisateur.         *
@@ -303,11 +302,9 @@ static void g_cdb_archive_finalize(GCdbArchive *archive)
 *                                                                             *
 ******************************************************************************/
 
-GCdbArchive *g_cdb_archive_new(const char *basedir, const rle_string *hash, const rle_string *user, DBError *error)
+GCdbArchive *g_cdb_archive_new(const char *basedir, const char *tmpdir, const rle_string *hash, DBError *error)
 {
     GCdbArchive *result;                    /* Adresse à retourner         */
-    const char *tmpdir;                     /* Répertoire d'accueil        */
-    bool status;                            /* Bilan d'un consultation     */
     int ret;                                /* Retour d'un appel           */
     struct stat finfo;                      /* Information sur l'archive   */
 
@@ -318,7 +315,6 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const rle_string *hash, cons
     /* Chemin de l'archive */
 
     result->filename = strdup(basedir);
-    result->filename = stradd(result->filename, G_DIR_SEPARATOR_S);
     result->filename = stradd(result->filename, hash->data);
     result->filename = stradd(result->filename, ".cdb.tar.xz");
 
@@ -327,8 +323,8 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const rle_string *hash, cons
 
     /* Chemin des enregistrements temporaires */
 
-    status = g_generic_config_get_value(get_main_configuration(), MPK_TMPDIR, &tmpdir);
-    if (!status) goto gcan_no_tmp;
+    if (!mkpath(tmpdir))
+        goto gcan_error;
 
     ret = asprintf(&result->xml_desc, "%s" G_DIR_SEPARATOR_S "%s_desc.xml", tmpdir, result->hash.data);
     if (ret == -1) goto gcan_no_tmp;
@@ -348,7 +344,7 @@ GCdbArchive *g_cdb_archive_new(const char *basedir, const rle_string *hash, cons
         /* Le soucis ne vient pas de l'absence du fichier... */
         if (errno != ENOENT) goto gcan_error;
 
-        g_cdb_archive_create_xml_desc(result, user);
+        g_cdb_archive_create_xml_desc(result);
         g_cdb_archive_create_db(result);
 
         *error = g_cdb_archive_write(result);
@@ -573,7 +569,6 @@ int g_cdb_archive_compare_hash(const GCdbArchive *archive, const rle_string *has
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : archive = archive à constituer.                              *
-*                user    = désignation d'un éventuel nouveau créateur.        *
 *                                                                             *
 *  Description : Crée la description XML correspondant à l'archive.           *
 *                                                                             *
@@ -583,34 +578,38 @@ int g_cdb_archive_compare_hash(const GCdbArchive *archive, const rle_string *has
 *                                                                             *
 ******************************************************************************/
 
-static bool g_cdb_archive_create_xml_desc(GCdbArchive *archive, const rle_string *user)
+static bool g_cdb_archive_create_xml_desc(GCdbArchive *archive)
 {
     bool result;                            /* Bilan à retourner           */
     timestamp_t timestamp;                  /* Date de création            */
     char tmp[sizeof(XSTR(UINT64_MAX))];     /* Stockage temporaire         */
 
     result = create_new_xml_file(&archive->xdoc, &archive->context);
-    if (!result) return false;
 
-    result &= add_content_to_node(archive->xdoc, archive->context,
-                                  "/ChrysalideBinary/Version", PACKAGE_VERSION);
+    if (result)
+        result = add_content_to_node(archive->xdoc, archive->context,
+                                     "/ChrysalideBinary/Version", PACKAGE_VERSION);
 
-    result &= add_content_to_node(archive->xdoc, archive->context,
-                                  "/ChrysalideBinary/Protocol", XSTR(CDB_PROTOCOL_VERSION));
+    if (result)
+        result = add_content_to_node(archive->xdoc, archive->context,
+                                     "/ChrysalideBinary/Protocol", XSTR(CDB_PROTOCOL_VERSION));
 
-    result &= add_content_to_node(archive->xdoc, archive->context,
-                                  "/ChrysalideBinary/Hash", archive->hash.data);
+    if (result)
+        result = add_content_to_node(archive->xdoc, archive->context,
+                                     "/ChrysalideBinary/Hash", archive->hash.data);
 
-    result &= add_content_to_node(archive->xdoc, archive->context,
-                                  "/ChrysalideBinary/Creation/Author", user->data);
+    if (result)
+    {
+        init_timestamp(&timestamp);
+        snprintf(tmp, sizeof(tmp), "%" PRIu64, timestamp);
 
-    init_timestamp(&timestamp);
-    snprintf(tmp, sizeof(tmp), "%" PRIu64, timestamp);
+        result = add_content_to_node(archive->xdoc, archive->context,
+                                     "/ChrysalideBinary/CreationDate", tmp);
 
-    result &= add_content_to_node(archive->xdoc, archive->context,
-                                  "/ChrysalideBinary/Creation/Date", tmp);
+    }
 
-    save_xml_file(archive->xdoc, archive->xml_desc);
+    if (result)
+        result = save_xml_file(archive->xdoc, archive->xml_desc);
 
     return result;
 
@@ -863,7 +862,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
         {
             if (errno == EINTR) continue;
 
-            perror("poll");
+            LOG_ERROR_N("poll");
             break;
 
         }
@@ -874,7 +873,15 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
         {
             /* Le canal est fermé, une sortie doit être demandée... */
             if (fds[i].revents & POLLNVAL)
-                goto gcap_closed_exchange;
+                goto closed_exchange;
+
+            /**
+             * Même chose, cf. "TCP: When is EPOLLHUP generated?"
+             * https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated/52976327#52976327
+             */
+
+            if (fds[i].revents & (POLLHUP | POLLRDHUP))
+                goto closed_exchange;
 
             /* Données présentes en entrée */
             if (fds[i].revents & (POLLIN | POLLPRI))
@@ -979,7 +986,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 
                 exit_packed_buffer(&in_pbuf);
 
- gcap_closed_exchange:
+ closed_exchange:
 
                 g_cdb_archive_remove_client(archive, i);
 
@@ -1012,7 +1019,6 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 *                                                                             *
 *  Paramètres  : archive = archive à connecter avec un utilisateur.           *
 *                fd      = canal de communication réseau ouvert.              *
-*                user    = désignation de l'utilisateur associé.              *
 *                                                                             *
 *  Description : Associe un nouvel utilisateur à l'archive.                   *
 *                                                                             *
@@ -1022,8 +1028,9 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 *                                                                             *
 ******************************************************************************/
 
-void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd, const rle_string *user)
+void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd)
 {
+    X509 *peer_cert;                        /* Certificat présenté         */
     volatile pthread_t *process_id;         /* Identifiant de la procédure */
 
     /**
@@ -1051,7 +1058,12 @@ void g_cdb_archive_add_client(GCdbArchive *archive, SSL *fd, const rle_string *u
     archive->clients = realloc(archive->clients, ++archive->count * sizeof(cdb_client));
 
     archive->clients[archive->count - 1].ssl_fd = fd;
-    dup_into_rle_string(&archive->clients[archive->count - 1].user, get_rle_string(user));
+
+    peer_cert = SSL_get_peer_certificate(fd);
+
+    archive->clients[archive->count - 1].user = X509_NAME_oneline(X509_get_subject_name(peer_cert), NULL, -1);
+
+    X509_free(peer_cert);
 
     /* Démarrage ou redémarrage du processus d'écoute */
 
@@ -1099,7 +1111,7 @@ static void g_cdb_archive_remove_client(GCdbArchive *archive, size_t index)
     g_mutex_lock(&archive->clients_access);
 
     SSL_free(client->ssl_fd);
-    exit_rle_string(&client->user);
+    free(client->user);
 
     if ((index + 1) < archive->count)
         memmove(&archive->clients[index], &archive->clients[index + 1],
diff --git a/src/analysis/db/cdb.h b/src/analysis/db/cdb.h
index 601b10f..8d4a86b 100644
--- a/src/analysis/db/cdb.h
+++ b/src/analysis/db/cdb.h
@@ -54,7 +54,7 @@ typedef struct _GCdbArchiveClass GCdbArchiveClass;
 GType g_cdb_archive_get_type(void);
 
 /* Prépare un client pour une connexion à une BD. */
-GCdbArchive *g_cdb_archive_new(const char *, const rle_string *, const rle_string *, DBError *);
+GCdbArchive *g_cdb_archive_new(const char *, const char *, const rle_string *, DBError *);
 
 /* Enregistre une archive avec tous les éléments à conserver. */
 DBError g_cdb_archive_write(const GCdbArchive *);
@@ -67,7 +67,7 @@ int g_cdb_archive_compare_hash(const GCdbArchive *, const rle_string *);
 
 
 /* Associe un nouvel utilisateur à l'archive. */
-void g_cdb_archive_add_client(GCdbArchive *, SSL *, const rle_string *);
+void g_cdb_archive_add_client(GCdbArchive *, SSL *);
 
 
 
diff --git a/src/analysis/db/certs.c b/src/analysis/db/certs.c
index 41a40b7..a333d9a 100644
--- a/src/analysis/db/certs.c
+++ b/src/analysis/db/certs.c
@@ -252,7 +252,7 @@ static RSA *generate_rsa_key(unsigned int bits, unsigned long e)
 *                                                                             *
 ******************************************************************************/
 
-bool make_ca(const char *dir, const char *label, unsigned long valid, const x509_entries *entries)
+bool build_keys_and_ca(const char *dir, const char *label, unsigned long valid, const x509_entries *entries)
 {
     RSA *rsa;                               /* Clef RSA pour le certificat */
     EVP_PKEY *pk;                           /* Enveloppe pour clef publique*/
@@ -455,7 +455,7 @@ static bool add_extension_to_req(STACK_OF(X509_EXTENSION) *sk, int nid, /*const
 *                                                                             *
 ******************************************************************************/
 
-bool make_request(const char *dir, const char *label, const x509_entries *entries)
+bool build_keys_and_request(const char *dir, const char *label, const x509_entries *entries)
 {
     RSA *rsa;                               /* Clef RSA pour le certificat */
     EVP_PKEY *pk;                           /* Enveloppe pour clef publique*/
diff --git a/src/analysis/db/certs.h b/src/analysis/db/certs.h
index 31da82f..132cd7f 100644
--- a/src/analysis/db/certs.h
+++ b/src/analysis/db/certs.h
@@ -49,10 +49,10 @@ bool are_x509_entries_empty(const x509_entries *);
 void free_x509_entries(x509_entries *);
 
 /* Crée un certificat de signature racine. */
-bool make_ca(const char *, const char *, unsigned long, const x509_entries *);
+bool build_keys_and_ca(const char *, const char *, unsigned long, const x509_entries *);
 
 /* Crée un certificat pour application. */
-bool make_request(const char *, const char *, const x509_entries *);
+bool build_keys_and_request(const char *, const char *, const x509_entries *);
 
 /* Signe un certificat pour application. */
 bool sign_cert(const char *, const char *, const char *, const char *, unsigned long);
diff --git a/src/analysis/db/client.c b/src/analysis/db/client.c
index 24198f6..c608bfa 100644
--- a/src/analysis/db/client.c
+++ b/src/analysis/db/client.c
@@ -37,31 +37,39 @@
 #include <i18n.h>
 
 
-#include "keymgn.h"
+#include "auth.h"
 #include "protocol.h"
 #include "misc/rlestr.h"
+#include "../../common/extstr.h"
 #include "../../common/io.h"
 #include "../../common/xdg.h"
 #include "../../core/logs.h"
 
 
 
+/* Format générique des adresses de connexion */
+typedef union _gen_sockaddr_t
+{
+    struct sockaddr_in inet4_addr;          /* Adresse d'écoute IPv4       */
+    struct sockaddr_in6 inet6_addr;         /* Adresse d'écoute IPv6       */
+    struct sockaddr inet_4_6_addr;          /* Adresse d'écoute IPv4/6     */
+
+} gen_sockaddr_t;
+
+
 /* Description de client à l'écoute (instance) */
-struct _GDbClient
+struct _GHubClient
 {
     GObject parent;                         /* A laisser en premier        */
 
-    char *author;                           /* Utilisateur représenté      */
-    //char *key_file;                         /* Accès sa la clef privée     */
-
     const char *name;                       /* Désignation du binaire      */
 
     rle_string hash;                        /* Empreinte du binaire lié    */
     GList *collections;                     /* Collections d'un binaire    */
 
+    char *working;                          /* Répertoire de travail       */
+
     SSL_CTX *tls_ctx;                       /* Contexte du chiffrement     */
-    char *cert_file;                        /* Fichier du certificat       */
-    char *key_file;                         /* Fichier de la clef privée   */
 
     int fd;                                 /* Canal de communication      */
     SSL *tls_fd;                            /* Même canal, mais sécurisé   */
@@ -74,7 +82,7 @@ struct _GDbClient
 };
 
 /* Description de client à l'écoute (classe) */
-struct _GDbClientClass
+struct _GHubClientClass
 {
     GObjectClass parent;                    /* A laisser en premier        */
 
@@ -82,24 +90,27 @@ struct _GDbClientClass
 
 
 /* Initialise la classe des descriptions de fichier binaire. */
-static void g_db_client_class_init(GDbClientClass *);
+static void g_hub_client_class_init(GHubClientClass *);
 
 /* Initialise une description de fichier binaire. */
-static void g_db_client_init(GDbClient *);
+static void g_hub_client_init(GHubClient *);
+
+/* Supprime toutes les références externes. */
+static void g_hub_client_dispose(GHubClient *);
 
 /* Procède à la libération totale de la mémoire. */
-static void g_db_client_finalize(GDbClient *);
+static void g_hub_client_finalize(GHubClient *);
 
 /* Démarre réellement la connexion à la base de données. */
-static bool g_db_client_start_common(GDbClient *, char *);
+static bool g_hub_client_start_common(GHubClient *, char *);
 
 /* Assure l'accueil des nouvelles mises à jour. */
-static void *g_db_client_update(GDbClient *);
+static void *g_hub_client_update(GHubClient *);
 
 
 
 /* Indique le type défini pour une description de client à l'écoute. */
-G_DEFINE_TYPE(GDbClient, g_db_client, G_TYPE_OBJECT);
+G_DEFINE_TYPE(GHubClient, g_hub_client, G_TYPE_OBJECT);
 
 
 /******************************************************************************
@@ -114,13 +125,14 @@ G_DEFINE_TYPE(GDbClient, g_db_client, G_TYPE_OBJECT);
 *                                                                             *
 ******************************************************************************/
 
-static void g_db_client_class_init(GDbClientClass *klass)
+static void g_hub_client_class_init(GHubClientClass *klass)
 {
     GObjectClass *object;                   /* Autre version de la classe  */
 
     object = G_OBJECT_CLASS(klass);
 
-    object->finalize = (GObjectFinalizeFunc)g_db_client_finalize;
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_hub_client_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_hub_client_finalize;
 
 }
 
@@ -137,11 +149,11 @@ static void g_db_client_class_init(GDbClientClass *klass)
 *                                                                             *
 ******************************************************************************/
 
-static void g_db_client_init(GDbClient *client)
+static void g_hub_client_init(GHubClient *client)
 {
+    client->working = NULL;
+
     client->tls_ctx = NULL;
-    client->cert_file = NULL;
-    client->key_file = NULL;
 
     client->fd = -1;
     client->tls_fd = NULL;
@@ -152,6 +164,27 @@ static void g_db_client_init(GDbClient *client)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : archive = instance d'objet GLib à traiter.                   *
+*                                                                             *
+*  Description : Supprime toutes les références externes.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_hub_client_dispose(GHubClient *client)
+{
+    g_hub_client_stop(client);
+
+    G_OBJECT_CLASS(g_hub_client_parent_class)->dispose(G_OBJECT(client));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : client = instance d'objet GLib à traiter.                    *
 *                                                                             *
 *  Description : Procède à la libération totale de la mémoire.                *
@@ -162,36 +195,28 @@ static void g_db_client_init(GDbClient *client)
 *                                                                             *
 ******************************************************************************/
 
-static void g_db_client_finalize(GDbClient *client)
+static void g_hub_client_finalize(GHubClient *client)
 {
-    free(client->author);
-
     unset_rle_string(&client->hash);
 
-    assert(client->tls_ctx == NULL);
-
-    if (client->cert_file != NULL)
-        free(client->cert_file);
+    if (client->working != NULL)
+        free(client->working);
 
-    if (client->key_file != NULL)
-        free(client->key_file);
+    assert(client->tls_ctx == NULL);
 
     assert(client->tls_fd == NULL);
 
     if (client->desc != NULL)
         free(client->desc);
 
-    G_OBJECT_CLASS(g_db_client_parent_class)->finalize(G_OBJECT(client));
+    G_OBJECT_CLASS(g_hub_client_parent_class)->finalize(G_OBJECT(client));
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : author      = utilisateur à représenter via le client.       *
-*                kfile       = clef menant à sa clef privée.                  *
-*                name        = désignation humaine du binaire associé.        *
-*                hash        = empreinte d'un binaire en cours d'analyse.     *
+*  Paramètres  : hash        = empreinte d'un binaire en cours d'analyse.     *
 *                collections = ensemble de collections existantes.            *
 *                                                                             *
 *  Description : Prépare un client pour une connexion à une BD.               *
@@ -202,16 +227,11 @@ static void g_db_client_finalize(GDbClient *client)
 *                                                                             *
 ******************************************************************************/
 
-GDbClient *g_db_client_new(char *author, char *kfile, const char *name, const char *hash, GList *collections)
+GHubClient *g_hub_client_new(const char *hash, GList *collections)
 {
-    GDbClient *result;                      /* Adresse à retourner         */
+    GHubClient *result;                     /* Adresse à retourner         */
 
-    result = g_object_new(G_TYPE_DB_CLIENT, NULL);
-
-    result->author = author;
-    result->key_file = kfile;
-
-    result->name = name;
+    result = g_object_new(G_TYPE_HUB_CLIENT, NULL);
 
     init_static_rle_string(&result->hash, hash);
     result->collections = collections;
@@ -233,7 +253,7 @@ GDbClient *g_db_client_new(char *author, char *kfile, const char *name, const ch
 *                                                                             *
 ******************************************************************************/
 
-bool g_db_client_start_internal(GDbClient *client)
+bool g_hub_client_start_internal(GHubClient *client)
 {
     bool status;                            /* Bilan de la connexion       */
     struct sockaddr_un addr;                /* Adresse de transmission     */
@@ -242,42 +262,43 @@ bool g_db_client_start_internal(GDbClient *client)
 
     /* Identification du serveur à contacter */
 
-    status = build_tmp_socket("internal-server", &addr);
-    if (!status) goto gdcni_error;
+    status = build_internal_server_socket(&addr);
+    if (!status) goto fs_error;
 
     /* Création d'un canal de communication */
 
     client->fd = socket(AF_UNIX, SOCK_STREAM, 0);
     if (client->fd == -1)
     {
-        perror("socket");
-        goto gdcni_error;
+        LOG_ERROR_N("socket");
+        goto fs_error;
     }
 
     ret = connect(client->fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un));
     if (ret == -1)
     {
-        perror("connect");
-        goto gdcsi_no_listening;
+        LOG_ERROR_N("connect");
+        goto no_listening;
     }
 
     asprintf(&desc, "unix://%s", addr.sun_path);
 
-    status = g_db_client_start_common(client, desc);
+    client->working = get_db_working_directory("clients", "standalone", NULL, NULL);
+
+    status = g_hub_client_start_common(client, desc);
 
     if (!status)
-        goto gdcsi_no_listening;
+        goto no_listening;
 
     return true;
 
- gdcsi_no_listening:
+ no_listening:
 
     close(client->fd);
-
- gdcni_error:
-
     client->fd = -1;
 
+ fs_error:
+
     return false;
 
 }
@@ -288,6 +309,7 @@ bool g_db_client_start_internal(GDbClient *client)
 *  Paramètres  : client = client pour les accès distants à manipuler.         *
 *                host   = hôte à représenter pour le service.                 *
 *                port   = port de connexion pour les clients.                 *
+*                ipv6   = adopte une préférence pour les adresses IPv6.       *
 *                                                                             *
 *  Description : Démarre la connexion à la base de données distante.          *
 *                                                                             *
@@ -297,54 +319,114 @@ bool g_db_client_start_internal(GDbClient *client)
 *                                                                             *
 ******************************************************************************/
 
-bool g_db_client_start_remote(GDbClient *client, const char *host, unsigned short port)
+bool g_hub_client_start_remote(GHubClient *client, const char *host, const char *port, bool ipv6)
 {
-    struct hostent *hp;                     /* Informations sur l'hôte     */
-    struct sockaddr_in addr;                /* Adresse de transmission     */
-    int ret;                                /* Bilan d'un appel            */
+    struct addrinfo hints;                  /* Cadre de connexion souhaité */
+    struct addrinfo *available;             /* Cadres de connexion dispos  */
+    int ret;                                /* Bilan d'une consultation    */
+    int domain;                             /* Domaine du canal            */
+    struct addrinfo *iter;                  /* Boucle de parcours          */
+    gen_sockaddr_t addr;                    /* Adresse d'écoute générique  */
+    socklen_t sock_len;                     /* Taille de cette adresse     */
     char *desc;                             /* Description du serveur ciblé*/
     bool status;                            /* Bilan de la connexion       */
 
-    /* Identification du serveur à contacter */
+    /* Détermination du point d'écoute */
+
+    memset(&hints, 0, sizeof(hints));
+
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = IPPROTO_TCP;
+
+    ret = getaddrinfo(host, port, &hints, &available);
+    if (ret != 0)
+    {
+        LOG_ERROR_GAI_N("getaddrinfo", ret);
+        goto no_target;
+    }
+
+    domain = AF_UNSPEC;
+
+    /**
+     * Premier tour : on essaie de se plier à la demande.
+     */
+
+    for (iter = available; iter != NULL && domain == AF_UNSPEC; iter = iter->ai_next)
+    {
+        if (ipv6 && iter->ai_family != AF_INET6)
+            continue;
 
-    hp = gethostbyname(host);
-    if (hp == NULL) return false;
+        if (!ipv6 && iter->ai_family != AF_INET)
+            continue;
+
+        domain = iter->ai_family;
+
+        memcpy(&addr.inet_4_6_addr, iter->ai_addr, iter->ai_addrlen);
+        sock_len = iter->ai_addrlen;
+
+    }
+
+    /**
+     * Second tour : on fait avec ce qu'on a.
+     */
+
+    for (iter = available; iter != NULL && domain == AF_UNSPEC; iter = iter->ai_next)
+    {
+        if (iter->ai_family != AF_INET6 && iter->ai_family != AF_INET)
+            continue;
+
+        domain = iter->ai_family;
+
+        memcpy(&addr.inet_4_6_addr, iter->ai_addr, iter->ai_addrlen);
+        sock_len = iter->ai_addrlen;
+
+    }
 
-    addr.sin_family = hp->h_addrtype;
-    memcpy(&addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+    if (available != NULL)
+        freeaddrinfo(available);
 
-    addr.sin_port = htons(port);
+    if (domain == AF_UNSPEC)
+    {
+        log_variadic_message(LMT_ERROR, _("No suitable address found for %s:%s"), host, port);
+        goto no_target;
+    }
 
     /* Création d'un canal de communication */
 
-    client->fd = socket(AF_INET, SOCK_STREAM, 0);
+    client->fd = socket(domain, SOCK_STREAM, 0);
     if (client->fd == -1)
     {
-        perror("socket");
-        return false;
+        LOG_ERROR_N("socket");
+        goto error_socket;
     }
 
-    ret = connect(client->fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
+    ret = connect(client->fd, (struct sockaddr *)&addr, sock_len);
     if (ret == -1)
     {
-        perror("connect");
-        goto gdcsr_no_listening;
+        LOG_ERROR_N("connect");
+        goto no_listening;
     }
 
-    asprintf(&desc, "%s:%hu", host, port);
+    asprintf(&desc, "%s:%s", host, port);
 
-    status = g_db_client_start_common(client, desc);
+    client->working = get_db_working_directory("clients", host, port, NULL);
+
+    status = g_hub_client_start_common(client, desc);
 
     if (!status)
-        goto gdcsr_no_listening;
+        goto no_listening;
 
     return true;
 
- gdcsr_no_listening:
+ no_listening:
 
     close(client->fd);
     client->fd = -1;
 
+ error_socket:
+ no_target:
+
     return false;
 
 }
@@ -364,26 +446,21 @@ bool g_db_client_start_remote(GDbClient *client, const char *host, unsigned shor
 *                                                                             *
 ******************************************************************************/
 
-static bool g_db_client_start_common(GDbClient *client, char *desc)
+static bool g_hub_client_start_common(GHubClient *client, char *desc)
 {
     const SSL_METHOD *method;               /* Mode du canal sécurisé      */
+    char *filename;                         /* Fichier PEM à manipuler     */
     int ret;                                /* Bilan d'un appel            */
+    char *rootdir;                          /* Racine pour le client       */
     packed_buffer out_pbuf;                 /* Tampon d'émission           */
     bool status;                            /* Bilan d'une opération       */
-    rle_string user;                        /* Nom d'utilisateur associé   */
-    GChecksum *checksum;                    /* Empreinte MD5 à signer      */
-    unsigned char md5_digest[16];           /* Empreinte MD5 calculée      */
-    RSA *key;                               /* Clef pour la signature      */
-    unsigned char sig[RSA_USED_SIZE];       /* Signature effectuée         */
     packed_buffer in_pbuf;                  /* Tampon de réception         */
     uint32_t data;                          /* Mot de données lues         */
     DBError error;                          /* Validation de la connexion  */
 
     client->desc = desc;
 
-    /**
-     * Mise en place d'un environnement sécurisé.
-     */
+    /* Définition d'un environnement TLS */
 
     method = TLS_client_method();
 
@@ -395,6 +472,54 @@ static bool g_db_client_start_common(GDbClient *client, char *desc)
         goto quick_error;
     }
 
+    filename = strdup(client->working);
+    filename = stradd(filename, "client-cert.pem");
+
+    ret = SSL_CTX_use_certificate_file(client->tls_ctx, filename, SSL_FILETYPE_PEM);
+
+    free(filename);
+
+    if (ret != 1)
+    {
+        LOG_ERROR_OPENSSL;
+        goto tls_error;
+    }
+
+    rootdir = get_db_working_directory("clients", NULL, NULL, NULL);
+
+    filename = strdup(rootdir);
+    filename = stradd(filename, "client-key.pem");
+
+    ret = SSL_CTX_use_PrivateKey_file(client->tls_ctx, filename, SSL_FILETYPE_PEM);
+
+    free(filename);
+    free(rootdir);
+
+    if (ret != 1)
+    {
+        LOG_ERROR_OPENSSL;
+        goto tls_error;
+    }
+
+    /* Validation des certificats */
+
+    SSL_CTX_set_verify(client->tls_ctx, SSL_VERIFY_PEER, NULL);
+
+    filename = strdup(client->working);
+    filename = stradd(filename, "ca-cert.pem");
+
+    ret = SSL_CTX_load_verify_locations(client->tls_ctx, filename, NULL);
+
+    free(filename);
+
+    if (ret != 1)
+    {
+        LOG_ERROR_OPENSSL;
+        goto tls_error;
+    }
+
+    /* Mise en place d'un canal de communication */
+
     client->tls_fd = SSL_new(client->tls_ctx);
 
     if (client->tls_fd == NULL)
@@ -410,7 +535,7 @@ static bool g_db_client_start_common(GDbClient *client, char *desc)
     if (ret != 1)
     {
         LOG_ERROR_OPENSSL;
-        goto tls_error;
+        goto ssl_error;
     }
 
     /**
@@ -418,8 +543,6 @@ static bool g_db_client_start_common(GDbClient *client, char *desc)
      *    - la commande 'DBC_HELO'.
      *    - le numéro de version du client.
      *    - l'empreinte du binaire analysé.
-     *    - l'identifiant de l'utilisateur effectuant des modifications.
-     *    - la signature de l'empreinte MD5 de l'identifiant.
      *
      * Tout ceci est à synchroniser avec la fonction g_db_server_listener().
      */
@@ -435,30 +558,6 @@ static bool g_db_client_start_common(GDbClient *client, char *desc)
     status = pack_rle_string(&client->hash, &out_pbuf);
     if (!status) goto setup_error;
 
-    dup_into_rle_string(&user, client->author);
-
-    status = pack_rle_string(&user, &out_pbuf);
-    if (!status) goto setup_error;
-
-    checksum = g_checksum_new(G_CHECKSUM_MD5);
-    g_checksum_update(checksum, (guchar *)get_rle_string(&user), get_rle_length(&user));
-    g_checksum_get_digest(checksum, (guint8 *)md5_digest, (gsize []) { sizeof(md5_digest) });
-    g_checksum_free(checksum);
-
-    key = load_rsa_key(client->key_file, true);
-    if (key == NULL) goto setup_error;
-
-    if (!sign_md5_hash(key, md5_digest, sig))
-    {
-        RSA_free(key);
-        goto setup_error;
-    }
-
-    RSA_free(key);
-
-    status = extend_packed_buffer(&out_pbuf, sig, RSA_USED_SIZE, false);
-    if (!status) goto setup_error;
-
     status = ssl_send_packed_buffer(&out_pbuf, client->tls_fd);
     if (!status) goto setup_error;
 
@@ -522,7 +621,7 @@ static bool g_db_client_start_common(GDbClient *client, char *desc)
 
     client->can_get_updates = false;
 
-    client->update = g_thread_try_new("cdb_client", (GThreadFunc)g_db_client_update, client, NULL);
+    client->update = g_thread_try_new("cdb_client", (GThreadFunc)g_hub_client_update, client, NULL);
     if (client->update == NULL)
     {
         log_variadic_message(LMT_ERROR, _("Failed to start a listening thread for the server '%s'!"),
@@ -543,7 +642,10 @@ static bool g_db_client_start_common(GDbClient *client, char *desc)
 
     exit_packed_buffer(&out_pbuf);
 
-    unset_rle_string(&user);
+ ssl_error:
+
+    SSL_free(client->tls_fd);
+    client->tls_fd = NULL;
 
  tls_error:
 
@@ -552,9 +654,6 @@ static bool g_db_client_start_common(GDbClient *client, char *desc)
 
  quick_error:
 
-    close(client->fd);
-    client->fd = -1;
-
     return false;
 
 }
@@ -572,7 +671,7 @@ static bool g_db_client_start_common(GDbClient *client, char *desc)
 *                                                                             *
 ******************************************************************************/
 
-static void *g_db_client_update(GDbClient *client)
+static void *g_hub_client_update(GHubClient *client)
 {
     packed_buffer out_pbuf;                 /* Tampon d'émission           */
     bool status;                            /* Bilan d'une opération       */
@@ -617,7 +716,7 @@ static void *g_db_client_update(GDbClient *client)
 
     init_packed_buffer(&in_pbuf);
 
-    while (1)
+    while (client->fd != -1)
     {
         ret = poll(&fds, 1, -1);
         if (ret != 1) continue;
@@ -626,6 +725,14 @@ static void *g_db_client_update(GDbClient *client)
         if (fds.revents & POLLNVAL)
             break;
 
+        /**
+         * Même chose, cf. "TCP: When is EPOLLHUP generated?"
+         * https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated/52976327#52976327
+         */
+
+        if (fds.revents & (POLLHUP | POLLRDHUP))
+            break;
+
         if (fds.revents & (POLLIN | POLLPRI))
         {
             reset_packed_buffer(&in_pbuf);
@@ -701,7 +808,7 @@ static void *g_db_client_update(GDbClient *client)
 
  exit:
 
-    g_db_client_stop(client);
+    g_hub_client_stop(client);
 
     exit_packed_buffer(&in_pbuf);
 
@@ -722,13 +829,14 @@ static void *g_db_client_update(GDbClient *client)
 *                                                                             *
 ******************************************************************************/
 
-void g_db_client_stop(GDbClient *client)
+void g_hub_client_stop(GHubClient *client)
 {
+    int fd;                                 /* Canal à clôturer            */
     int ret;                                /* Bilan d'un appel            */
 
     /* Canal de communication */
 
-    if (client->fd != -1)
+    if (client->fd == -1)
     {
         /**
          * Si la fermture est forcée, le thread de traitement va terminer en erreur.
@@ -741,15 +849,20 @@ void g_db_client_stop(GDbClient *client)
         return;
     }
 
-    ret = close(client->fd);
-    if (ret == -1) perror("close");
+    fd = client->fd;
 
     client->fd = -1;
 
+    ret = close(fd);
+    if (ret == -1) LOG_ERROR_N("close");
+
     g_thread_join(client->update);
 
     /* Environnement TLS */
 
+    SSL_free(client->tls_fd);
+    client->tls_fd = NULL;
+
     SSL_CTX_free(client->tls_ctx);
     client->tls_ctx = NULL;
 
@@ -768,7 +881,7 @@ void g_db_client_stop(GDbClient *client)
 *                                                                             *
 ******************************************************************************/
 
-int g_db_client_get_fd(GDbClient *client)
+int g_hub_client_get_fd(GHubClient *client)
 {
     g_mutex_lock(&client->sending_lock);
 
@@ -789,7 +902,7 @@ int g_db_client_get_fd(GDbClient *client)
 *                                                                             *
 ******************************************************************************/
 
-void g_db_client_put_fd(GDbClient *client)
+void g_hub_client_put_fd(GHubClient *client)
 {
     g_mutex_unlock(&client->sending_lock);
 
@@ -808,18 +921,18 @@ void g_db_client_put_fd(GDbClient *client)
 *                                                                             *
 ******************************************************************************/
 
-bool g_db_client_save(GDbClient *client)
+bool g_hub_client_save(GHubClient *client)
 {
     bool result;                            /* Bilan partiel à remonter    */
     int sent;                               /* Quantité de données traitées*/
 
-    g_db_client_get_fd(client);
+    g_hub_client_get_fd(client);
 
     sent = SSL_write(client->tls_fd, (uint32_t []) { htobe32(DBC_SAVE) }, sizeof(uint32_t));
 
     result = (sent == sizeof(uint32_t));
 
-    g_db_client_put_fd(client);
+    g_hub_client_put_fd(client);
 
     return result;
 
@@ -839,21 +952,21 @@ bool g_db_client_save(GDbClient *client)
 *                                                                             *
 ******************************************************************************/
 
-bool g_db_client_set_last_active(GDbClient *client, timestamp_t timestamp)
+bool g_hub_client_set_last_active(GHubClient *client, timestamp_t timestamp)
 {
     bool result;                            /* Bilan partiel à remonter    */
     packed_buffer out_pbuf;                 /* Tampon d'émission           */
 
     init_packed_buffer(&out_pbuf);
 
-    g_db_client_get_fd(client);
+    g_hub_client_get_fd(client);
 
     result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_LAST_ACTIVE }, sizeof(uint32_t), true);
 
     if (result)
         result = pack_timestamp(&timestamp, &out_pbuf);
 
-    g_db_client_put_fd(client);
+    g_hub_client_put_fd(client);
 
     if (result)
         result = ssl_send_packed_buffer(&out_pbuf, client->tls_fd);
diff --git a/src/analysis/db/client.h b/src/analysis/db/client.h
index 23b85f8..66ca6ab 100644
--- a/src/analysis/db/client.h
+++ b/src/analysis/db/client.h
@@ -33,47 +33,47 @@
 
 
 
-#define G_TYPE_DB_CLIENT            g_db_client_get_type()
-#define G_DB_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_DB_CLIENT, GDbClient))
-#define G_IS_DB_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_DB_CLIENT))
-#define G_DB_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DB_CLIENT, GDbClientClass))
-#define G_IS_DB_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DB_CLIENT))
-#define G_DB_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DB_CLIENT, GDbClientClass))
+#define G_TYPE_HUB_CLIENT            g_hub_client_get_type()
+#define G_HUB_CLIENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_HUB_CLIENT, GHubClient))
+#define G_IS_HUB_CLIENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_HUB_CLIENT))
+#define G_HUB_CLIENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_HUB_CLIENT, GHubClientClass))
+#define G_IS_HUB_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_HUB_CLIENT))
+#define G_HUB_CLIENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_HUB_CLIENT, GHubClientClass))
 
 
 /* Description de client à l'écoute (instance) */
-typedef struct _GDbClient GDbClient;
+typedef struct _GHubClient GHubClient;
 
 /* Description de client à l'écoute (classe) */
-typedef struct _GDbClientClass GDbClientClass;
+typedef struct _GHubClientClass GHubClientClass;
 
 
 /* Indique le type défini pour une description de client à l'écoute. */
-GType g_db_client_get_type(void);
+GType g_hub_client_get_type(void);
 
 /* Prépare un client pour une connexion à une BD. */
-GDbClient *g_db_client_new(char *, char *, const char *, const char *, GList *);
+GHubClient *g_hub_client_new(const char *, GList *);
 
 /* Démarre la connexion à la base de données interne. */
-bool g_db_client_start_internal(GDbClient *);
+bool g_hub_client_start_internal(GHubClient *);
 
 /* Démarre la connexion à la base de données distante. */
-bool g_db_client_start_remote(GDbClient *, const char *, unsigned short);
+bool g_hub_client_start_remote(GHubClient *, const char *, const char *, bool);
 
 /* Arrête la connexion à la base de données. */
-void g_db_client_stop(GDbClient *);
+void g_hub_client_stop(GHubClient *);
 
 /* Identifie le canal de communication pour envois au serveur. */
-int g_db_client_get_fd(GDbClient *);
+int g_hub_client_get_fd(GHubClient *);
 
 /* Marque le canal de communication comme disponible. */
-void g_db_client_put_fd(GDbClient *);
+void g_hub_client_put_fd(GHubClient *);
 
 /* Effectue une demande de sauvegarde de l'état courant. */
-bool g_db_client_save(GDbClient *);
+bool g_hub_client_save(GHubClient *);
 
 /* Active les éléments en amont d'un horodatage donné. */
-bool g_db_client_set_last_active(GDbClient *, timestamp_t);
+bool g_hub_client_set_last_active(GHubClient *, timestamp_t);
 
 
 
diff --git a/src/analysis/db/keymgn.c b/src/analysis/db/keymgn.c
index 4334896..d2abd7c 100644
--- a/src/analysis/db/keymgn.c
+++ b/src/analysis/db/keymgn.c
@@ -329,10 +329,10 @@ bool register_server_cert(const char *name, const x509_entries *entries)
         if (!result)
             goto rsc_quick_exit;
 
-        result = make_ca(working, "ca", valid, entries);
+        result = build_keys_and_ca(working, "ca", valid, entries);
         if (!result) goto rsc_quick_exit;
 
-        result = make_request(working, "server", entries);
+        result = build_keys_and_request(working, "server", entries);
         if (!result) goto rsc_quick_exit;
 
         csr = build_absolute_filename(working, "server-csr.pem");
@@ -384,7 +384,7 @@ bool make_client_sign_request(const char *name, const x509_entries *entries)
     result = mkpath(working);
 
     if (result)
-        result = make_request(working, "client", entries);
+        result = build_keys_and_request(working, "client", entries);
 
     if (result)
         store_identity(entries, true);
diff --git a/src/analysis/db/keymgn.h b/src/analysis/db/keymgn.h
index df4f1f5..24ada61 100644
--- a/src/analysis/db/keymgn.h
+++ b/src/analysis/db/keymgn.h
@@ -31,6 +31,8 @@
 #include "certs.h"
 
 
+#include "auth.h"
+
 
 /* Charge en mémoire la définition de l'identité courante. */
 void load_identity(bool, x509_entries *);
diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h
index 311a691..1c03d62 100644
--- a/src/analysis/db/protocol.h
+++ b/src/analysis/db/protocol.h
@@ -29,7 +29,7 @@
 /**
  * Version de la définition courante du protocole.
  */
-#define CDB_PROTOCOL_VERSION 0xc0de0003
+#define CDB_PROTOCOL_VERSION 0xc0de0004
 
 
 
diff --git a/src/analysis/db/server.c b/src/analysis/db/server.c
index deb54e9..bd221d7 100644
--- a/src/analysis/db/server.c
+++ b/src/analysis/db/server.c
@@ -25,6 +25,8 @@
 
 
 #include <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
 #include <malloc.h>
 #include <netdb.h>
 #include <poll.h>
@@ -33,55 +35,50 @@
 #include <arpa/inet.h>
 #include <openssl/err.h>
 #include <openssl/ssl.h>
+#include <sys/file.h>
 
 
 #include <i18n.h>
 
 
+#include "auth.h"
 #include "cdb.h"
-#include "keymgn.h"
 #include "protocol.h"
 #include "misc/rlestr.h"
+#include "../../common/extstr.h"
 #include "../../common/io.h"
 #include "../../common/pathname.h"
 #include "../../common/xdg.h"
 #include "../../core/logs.h"
-#include "../../core/params.h"
 
 
 
-/* Utilisateur enregistré */
-typedef struct _registered_user
-{
-    rle_string author;                      /* Désignation d'utilisateur   */
-    char *key_file;                         /* Chemin vers sa clef publique*/
-
-} registered_user;
-
 /* Format générique des adresses de connexion */
 typedef union _gen_sockaddr_t
 {
-    struct sockaddr_in inet_addr;           /* Adresse d'écoute IPv4       */
     struct sockaddr_un unix_addr;           /* Adresse d'écoute Unix       */
+    struct sockaddr_in inet4_addr;          /* Adresse d'écoute IPv4       */
+    struct sockaddr_in6 inet6_addr;         /* Adresse d'écoute IPv6       */
+    struct sockaddr inet_4_6_addr;          /* Adresse d'écoute IPv4/6     */
 
 } gen_sockaddr_t;
 
 
 /* Assure que le point de connexion est vierge. */
-typedef bool (* clean_server_socket_cb) (GDbServer *);
+typedef bool (* lock_server_socket_cb) (GHubServer *);
+
+/* Assure que le point de connexion est rendu disponible. */
+typedef void (* unlock_server_socket_cb) (GHubServer *);
 
 
 /* Description de serveur à l'écoute (instance) */
-struct _GDbServer
+struct _GHubServer
 {
     GObject parent;                         /* A laisser en premier        */
 
-    registered_user *users;                 /* Liste d'utilisateurs        */
-    size_t users_count;                     /* Nombre d'enregistrés        */
+    char *working;                          /* Répertoire de travail       */
 
     SSL_CTX *tls_ctx;                       /* Contexte du chiffrement     */
-    char *cert_file;                        /* Fichier du certificat       */
-    char *key_file;                         /* Fichier de la clef privée   */
 
     int fd;                                 /* Canal de communication      */
     int domain;                             /* Domaine du canal            */
@@ -89,56 +86,60 @@ struct _GDbServer
     socklen_t sock_len;                     /* Taille de cette adresse     */
     char *desc;                             /* Désignation du serveur      */
 
-    clean_server_socket_cb clean_socket;    /* Procédure de nettoyage ?    */
-
-    char *basedir;                          /* Répertoire de stockage      */
+    lock_server_socket_cb lock_socket;      /* Procédure de nettoyage ?    */
+    unlock_server_socket_cb unlock_socket;  /* Procédure de nettoyage ?    */
+    int lock_fd;                            /* Eventuel verrou d'accès     */
 
     GThread *listener;                      /* Procédure de traitement     */
 
     GList *archives;                        /* Liste des binaires ouverts  */
     GMutex mutex;                           /* Verrou pour l'accès         */
 
+    GMutex wait_mutex;                      /* Accès à la condition        */
+    GCond wait_cond;                        /* Attente de signal           */
+
 };
 
 /* Description de serveur à l'écoute (classe) */
-struct _GDbServerClass
+struct _GHubServerClass
 {
     GObjectClass parent;                    /* A laisser en premier        */
 
 };
 
 
+/* Indice pour le passage d'arguments */
+static int _ssl_data_index = -1;
+
+
 /* Initialise la classe des descriptions de fichier binaire. */
-static void g_db_server_class_init(GDbServerClass *);
+static void g_hub_server_class_init(GHubServerClass *);
 
 /* Initialise une description de fichier binaire. */
-static void g_db_server_init(GDbServer *);
+static void g_hub_server_init(GHubServer *);
 
 /* Supprime toutes les références externes. */
-static void g_db_server_dispose(GDbServer *);
+static void g_hub_server_dispose(GHubServer *);
 
 /* Procède à la libération totale de la mémoire. */
-static void g_db_server_finalize(GDbServer *);
+static void g_hub_server_finalize(GHubServer *);
 
 /* Assure que le point de connexion est vierge. */
-static bool g_db_server_clean_internal_socket(GDbServer *);
+static bool g_hub_server_lock_internal_socket(GHubServer *);
 
-/* Supprime toute trace d'utilisateur inscrit. */
-static void g_db_server_unregister_all_user(GDbServer *);
+/* Assure que le point de connexion est rendu disponible. */
+static void g_hub_server_unlock_internal_socket(GHubServer *);
 
-/* Inscrit un nouvel utilisateur comme étant enregistré. */
-static bool g_db_server_register_user(GDbServer *, const char *, char *);
-
-/* Inscrit un nouvel utilisateur comme étant enregistré. */
-static bool g_db_server_is_registered_user(GDbServer *, const rle_string *, unsigned char *);
+/* Vérifie la légitimité d'une connexion sécurisée au serveur. */
+static int g_hub_server_verify(int, X509_STORE_CTX *);
 
 /* Assure l'accueil des nouveaux clients. */
-static void *g_db_server_listener(GDbServer *);
+static void *g_hub_server_listener(GHubServer *);
 
 
 
 /* Indique le type défini pour une description de serveur à l'écoute. */
-G_DEFINE_TYPE(GDbServer, g_db_server, G_TYPE_OBJECT);
+G_DEFINE_TYPE(GHubServer, g_hub_server, G_TYPE_OBJECT);
 
 
 /******************************************************************************
@@ -153,14 +154,14 @@ G_DEFINE_TYPE(GDbServer, g_db_server, G_TYPE_OBJECT);
 *                                                                             *
 ******************************************************************************/
 
-static void g_db_server_class_init(GDbServerClass *klass)
+static void g_hub_server_class_init(GHubServerClass *klass)
 {
     GObjectClass *object;                   /* Autre version de la classe  */
 
     object = G_OBJECT_CLASS(klass);
 
-    object->dispose = (GObjectFinalizeFunc/* ! */)g_db_server_dispose;
-    object->finalize = (GObjectFinalizeFunc)g_db_server_finalize;
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_hub_server_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_hub_server_finalize;
 
 }
 
@@ -177,18 +178,23 @@ static void g_db_server_class_init(GDbServerClass *klass)
 *                                                                             *
 ******************************************************************************/
 
-static void g_db_server_init(GDbServer *server)
+static void g_hub_server_init(GHubServer *server)
 {
+    server->working = NULL;
+
     server->tls_ctx = NULL;
-    server->cert_file = NULL;
-    server->key_file = NULL;
 
     server->fd = -1;
 
-    server->basedir = NULL;
+    server->lock_socket = NULL;
+    server->unlock_socket = NULL;
+    server->lock_fd = -1;
 
     g_mutex_init(&server->mutex);
 
+    g_mutex_init(&server->wait_mutex);
+    g_cond_init(&server->wait_cond);
+
 }
 
 
@@ -204,11 +210,11 @@ static void g_db_server_init(GDbServer *server)
 *                                                                             *
 ******************************************************************************/
 
-static void g_db_server_dispose(GDbServer *server)
+static void g_hub_server_dispose(GHubServer *server)
 {
     GList *iter;                            /* Boucle de parcours          */
 
-    g_db_server_stop(server);
+    g_hub_server_stop(server);
 
     for (iter = g_list_first(server->archives);
          iter != NULL;
@@ -220,7 +226,10 @@ static void g_db_server_dispose(GDbServer *server)
 
     g_mutex_clear(&server->mutex);
 
-    G_OBJECT_CLASS(g_db_server_parent_class)->dispose(G_OBJECT(server));
+    g_mutex_clear(&server->wait_mutex);
+    g_cond_clear(&server->wait_cond);
+
+    G_OBJECT_CLASS(g_hub_server_parent_class)->dispose(G_OBJECT(server));
 
 }
 
@@ -237,32 +246,37 @@ static void g_db_server_dispose(GDbServer *server)
 *                                                                             *
 ******************************************************************************/
 
-static void g_db_server_finalize(GDbServer *server)
+static void g_hub_server_finalize(GHubServer *server)
 {
-    g_db_server_unregister_all_user(server);
+    int ret;                                /* Bilan d'un appel            */
 
-    free(server->desc);
+    if (server->working != NULL)
+        free(server->working);
 
     assert(server->tls_ctx == NULL);
 
-    if (server->cert_file != NULL)
-        free(server->cert_file);
+    free(server->desc);
 
-    if (server->key_file != NULL)
-        free(server->key_file);
+    if (server->lock_fd != -1)
+    {
+        ret = flock(server->lock_fd, LOCK_UN);
+        if (ret == -1)
+            LOG_ERROR_N("flock");
 
-    if (server->basedir != NULL)
-        free(server->basedir);
+        ret = close(server->lock_fd);
+        if (ret == -1)
+            LOG_ERROR_N("close");
 
-    G_OBJECT_CLASS(g_db_server_parent_class)->finalize(G_OBJECT(server));
+    }
+
+    G_OBJECT_CLASS(g_hub_server_parent_class)->finalize(G_OBJECT(server));
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : author = utilisateur à représenter via le client.            *
-*                kfile  = clef menant à sa clef publique.                     *
+*  Paramètres  : -                                                            *
 *                                                                             *
 *  Description : Prépare un serveur de BD pour les clients internes.          *
 *                                                                             *
@@ -272,49 +286,21 @@ static void g_db_server_finalize(GDbServer *server)
 *                                                                             *
 ******************************************************************************/
 
-GDbServer *g_db_server_new_internal(const char *author, char *kfile)
+GHubServer *g_hub_server_new_internal(void)
 {
-    GDbServer *result;                      /* Adresse à retourner         */
+    GHubServer *result;                     /* Adresse à retourner         */
     bool status;                            /* Bilan d'un chargement       */
-    char *working;                          /* Répertoire par le client    */
-    int ret[2];                             /* Bilan d'une vérification    */
-
-    result = g_object_new(G_TYPE_DB_SERVER, NULL);
-
-    /* Chargement du profil */
-
-    status = g_db_server_register_user(result, author, kfile);
-    if (!status) goto gdsni_error;
-
-    /* Paramètres de chiffrement */
-
-    working = get_cert_working_directory("servers", "standalone");
-
-    result->cert_file = build_absolute_filename(working, "server-cert.pem");
-    result->key_file = build_absolute_filename(working, "server-key.pem");
 
-    ret[0] = access(result->cert_file, R_OK);
-    ret[1] = access(result->key_file, R_OK);
+    result = g_object_new(G_TYPE_HUB_SERVER, NULL);
 
-    if (ret[0] != 0 || ret[1] != 0)
-    {
-        log_variadic_message(LMT_ERROR, _("The serveur certificate is missing in the '%s' directory!"), working);
-        log_simple_message(LMT_ERROR, _("Generate it using the menu (Tool > Identity) and restart the program."));
-
-        free(working);
-
-        goto gdsni_missing_pem;
-
-    }
-
-    free(working);
+    result->working = get_db_working_directory("servers", "standalone", NULL, NULL);
 
     /* Détermination du point d'écoute */
 
     result->domain = AF_UNIX;
 
-    status = build_tmp_socket("internal-server", &result->addr.unix_addr);
-    if (!status) goto gdsni_error;
+    status = build_internal_server_socket(&result->addr.unix_addr);
+    if (!status) goto sock_error;
 
     result->sock_len = sizeof(struct sockaddr_un);
 
@@ -324,17 +310,12 @@ GDbServer *g_db_server_new_internal(const char *author, char *kfile)
 
     /* Aide pour une sortie propre ? */
 
-    result->clean_socket = g_db_server_clean_internal_socket;
-
-    /* Répertoire de stockage */
-
-    result->basedir = get_xdg_config_dir("chrysalide" G_DIR_SEPARATOR_S "cdbs");
+    result->lock_socket = g_hub_server_lock_internal_socket;
+    result->unlock_socket = g_hub_server_unlock_internal_socket;
 
     return result;
 
- gdsni_missing_pem:
-
- gdsni_error:
+ sock_error:
 
     g_object_unref(G_OBJECT(result));
 
@@ -355,16 +336,42 @@ GDbServer *g_db_server_new_internal(const char *author, char *kfile)
 *                                                                             *
 ******************************************************************************/
 
-static bool g_db_server_clean_internal_socket(GDbServer *server)
+static bool g_hub_server_lock_internal_socket(GHubServer *server)
 {
     bool result;                            /* Bilan à faire remonter      */
     char *sock_path;                        /* Chemin vers le canal UNIX   */
+    char *lock_path;                        /* Chemin vers le verrou       */
     int ret;                                /* Bilan d'un appel            */
 
-    result = true;
+    result = false;
 
     sock_path = server->addr.unix_addr.sun_path;
 
+    /* Partie d'exclusivité */
+
+    lock_path = strdup(sock_path);
+    lock_path = stradd(lock_path, ".lock");
+
+    assert(server->lock_fd == -1);
+
+    server->lock_fd = open(lock_path, O_RDONLY | O_CREAT, 0600);
+    if (server->lock_fd == -1)
+    {
+        LOG_ERROR_N("open");
+        goto exit;
+    }
+
+    free(lock_path);
+
+    ret = flock(server->lock_fd, LOCK_EX | LOCK_NB);
+
+    result = (ret == 0);
+
+    if (!result)
+        goto exit;
+
+    /* Partie de nettoye */
+
     ret = access(sock_path, F_OK);
 
     if (ret == 0)
@@ -373,12 +380,24 @@ static bool g_db_server_clean_internal_socket(GDbServer *server)
 
         if (ret != 0)
         {
-            perror("unlink");
+            LOG_ERROR_N("unlink");
+
+            ret = flock(server->lock_fd, LOCK_UN);
+            if (ret == -1)
+                LOG_ERROR_N("flock");
+
+            ret = close(server->lock_fd);
+            if (ret == -1)
+                LOG_ERROR_N("close");
+
             result = false;
+
         }
 
     }
 
+ exit:
+
     return result;
 
 }
@@ -386,90 +405,164 @@ static bool g_db_server_clean_internal_socket(GDbServer *server)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : conf = fichier de configuration à interpréter.               *
+*  Paramètres  : server = instance à consulter et nettoyer.                   *
 *                                                                             *
-*  Description : Prépare un serveur de BD pour les clients distants.          *
+*  Description : Assure que le point de connexion est rendu disponible.       *
 *                                                                             *
-*  Retour      : Structure mise en place ou NULL en cas d'échec.              *
+*  Retour      : -                                                            *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-GDbServer *g_db_server_new_remote(const char *conf)
+static void g_hub_server_unlock_internal_socket(GHubServer *server)
 {
-    GDbServer *result;                      /* Adresse à retourner         */
+    int ret;                                /* Bilan d'un appel            */
+
+    assert(server->lock_fd != -1);
+
+    ret = flock(server->lock_fd, LOCK_UN);
+    if (ret == -1)
+        LOG_ERROR_N("flock");
 
-    char *host;
-    short port;
+    ret = close(server->lock_fd);
+    if (ret == -1)
+        LOG_ERROR_N("close");
+
+    server->lock_fd = -1;
+
+}
 
 
-    struct hostent *hp;                     /* Informations sur l'hôte     */
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : host = désignation du serveur à lancer.                      *
+*                port = port d'écoute à ouvrir.                               *
+*                ipv6 = adopte une préférence pour les adresses IPv6.         *
+*                                                                             *
+*  Description : Prépare un serveur de BD pour les clients distants.          *
+*                                                                             *
+*  Retour      : Structure mise en place ou NULL en cas d'échec.              *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GHubServer *g_hub_server_new_remote(const char *host, const char *port, bool ipv6)
+{
+    GHubServer *result;                     /* Adresse à retourner         */
+    struct addrinfo hints;                  /* Cadre de connexion souhaité */
+    struct addrinfo *available;             /* Cadres de connexion dispos  */
+    int ret;                                /* Bilan d'une consultation    */
+    struct addrinfo *iter;                  /* Boucle de parcours          */
     size_t desclen;                         /* Taille de désignation       */
+    struct sockaddr_in *addr4;              /* Adresse IPv4 brute          */
+    struct sockaddr_in6 *addr6;             /* Adresse IPv6 brute          */
     const char *ip;                         /* Adresse IPv4 ou IPv6        */
-    const char *tmpdir;                     /* Répertoire de travail       */
-    bool status;                            /* Bilan d'un consultation     */
 
-    result = g_object_new(G_TYPE_DB_SERVER, NULL);
+    result = g_object_new(G_TYPE_HUB_SERVER, NULL);
 
-    /* Chargement des profils */
+    assert(host != NULL);
 
+    if (port == NULL)
+        port = "1337";
 
-    /*
-    ret = g_db_server_register_user(result, author, kfile);
-    if (!ret) goto gdsni_error;
-    */
+    result->working = get_db_working_directory("servers", host, port, NULL);
 
-    result->domain = AF_INET;
+    /* Détermination du point d'écoute */
 
-    host = "localhost";
-    port = 9999;
+    memset(&hints, 0, sizeof(hints));
 
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_protocol = IPPROTO_TCP;
 
-    /* Détermination du point d'écoute */
+    ret = getaddrinfo(host, port, &hints, &available);
+    if (ret != 0)
+    {
+        LOG_ERROR_GAI_N("getaddrinfo", ret);
+        goto error;
+    }
+
+    result->domain = AF_UNSPEC;
 
-    hp = gethostbyname(host);
-    if (hp == NULL)
+    /**
+     * Premier tour : on essaie de se plier à la demande.
+     */
+
+    for (iter = available; iter != NULL && result->domain == AF_UNSPEC; iter = iter->ai_next)
     {
-        perror("gethostbyname");
-        goto gdsn_error;
+        if (ipv6 && iter->ai_family != AF_INET6)
+            continue;
+
+        if (!ipv6 && iter->ai_family != AF_INET)
+            continue;
+
+        result->domain = iter->ai_family;
+
+        memcpy(&result->addr.inet_4_6_addr, iter->ai_addr, iter->ai_addrlen);
+        result->sock_len = iter->ai_addrlen;
+
     }
 
-    result->addr.inet_addr.sin_family = hp->h_addrtype;
-    memcpy(&result->addr.inet_addr.sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
+    /**
+     * Second tour : on fait avec ce qu'on a.
+     */
 
-    result->addr.inet_addr.sin_port = htons(port);
+    for (iter = available; iter != NULL && result->domain == AF_UNSPEC; iter = iter->ai_next)
+    {
+        if (iter->ai_family != AF_INET6 && iter->ai_family != AF_INET)
+            continue;
 
-    result->sock_len = sizeof(struct sockaddr_in);
+        result->domain = iter->ai_family;
 
-    desclen = INET6_ADDRSTRLEN + 1 + 5 + 1;
-    result->desc = calloc(desclen, sizeof(char));
+        memcpy(&result->addr.inet_4_6_addr, iter->ai_addr, iter->ai_addrlen);
+        result->sock_len = iter->ai_addrlen;
 
-    ip = inet_ntop(AF_INET, &result->addr.inet_addr.sin_addr, result->desc, INET6_ADDRSTRLEN);
-    if (ip == NULL)
+    }
+
+    if (available != NULL)
+        freeaddrinfo(available);
+
+    if (result->domain == AF_UNSPEC)
     {
-        perror("inet_ntop");
-        goto gdsn_error;
+        log_variadic_message(LMT_ERROR, _("No suitable address found for %s:%s"), host, port);
+        goto error;
     }
 
     /* Désignation humaine */
 
-    snprintf(result->desc + strlen(ip), 1 + 5, ":%hu", port);
+    desclen = INET6_ADDRSTRLEN + 1 + strlen(port) + 1;
+    result->desc = calloc(desclen, sizeof(char));
 
-    /* Aide pour une sortie propre ? */
+    if (result->domain == AF_INET)
+    {
+        addr4 = (struct sockaddr_in *)&result->addr.inet_4_6_addr;
+        ip = inet_ntop(result->domain, &addr4->sin_addr, result->desc, INET6_ADDRSTRLEN);
+    }
 
-    result->clean_socket = NULL;
+    else if (result->domain == AF_INET6)
+    {
+        addr6 = (struct sockaddr_in6 *)&result->addr.inet_4_6_addr;
+        ip = inet_ntop(result->domain, &addr6->sin6_addr, result->desc, INET6_ADDRSTRLEN);
+    }
 
-    /* Répertoire de stockage */
+    else
+        assert(false);
 
-    status = g_generic_config_get_value(get_main_configuration(), MPK_TMPDIR, &tmpdir);
-    if (!status) goto gdsn_error;
+    if (ip == NULL)
+    {
+        LOG_ERROR_N("inet_ntop");
+        goto error;
+    }
 
-    result->basedir = strdup(tmpdir);
+    log_variadic_message(LMT_INFO, _("Resolved server IP: %s"), ip);
+
+    snprintf(result->desc + strlen(ip), 1 + strlen(port) + 1, ":%s", port);
 
     return result;
 
- gdsn_error:
+ error:
 
     g_object_unref(G_OBJECT(result));
 
@@ -480,112 +573,126 @@ GDbServer *g_db_server_new_remote(const char *conf)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : server = serveur pour les accès distants à manipuler.        *
+*  Paramètres  : preverify_ok = état de la prévalidation.                     *
+*                ctx          = contexte de la certification en cours.        *
 *                                                                             *
-*  Description : Supprime toute trace d'utilisateur inscrit.                  *
+*  Description : Vérifie la légitimité d'une connexion sécurisée au serveur.  *
 *                                                                             *
-*  Retour      : -                                                            *
+*  Retour      : 1 si la connexion est validée, 0 sinon.                      *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static void g_db_server_unregister_all_user(GDbServer *server)
+static int g_hub_server_verify(int preverify_ok, X509_STORE_CTX *ctx)
 {
-    size_t i;                               /* Boucle de parcours          */
+    int result;                             /* Bilan à retourner           */
+    X509 *peer_cert;                        /* Certificat côté client      */
+    SSL *ssl;                               /* Structure de connexion SLL  */
+    SSL_CTX *ssl_ctx;                       /* Contexte SSL du serveur     */
+    GHubServer *server;                     /* Serveur concerné            */
+    char *filename;                         /* Chemin d'accès reconstruit  */
+    FILE *stream;                           /* Flux ouvert en lecture      */
+    X509 *ca_cert;                          /* Certificat CA du serveur    */
+    int status;                             /* Bilan d'une comparaison     */
+    char *authorized_dir;                   /* Répertoire des autorisations*/
+    DIR *dir;                               /* Répertoire à parcourir      */
+    struct dirent *entry;                   /* Elément trouvé              */
+    X509 *authorized_cert;                  /* Certificat de client validé */
 
-    for (i = 0; i < server->users_count; i++)
-    {
-        exit_rle_string(&server->users[i].author);
-        free(server->users[i].key_file);
-    }
+    result = 0;
 
-    if (server->users != NULL)
-        free(server->users);
+    peer_cert = X509_STORE_CTX_get_current_cert(ctx);
 
-    server->users = NULL;
-    server->users_count = 0;
+    /* Récupération du serveur interne */
 
-}
+    ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+    ssl_ctx = SSL_get_SSL_CTX(ssl);
 
+    server = SSL_CTX_get_ex_data(ssl_ctx, _ssl_data_index);
+    assert(server != NULL);
 
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : server = serveur pour les accès distants à manipuler.        *
-*                author = utilisateur à représenter via le client.            *
-*                kfile  = clef menant à sa clef publique.                     *
-*                                                                             *
-*  Description : Inscrit un nouvel utilisateur comme étant enregistré.        *
-*                                                                             *
-*  Retour      : Bilan de l'inscription.                                      *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
+    /* Test du certificat d'autorité */
 
-static bool g_db_server_register_user(GDbServer *server, const char *author, char *kfile)
-{
-    if (strlen(author) == 0)
-        return false;
+    filename = strdup(server->working);
+    filename = stradd(filename, "ca-cert.pem");
 
-    server->users = realloc(server->users, ++server->users_count * sizeof(registered_user));
+    stream = fopen(filename, "rb");
+    if (stream == NULL) goto authorized;
 
-    dup_into_rle_string(&server->users[server->users_count - 1].author, author);
-    server->users[server->users_count - 1].key_file = kfile;
+    ca_cert = PEM_read_X509(stream, NULL, NULL, NULL);
 
-    return true;
+    fclose(stream);
 
-}
+    status = X509_cmp(peer_cert, ca_cert);
 
+    if (status == 0)
+        result = 1;
 
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : server = serveur pour les accès distants à manipuler.        *
-*                author = utilisateur à représenter via le client.            *
-*                sig    = signature d'utilisateur à valider.                  *
-*                                                                             *
-*  Description : Inscrit un nouvel utilisateur comme étant enregistré.        *
-*                                                                             *
-*  Retour      : true si l'utilisateur est bien enregistré, false sinon.      *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
+    X509_free(ca_cert);
 
-static bool g_db_server_is_registered_user(GDbServer *server, const rle_string *author, unsigned char *sig)
-{
-    bool result;                            /* Bilan à retourner           */
-    size_t i;                               /* Boucle de parcours          */
-    GChecksum *checksum;                    /* Empreinte MD5 à signer      */
-    unsigned char md5_digest[16];           /* Empreinte MD5 calculée      */
-    RSA *key;                               /* Clef pour la signature      */
+    free(filename);
 
-    result = false;
+    if (result == 1)
+        goto verified;
+
+    /* Détermination du répertoire des autorisations */
+
+ authorized:
+
+    authorized_dir = strdup(server->working);
+    authorized_dir = stradd(authorized_dir, "authorized" G_DIR_SEPARATOR_S);
 
-    /* Recherche de l'utilisateur */
+    dir = opendir(authorized_dir);
+    if (dir == NULL) goto verified;
+
+    /* Recherche d'une entrée de validation */
+
+    while (result == 0)
+    {
+        entry = readdir(dir);
+
+        if (entry == NULL)
+        {
+            if (errno != 0)
+                LOG_ERROR_N("readdir");
 
-    for (i = 0; i < server->users_count; i++)
-        if (cmp_rle_string(&server->users[i].author, author) == 0)
             break;
 
-    if (i == server->users_count)
-        goto gdsiru_exit;
+        }
+
+        if (entry->d_type != DT_REG && entry->d_type != DT_LNK) continue;
+        if (entry->d_name[0] == '.') continue;
+
+        filename = strdup(authorized_dir);
+        filename = stradd(filename, G_DIR_SEPARATOR_S);
+        filename = stradd(filename, entry->d_name);
+
+        stream = fopen(filename, "rb");
+        if (stream == NULL) goto next;
 
-    /* Validation de la signature présentée */
+        authorized_cert = PEM_read_X509(stream, NULL, NULL, NULL);
 
-    checksum = g_checksum_new(G_CHECKSUM_MD5);
-    g_checksum_update(checksum, (guchar *)get_rle_string(author), get_rle_length(author));
-    g_checksum_get_digest(checksum, (guint8 *)md5_digest, (gsize []) { sizeof(md5_digest) });
-    g_checksum_free(checksum);
+        fclose(stream);
 
-    key = load_rsa_key(server->users[i].key_file, false);
-    if (key == NULL) goto gdsiru_exit;
+        status = X509_cmp(peer_cert, authorized_cert);
 
-    result = verify_md5_hash(key, md5_digest, sig);
+        if (status == 0)
+            result = 1;
 
-    RSA_free(key);
+        X509_free(authorized_cert);
 
- gdsiru_exit:
+ next:
+
+        free(filename);
+
+    }
+
+    /* Sortie */
+
+    closedir(dir);
+
+ verified:
 
     return result;
 
@@ -604,7 +711,7 @@ static bool g_db_server_is_registered_user(GDbServer *server, const rle_string *
 *                                                                             *
 ******************************************************************************/
 
-static void *g_db_server_listener(GDbServer *server)
+static void *g_hub_server_listener(GHubServer *server)
 {
     struct pollfd fds;                      /* Surveillance des flux       */
     int ret;                                /* Bilan d'un appel            */
@@ -612,29 +719,37 @@ static void *g_db_server_listener(GDbServer *server)
     int fd;                                 /* Canal établi vers un client */
     SSL *tls_fd;                            /* Même canal, mais sécurisé   */
     rle_string hash;                        /* Empreinte du binaire visé   */
-    rle_string user;                        /* Nom d'utilisateur du client */
     const char *ip;                         /* Statut de la conversion     */
     char *peer_name;                        /* Désignation du correspondant*/
     DBError error;                          /* Validation de la connexion  */
     GCdbArchive *archive;                   /* Destinataire final du client*/
+    GList *iter;                            /* Boucle de parcours          */
     packed_buffer in_pbuf;                  /* Tampon de réception         */
     bool status;                            /* Bilan d'une opération       */
     uint32_t cmd;                           /* Commande initiale lue       */
     uint32_t version;                       /* Version du client lue       */
-    unsigned char sig[RSA_USED_SIZE];       /* Signature effectuée         */
-    GList *iter;                            /* Boucle de parcours          */
+    char *basedir;                          /* Répertoire de stockage      */
+    char *tmpdir;                           /* Répertoire de travail       */
     packed_buffer out_pbuf;                 /* Tampon d'émission           */
 
     fds.fd = server->fd;
     fds.events = POLLIN | POLLPRI;
 
-    while (1)
+    while (server->fd != -1)
     {
         ret = poll(&fds, 1, -1);
         if (ret != 1) continue;
 
         /* Le canal est fermé, une sortie doit être demandée... */
-        if (fds.revents & POLLHUP)
+        if (fds.revents & POLLNVAL)
+            break;
+
+        /**
+         * Même chose, cf. "TCP: When is EPOLLHUP generated?"
+         * https://stackoverflow.com/questions/52976152/tcp-when-is-epollhup-generated/52976327#52976327
+         */
+
+        if (fds.revents & (POLLHUP | POLLRDHUP))
             break;
 
         if (fds.revents & (POLLIN | POLLPRI))
@@ -642,7 +757,7 @@ static void *g_db_server_listener(GDbServer *server)
             fd = accept(server->fd, (struct sockaddr *)&peer, (socklen_t []) { sizeof(gen_sockaddr_t) });
             if (fd == -1)
             {
-                perror("accept");
+                LOG_ERROR_N("accept");
                 continue;
             }
 
@@ -667,7 +782,6 @@ static void *g_db_server_listener(GDbServer *server)
             /* Initialisation à vide pour les sorties en erreur */
 
             init_dynamic_rle_string(&hash, NULL);
-            init_dynamic_rle_string(&user, NULL);
 
             /* Construction d'une représentation */
 
@@ -678,14 +792,29 @@ static void *g_db_server_listener(GDbServer *server)
             {
                 peer_name = calloc(INET6_ADDRSTRLEN + 1 + 5 + 1, sizeof(char));
 
-                ip = inet_ntop(AF_INET, &peer.inet_addr.sin_addr, peer_name, INET6_ADDRSTRLEN);
+                ip = inet_ntop(AF_INET, &peer.inet4_addr.sin_addr, peer_name, INET6_ADDRSTRLEN);
+                if (ip == NULL)
+                {
+                    LOG_ERROR_N("inet_ntop");
+                    goto id_error;
+                }
+
+                snprintf(peer_name + strlen(ip), 1 + 5, ":%hu", ntohs(peer.inet4_addr.sin_port));
+
+            }
+
+            else if (*((sa_family_t *)&peer) == AF_INET6)
+            {
+                peer_name = calloc(INET6_ADDRSTRLEN + 1 + 5 + 1, sizeof(char));
+
+                ip = inet_ntop(AF_INET6, &peer.inet6_addr.sin6_addr, peer_name, INET6_ADDRSTRLEN);
                 if (ip == NULL)
                 {
-                    perror("inet_ntop");
+                    LOG_ERROR_N("inet_ntop");
                     goto id_error;
                 }
 
-                snprintf(peer_name + strlen(ip), 1 + 5, ":%hu", ntohs(peer.inet_addr.sin_port));
+                snprintf(peer_name + strlen(ip), 1 + 5, ":%hu", ntohs(peer.inet6_addr.sin6_port));
 
             }
 
@@ -695,13 +824,13 @@ static void *g_db_server_listener(GDbServer *server)
             error = DBE_NONE;
             archive = NULL;
 
+            iter = NULL;
+
             /**
              * Le premier "paquet" reçu de la part d'un client doit contenir les informations suivantes :
              *    - la commande 'DBC_HELO'.
              *    - le numéro de version du client.
              *    - l'empreinte du binaire analysé.
-             *    - l'identifiant de l'utilisateur effectuant des modifications.
-             *    - la signature de l'empreinte MD5 de l'identifiant.
              *
              * Tout ceci est à synchroniser avec la fonction g_db_client_start().
              */
@@ -744,24 +873,6 @@ static void *g_db_server_listener(GDbServer *server)
                 goto error_sending;
             }
 
-            status = unpack_rle_string(&user, &in_pbuf);
-            if (!status)
-            {
-                log_variadic_message(LMT_ERROR, _("Error while getting the user name from '%s'..."),
-                                     peer_name);
-                error = DBE_BAD_EXCHANGE;
-                goto error_sending;
-            }
-
-            status = extract_packed_buffer(&in_pbuf, sig, RSA_USED_SIZE, false);
-            if (!status)
-            {
-                log_variadic_message(LMT_ERROR, _("Error while getting the signature from '%s'..."),
-                                     peer_name);
-                error = DBE_BAD_EXCHANGE;
-                goto error_sending;
-            }
-
             if (cmd != DBC_HELO)
             {
                 log_variadic_message(LMT_ERROR, _("The client from '%s' did not introduce itself!"),
@@ -786,21 +897,6 @@ static void *g_db_server_listener(GDbServer *server)
                 goto error_sending;
             }
 
-            if (is_rle_string_empty(&user))
-            {
-                log_variadic_message(LMT_ERROR, _("No user is associated with the client from '%s'..."),
-                                     peer_name);
-                error = DBE_BAD_EXCHANGE;
-                goto error_sending;
-            }
-
-            if (!g_db_server_is_registered_user(server, &user, sig))
-            {
-                log_variadic_message(LMT_ERROR, _("This user is not registered."));
-                error = DBE_BAD_EXCHANGE;
-                goto error_sending;
-            }
-
             /**
              * On met en place le maximum ici, de manière à pouvoir indiquer une erreur
              * en cas d'échec, et être le plus précis possible dans la courte réponse.
@@ -818,7 +914,19 @@ static void *g_db_server_listener(GDbServer *server)
             }
 
             if (iter == NULL)
-                archive = g_cdb_archive_new(server->basedir, &hash, &user, &error);
+            {
+                basedir = strdup(server->working);
+                basedir = stradd(basedir, "cdbs" G_DIR_SEPARATOR_S);
+
+                tmpdir = strdup(server->working);
+                tmpdir = stradd(tmpdir, "tmp" G_DIR_SEPARATOR_S);
+
+                archive = g_cdb_archive_new(basedir, tmpdir, &hash, &error);
+
+                free(tmpdir);
+                free(basedir);
+
+            }
 
             /**
              * Le serveur doit répondre pour un message type :
@@ -856,14 +964,13 @@ static void *g_db_server_listener(GDbServer *server)
                 if (iter == NULL)
                     server->archives = g_list_append(server->archives, archive);
 
-                g_cdb_archive_add_client(archive, tls_fd, &user);
+                g_cdb_archive_add_client(archive, tls_fd);
 
                 exit_packed_buffer(&out_pbuf);
 
                 free(peer_name);
 
                 exit_rle_string(&hash);
-                exit_rle_string(&user);
 
                 continue;
 
@@ -884,7 +991,6 @@ static void *g_db_server_listener(GDbServer *server)
             free(peer_name);
 
             exit_rle_string(&hash);
-            exit_rle_string(&user);
 
  invalid_conn:
 
@@ -898,6 +1004,8 @@ static void *g_db_server_listener(GDbServer *server)
 
     }
 
+    g_hub_server_stop(server);
+
     return NULL;
 
 }
@@ -905,7 +1013,9 @@ static void *g_db_server_listener(GDbServer *server)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : server = serveur pour les accès distants à manipuler.        *
+*  Paramètres  : server  = serveur pour les accès distants à manipuler.       *
+*                backlog = nombre de connexions maximal.                      *
+*                keep    = conservation du serveur en avant plan.             *
 *                                                                             *
 *  Description : Démarre le serveur de base de données.                       *
 *                                                                             *
@@ -915,12 +1025,16 @@ static void *g_db_server_listener(GDbServer *server)
 *                                                                             *
 ******************************************************************************/
 
-bool g_db_server_start(GDbServer *server)
+ServerStartStatus g_hub_server_start(GHubServer *server, int backlog, bool keep)
 {
+    ServerStartStatus result;               /* Bilan à retourner           */
     const SSL_METHOD *method;               /* Mode du canal sécurisé      */
+    char *filename;                         /* Fichier PEM à manipuler     */
     int ret;                                /* Bilan d'un appel            */
+    STACK_OF(X509_NAME) *ca_cert;           /* Certificat de l'autorité    */
     bool status;                            /* Bilan d'un nettoyage        */
-    int backlog;                            /* Nombre de connexions maximal*/
+
+    result = SSS_FAILURE;
 
     /* Définition d'un environnement TLS */
 
@@ -934,7 +1048,12 @@ bool g_db_server_start(GDbServer *server)
         goto quick_error;
     }
 
-    ret = SSL_CTX_use_certificate_file(server->tls_ctx, server->cert_file, SSL_FILETYPE_PEM);
+    filename = strdup(server->working);
+    filename = stradd(filename, "server-cert.pem");
+
+    ret = SSL_CTX_use_certificate_file(server->tls_ctx, filename, SSL_FILETYPE_PEM);
+
+    free(filename);
 
     if (ret != 1)
     {
@@ -942,7 +1061,12 @@ bool g_db_server_start(GDbServer *server)
         goto tls_error;
     }
 
-    ret = SSL_CTX_use_PrivateKey_file(server->tls_ctx, server->key_file, SSL_FILETYPE_PEM);
+    filename = strdup(server->working);
+    filename = stradd(filename, "server-key.pem");
+
+    ret = SSL_CTX_use_PrivateKey_file(server->tls_ctx, filename, SSL_FILETYPE_PEM);
+
+    free(filename);
 
     if (ret != 1)
     {
@@ -958,52 +1082,99 @@ bool g_db_server_start(GDbServer *server)
         goto tls_error;
     }
 
+    /* Validation des certificats */
+
+    if (_ssl_data_index == -1)
+    {
+        _ssl_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+        assert(_ssl_data_index != -1);
+    }
+
+    ret = SSL_CTX_set_ex_data(server->tls_ctx, _ssl_data_index, server);
+    if (ret != 1)
+    {
+        LOG_ERROR_OPENSSL;
+        goto tls_error;
+    }
+
+    SSL_CTX_set_verify(server->tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, g_hub_server_verify);
+
+    filename = strdup(server->working);
+    filename = stradd(filename, "ca-cert.pem");
+
+    ca_cert = SSL_load_client_CA_file(filename);
+
+    free(filename);
+
+    if (ca_cert == NULL)
+    {
+        LOG_ERROR_OPENSSL;
+        goto tls_error;
+    }
+
+    SSL_CTX_set_client_CA_list(server->tls_ctx, ca_cert);
+
     /* Mise en place d'un canal de communication */
 
     server->fd = socket(server->domain, SOCK_STREAM, 0);
     if (server->fd == -1)
     {
-        perror("socket");
+        LOG_ERROR_N("socket");
         return false;
     }
 
     ret = setsockopt(server->fd, SOL_SOCKET, SO_REUSEADDR, (int []) { 1 }, sizeof(int));
     if (ret == -1)
     {
-        perror("setsockopt");
-        goto gdss_error;
+        LOG_ERROR_N("setsockopt");
+        goto network_error;
     }
 
-    if (server->clean_socket != NULL)
+    if (server->lock_socket != NULL)
     {
-        status = server->clean_socket(server);
-        if (!status) goto gdss_error;
+        status = server->lock_socket(server);
+        if (!status)
+        {
+            result = SSS_ALREADY_RUNNING;
+            goto network_error;
+        }
+
     }
 
     ret = bind(server->fd, (struct sockaddr *)&server->addr, server->sock_len);
     if (ret == -1)
     {
-        perror("bind");
-        goto gdss_error;
+        LOG_ERROR_N("bind");
+        goto network_error;
     }
 
-    if (!g_generic_config_get_value(get_main_configuration(), MPK_SERVER_BACKLOG, &backlog))
-        goto gdss_error;
-
     ret = listen(server->fd, backlog);
     if (ret == -1)
     {
-        perror("listen");
-        goto gdss_error;
+        LOG_ERROR_N("listen");
+        goto network_error;
     }
 
-    server->listener = g_thread_new("cdb_listener", (GThreadFunc)g_db_server_listener, server);
+    if (!keep)
+    {
+        ret = daemon(1, 1);
+        if (ret != 0)
+        {
+            LOG_ERROR_N("daemon");
+            goto network_error;
+        }
+
+    }
+
+    server->listener = g_thread_new("cdb_listener", (GThreadFunc)g_hub_server_listener, server);
 
     log_variadic_message(LMT_PROCESS, _("Server started and listening at %s"), server->desc);
 
-    return true;
+    result = SSS_SUCCESS;
 
- gdss_error:
+    return result;
+
+ network_error:
 
     close(server->fd);
     server->fd = -1;
@@ -1015,7 +1186,30 @@ bool g_db_server_start(GDbServer *server)
 
  quick_error:
 
-    return false;
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : server = serveur pour les accès distants à consulter.        *
+*                                                                             *
+*  Description : Attend l'arrête du serveur de base de données.               *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_hub_server_wait_for_stop(GHubServer *server)
+{
+    g_mutex_lock(&server->wait_mutex);
+
+    g_cond_wait(&server->wait_cond, &server->wait_mutex);
+
+    g_mutex_unlock(&server->wait_mutex);
 
 }
 
@@ -1032,34 +1226,52 @@ bool g_db_server_start(GDbServer *server)
 *                                                                             *
 ******************************************************************************/
 
-void g_db_server_stop(GDbServer *server)
+void g_hub_server_stop(GHubServer *server)
 {
+    int fd;                                 /* Canal à clôturer            */
     int ret;                                /* Bilan d'un appel            */
 
     /* Canal de communication */
 
     if (server->fd == -1)
     {
-        assert(server->tls_ctx == NULL);
+        /**
+         * Si la fermture est forcée, le thread de traitement va terminer en erreur.
+         * Donc cette fonction sera appelée deux fois. Seule la première va affecter
+         * le contexte, donc on le peut pas s'assurer de la condition suivante dans
+         * tous les cas.
+         */
+
+        /*assert(client->tls_ctx == NULL);*/
         return;
     }
 
-    ret = shutdown(server->fd, SHUT_RDWR);
-    if (ret == -1) perror("shutdown");
+    fd = server->fd;
 
-    g_thread_join(server->listener);
+    server->fd = -1;
 
-    ret = close(server->fd);
-    if (ret == -1) perror("close");
+    ret = shutdown(fd, SHUT_RDWR);
+    if (ret == -1) LOG_ERROR_N("shutdown");
 
-    server->fd = -1;
+    ret = close(fd);
+    if (ret == -1) LOG_ERROR_N("close");
+
+    g_thread_join(server->listener);
+
+    /* Verrou d'accès */
 
-    if (server->clean_socket != NULL)
-        server->clean_socket(server);
+    if (server->unlock_socket != NULL)
+        server->unlock_socket(server);
 
     /* Environnement TLS */
 
     SSL_CTX_free(server->tls_ctx);
     server->tls_ctx = NULL;
 
+    /* Fin de service */
+
+    g_mutex_lock(&server->wait_mutex);
+    g_cond_signal(&server->wait_cond);
+    g_mutex_unlock(&server->wait_mutex);
+
 }
diff --git a/src/analysis/db/server.h b/src/analysis/db/server.h
index 5be7886..44aef54 100644
--- a/src/analysis/db/server.h
+++ b/src/analysis/db/server.h
@@ -30,35 +30,47 @@
 
 
 
-#define G_TYPE_DB_SERVER            g_db_server_get_type()
-#define G_DB_SERVER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_DB_SERVER, GDbServer))
-#define G_IS_DB_SERVER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_DB_SERVER))
-#define G_DB_SERVER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DB_SERVER, GDbServerClass))
-#define G_IS_DB_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DB_SERVER))
-#define G_DB_SERVER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DB_SERVER, GDbServerClass))
+#define G_TYPE_HUB_SERVER            g_hub_server_get_type()
+#define G_HUB_SERVER(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_HUB_SERVER, GHubServer))
+#define G_IS_HUB_SERVER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_HUB_SERVER))
+#define G_HUB_SERVER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_HUB_SERVER, GHubServerClass))
+#define G_IS_HUB_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_HUB_SERVER))
+#define G_HUB_SERVER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_HUB_SERVER, GHubServerClass))
 
 
 /* Description de serveur à l'écoute (instance) */
-typedef struct _GDbServer GDbServer;
+typedef struct _GHubServer GHubServer;
 
 /* Description de serveur à l'écoute (classe) */
-typedef struct _GDbServerClass GDbServerClass;
+typedef struct _GHubServerClass GHubServerClass;
 
 
 /* Indique le type défini pour une description de serveur à l'écoute. */
-GType g_db_server_get_type(void);
+GType g_hub_server_get_type(void);
 
 /* Prépare un serveur de BD pour les clients internes. */
-GDbServer *g_db_server_new_internal(const char *, char *);
+GHubServer *g_hub_server_new_internal(void);
 
 /* Prépare un serveur de BD pour les clients distants. */
-GDbServer *g_db_server_new_remote(const char *);
+GHubServer *g_hub_server_new_remote(const char *, const char *, bool);
+
+/* Bilan du lancement d'un serveur */
+typedef enum _ServerStartStatus
+{
+    SSS_FAILURE,                            /* Echec du démarrage          */
+    SSS_SUCCESS,                            /* Serveur démarré             */
+    SSS_ALREADY_RUNNING,                    /* Instance déjà en place      */
+
+} ServerStartStatus;
 
 /* Démarre le serveur de base de données. */
-bool g_db_server_start(GDbServer *);
+ServerStartStatus g_hub_server_start(GHubServer *, int, bool);
+
+/* Attend l'arrête du serveur de base de données. */
+void g_hub_server_wait_for_stop(GHubServer *);
 
 /* Arrête le serveur de base de données. */
-void g_db_server_stop(GDbServer *);
+void g_hub_server_stop(GHubServer *);
 
 
 
diff --git a/src/common/io.c b/src/common/io.c
index 2a68649..754b8a9 100644
--- a/src/common/io.c
+++ b/src/common/io.c
@@ -25,6 +25,7 @@
 
 
 #include <errno.h>
+#include <fcntl.h>
 #include <libgen.h>
 #include <malloc.h>
 #include <stdint.h>
@@ -387,60 +388,75 @@ int make_tmp_file(const char *prefix, const char *suffix, char **filename)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : base = préfixe du nom du fichier temporaire à créer.         *
-*                addr = adresse UNIX constituée. [OUT]                        *
+*  Paramètres  : dest = fichier de destination de la copie.                   *
+*                src  = fichier source à copier.                              *
 *                                                                             *
-*  Description : Met en place un canal UNIX temporaire.                       *
+*  Description : Copie un fichier.                                            *
 *                                                                             *
-*  Retour      : Bilan de la définition.                                      *
+*  Retour      : Bilan de l'opération.                                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-bool build_tmp_socket(const char *base, struct sockaddr_un *addr)
+bool copy_file(const char *dest, const char *src)
 {
     bool result;                            /* Bilan à retourner           */
-    const char *tmpdir;                     /* Répertoire d'accueil        */
-    bool status;                            /* Bilan d'un consultation     */
-    char *path;                             /* Chemin d'accès au canal     */
-    int ret;                                /* Bilan intermédiaire         */
-    size_t length;                          /* Taille du chemin complet    */
-
-    status = g_generic_config_get_value(get_main_configuration(), MPK_TMPDIR, &tmpdir);
-    if (!status) return false;
+    int fd;                                 /* Descripteur du fichier      */
+    struct stat info;                       /* Informations sur le fichier */
+    int ret;                                /* Bilan d'un appel            */
+    void *data;                             /* Quantité de données traitées*/
+    bool status;                            /* Bilan de la lecture         */
 
     result = false;
 
-    asprintf(&path, "%s" G_DIR_SEPARATOR_S "%s-%d", tmpdir, base, getpid());
+    /* Côté source */
 
-    ret = ensure_path_exists(path);
-    if (ret != 0) goto mts_exit;
+    fd = open(src, O_RDONLY);
+    if (fd == -1)
+    {
+        LOG_ERROR_N("open");
+        goto exit;
+    }
 
-    length = strlen(path) + 1;
+    ret = fstat(fd, &info);
+    if (ret == -1)
+    {
+        LOG_ERROR_N("fstat");
+        goto done;
+    }
 
-#ifndef UNIX_PATH_MAX
-#   define UNIX_PATH_MAX 108
-#endif
+    data = malloc(info.st_size);
 
-    if (length > UNIX_PATH_MAX)
+    status = safe_read(fd, data, info.st_size);
+    if (!status) goto clean;
+
+    close(fd);
+
+    /* Côté destination */
+
+    fd = open(dest, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+    if (fd == -1)
     {
-        log_variadic_message(LMT_ERROR,
-                             _("Impossible to use '%s' as UNIX socket path: string is too long ! (%zu vs %u)\n"),
-                             path, length, UNIX_PATH_MAX);
-        goto mts_exit;
+        LOG_ERROR_N("open");
+        free(data);
+        goto exit;
     }
 
-    memset(addr, 0, sizeof(struct sockaddr_un));
-
-    addr->sun_family = AF_UNIX;
-    strncpy(addr->sun_path, path, UNIX_PATH_MAX - 1);
+    status = safe_write(fd, data, info.st_size);
+    if (!status) goto clean;
 
     result = true;
 
- mts_exit:
+ clean:
+
+    free(data);
+
+ done:
+
+    close(fd);
 
-    free(path);
+ exit:
 
     return result;
 
diff --git a/src/common/io.h b/src/common/io.h
index 3639d98..b67359b 100644
--- a/src/common/io.h
+++ b/src/common/io.h
@@ -27,8 +27,6 @@
 
 #include <stdbool.h>
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
 
 
 
@@ -53,8 +51,8 @@ int ensure_path_exists(const char *);
 /* Met en place un fichier temporaire. */
 int make_tmp_file(const char *, const char *, char **);
 
-/* Met en place un canal UNIX temporaire. */
-bool build_tmp_socket(const char *, struct sockaddr_un *);
+/* Copie un fichier. */
+bool copy_file(const char *, const char *);
 
 
 
diff --git a/src/core/logs.h b/src/core/logs.h
index afdcfa2..4ddb6ab 100644
--- a/src/core/logs.h
+++ b/src/core/logs.h
@@ -26,6 +26,7 @@
 
 
 #include <errno.h>
+#include <netdb.h>
 #include <stdarg.h>
 #include <string.h>
 #include <openssl/err.h>
@@ -87,6 +88,19 @@ void log_variadic_message(LogMessageType, const char *, ...);
     }                                                                                                   \
     while (0)
 
+#define LOG_ERROR_GAI_N(func, errcode)                                                                  \
+    do                                                                                                  \
+    {                                                                                                   \
+        char __msg[1024];                                                                               \
+        const char *__msg_ptr;                                                                          \
+        if (errcode == EAI_SYSTEM)                                                                      \
+            __msg_ptr = strerror_r(errno, __msg, sizeof(__msg));                                        \
+        else                                                                                            \
+            __msg_ptr = gai_strerror(errcode);                                                          \
+        log_variadic_message(LMT_EXT_ERROR, "[%s:%u] %s: %s", __FUNCTION__, __LINE__, func, __msg_ptr); \
+    }                                                                                                   \
+    while (0)
+
 #define LOG_ERROR_OPENSSL                                                                               \
     do                                                                                                  \
     {                                                                                                   \
diff --git a/src/gui/panels/history.c b/src/gui/panels/history.c
index 4b694c7..4383ed6 100644
--- a/src/gui/panels/history.c
+++ b/src/gui/panels/history.c
@@ -599,7 +599,7 @@ static void do_history_undo(GtkButton *button, GHistoryPanel *panel)
     GtkTreeModel *model;                    /* Modèle de gestion de données*/
     GtkTreeIter iter;                       /* Pointeur vers la ligne visée*/
     GDbItem *item;                          /* Elément de collection       */
-    GDbClient *client;                      /* Connexion vers la base      */
+    GHubClient *client;                     /* Connexion vers la base      */
 
     builder = G_PANEL_ITEM(panel)->builder;
 
@@ -615,7 +615,7 @@ static void do_history_undo(GtkButton *button, GHistoryPanel *panel)
         gtk_tree_model_get(model, &iter, HTC_ITEM, &item, -1);
 
         client = g_loaded_binary_get_db_client(panel->binary);
-        g_db_client_set_last_active(client, g_db_item_get_timestamp(item));
+        g_hub_client_set_last_active(client, g_db_item_get_timestamp(item));
         g_object_unref(G_OBJECT(client));
 
         g_object_unref(G_OBJECT(item));
@@ -646,7 +646,7 @@ static void do_history_redo(GtkButton *button, GHistoryPanel *panel)
     GtkTreeModel *model;                    /* Modèle de gestion de données*/
     GtkTreeIter iter;                       /* Pointeur vers la ligne visée*/
     GDbItem *item;                          /* Elément de collection       */
-    GDbClient *client;                      /* Connexion vers la base      */
+    GHubClient *client;                     /* Connexion vers la base      */
 
     builder = G_PANEL_ITEM(panel)->builder;
 
@@ -659,7 +659,7 @@ static void do_history_redo(GtkButton *button, GHistoryPanel *panel)
         gtk_tree_model_get(model, &iter, HTC_ITEM, &item, -1);
 
         client = g_loaded_binary_get_db_client(panel->binary);
-        g_db_client_set_last_active(client, g_db_item_get_timestamp(item));
+        g_hub_client_set_last_active(client, g_db_item_get_timestamp(item));
         g_object_unref(G_OBJECT(client));
 
         g_object_unref(G_OBJECT(item));
diff --git a/src/hub.c b/src/hub.c
new file mode 100644
index 0000000..916aea5
--- /dev/null
+++ b/src/hub.c
@@ -0,0 +1,1036 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * hub.c - fichier d'entrée du centre de collecte
+ *
+ * Copyright (C) 2019 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <getopt.h>
+#include <libgen.h>
+#include <malloc.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <gtk/gtk.h>
+
+
+#include <config.h>
+#include <i18n.h>
+
+
+#include "gleak.h"
+#include "analysis/db/auth.h"
+#include "analysis/db/server.h"
+#include "core/global.h"
+#include "core/logs.h"
+
+
+
+/* Liste des commandes principales */
+typedef enum _HubMainCommand
+{
+    HMC_NONE,                               /* Absence de commande         */
+    HMC_CLIENT_ID,                          /* Création d'une identité     */
+    HMC_SERVER_ID,                          /* Création d'une identité     */
+    HMC_ADD_CLIENT,                         /* Enregistrement d'utilisateur*/
+    HMC_RUN                                 /* Lancement d'un serveur      */
+
+} HubMainCommand;
+
+
+/* Affiche des indications quant à l'utilisation du programme. */
+static void show_hub_help(const char *);
+
+/* Affiche des indications sur la version courante du programme. */
+static void show_hub_version(void);
+
+/* Construit une identité selon les indications fournies. */
+static int parse_identity_properties(const char *, x509_entries *);
+
+/* Traite la commande "client-id" et ses arguments. */
+static int exec_cmd_client_identity(int, char **);
+
+/* Traite la commande "server-id" et ses arguments. */
+static int exec_cmd_server_identity(int, char **);
+
+/* Traite la commande "add-client" et ses arguments. */
+static int exec_cmd_add_client(int, char **);
+
+/* Traite la commande "run" et ses arguments. */
+static int exec_cmd_run_server(int, char **);
+
+
+/* Serveur pour les enregistrements en base */
+static GHubServer *_server = NULL;
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : name = nom du programme en question.                         *
+*                                                                             *
+*  Description : Affiche des indications quant à l'utilisation du programme.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void show_hub_help(const char *name)
+{
+    char *tmp;                              /* Conservation modifiable     */
+    char *base;                             /* Version courte du nom       */
+
+    tmp = strdup(name);
+
+    base = basename(tmp);
+
+    printf("\n");
+
+    printf("Usage: %s [--help] [--version] [--verbosity] <cmd> [options]\n", base);
+
+    printf("\n");
+
+    printf("\t-h --help\t\tShow this help message.\n");
+    printf("\t-v --version\t\tDisplay the program version.\n");
+
+    printf("\n");
+
+    printf("\t-V --verbosity=level\tSet the log level (0 for all messages, %u for none).\n", LMT_COUNT);
+
+    printf("\n");
+
+    printf("Command client-id:\n");
+    printf("------------------\n");
+
+    printf("\n");
+
+    printf("Usage: %s client-id [--help] [--version] [--verbosity] [--long <integer>] <fields>\n",
+           base);
+
+    printf("\n");
+
+    printf("\t-l --long=integer\tProvide the validity time of the certicate, in seconds (default: 3 years)\n");
+    printf("\tfields\t\t\tCertificate's subject fields, as comma-separated key=value pairs.\n");
+
+    printf("\n");
+
+    printf("Command server-id:\n");
+    printf("------------------\n");
+
+    printf("\n");
+
+    printf("Usage: %s server-id [--help] [--version] [--verbosity] [--name <string>] [--port <integer>] [--long <integer>] <fields>\n",
+           base);
+
+    printf("\n");
+
+    printf("\t-n --name=string\tDefine the name of the server to reach (default: standalone)\n");
+    printf("\t-p --port=integer\tSpecify the listening port of this server (default: 1337)\n");
+    printf("\t-l --long=integer\tProvide the validity time of the certicate, in seconds (default: 3 years)\n");
+    printf("\tfields\t\t\tCertificate's subject fields, as comma-separated key=value pairs.\n");
+
+    printf("\n");
+
+    printf("Command add-client:\n");
+    printf("-------------------\n");
+
+    printf("\n");
+
+    printf("Usage: %s add-client [--help] [--version] [--verbosity] [--name <string>] [--port <integer>] [--long <integer>] <csr> <outdir>\n",
+           base);
+
+    printf("\n");
+
+    printf("\t-n --name=string\tDefine the name of the server to reach (default: standalone)\n");
+    printf("\t-p --port=integer\tSpecify the listening port of this server (default: 1337)\n");
+    printf("\t-l --long=integer\tProvide the validity time of the certicate, in seconds (default: 3 years)\n");
+    printf("\tcsr\t\t\tCertificate Signing Request file to use in order to give an authorized access to server.\n");
+    printf("\toutdir\t\t\tOutput directory for the signed certificate and the copied server CA, for the client side.\n");
+
+    printf("\n");
+
+    printf("Command run:\n");
+    printf("------------\n");
+
+    printf("\n");
+
+    printf("Usage: %s add-client [--help] [--version] [--verbosity] [--name <string>] [--port <integer>] [--backlog <integer>]\n",
+           base);
+
+    printf("\n");
+
+    printf("\t-n --name=string\tDefine the name of the server to reach (default: standalone)\n");
+    printf("\t-p --port=integer\tSpecify the listening port of this server (default: 1337)\n");
+    printf("\t-4 --ipv4=integer\tPrefer using an IPv4 address if possible (IPv6 by default)\n");
+    printf("\t-b --backlog=integer\tSet the maximum number of incoming connections (default: 10)\n");
+
+    printf("\n");
+
+    free(tmp);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Affiche des indications sur la version courante du programme.*
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void show_hub_version(void)
+{
+    printf("\n");
+
+    printf("-o-  Chrysalide Hub r%u  -o-\n", REVISION);
+    printf(_("Last compiled on %s at %s\n"), __DATE__, __TIME__);
+
+    printf("\n");
+
+    printf(_("Pictures directory: %s\n"), PIXMAPS_DIR);
+    printf(_("Themes directory: %s\n"), THEMES_DIR);
+    printf(_("Plugins library directory: %s\n"), PLUGINS_LIB_DIR);
+    printf(_("Plugins data directory: %s\n"), PLUGINS_DATA_DIR);
+    printf(_("Locale directory: %s\n"), LOCALE_DIR);
+
+    printf("\n");
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : argc = nombre d'arguments dans la ligne de commande.         *
+*                argv = arguments de la ligne de commande.                    *
+*                                                                             *
+*  Description : Point d'entrée du programme.                                 *
+*                                                                             *
+*  Retour      : EXIT_SUCCESS si le prgm s'est déroulé sans encombres.        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int main(int argc, char **argv)
+{
+    int result;                             /* Bilan de l'exécution        */
+    HubMainCommand command;                 /* Commande à satisfaire       */
+    bool show_help;                         /* Affichage de l'aide ?       */
+    bool show_version;                      /* Affichage de la version ?   */
+    LogMessageType verbosity;               /* Niveau de filtre de message */
+    int index;                              /* Indice d'argument           */
+    int ret;                                /* Bilan d'un appel            */
+
+    static struct option long_options[] = {
+        { "help",       no_argument,        NULL,   'h' },
+        { "version",    no_argument,        NULL,   'v' },
+        { "verbosity",  required_argument,  NULL,   'V' },
+        { NULL,         0,                  NULL,   0 }
+    };
+
+    result = EXIT_FAILURE;
+
+    /* Décodage de la commande principale */
+
+    command = HMC_NONE;
+
+    if (argc >= 2)
+    {
+        if (strcmp(argv[1], "client-id") == 0)
+            command = HMC_CLIENT_ID;
+
+        else if (strcmp(argv[1], "server-id") == 0)
+            command = HMC_SERVER_ID;
+
+        else if (strcmp(argv[1], "add-client") == 0)
+            command = HMC_ADD_CLIENT;
+
+        else if (strcmp(argv[1], "run") == 0)
+            command = HMC_RUN;
+
+    }
+
+    /* Décodage des options */
+
+    show_help = false;
+    show_version = false;
+
+    verbosity = LMT_INFO;
+
+    if (command == HMC_NONE)
+        while (true)
+        {
+            ret = getopt_long(argc, argv, "hvV:", long_options, &index);
+            if (ret == -1) break;
+
+            switch (ret)
+            {
+                case 'h':
+                    show_help = true;
+                    break;
+
+                case 'v':
+                    show_version = true;
+                    break;
+
+                case 'V':
+                    verbosity = strtoul(optarg, NULL, 10);
+                    break;
+
+            }
+
+        }
+
+    /* Actions de base */
+
+    if (show_help)
+    {
+        show_hub_help(argv[0]);
+        result = EXIT_SUCCESS;
+        goto done;
+    }
+
+    if (show_version)
+    {
+        show_hub_version();
+        result = EXIT_SUCCESS;
+        goto done;
+    }
+
+    /* Lancement des choses sérieuses */
+
+    setlocale(LC_ALL, "");
+    bindtextdomain(PACKAGE, LOCALE_DIR);
+    textdomain(PACKAGE);
+
+    /* Initialisation de GTK */
+    g_set_prgname("Chrysalide Hub");
+    setlocale (LC_ALL, "");
+    gtk_init(&argc, &argv);
+
+    /* Initialisation du programme */
+
+    set_batch_mode();
+
+    set_log_verbosity(verbosity);
+
+    /* Traitement des commandes */
+
+    switch (command)
+    {
+        case HMC_CLIENT_ID:
+            result = exec_cmd_client_identity(argc, argv);
+            break;
+
+        case HMC_SERVER_ID:
+            result = exec_cmd_server_identity(argc, argv);
+            break;
+
+        case HMC_ADD_CLIENT:
+            result = exec_cmd_add_client(argc, argv);
+            break;
+
+        case HMC_RUN:
+            result = exec_cmd_run_server(argc, argv);
+            break;
+
+        default:
+            show_hub_help(argv[0]);
+            goto done;
+            break;
+
+    }
+
+#ifdef TRACK_GOBJECT_LEAKS
+    remember_gtypes_for_leaks();
+#endif
+
+#ifdef TRACK_GOBJECT_LEAKS
+    dump_remaining_gtypes();
+#endif
+
+ done:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : properties = propriétés brutes à convertir.                  *
+*                identity   = éléments de l'identité à définir. [OUT]         *
+*                                                                             *
+*  Description : Construit une identité selon les indications fournies.       *
+*                                                                             *
+*  Retour      : Bilan de l'opération : EXIT_SUCCES ou un indicatif d'erreur. *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int parse_identity_properties(const char *properties, x509_entries *identity)
+{
+    int result;                             /* Bilan de l'exécution        */
+    char *tmp;                              /* Copie modifiable            */
+    char *saveptr;                          /* Sauvegarde pour traitement  */
+    char *pair;                             /* Ensemble clef=valeur        */
+    char *eq;                               /* Signe égal présent          */
+
+    result = EXIT_SUCCESS;
+
+    memset(identity, 0, sizeof(*identity));
+
+    tmp = strdup(properties);
+
+    for (pair = strtok_r(tmp, ",", &saveptr);
+         pair != NULL;
+         pair = strtok_r(NULL, ",", &saveptr))
+    {
+        eq = strchr(pair, '=');
+
+        if (eq == NULL)
+        {
+            log_variadic_message(LMT_ERROR, _("Malformed identity properties: '%s'"), properties);
+
+            result = 3;
+            goto id_error;
+
+        }
+
+        *eq = '\0';
+
+        if (strcasecmp(pair, "C") == 0)
+            identity->country = strdup(eq + 1);
+
+        else if (strcasecmp(pair, "ST") == 0)
+            identity->state = strdup(eq + 1);
+
+        else if (strcasecmp(pair, "L") == 0)
+            identity->locality = strdup(eq + 1);
+
+        else if (strcasecmp(pair, "O") == 0)
+            identity->organisation = strdup(eq + 1);
+
+        else if (strcasecmp(pair, "OU") == 0)
+            identity->organisational_unit = strdup(eq + 1);
+
+        else if (strcasecmp(pair, "CN") == 0)
+            identity->common_name = strdup(eq + 1);
+
+        else
+        {
+            log_variadic_message(LMT_ERROR, _("Unknown identity property: '%s=%s'"), pair, eq + 1);
+
+            result = 4;
+            goto id_error;
+
+        }
+
+    }
+
+ id_error:
+
+    free(tmp);
+
+    if (result != EXIT_SUCCESS)
+        free_x509_entries(identity);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : argc = nombre d'arguments dans la ligne de commande.         *
+*                argv = arguments de la ligne de commande.                    *
+*                                                                             *
+*  Description : Traite la commande "client-id" et ses arguments.             *
+*                                                                             *
+*  Retour      : EXIT_SUCCESS si le programme s'est déroulé sans encombres.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int exec_cmd_client_identity(int argc, char **argv)
+{
+    int result;                             /* Bilan de l'exécution        */
+    bool show_help;                         /* Affichage de l'aide ?       */
+    bool show_version;                      /* Affichage de la version ?   */
+    LogMessageType verbosity;               /* Niveau de filtre de message */
+    unsigned long valid;                    /* Durée de validité           */
+    int index;                              /* Indice d'argument           */
+    int ret;                                /* Bilan d'un appel            */
+    x509_entries identity;                  /* Nouvelle identité à pousser */
+    bool status;                            /* Bilan d'opérations          */
+
+    static struct option long_options[] = {
+        { "help",       no_argument,        NULL,   'h' },
+        { "version",    no_argument,        NULL,   'v' },
+        { "verbosity",  required_argument,  NULL,   'V' },
+        { "long",       required_argument,  NULL,   'l' },
+        { NULL,         0,                  NULL,   0 }
+    };
+
+    result = EXIT_FAILURE;
+
+    /* Décodage des options */
+
+    show_help = false;
+    show_version = false;
+
+    verbosity = LMT_INFO;
+    valid = 3 * 365 * 24 * 60 * 60;
+
+    while (true)
+    {
+        ret = getopt_long(argc - 1, argv + 1, "hvV:", long_options, &index);
+        if (ret == -1) break;
+
+        switch (ret)
+        {
+            case 'h':
+                show_help = true;
+                break;
+
+            case 'v':
+                show_version = true;
+                break;
+
+            case 'V':
+                verbosity = strtoul(optarg, NULL, 10);
+                break;
+
+            case 'l':
+                valid = strtoul(optarg, NULL, 10);
+                break;
+
+        }
+
+    }
+
+    /* Actions de base */
+
+    if (show_help)
+    {
+        show_hub_help(argv[0]);
+        result = EXIT_SUCCESS;
+        goto done;
+    }
+
+    if (show_version)
+    {
+        show_hub_version();
+        result = EXIT_SUCCESS;
+        goto done;
+    }
+
+    /* Initialisation du programme */
+
+    set_log_verbosity(verbosity);
+
+    /* Elaboration de l'identité */
+
+    if ((optind + 1) == argc)
+    {
+        log_simple_message(LMT_ERROR,
+                           _("Identity properties are missing; please provide at least an empty string"));
+        result = 2;
+        goto done;
+    }
+
+    ret = parse_identity_properties(argv[optind + 1], &identity);
+    if (ret != EXIT_SUCCESS)
+    {
+        result = ret;
+        goto done;
+    }
+
+    /* Traitement de la commande */
+
+    status = setup_client_identity(valid, &identity);
+
+    if (status)
+        result = EXIT_SUCCESS;
+
+    free_x509_entries(&identity);
+
+ done:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : argc = nombre d'arguments dans la ligne de commande.         *
+*                argv = arguments de la ligne de commande.                    *
+*                                                                             *
+*  Description : Traite la commande "server-id" et ses arguments.             *
+*                                                                             *
+*  Retour      : EXIT_SUCCESS si le programme s'est déroulé sans encombres.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int exec_cmd_server_identity(int argc, char **argv)
+{
+    int result;                             /* Bilan de l'exécution        */
+    bool show_help;                         /* Affichage de l'aide ?       */
+    bool show_version;                      /* Affichage de la version ?   */
+    LogMessageType verbosity;               /* Niveau de filtre de message */
+    char *name;                             /* Désignation du serveur      */
+    char *port;                             /* Définition du port associé  */
+    unsigned long valid;                    /* Durée de validité           */
+    int index;                              /* Indice d'argument           */
+    int ret;                                /* Bilan d'un appel            */
+    x509_entries identity;                  /* Nouvelle identité à pousser */
+    bool status;                            /* Bilan d'opérations          */
+
+    static struct option long_options[] = {
+        { "help",       no_argument,        NULL,   'h' },
+        { "version",    no_argument,        NULL,   'v' },
+        { "verbosity",  required_argument,  NULL,   'V' },
+        { "name",       required_argument,  NULL,   'n' },
+        { "port",       required_argument,  NULL,   'p' },
+        { "long",       required_argument,  NULL,   'l' },
+        { NULL,         0,                  NULL,   0 }
+    };
+
+    result = EXIT_FAILURE;
+
+    /* Décodage des options */
+
+    show_help = false;
+    show_version = false;
+
+    verbosity = LMT_INFO;
+    name = NULL;
+    port = NULL;
+    valid = 3 * 365 * 24 * 60 * 60;
+
+    while (true)
+    {
+        ret = getopt_long(argc - 1, argv + 1, "hvV:n:p:l:", long_options, &index);
+        if (ret == -1) break;
+
+        switch (ret)
+        {
+            case 'h':
+                show_help = true;
+                break;
+
+            case 'v':
+                show_version = true;
+                break;
+
+            case 'V':
+                verbosity = strtoul(optarg, NULL, 10);
+                break;
+
+            case 'n':
+                name = optarg;
+                break;
+
+            case 'p':
+                port = optarg;
+                break;
+
+            case 'l':
+                valid = strtoul(optarg, NULL, 10);
+                break;
+
+        }
+
+    }
+
+    /* Actions de base */
+
+    if (show_help)
+    {
+        show_hub_help(argv[0]);
+        result = EXIT_SUCCESS;
+        goto done;
+    }
+
+    if (show_version)
+    {
+        show_hub_version();
+        result = EXIT_SUCCESS;
+        goto done;
+    }
+
+    /* Initialisation du programme */
+
+    set_log_verbosity(verbosity);
+
+    /* Elaboration de l'identité */
+
+    if ((optind + 1) == argc)
+    {
+        log_simple_message(LMT_ERROR,
+                           _("Identity properties are missing; please provide at least an empty string"));
+        result = 2;
+        goto done;
+    }
+
+    ret = parse_identity_properties(argv[optind + 1], &identity);
+    if (ret != EXIT_SUCCESS)
+    {
+        result = ret;
+        goto done;
+    }
+
+    /* Traitement de la commande */
+
+    status = setup_server_identity(name, port, valid, &identity);
+
+    if (status)
+        result = EXIT_SUCCESS;
+
+    free_x509_entries(&identity);
+
+ done:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : argc = nombre d'arguments dans la ligne de commande.         *
+*                argv = arguments de la ligne de commande.                    *
+*                                                                             *
+*  Description : Traite la commande "add-client" et ses arguments.            *
+*                                                                             *
+*  Retour      : EXIT_SUCCESS si le programme s'est déroulé sans encombres.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int exec_cmd_add_client(int argc, char **argv)
+{
+    int result;                             /* Bilan de l'exécution        */
+    bool show_help;                         /* Affichage de l'aide ?       */
+    bool show_version;                      /* Affichage de la version ?   */
+    LogMessageType verbosity;               /* Niveau de filtre de message */
+    char *name;                             /* Désignation du serveur      */
+    char *port;                             /* Définition du port associé  */
+    unsigned long valid;                    /* Durée de validité           */
+    int index;                              /* Indice d'argument           */
+    int ret;                                /* Bilan d'un appel            */
+    bool status;                            /* Bilan d'opérations          */
+
+    static struct option long_options[] = {
+        { "help",       no_argument,        NULL,   'h' },
+        { "version",    no_argument,        NULL,   'v' },
+        { "verbosity",  required_argument,  NULL,   'V' },
+        { "name",       required_argument,  NULL,   'n' },
+        { "port",       required_argument,  NULL,   'p' },
+        { "long",       required_argument,  NULL,   'l' },
+        { NULL,         0,                  NULL,   0 }
+    };
+
+    result = EXIT_FAILURE;
+
+    /* Décodage des options */
+
+    show_help = false;
+    show_version = false;
+
+    verbosity = LMT_INFO;
+    name = NULL;
+    port = NULL;
+    valid = 3 * 365 * 24 * 60 * 60;
+
+    while (true)
+    {
+        ret = getopt_long(argc - 1, argv + 1, "hvV:n:p:l:", long_options, &index);
+        if (ret == -1) break;
+
+        switch (ret)
+        {
+            case 'h':
+                show_help = true;
+                break;
+
+            case 'v':
+                show_version = true;
+                break;
+
+            case 'V':
+                verbosity = strtoul(optarg, NULL, 10);
+                break;
+
+            case 'n':
+                name = optarg;
+                break;
+
+            case 'p':
+                port = optarg;
+                break;
+
+            case 'l':
+                valid = strtoul(optarg, NULL, 10);
+                break;
+
+        }
+
+    }
+
+    /* Actions de base */
+
+    if (show_help)
+    {
+        show_hub_help(argv[0]);
+        result = EXIT_SUCCESS;
+        goto done;
+    }
+
+    if (show_version)
+    {
+        show_hub_version();
+        result = EXIT_SUCCESS;
+        goto done;
+    }
+
+    if ((optind + 2) >= argc)
+    {
+        show_hub_help(argv[0]);
+        goto done;
+    }
+
+    /* Initialisation du programme */
+
+    set_log_verbosity(verbosity);
+
+    /* Traitement de la commande */
+
+    status = add_client_to_server(name, port, valid, argv[optind + 1], argv[optind + 2]);
+
+    if (status)
+        result = EXIT_SUCCESS;
+
+ done:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : sig = numéro du signal reçu.                                 *
+*                                                                             *
+*  Description : Réagit à la réception d'un signal SIGTERM.                   *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_term_signal(int sig)
+{
+    log_simple_message(LMT_INFO, _("Stopping the server..."));
+
+    g_hub_server_stop(_server);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : argc = nombre d'arguments dans la ligne de commande.         *
+*                argv = arguments de la ligne de commande.                    *
+*                                                                             *
+*  Description : Traite la commande "run" et ses arguments.                   *
+*                                                                             *
+*  Retour      : EXIT_SUCCESS si le programme s'est déroulé sans encombres.   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int exec_cmd_run_server(int argc, char **argv)
+{
+    int result;                             /* Bilan de l'exécution        */
+    bool show_help;                         /* Affichage de l'aide ?       */
+    bool show_version;                      /* Affichage de la version ?   */
+    LogMessageType verbosity;               /* Niveau de filtre de message */
+    char *name;                             /* Désignation du serveur      */
+    char *port;                             /* Définition du port associé  */
+    bool ipv6;                              /* Préférence pour IPv6 ?      */
+    int backlog;                            /* Nombre de connexions max.   */
+    bool keep;                              /* Maintien en avant plan ?    */
+    int index;                              /* Indice d'argument           */
+    int ret;                                /* Bilan d'un appel            */
+    ServerStartStatus status;               /* Bilan d'un lancement        */
+    sighandler_t prev;                      /* Gestionnaire précédent      */
+
+    static struct option long_options[] = {
+        { "help",       no_argument,        NULL,   'h' },
+        { "version",    no_argument,        NULL,   'v' },
+        { "verbosity",  required_argument,  NULL,   'V' },
+        { "name",       required_argument,  NULL,   'n' },
+        { "port",       required_argument,  NULL,   'p' },
+        { "ipv4",       no_argument,        NULL,   '4' },
+        { "backlog",    required_argument,  NULL,   'b' },
+        { "keep",       no_argument,        NULL,   'k' },
+        { NULL,         0,                  NULL,   0 }
+    };
+
+    result = EXIT_FAILURE;
+
+    /* Décodage des options */
+
+    show_help = false;
+    show_version = false;
+
+    verbosity = LMT_INFO;
+    name = NULL;
+    port = NULL;
+    ipv6 = true;
+    backlog = 10;
+    keep = false;
+
+    while (true)
+    {
+        ret = getopt_long(argc - 1, argv + 1, "hvV:n:p:4b:k", long_options, &index);
+        if (ret == -1) break;
+
+        switch (ret)
+        {
+            case 'h':
+                show_help = true;
+                break;
+
+            case 'v':
+                show_version = true;
+                break;
+
+            case 'V':
+                verbosity = strtoul(optarg, NULL, 10);
+                break;
+
+            case 'n':
+                name = optarg;
+                break;
+
+            case 'p':
+                port = optarg;
+                break;
+
+            case '4':
+                ipv6 = false;
+                break;
+
+            case 'b':
+                backlog = atoi(optarg);
+                break;
+
+            case 'k':
+                keep = true;
+                break;
+
+        }
+
+    }
+
+    /* Actions de base */
+
+    if (show_help)
+    {
+        show_hub_help(argv[0]);
+        result = EXIT_SUCCESS;
+        goto done;
+    }
+
+    if (show_version)
+    {
+        show_hub_version();
+        result = EXIT_SUCCESS;
+        goto done;
+    }
+
+    /* Initialisation du programme */
+
+    set_log_verbosity(verbosity);
+
+    /* Traitement de la commande */
+
+    if (name == NULL)
+        _server = g_hub_server_new_internal();
+    else
+        _server = g_hub_server_new_remote(name, port, ipv6);
+
+    status = g_hub_server_start(_server, backlog, keep);
+
+    switch (status)
+    {
+        case SSS_FAILURE:
+            goto stopped;
+            break;
+
+        case SSS_SUCCESS:
+
+            prev = signal(SIGTERM, on_term_signal);
+            if (prev == SIG_ERR)
+            {
+                LOG_ERROR_N("signal");
+                g_hub_server_stop(_server);
+                goto stopped;
+            }
+
+            g_hub_server_wait_for_stop(_server);
+
+            result = EXIT_SUCCESS;
+            break;
+
+        case SSS_ALREADY_RUNNING:
+            result = EXIT_SUCCESS;
+            break;
+
+    }
+
+ stopped:
+
+    g_object_unref(G_OBJECT(_server));
+
+ done:
+
+    return result;
+
+}
diff --git a/src/main.c b/src/main.c
index 5dc9e3c..84b0eaa 100644
--- a/src/main.c
+++ b/src/main.c
@@ -23,12 +23,17 @@
 
 
 #include <getopt.h>
+#include <libgen.h>
 #include <limits.h>
 #include <locale.h>
+#include <malloc.h>
+#include <stdbool.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 #include <gtk/gtk.h>
 
+
 #include <config.h>
 #include <i18n.h>
 
@@ -37,8 +42,7 @@
 #include "analysis/binary.h"
 #include "analysis/loading.h"
 #include "analysis/contents/file.h"
-#include "analysis/db/server.h"
-#include "common/xdg.h"
+#include "analysis/db/auth.h"
 #include "core/core.h"
 #include "core/global.h"
 #include "core/logs.h"
@@ -83,15 +87,20 @@ static int save_binary_caches(void);
 
 static void show_chrysalide_help(const char *name)
 {
+    char *tmp;                              /* Conservation modifiable     */
     char *base;                             /* Version courte du nom       */
 
-    base = basename(name);
+    tmp = strdup(name);
+
+    base = basename(tmp);
 
     printf("\n");
 
     printf("Usage: %s [--help] [--version]\n", base);
     printf("       %s [args] <filename(s)...>\n", base);
 
+    free(tmp);
+
     printf("\n");
 
     printf("\t-h --help\t\tShow this help message.\n");
@@ -167,13 +176,7 @@ int main(int argc, char **argv)
     int ret;                                /* Bilan d'un appel            */
     bool status;                            /* Bilan d'opérations          */
     GtkWidget *editor;                      /* Fenêtre graphique           */
-    GDbServer *server;                      /* Enregistrements locaux      */
     GGenConfig *config;                     /* Configuration globale       */
-
-
-    char *author;                           /* Identification à diffuser   */
-    char *pub;                              /* Chemin de la clef publique  */
-
     bool welcome;                           /* Affichage de la bienvenue ? */
     char resolved[PATH_MAX];                /* Résolution de nom de fichier*/
     GStudyProject *project;                 /* Nouveau projet courant      */
@@ -313,19 +316,17 @@ int main(int argc, char **argv)
         if (!status) goto exit_complete_gui;
     }
 
-    /* Utilisateur représenté */
+    /* Lancement du serveur local */
 
-    if (!g_generic_config_get_value(config, MPK_AUTHOR_NAME, &author))
-        /*goto glbcl_exit*/;
+    status = ensure_internal_connections_setup();
 
-    /* Chemin vers la clef privée */
+    if (!status)
+        goto no_internal_server;
 
-    pub = get_xdg_config_dir("chrysalide" G_DIR_SEPARATOR_S "id_rsa.pub");
+    status = launch_internal_server();
 
-    server = g_db_server_new_internal(author, pub);
-
-    if (server != NULL)
-        g_db_server_start(server);
+    if (!status)
+        goto no_internal_server;
 
     /* Charge le dernier projet ? */
 
@@ -404,13 +405,7 @@ int main(int argc, char **argv)
 
  bad_project:
 
-    if (server != NULL)
-    {
-        g_db_server_stop(server);
-
-        g_object_unref(G_OBJECT(server));
-
-    }
+ no_internal_server:
 
  exit_complete_gui:
 
-- 
cgit v0.11.2-87-g4458