From d7c69bcb27a7d06932cd25021144b6cbbe4eb82f Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sat, 19 Oct 2019 14:50:37 +0200
Subject: Exchanged the list of all snapshots.

---
 plugins/pychrysalide/analysis/db/client.c | 137 ++++++++++++++++++++++
 src/analysis/db/cdb.c                     |  21 ++++
 src/analysis/db/client.c                  | 185 +++++++++++++++++++++++++++++-
 src/analysis/db/client.h                  |   3 +
 src/analysis/db/misc/rlestr.c             |   2 +-
 src/analysis/db/misc/snapshot.c           |  78 ++++++++++++-
 src/analysis/db/misc/snapshot.h           |  14 ++-
 src/analysis/db/misc/timestamp.c          |  19 +++
 src/analysis/db/misc/timestamp.h          |   3 +
 src/analysis/db/protocol.h                |  31 +++++
 src/analysis/db/snapshot.c                |  63 +++++++++-
 src/analysis/db/snapshot.h                |   3 +
 src/common/packed.c                       | 110 +++++++++++++-----
 src/common/packed.h                       |   6 +
 14 files changed, 633 insertions(+), 42 deletions(-)

diff --git a/plugins/pychrysalide/analysis/db/client.c b/plugins/pychrysalide/analysis/db/client.c
index afece26..f9ce001 100644
--- a/plugins/pychrysalide/analysis/db/client.c
+++ b/plugins/pychrysalide/analysis/db/client.c
@@ -25,6 +25,7 @@
 #include "client.h"
 
 
+#include <assert.h>
 #include <pygobject.h>
 
 
@@ -36,6 +37,7 @@
 #include "collection.h"
 #include "../../access.h"
 #include "../../helpers.h"
+#include "../../struct.h"
 
 
 
@@ -54,6 +56,9 @@ static PyObject *py_hub_client_save(PyObject *, PyObject *);
 /* Active les éléments en amont d'un horodatage donné. */
 static PyObject *py_hub_client_set_last_active(PyObject *, PyObject *);
 
+/* Fournit la liste des instantanés existants. */
+static PyObject *py_hub_client_get_snapshots(PyObject *, void *);
+
 /* Fournit l'identifiant de l'instantané courant. */
 static PyObject *py_hub_client_get_current_snapshot(PyObject *, void *);
 
@@ -338,6 +343,137 @@ static PyObject *py_hub_client_set_last_active(PyObject *self, PyObject *args)
 *  Paramètres  : self    = objet Python concerné par l'appel.                 *
 *                closure = non utilisé ici.                                   *
 *                                                                             *
+*  Description : Fournit la liste des instantanés existants.                  *
+*                                                                             *
+*  Retour      : Liste d'instantanés ou None.                                 *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_hub_client_get_snapshots(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    GHubClient *client;                     /* Version native du serveur   */
+    snapshot_info_t *info;                  /* Liste d'instantanés présents*/
+    size_t count;                           /* Taille de cette liste       */
+    bool status;                            /* Validité de cet identifiant */
+    PyTypeObject *base;                     /* Modèle d'objet à créer      */
+    size_t i;                               /* Boucle de parcours          */
+    PyObject *item;                         /* Nouvelle description        */
+    char *text;                             /* Valeur textuelle à placer   */
+    PyObject *attrib;                       /* Attribut à constituer       */
+    int ret;                                /* Bilan d'une mise en place   */
+    bool failed;                            /* Détection d'une erreur      */
+
+#define HUB_CLIENT_SNAPSHOTS_ATTRIB PYTHON_GET_DEF_FULL                                     \
+(                                                                                           \
+    snapshots, py_hub_client,                                                               \
+    "List of all existing snapshots, provided as a tuple of pychrysalide.PyStructObject."   \
+    "\n"                                                                                    \
+    "Each snapshot is characterised by the following properties :\n"                        \
+    "* parent_id : identifier of the parent snapshot;\n"                                    \
+    "* id : identifier of the snapshot;\n"                                                  \
+    "* created : timestamp of the creation date;\n"                                         \
+    "* name : name of the snapshot, or None;\n"                                             \
+    "* desc : description of the snapshot, or None."                                        \
+)
+
+    client = G_HUB_CLIENT(pygobject_get(self));
+
+    status = g_hub_client_get_snapshots(client, &info, &count);
+
+    if (status)
+    {
+        result = PyTuple_New(count);
+
+        base = get_python_py_struct_type();
+
+        failed = false;
+
+        for (i = 0; i < count; i++)
+        {
+            item = PyObject_CallFunction((PyObject *)base, NULL);
+            assert(item != NULL);
+
+            text = snapshot_id_as_string(get_snapshot_info_parent_id(&info[i]));
+            attrib = PyUnicode_FromString(text);
+            ret = PyDict_SetItemString(item, "parent_id", attrib);
+            if (ret != 0) break;
+
+            text = snapshot_id_as_string(get_snapshot_info_id(&info[i]));
+            attrib = PyUnicode_FromString(text);
+            ret = PyDict_SetItemString(item, "id", attrib);
+            if (ret != 0) break;
+
+            attrib = PyLong_FromUnsignedLongLong(get_snapshot_info_created(&info[i]));
+            ret = PyDict_SetItemString(item, "created", attrib);
+            if (ret != 0) break;
+
+            text = get_snapshot_info_name(&info[i]);
+
+            if (text != NULL)
+                attrib = PyUnicode_FromString(text);
+            else
+            {
+                attrib = Py_None;
+                Py_INCREF(attrib);
+            }
+
+            ret = PyDict_SetItemString(item, "name", attrib);
+            if (ret != 0) break;
+
+            text = get_snapshot_info_desc(&info[i]);
+
+            if (text != NULL)
+                attrib = PyUnicode_FromString(text);
+            else
+            {
+                attrib = Py_None;
+                Py_INCREF(attrib);
+            }
+
+            ret = PyDict_SetItemString(item, "desc", attrib);
+            if (ret != 0) break;
+
+            PyTuple_SetItem(result, i, item);
+
+        }
+
+        failed = (i < count);
+
+        for (i = 0; i < count; i++)
+            exit_snapshot_info(&info[i]);
+
+        free(info);
+
+        if (failed)
+            goto on_failure;
+
+    }
+
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    return result;
+
+ on_failure:
+
+    Py_DECREF(result);
+
+    return NULL;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                closure = non utilisé ici.                                   *
+*                                                                             *
 *  Description : Fournit l'identifiant de l'instantané courant.               *
 *                                                                             *
 *  Retour      : Identifiant d'instantané ou None.                            *
@@ -452,6 +588,7 @@ PyTypeObject *get_python_hub_client_type(void)
     };
 
     static PyGetSetDef py_hub_client_getseters[] = {
+        HUB_CLIENT_SNAPSHOTS_ATTRIB,
         HUB_CLIENT_CURRENT_SNAPSHOT_ATTRIB,
         { NULL }
     };
diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c
index 8e8813a..2df556b 100644
--- a/src/analysis/db/cdb.c
+++ b/src/analysis/db/cdb.c
@@ -1003,6 +1003,27 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 
                         break;
 
+                    case DBC_GET_SNAPSHOTS:
+
+                        init_packed_buffer(&out_pbuf);
+
+                        status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SNAPSHOTS_UPDATED },
+                                                      sizeof(uint32_t), true);
+                        if (!status) goto gcap_bad_reply;
+
+                        status = g_db_snapshot_pack_all(archive->snapshot, &out_pbuf);
+                        if (!status) goto gcap_bad_reply;
+
+                        status = extend_packed_buffer(&out_pbuf, SNAPSHOT_END_MARK, SNAP_ID_HEX_SZ, false);
+                        if (!status) goto gcap_bad_reply;
+
+                        status = ssl_send_packed_buffer(&out_pbuf, archive->clients[i].ssl_fd);
+                        if (!status) goto gcap_bad_reply;
+
+                        exit_packed_buffer(&out_pbuf);
+
+                        break;
+
                     case DBC_GET_CUR_SNAPSHOT:
 
                         init_packed_buffer(&out_pbuf);
diff --git a/src/analysis/db/client.c b/src/analysis/db/client.c
index 3372c74..2cab655 100644
--- a/src/analysis/db/client.c
+++ b/src/analysis/db/client.c
@@ -25,6 +25,7 @@
 
 
 #include <assert.h>
+#include <malloc.h>
 #include <netdb.h>
 #include <poll.h>
 #include <stdio.h>
@@ -76,11 +77,13 @@ struct _GHubClient
     bool can_get_updates;                   /* Réception de maj possibles ?*/
     GThread *update;                        /* Procédure de traitement     */
 
-
-
+    snapshot_info_t *snapshots;             /* Liste des instantanés       */
+    size_t snap_count;                      /* Taille de cette liste       */
+    GMutex snap_lock;                       /* Concurrence des accès       */
 
     snapshot_id_t current;                  /* Instantané courant          */
     bool has_current;                       /* Validité de l'identifiant   */
+    GMutex cur_lock;                        /* Concurrence des accès       */
 
 };
 
@@ -110,6 +113,9 @@ static bool g_hub_client_start_common(GHubClient *, char *);
 /* Assure l'accueil des nouvelles mises à jour. */
 static void *g_hub_client_update(GHubClient *);
 
+/* Met à jour la liste des instantanés courants. */
+static bool g_hub_client_update_snapshots(GHubClient *, packed_buffer *);
+
 /* Identifie le canal de communication pour envois au serveur. */
 static SSL *g_hub_client_get_ssl_fd(GHubClient *);
 
@@ -160,6 +166,9 @@ static void g_hub_client_class_init(GHubClientClass *klass)
 
 static void g_hub_client_init(GHubClient *client)
 {
+    init_static_rle_string(&client->hash, NULL);
+    client->collections = NULL;
+
     client->working = NULL;
 
     client->tls_ctx = NULL;
@@ -168,7 +177,16 @@ static void g_hub_client_init(GHubClient *client)
     client->tls_fd = NULL;
     client->desc = NULL;
 
+    g_mutex_init(&client->sending_lock);
+    client->can_get_updates = false;
+    client->update = NULL;
+
+    client->snapshots = NULL;
+    client->snap_count = 0;
+    g_mutex_init(&client->snap_lock);
+
     client->has_current = false;
+    g_mutex_init(&client->cur_lock);
 
 }
 
@@ -189,6 +207,12 @@ static void g_hub_client_dispose(GHubClient *client)
 {
     g_hub_client_stop(client);
 
+    g_mutex_clear(&client->cur_lock);
+
+    g_mutex_clear(&client->snap_lock);
+
+    g_mutex_clear(&client->sending_lock);
+
     G_OBJECT_CLASS(g_hub_client_parent_class)->dispose(G_OBJECT(client));
 
 }
@@ -208,18 +232,28 @@ static void g_hub_client_dispose(GHubClient *client)
 
 static void g_hub_client_finalize(GHubClient *client)
 {
+    size_t i;                               /* Boucle de parcours          */
+
     unset_rle_string(&client->hash);
 
     if (client->working != NULL)
         free(client->working);
 
     assert(client->tls_ctx == NULL);
-
     assert(client->tls_fd == NULL);
 
     if (client->desc != NULL)
         free(client->desc);
 
+    if (client->snapshots != NULL)
+    {
+        for (i = 0; i < client->snap_count; i++)
+            exit_snapshot_info(&client->snapshots[i]);
+
+        free(client->snapshots);
+
+    }
+
     G_OBJECT_CLASS(g_hub_client_parent_class)->finalize(G_OBJECT(client));
 
 }
@@ -703,6 +737,13 @@ static void *g_hub_client_update(GHubClient *client)
 
     init_packed_buffer(&out_pbuf);
 
+    status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_GET_SNAPSHOTS }, sizeof(uint32_t), true);
+    if (!status)
+    {
+        exit_packed_buffer(&out_pbuf);
+        goto exit;
+    }
+
     status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_GET_CUR_SNAPSHOT }, sizeof(uint32_t), true);
     if (!status)
     {
@@ -814,6 +855,13 @@ static void *g_hub_client_update(GHubClient *client)
                     client->can_get_updates = (tmp8 == 0x1);
                     break;
 
+                case DBC_SNAPSHOTS_UPDATED:
+
+                    status = g_hub_client_update_snapshots(client, &in_pbuf);
+                    if (!status) goto gdcu_bad_exchange;
+
+                    break;
+
                 case DBC_GET_CUR_SNAPSHOT:
                     log_variadic_message(LMT_INFO,
                                          _("This command is not available on this side: 0x%08x"), command);
@@ -825,9 +873,13 @@ static void *g_hub_client_update(GHubClient *client)
                     status = unpack_snapshot_id(&id, &in_pbuf);
                     if (!status) goto gdcu_bad_exchange;
 
+                    g_mutex_lock(&client->cur_lock);
+
                     copy_snapshot_id(&client->current, &id);
                     client->has_current = true;
 
+                    g_mutex_unlock(&client->cur_lock);
+
                     break;
 
                 case DBC_SET_CUR_SNAPSHOT:
@@ -872,6 +924,82 @@ static void *g_hub_client_update(GHubClient *client)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : client = client pour les accès distants à manipuler.         *
+*                pbuf   = données présentes à traiter.                        *
+*                                                                             *
+*  Description : Met à jour la liste des instantanés courants.                *
+*                                                                             *
+*  Retour      : true si la liste retournée est valide, false sinon.          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool g_hub_client_update_snapshots(GHubClient *client, packed_buffer *pbuf)
+{
+    bool result;                            /* Validité à retourner        */
+    size_t i;                               /* Boucle de parcours          */
+    char id[SNAP_ID_HEX_SZ];                /* Caractères hexadécimaux     */
+    snapshot_info_t info;                   /* Description d'instantané    */
+    snapshot_info_t *dest;                  /* Destination de description  */
+
+    result = true;
+
+    g_mutex_lock(&client->snap_lock);
+
+    if (client->snapshots != NULL)
+    {
+        for (i = 0; i < client->snap_count; i++)
+            exit_snapshot_info(&client->snapshots[i]);
+
+        free(client->snapshots);
+
+        client->snapshots = NULL;
+        client->snap_count = 0;
+
+    }
+
+    do
+    {
+        result = peek_packed_buffer(pbuf, id, SNAP_ID_HEX_SZ, false);
+        if (!result) break;
+
+        if (strncmp(id, SNAPSHOT_END_MARK, SNAP_ID_HEX_SZ) == 0)
+        {
+            advance_packed_buffer(pbuf, SNAP_ID_HEX_SZ);
+            break;
+        }
+
+        else
+        {
+            setup_empty_snapshot_info(&info);
+
+            result = unpack_snapshot_info(&info, pbuf);
+            if (!result) break;
+
+            client->snapshots = realloc(client->snapshots, ++client->snap_count * sizeof(snapshot_info_t));
+
+            dest = &client->snapshots[client->snap_count - 1];
+
+            setup_empty_snapshot_info(dest);
+            copy_snapshot_info(dest, &info);
+
+            exit_snapshot_info(&info);
+
+        }
+
+    }
+    while (true);
+
+    g_mutex_unlock(&client->snap_lock);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : client = client pour les accès distants à manipuler.         *
 *                                                                             *
 *  Description : Arrête la connexion à la base de données.                    *
 *                                                                             *
@@ -1135,6 +1263,53 @@ bool g_hub_client_set_last_active(GHubClient *client, timestamp_t timestamp)
 /******************************************************************************
 *                                                                             *
 *  Paramètres  : client = client pour les accès distants à manipuler.         *
+*                info   = description des instantanés présents. [OUT]         *
+*                count  = taille de la liste retournée. [OUT]                 *
+*                                                                             *
+*  Description : Fournit la liste des instantanés existants.                  *
+*                                                                             *
+*  Retour      : true si la liste retournée est valide, false sinon.          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_hub_client_get_snapshots(GHubClient *client, snapshot_info_t **info, size_t *count)
+{
+    bool result;                            /* Validité à retourner        */
+    size_t i;                               /* Boucle de parcours          */
+    snapshot_info_t *dest;                  /* Destination de description  */
+
+    g_mutex_lock(&client->snap_lock);
+
+    result = (client->snap_count > 0);
+
+    if (result)
+    {
+        *info = malloc(client->snap_count * sizeof(snapshot_info_t));
+        *count = client->snap_count;
+
+        for (i = 0; i < client->snap_count; i++)
+        {
+            dest = &(*info)[i];
+
+            setup_empty_snapshot_info(dest);
+            copy_snapshot_info(dest, &client->snapshots[i]);
+
+        }
+
+    }
+
+    g_mutex_unlock(&client->snap_lock);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : client = client pour les accès distants à manipuler.         *
 *                id     = identifiant d'instantané à renseigner. [OUT]        *
 *                                                                             *
 *  Description : Fournit l'identifiant de l'instantané courant.               *
@@ -1149,11 +1324,15 @@ bool g_hub_client_get_current_snapshot(GHubClient *client, snapshot_id_t *id)
 {
     bool result;                            /* Validité à retourner        */
 
+    g_mutex_lock(&client->cur_lock);
+
     result = client->has_current;
 
     if (result)
         copy_snapshot_id(id, &client->current);
 
+    g_mutex_unlock(&client->cur_lock);
+
     return result;
 
 }
diff --git a/src/analysis/db/client.h b/src/analysis/db/client.h
index 36d24e6..039631e 100644
--- a/src/analysis/db/client.h
+++ b/src/analysis/db/client.h
@@ -74,6 +74,9 @@ bool g_hub_client_add_item(GHubClient *, const GDbItem *);
 /* Active les éléments en amont d'un horodatage donné. */
 bool g_hub_client_set_last_active(GHubClient *, timestamp_t);
 
+/* Fournit la liste des instantanés existants. */
+bool g_hub_client_get_snapshots(GHubClient *, snapshot_info_t **, size_t *);
+
 /* Fournit l'identifiant de l'instantané courant. */
 bool g_hub_client_get_current_snapshot(GHubClient *, snapshot_id_t *);
 
diff --git a/src/analysis/db/misc/rlestr.c b/src/analysis/db/misc/rlestr.c
index 0fe182b..c1cc866 100644
--- a/src/analysis/db/misc/rlestr.c
+++ b/src/analysis/db/misc/rlestr.c
@@ -286,7 +286,7 @@ bool unpack_rle_string(rle_string *str, packed_buffer *pbuf)
 
     if (result && str->length > 0)
     {
-        str->data = (char *)malloc(str->length + 1);
+        str->data = malloc(str->length + 1);
         str->dynamic = true;
 
         result = extract_packed_buffer(pbuf, str->data, str->length + 1, false);
diff --git a/src/analysis/db/misc/snapshot.c b/src/analysis/db/misc/snapshot.c
index 546191b..9aa096f 100644
--- a/src/analysis/db/misc/snapshot.c
+++ b/src/analysis/db/misc/snapshot.c
@@ -41,6 +41,25 @@
 *                                                                             *
 *  Paramètres  : id = identifiant d'instantané à initialiser. [OUT]           *
 *                                                                             *
+*  Description : Prépare un identifiant pour instantané à une définition.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void setup_empty_snapshot_id(snapshot_id_t *id)
+{
+    memset(id, 0, sizeof(snapshot_id_t));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : id = identifiant d'instantané à initialiser. [OUT]           *
+*                                                                             *
 *  Description : Construit un identifiant pour instantané de base de données. *
 *                                                                             *
 *  Retour      : Bilan de l'opération.                                        *
@@ -214,6 +233,32 @@ bool pack_snapshot_id(const snapshot_id_t *id, packed_buffer *pbuf)
 *                                                                             *
 *  Paramètres  : info = description d'instantané à initialiser. [OUT]         *
 *                                                                             *
+*  Description : Prépare une description pour instantané à une définition.    *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void setup_empty_snapshot_info(snapshot_info_t *info)
+{
+    setup_empty_snapshot_id(&info->parent_id);
+
+    setup_empty_snapshot_id(&info->id);
+
+    setup_empty_timestamp(&info->created);
+
+    info->name = NULL;
+    info->desc = NULL;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : info = description d'instantané à initialiser. [OUT]         *
+*                                                                             *
 *  Description : Construit une description pour instantané de base de données.*
 *                                                                             *
 *  Retour      : Bilan de l'opération.                                        *
@@ -226,7 +271,10 @@ bool init_snapshot_info(snapshot_info_t *info)
 {
     bool result;                            /* Bilan à retourner           */
 
-    result = init_snapshot_id(&info->id);
+    result = init_snapshot_id_from_text(&info->parent_id, NO_SNAPSHOT_ROOT);
+
+    if (result)
+        result = init_snapshot_id(&info->id);
 
     if (result)
         result = init_timestamp(&info->created);
@@ -262,7 +310,10 @@ bool init_snapshot_info_from_text(snapshot_info_t *info, const char *id, uint64_
 {
     bool result;                            /* Bilan à retourner           */
 
-    result = init_snapshot_id_from_text(&info->id, id);
+    result = init_snapshot_id_from_text(&info->parent_id, NO_SNAPSHOT_ROOT);
+
+    if (result)
+        result = init_snapshot_id_from_text(&info->id, id);
 
     if (result)
         result = init_timestamp_from_value(&info->created, created);
@@ -332,6 +383,8 @@ void copy_snapshot_info(snapshot_info_t *dest, const snapshot_info_t *src)
 {
     exit_snapshot_info(dest);
 
+    copy_snapshot_id(&dest->parent_id, &src->parent_id);
+
     copy_snapshot_id(&dest->id, &src->id);
 
     copy_timestamp(&dest->created, &src->created);
@@ -362,19 +415,26 @@ bool unpack_snapshot_info(snapshot_info_t *info, packed_buffer *pbuf)
 {
     bool result;                            /* Bilan à retourner           */
     rle_string string;                      /* Chaîne à transmettre        */
+    const char *text;                       /* Valeur textuelle obtenue    */
 
-    result = unpack_snapshot_id(&info->id, pbuf);
+    result = unpack_snapshot_id(&info->parent_id, pbuf);
+
+    if (result)
+        result = unpack_snapshot_id(&info->id, pbuf);
 
     if (result)
         result = unpack_timestamp(&info->created, pbuf);
 
     if (result)
     {
+        init_static_rle_string(&string, NULL);
+
         result = unpack_rle_string(&string, pbuf);
 
         if (result)
         {
-            info->name = strdup(get_rle_string(&string));
+            text = get_rle_string(&string);
+            info->name = (text != NULL ? strdup(text) : NULL);
             exit_rle_string(&string);
         }
 
@@ -382,11 +442,14 @@ bool unpack_snapshot_info(snapshot_info_t *info, packed_buffer *pbuf)
 
     if (result)
     {
+        init_static_rle_string(&string, NULL);
+
         result = unpack_rle_string(&string, pbuf);
 
         if (result)
         {
-            info->desc = strdup(get_rle_string(&string));
+            text = get_rle_string(&string);
+            info->desc = (text != NULL ? strdup(text) : NULL);
             exit_rle_string(&string);
         }
 
@@ -415,7 +478,10 @@ bool pack_snapshot_info(const snapshot_info_t *info, packed_buffer *pbuf)
     bool result;                            /* Bilan à retourner           */
     rle_string string;                      /* Chaîne à transmettre        */
 
-    result = pack_snapshot_id(&info->id, pbuf);
+    result = pack_snapshot_id(&info->parent_id, pbuf);
+
+    if (result)
+        result = pack_snapshot_id(&info->id, pbuf);
 
     if (result)
         result = pack_timestamp(&info->created, pbuf);
diff --git a/src/analysis/db/misc/snapshot.h b/src/analysis/db/misc/snapshot.h
index 37fad7f..8f9a598 100644
--- a/src/analysis/db/misc/snapshot.h
+++ b/src/analysis/db/misc/snapshot.h
@@ -49,6 +49,13 @@ typedef struct _snapshot_id_t
 } snapshot_id_t;
 
 
+/* Identifiant d'un parent de racine */
+#define NO_SNAPSHOT_ROOT "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
+
+
+/* Prépare un identifiant pour instantané à une définition. */
+void setup_empty_snapshot_id(snapshot_id_t *);
+
 /* Construit un identifiant pour instantané de base de données. */
 bool init_snapshot_id(snapshot_id_t *);
 
@@ -77,6 +84,7 @@ bool pack_snapshot_id(const snapshot_id_t *, packed_buffer *);
 /* Description d'un instantané */
 typedef struct _snapshot_info_t
 {
+    snapshot_id_t parent_id;                /* Identifiant du propriétaire */
     snapshot_id_t id;                       /* Identifiant attribué        */
 
     timestamp_t created;                    /* Date de création            */
@@ -87,6 +95,9 @@ typedef struct _snapshot_info_t
 } snapshot_info_t;
 
 
+/* Prépare une description pour instantané à une définition. */
+void setup_empty_snapshot_info(snapshot_info_t *);
+
 /* Construit une description pour instantané de base de données. */
 bool init_snapshot_info(snapshot_info_t *);
 
@@ -96,8 +107,9 @@ bool init_snapshot_info_from_text(snapshot_info_t *, const char *, uint64_t, con
 /* Libère la mémoire occupée par une description d'instantané. */
 void exit_snapshot_info(snapshot_info_t *);
 
+#define get_snapshot_info_parent_id(nfo) &(nfo)->parent_id
 #define get_snapshot_info_id(nfo) &(nfo)->id
-#define get_snapshot_info_creation(nfo) (nfo)->created
+#define get_snapshot_info_created(nfo) (nfo)->created
 #define get_snapshot_info_name(nfo) (nfo)->name
 #define get_snapshot_info_desc(nfo) (nfo)->desc
 
diff --git a/src/analysis/db/misc/timestamp.c b/src/analysis/db/misc/timestamp.c
index dfc6f25..4d457e2 100644
--- a/src/analysis/db/misc/timestamp.c
+++ b/src/analysis/db/misc/timestamp.c
@@ -37,6 +37,25 @@
 *                                                                             *
 *  Paramètres  : timestamp = horodatage à initialiser. [OUT]                  *
 *                                                                             *
+*  Description : Prépare un horodatage à une définition.                      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void setup_empty_timestamp(timestamp_t *timestamp)
+{
+    *timestamp = 0;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : timestamp = horodatage à initialiser. [OUT]                  *
+*                                                                             *
 *  Description : Obtient un horodatage initialisé au moment même.             *
 *                                                                             *
 *  Retour      : Bilan de l'opération.                                        *
diff --git a/src/analysis/db/misc/timestamp.h b/src/analysis/db/misc/timestamp.h
index 7f6290876..52b99f8 100644
--- a/src/analysis/db/misc/timestamp.h
+++ b/src/analysis/db/misc/timestamp.h
@@ -38,6 +38,9 @@
 typedef uint64_t timestamp_t;
 
 
+/* Prépare un horodatage à une définition. */
+void setup_empty_timestamp(timestamp_t *);
+
 /* Obtient un horodatage initialisé au moment même. */
 bool init_timestamp(timestamp_t *);
 
diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h
index 22f564a..273be8e 100644
--- a/src/analysis/db/protocol.h
+++ b/src/analysis/db/protocol.h
@@ -103,6 +103,9 @@ typedef enum _DBAction
 
 
 
+/* Marqueur de fin pour une transmission d'identifiants */
+#define SNAPSHOT_END_MARK "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
+
 
 
 
@@ -198,6 +201,34 @@ typedef enum _DBCommand
 
     /* ------- Gestion des instantanés ------- */
 
+    /**
+     * Gestion de la commande 'DBC_GET_SNAPSHOTS'.
+     *
+     * Le client connecté envoie un paquet de la forme suivante :
+     *
+     *    [ Gestion d'instantané : DBC_GET_SNAPSHOTS            ]
+     *
+     * La définition d'un nouvel identifiant d'instantané courant se réalise dans :
+     *  - g_hub_client_update() pour la partie client ;
+     *  - g_cdb_archive_process() pour la partie serveur.
+     */
+
+    DBC_GET_SNAPSHOTS,
+
+    /**
+     * Gestion de la commande 'DBC_SNAPSHOTS_UPDATED'.
+     *
+     * Le serveur envoie au client un paquet de la forme suivante :
+     *
+     *    [ Gestion d'instantané : DBC_SNAPSHOTS_UPDATED        ]
+     *    [ <liste de descriptions d'instantanés>               ]
+     *    [ Marqueur de fin : SNAPSHOT_END_MARK                 ]
+     *
+     * La définition d'un nouvel identifiant d'instantané courant se réalise dans :
+     *  - g_cdb_archive_process() pour la partie serveur ;
+     *  - g_hub_client_update() pour la partie client.
+     */
+
     DBC_SNAPSHOTS_UPDATED,                  /* Identification d'instantanés*/
 
     /**
diff --git a/src/analysis/db/snapshot.c b/src/analysis/db/snapshot.c
index 6b762b7..79128ef 100644
--- a/src/analysis/db/snapshot.c
+++ b/src/analysis/db/snapshot.c
@@ -82,6 +82,9 @@ static snapshot_node_t *find_snapshot_node(snapshot_node_t *, const snapshot_id_
 /* Ajoute un instantané comme prolongement d'un instantané. */
 static void add_snapshot_node(snapshot_node_t *, snapshot_node_t *);
 
+/* Collecte les descriptions d'une arborescence d'instantanés. */
+static bool pack_snapshot_node(const snapshot_node_t *, packed_buffer *);
+
 
 
 /* --------------------- MANIPULATIONS D'ENSEMBLE D'INSTANTANES --------------------- */
@@ -378,7 +381,7 @@ static DBError save_snapshot_node(const snapshot_node_t *node, xmlDocPtr xdoc, x
         goto exit;
     }
 
-    created = get_snapshot_info_creation(&node->info);
+    created = get_snapshot_info_created(&node->info);
 
     status = _add_uint64_attribute_to_node(xml_node, "created", created);
 
@@ -497,13 +500,47 @@ static snapshot_node_t *find_snapshot_node(snapshot_node_t *node, const snapshot
 
 static void add_snapshot_node(snapshot_node_t *node, snapshot_node_t *child)
 {
+    snapshot_id_t *src;                     /* Identifiant d'instantané #0 */
+    snapshot_id_t *dest;                    /* Identifiant d'instantané #1 */
+
     node->children = realloc(node->children, ++node->count * sizeof(snapshot_node_t *));
 
     node->children[node->count - 1] = child;
 
+    src = get_snapshot_info_id(&node->info);
+    dest = get_snapshot_info_parent_id(&child->info);
+
+    copy_snapshot_id(dest, src);
+
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : node = définition d'instantané à consulter.                  *
+*                pbuf = paquet de données où venir inscrire des infos.        *
+*                                                                             *
+*  Description : Collecte les descriptions d'une arborescence d'instantanés.  *
+*                                                                             *
+*  Retour      : Bilan du déroulement des opérations.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool pack_snapshot_node(const snapshot_node_t *node, packed_buffer *pbuf)
+{
+    bool result;                            /* Bilan à retourner           */
+    size_t i;                               /* Boucle de parcours          */
+
+    result = pack_snapshot_info(&node->info, pbuf);
+
+    for (i = 0; i < node->count && result; i++)
+        result = pack_snapshot_node(node->children[i], pbuf);
+
+    return result;
+
+}
 
 
 
@@ -1044,3 +1081,27 @@ sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *snap, const snapshot_id_t
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : snap = gestionnaire d'instantanés à consulter.               *
+*                pbuf = paquet de données où venir inscrire des infos.        *
+*                                                                             *
+*  Description : Collecte les descriptions de l'ensemble des instantanés.     *
+*                                                                             *
+*  Retour      : Bilan du déroulement des opérations.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_db_snapshot_pack_all(const GDbSnapshot *snap, packed_buffer *pbuf)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = pack_snapshot_node(snap->nodes, pbuf);
+
+    return result;
+
+}
diff --git a/src/analysis/db/snapshot.h b/src/analysis/db/snapshot.h
index f568ba3..543d184 100644
--- a/src/analysis/db/snapshot.h
+++ b/src/analysis/db/snapshot.h
@@ -74,6 +74,9 @@ bool g_db_snapshot_get_current_id(const GDbSnapshot *, snapshot_id_t *);
 /* Fournit la base de données correspondant à instanné donné. */
 sqlite3 *g_db_snapshot_get_database(const GDbSnapshot *, const snapshot_id_t *);
 
+/* Collecte les descriptions de l'ensemble des instantanés. */
+bool g_db_snapshot_pack_all(const GDbSnapshot *, packed_buffer *);
+
 
 
 #endif  /* _ANALYSIS_DB_SNAPSHOT_H */
diff --git a/src/common/packed.c b/src/common/packed.c
index a8ce3c5..a10155d 100644
--- a/src/common/packed.c
+++ b/src/common/packed.c
@@ -241,7 +241,7 @@ bool extend_packed_buffer(packed_buffer *pbuf, const void *buf, size_t len, bool
 *                                                                             *
 ******************************************************************************/
 
-bool extract_packed_buffer(packed_buffer *pbuf, void *buf, size_t len, bool ntoh)
+bool peek_packed_buffer(packed_buffer *pbuf, void *buf, size_t len, bool ntoh)
 {
     bool result;                            /* Bilan à retourner           */
     uint16_t tmp16;                         /* Valeur intermédiaire 16b    */
@@ -250,52 +250,102 @@ bool extract_packed_buffer(packed_buffer *pbuf, void *buf, size_t len, bool ntoh
 
     result = ((pbuf->pos + len - sizeof(uint32_t)) <= pbuf->used);
 
+    if (!result)
+        goto failed;
+
     /* Conversion au formalisme du réseau */
 
     if (!ntoh)
         goto skip_conversion;
 
-    if (result)
+    switch (len)
     {
-        switch (len)
-        {
-            case 1:
-                *((uint8_t *)buf) = *((uint8_t *)(pbuf->data + pbuf->pos));
-                break;
+        case 1:
+            *((uint8_t *)buf) = *((uint8_t *)(pbuf->data + pbuf->pos));
+            break;
 
-            case 2:
-                tmp16 = be16toh(*(uint16_t *)(pbuf->data + pbuf->pos));
-                *((uint16_t *)buf) = tmp16;
-                break;
+        case 2:
+            tmp16 = be16toh(*(uint16_t *)(pbuf->data + pbuf->pos));
+            *((uint16_t *)buf) = tmp16;
+            break;
 
-            case 4:
-                tmp32 = be32toh(*(uint32_t *)(pbuf->data + pbuf->pos));
-                *((uint32_t *)buf) = tmp32;
-                break;
+        case 4:
+            tmp32 = be32toh(*(uint32_t *)(pbuf->data + pbuf->pos));
+            *((uint32_t *)buf) = tmp32;
+            break;
 
-            case 8:
-                tmp64 = be64toh(*(uint64_t *)(pbuf->data + pbuf->pos));
-                *((uint64_t *)buf) = tmp64;
-                break;
+        case 8:
+            tmp64 = be64toh(*(uint64_t *)(pbuf->data + pbuf->pos));
+            *((uint64_t *)buf) = tmp64;
+            break;
 
-            default:
+        default:
 
  skip_conversion:
 
-                /**
-                 * Dans ce cas de figure, c'est à l'appelant de s'assurer que la
-                 * conversion a bien été réalisée.
-                 */
-                assert(!ntoh);
+            /**
+             * Dans ce cas de figure, c'est à l'appelant de s'assurer que la
+             * conversion a bien été réalisée.
+             */
+            assert(!ntoh);
+
+            memcpy(buf, pbuf->data + pbuf->pos, len);
+            break;
 
-                memcpy(buf, pbuf->data + pbuf->pos, len);
-                break;
+    }
 
-        }
+ failed:
 
-        pbuf->pos += len;
+    return result;
 
-    }
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : pbuf = paquet de données à consulter.                        *
+*                len  = quantité de ces données.                              *
+*                                                                             *
+*  Description : Avance la tête de lecture dans les données d'un paquet.      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void advance_packed_buffer(packed_buffer *pbuf, size_t len)
+{
+    pbuf->pos += len;
+
+    assert((pbuf->pos - sizeof(uint32_t)) <= pbuf->used);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : pbuf = paquet de données à consulter.                        *
+*                buf  = nouvelles données à définir.                          *
+*                len  = quantité de ces données.                              *
+*                ntoh = indique si une conversion est à réaliser.             *
+*                                                                             *
+*  Description : Récupère des données depuis un paquet après une réception.   *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool extract_packed_buffer(packed_buffer *pbuf, void *buf, size_t len, bool ntoh)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = peek_packed_buffer(pbuf, buf, len, ntoh);
+
+    if (result)
+        advance_packed_buffer(pbuf, len);
 
     return result;
 
diff --git a/src/common/packed.h b/src/common/packed.h
index 4403ad0..019d21a 100644
--- a/src/common/packed.h
+++ b/src/common/packed.h
@@ -66,6 +66,12 @@ bool has_more_data_in_packed_buffer(const packed_buffer *);
 bool extend_packed_buffer(packed_buffer *, const void *, size_t, bool);
 
 /* Récupère des données depuis un paquet après une réception. */
+bool peek_packed_buffer(packed_buffer *, void *, size_t, bool);
+
+/* Avance la tête de lecture dans les données d'un paquet. */
+void advance_packed_buffer(packed_buffer *, size_t);
+
+/* Récupère des données depuis un paquet après une réception. */
 bool extract_packed_buffer(packed_buffer *, void *, size_t, bool);
 
 /* Lit des données depuis un flux local. */
-- 
cgit v0.11.2-87-g4458