From f439711a77e0719e7f1dcf4b5c5511157986c918 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Mon, 14 Oct 2019 01:24:57 +0200
Subject: Retrieved the current snapshot identifier from servers.

---
 plugins/pychrysalide/analysis/db/client.c | 100 +++++++++++++
 src/analysis/db/cdb.c                     |  42 ++++++
 src/analysis/db/client.c                  | 112 +++++++++++++++
 src/analysis/db/client.h                  |   7 +
 src/analysis/db/item.c                    |   9 ++
 src/analysis/db/misc/snapshot.c           | 228 ++++++++++++++++++++++++++++++
 src/analysis/db/misc/snapshot.h           |  30 +++-
 src/analysis/db/misc/timestamp.c          |  71 +++++++++-
 src/analysis/db/misc/timestamp.h          |   8 +-
 src/analysis/db/protocol.h                |  58 +++++++-
 src/common/packed.c                       |   2 +-
 src/common/xml.c                          | 138 ++++++++++++++++++
 src/common/xml.h                          |  13 ++
 13 files changed, 806 insertions(+), 12 deletions(-)

diff --git a/plugins/pychrysalide/analysis/db/client.c b/plugins/pychrysalide/analysis/db/client.c
index e5b67da..afece26 100644
--- a/plugins/pychrysalide/analysis/db/client.c
+++ b/plugins/pychrysalide/analysis/db/client.c
@@ -54,6 +54,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 l'identifiant de l'instantané courant. */
+static PyObject *py_hub_client_get_current_snapshot(PyObject *, void *);
+
 
 
 /******************************************************************************
@@ -332,6 +335,102 @@ 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 l'identifiant de l'instantané courant.               *
+*                                                                             *
+*  Retour      : Identifiant d'instantané ou None.                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_hub_client_get_current_snapshot(PyObject *self, void *closure)
+{
+    PyObject *result;                       /* Valeur à retourner          */
+    GHubClient *client;                     /* Version native du serveur   */
+    snapshot_id_t id;                       /* Identifiant à transmettre   */
+    bool status;                            /* Validité de cet identifiant */
+
+#define HUB_CLIENT_CURRENT_SNAPSHOT_ATTRIB PYTHON_GETSET_DEF_FULL   \
+(                                                                   \
+    current_snapshot, py_hub_client,                                \
+    "Identifier of the current snapshot, provided as a string."     \
+    "\n"                                                            \
+    "The returned value is a cached version of the value stored at" \
+    " server side. Thus, defining a new current snapshot is"        \
+    " successful as soon as the request to this server is sent."    \
+)
+
+    client = G_HUB_CLIENT(pygobject_get(self));
+
+    status = g_hub_client_get_current_snapshot(client, &id);
+
+    if (status)
+        result = PyUnicode_FromString(snapshot_id_as_string(&id));
+
+    else
+    {
+        result = Py_None;
+        Py_INCREF(result);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self    = objet Python concerné par l'appel.                 *
+*                value   = valeur fournie à intégrer ou prendre en compte.    *
+*                closure = adresse non utilisée ici.                          *
+*                                                                             *
+*  Description : Définit l'identifiant de l'instantané courant.               *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static int py_hub_client_set_current_snapshot(PyObject *self, PyObject *value, void *closure)
+{
+    int ret;                                /* Bilan d'analyse             */
+    void *raw;                              /* Valeur brute d'identifiant  */
+    snapshot_id_t id;                       /* Identifiant reconnu         */
+    bool status;                            /* Bilan d'une conversion      */
+    GHubClient *client;                     /* Version native du serveur   */
+
+    ret = PyUnicode_Check(value);
+    if (!ret) return -1;
+
+    raw = PyUnicode_DATA(value);
+
+    status = init_snapshot_id_from_text(&id, raw);
+    if (!status)
+    {
+        PyErr_SetString(PyExc_TypeError, _("provided value is not a valid snapshot identifier."));
+        return -1;
+    }
+
+    client = G_HUB_CLIENT(pygobject_get(self));
+
+    status = g_hub_client_set_current_snapshot(client, &id);
+    if (!status)
+    {
+        PyErr_SetString(PyExc_TypeError, "unable to send the provided snapshot identifier");
+        return -1;
+    }
+
+    return 0;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : -                                                            *
 *                                                                             *
 *  Description : Fournit un accès à une définition de type à diffuser.        *
@@ -353,6 +452,7 @@ PyTypeObject *get_python_hub_client_type(void)
     };
 
     static PyGetSetDef py_hub_client_getseters[] = {
+        HUB_CLIENT_CURRENT_SNAPSHOT_ATTRIB,
         { NULL }
     };
 
diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c
index 2013750..8e8813a 100644
--- a/src/analysis/db/cdb.c
+++ b/src/analysis/db/cdb.c
@@ -841,6 +841,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
     DBError error;                          /* Bilan d'une opération       */
     packed_buffer out_pbuf;                 /* Tampon d'émission           */
     GDbCollection *collec;                  /* Collection visée au final   */
+    snapshot_id_t id;                       /* Identifiant d'instantané    */
     char *msg;                              /* Erreur à faire remonter     */
 
     void interrupt_poll_with_sigusr1(int sig) { };
@@ -910,6 +911,8 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
                 status = ssl_recv_packed_buffer(&in_pbuf, archive->clients[i].ssl_fd);
                 if (!status) goto gcap_bad_exchange;
 
+ next_command:
+
                 status = extract_packed_buffer(&in_pbuf, &tmp32, sizeof(uint32_t), true);
                 if (!status) goto gcap_bad_exchange;
 
@@ -1000,6 +1003,42 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 
                         break;
 
+                    case DBC_GET_CUR_SNAPSHOT:
+
+                        init_packed_buffer(&out_pbuf);
+
+                        status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_CUR_SNAPSHOT_UPDATED },
+                                                      sizeof(uint32_t), true);
+                        if (!status) goto gcap_bad_reply;
+
+#ifndef NDEBUG
+                        status = g_db_snapshot_get_current_id(archive->snapshot, &id);
+                        assert(status);
+#else
+                        g_db_snapshot_get_current_id(archive->snapshot, &id);
+#endif
+
+                        status = pack_snapshot_id(&id, &out_pbuf);
+                        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_CUR_SNAPSHOT_UPDATED:
+                        log_variadic_message(LMT_INFO,
+                                             _("This command is not available on this side: 0x%08x"), command);
+                        goto gcap_bad_exchange;
+                        break;
+
+                    case DBC_SET_CUR_SNAPSHOT:
+
+
+                        break;
+
                     default:
                         asprintf(&msg, _("Bad protocol command: 0x%08x"), command);
                         LOG_ERROR(LMT_ERROR, msg);
@@ -1009,6 +1048,9 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
 
                 }
 
+                if (has_more_data_in_packed_buffer(&in_pbuf))
+                    goto next_command;
+
                 exit_packed_buffer(&in_pbuf);
 
                 continue;
diff --git a/src/analysis/db/client.c b/src/analysis/db/client.c
index 4c525e1..3372c74 100644
--- a/src/analysis/db/client.c
+++ b/src/analysis/db/client.c
@@ -76,6 +76,12 @@ struct _GHubClient
     bool can_get_updates;                   /* Réception de maj possibles ?*/
     GThread *update;                        /* Procédure de traitement     */
 
+
+
+
+    snapshot_id_t current;                  /* Instantané courant          */
+    bool has_current;                       /* Validité de l'identifiant   */
+
 };
 
 /* Description de client à l'écoute (classe) */
@@ -162,6 +168,8 @@ static void g_hub_client_init(GHubClient *client)
     client->tls_fd = NULL;
     client->desc = NULL;
 
+    client->has_current = false;
+
 }
 
 
@@ -686,6 +694,7 @@ static void *g_hub_client_update(GHubClient *client)
     DBError error;                          /* Bilan d'une commande passée */
     GDbCollection *collec;                  /* Collection visée au final   */
     uint8_t tmp8;                           /* Valeur sur 8 bits           */
+    snapshot_id_t id;                       /* Identifiant d'instantané    */
     char *msg;                              /* Message d'erreur à imprimer */
 
     /**
@@ -694,6 +703,13 @@ static void *g_hub_client_update(GHubClient *client)
 
     init_packed_buffer(&out_pbuf);
 
+    status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_GET_CUR_SNAPSHOT }, sizeof(uint32_t), true);
+    if (!status)
+    {
+        exit_packed_buffer(&out_pbuf);
+        goto exit;
+    }
+
     status = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_GET_ALL_ITEMS }, sizeof(uint32_t), true);
     if (!status)
     {
@@ -798,6 +814,28 @@ static void *g_hub_client_update(GHubClient *client)
                     client->can_get_updates = (tmp8 == 0x1);
                     break;
 
+                case DBC_GET_CUR_SNAPSHOT:
+                    log_variadic_message(LMT_INFO,
+                                         _("This command is not available on this side: 0x%08x"), command);
+                    goto gdcu_bad_exchange;
+                    break;
+
+                case DBC_CUR_SNAPSHOT_UPDATED:
+
+                    status = unpack_snapshot_id(&id, &in_pbuf);
+                    if (!status) goto gdcu_bad_exchange;
+
+                    copy_snapshot_id(&client->current, &id);
+                    client->has_current = true;
+
+                    break;
+
+                case DBC_SET_CUR_SNAPSHOT:
+                    log_variadic_message(LMT_INFO,
+                                         _("This command is not available on this side: 0x%08x"), command);
+                    goto gdcu_bad_exchange;
+                    break;
+
             }
 
             if (has_more_data_in_packed_buffer(&in_pbuf))
@@ -1092,3 +1130,77 @@ bool g_hub_client_set_last_active(GHubClient *client, timestamp_t timestamp)
     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.               *
+*                                                                             *
+*  Retour      : true si l'identifiant retourné est valide, false sinon.      *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_hub_client_get_current_snapshot(GHubClient *client, snapshot_id_t *id)
+{
+    bool result;                            /* Validité à retourner        */
+
+    result = client->has_current;
+
+    if (result)
+        copy_snapshot_id(id, &client->current);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : client = client pour les accès distants à manipuler.         *
+*                id     = identifiant d'instantané à activer.                 *
+*                                                                             *
+*  Description : Définit l'identifiant de l'instantané courant.               *
+*                                                                             *
+*  Retour      : true si la commande a bien été envoyée, false sinon.         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_hub_client_set_current_snapshot(GHubClient *client, const snapshot_id_t *id)
+{
+    bool result;                            /* Bilan partiel à remonter    */
+    packed_buffer out_pbuf;                 /* Tampon d'émission           */
+    SSL *tls_fd;                            /* Canal de communication SSL  */
+
+    init_packed_buffer(&out_pbuf);
+
+    tls_fd = g_hub_client_get_ssl_fd(client);
+
+    if (tls_fd == NULL)
+        result = false;
+
+    else
+    {
+        result = extend_packed_buffer(&out_pbuf, (uint32_t []) { DBC_SET_CUR_SNAPSHOT }, sizeof(uint32_t), true);
+
+        if (result)
+            result = pack_snapshot_id(id, &out_pbuf);
+
+        if (result)
+            result = ssl_send_packed_buffer(&out_pbuf, tls_fd);
+
+        g_hub_client_put_ssl_fd(client, tls_fd);
+
+    }
+
+    exit_packed_buffer(&out_pbuf);
+
+    return result;
+
+}
diff --git a/src/analysis/db/client.h b/src/analysis/db/client.h
index f7f7bcb..36d24e6 100644
--- a/src/analysis/db/client.h
+++ b/src/analysis/db/client.h
@@ -31,6 +31,7 @@
 
 
 #include "collection.h"
+#include "misc/snapshot.h"
 
 
 
@@ -73,6 +74,12 @@ 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 l'identifiant de l'instantané courant. */
+bool g_hub_client_get_current_snapshot(GHubClient *, snapshot_id_t *);
+
+/* Définit l'identifiant de l'instantané courant. */
+bool g_hub_client_set_current_snapshot(GHubClient *, const snapshot_id_t *);
+
 
 
 #endif  /* _ANALYSIS_DB_CLIENT_H */
diff --git a/src/analysis/db/item.c b/src/analysis/db/item.c
index 8fc1763..b3b00fe 100644
--- a/src/analysis/db/item.c
+++ b/src/analysis/db/item.c
@@ -200,7 +200,16 @@ DBFeatures g_db_item_get_feature(const GDbItem *item)
 
 void g_db_item_set_server_side(GDbItem *item)
 {
+#ifndef NDEBUG
+    bool status;                            /* Bilan d'une initialisation  */
+#endif
+
+#ifndef NDEBUG
+    status = init_timestamp(&item->created);
+    assert(status);
+#else
     init_timestamp(&item->created);
+#endif
 
 }
 
diff --git a/src/analysis/db/misc/snapshot.c b/src/analysis/db/misc/snapshot.c
index 982553b..546191b 100644
--- a/src/analysis/db/misc/snapshot.c
+++ b/src/analysis/db/misc/snapshot.c
@@ -210,8 +210,236 @@ bool pack_snapshot_id(const snapshot_id_t *id, packed_buffer *pbuf)
 /* ---------------------------------------------------------------------------------- */
 
 
+/******************************************************************************
+*                                                                             *
+*  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.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool init_snapshot_info(snapshot_info_t *info)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = init_snapshot_id(&info->id);
+
+    if (result)
+        result = init_timestamp(&info->created);
+
+    if (result)
+    {
+        info->name = NULL;
+        info->desc = NULL;
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : info    = description d'instantané à initialiser. [OUT]      *
+*                id      = source de données pour l'identifiant.              *
+*                created = source de données pour la date de création.        *
+*                name    = source de données éventuelle pour le nom.          *
+*                desc    = source de données éventuelle pour la description.  *
+*                                                                             *
+*  Description : Construit une description pour instantané de base de données.*
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool init_snapshot_info_from_text(snapshot_info_t *info, const char *id, uint64_t created, const char *name, const char *desc)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = init_snapshot_id_from_text(&info->id, id);
+
+    if (result)
+        result = init_timestamp_from_value(&info->created, created);
+
+    if (result)
+    {
+        if (name == NULL)
+            info->name = NULL;
+        else
+            info->name = strdup(name);
+
+        if (desc == NULL)
+            info->desc = NULL;
+        else
+            info->desc = strdup(desc);
+
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : info = description d'instantané à initialiser. [OUT]         *
+*                                                                             *
+*  Description : Libère la mémoire occupée par une description d'instantané.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void exit_snapshot_info(snapshot_info_t *info)
+{
+    if (info->name != NULL)
+    {
+        free(info->name);
+        info->name = NULL;
+    }
+
+    if (info->desc != NULL)
+    {
+        free(info->desc);
+        info->desc = NULL;
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : dest = destination de la copie de description. [OUT]         *
+*                src  = source de la description à copier.                    *
+*                                                                             *
+*  Description : Effectue une copie de description d'instantané.              *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void copy_snapshot_info(snapshot_info_t *dest, const snapshot_info_t *src)
+{
+    exit_snapshot_info(dest);
+
+    copy_snapshot_id(&dest->id, &src->id);
+
+    copy_timestamp(&dest->created, &src->created);
+
+    if (src->name != NULL)
+        dest->name = strdup(src->name);
+
+    if (src->desc != NULL)
+        dest->desc = strdup(src->desc);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : info = informations à constituer. [OUT]                      *
+*                pbuf = paquet de données où venir puiser les infos.          *
+*                                                                             *
+*  Description : Importe la description d'un identifiant d'instantané.        *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool unpack_snapshot_info(snapshot_info_t *info, packed_buffer *pbuf)
+{
+    bool result;                            /* Bilan à retourner           */
+    rle_string string;                      /* Chaîne à transmettre        */
+
+    result = unpack_snapshot_id(&info->id, pbuf);
+
+    if (result)
+        result = unpack_timestamp(&info->created, pbuf);
+
+    if (result)
+    {
+        result = unpack_rle_string(&string, pbuf);
 
+        if (result)
+        {
+            info->name = strdup(get_rle_string(&string));
+            exit_rle_string(&string);
+        }
+
+    }
 
+    if (result)
+    {
+        result = unpack_rle_string(&string, pbuf);
 
+        if (result)
+        {
+            info->desc = strdup(get_rle_string(&string));
+            exit_rle_string(&string);
+        }
 
+    }
 
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : info = informations à sauvegarer.                            *
+*                pbuf = paquet de données où venir inscrire les infos.        *
+*                                                                             *
+*  Description : Exporte la description d'un identifiant d'instantané.        *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+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);
+
+    if (result)
+        result = pack_timestamp(&info->created, pbuf);
+
+    if (result)
+    {
+        init_static_rle_string(&string, info->name);
+
+        result = pack_rle_string(&string, pbuf);
+
+        exit_rle_string(&string);
+
+    }
+
+    if (result)
+    {
+        init_static_rle_string(&string, info->desc);
+
+        result = pack_rle_string(&string, pbuf);
+
+        exit_rle_string(&string);
+
+    }
+
+    return result;
+
+}
diff --git a/src/analysis/db/misc/snapshot.h b/src/analysis/db/misc/snapshot.h
index 4aaa6f9..e3a003c 100644
--- a/src/analysis/db/misc/snapshot.h
+++ b/src/analysis/db/misc/snapshot.h
@@ -29,6 +29,8 @@
 #include <stdint.h>
 
 
+#include "rlestr.h"
+#include "timestamp.h"
 #include "../../../common/packed.h"
 
 
@@ -42,7 +44,7 @@
 /* Représentation d'un instantané */
 typedef struct _snapshot_id_t
 {
-    char name[SNAP_ID_HEX_SZ];
+    char name[SNAP_ID_HEX_SZ];              /* Caractères hexadécimaux     */
 
 } snapshot_id_t;
 
@@ -72,10 +74,36 @@ bool pack_snapshot_id(const snapshot_id_t *, packed_buffer *);
 /* --------------------------- PROPRIETES DES INSTANTANES --------------------------- */
 
 
+/* Description d'un instantané */
+typedef struct _snapshot_info_t
+{
+    snapshot_id_t id;                       /* Identifiant attribué        */
+
+    timestamp_t created;                    /* Date de création            */
+
+    char *name;                             /* Nom de l'instantané         */
+    char *desc;                             /* Description associée        */
+
+} snapshot_info_t;
+
+
+/* Construit une description pour instantané de base de données. */
+bool init_snapshot_info(snapshot_info_t *);
+
+/* Construit une description pour instantané de base de données. */
+bool init_snapshot_info_from_text(snapshot_info_t *, const char *, uint64_t, const char *, const char *);
 
+/* Libère la mémoire occupée par une description d'instantané. */
+void exit_snapshot_info(snapshot_info_t *);
 
+/* Effectue une copie de description d'instantané. */
+void copy_snapshot_info(snapshot_info_t *, const snapshot_info_t *);
 
+/* Importe la description d'un identifiant d'instantané. */
+bool unpack_snapshot_info(snapshot_info_t *, packed_buffer *);
 
+/* Exporte la description d'un identifiant d'instantané. */
+bool pack_snapshot_info(const snapshot_info_t *, packed_buffer *);
 
 
 
diff --git a/src/analysis/db/misc/timestamp.c b/src/analysis/db/misc/timestamp.c
index 12299eb..dfc6f25 100644
--- a/src/analysis/db/misc/timestamp.c
+++ b/src/analysis/db/misc/timestamp.c
@@ -29,6 +29,9 @@
 #include <time.h>
 
 
+#include "../../../core/logs.h"
+
+
 
 /******************************************************************************
 *                                                                             *
@@ -36,19 +39,59 @@
 *                                                                             *
 *  Description : Obtient un horodatage initialisé au moment même.             *
 *                                                                             *
-*  Retour      : -                                                            *
+*  Retour      : Bilan de l'opération.                                        *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-void init_timestamp(timestamp_t *timestamp)
+bool init_timestamp(timestamp_t *timestamp)
 {
+    bool result;                            /* Bilan à retourner           */
     struct timespec info;                   /* Détails sur l'époque        */
+    int ret;                                /* Bilan de la récupération    */
+
+    ret = clock_gettime(CLOCK_REALTIME, &info);
 
-    clock_gettime(CLOCK_REALTIME, &info);
+    if (ret != 0)
+    {
+        LOG_ERROR_N("clock_gettime");
+        result = false;
+    }
 
-    *timestamp = info.tv_sec * 1000000 + info.tv_nsec / 1000;
+    else
+    {
+        *timestamp = info.tv_sec * 1000000 + info.tv_nsec / 1000;
+        result = true;
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : timestamp = horodatage à initialiser. [OUT]                  *
+*                value     = valeur d'initialisation.                         *
+*                                                                             *
+*  Description : Obtient un horodatage initialisé avec une valeur donnée.     *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool init_timestamp_from_value(timestamp_t *timestamp, uint64_t value)
+{
+    bool result;                            /* Bilan à retourner           */
+
+    result = true;
+
+    *timestamp = value;
+
+    return result;
 
 }
 
@@ -79,6 +122,26 @@ bool timestamp_is_younger(timestamp_t stamp, timestamp_t limit)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : dest = destination de la copie d'horodatage. [OUT]           *
+*                src  = source de l'horodatage à copier.                      *
+*                                                                             *
+*  Description : Effectue une copie d'horodatage.                             *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void copy_timestamp(timestamp_t *dest, const timestamp_t *src)
+{
+    *dest = *src;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : t1 = premier horodatage à comparer.                          *
 *                t2 = second horodatage à comparer.                           *
 *                                                                             *
diff --git a/src/analysis/db/misc/timestamp.h b/src/analysis/db/misc/timestamp.h
index 002f4d4..7f6290876 100644
--- a/src/analysis/db/misc/timestamp.h
+++ b/src/analysis/db/misc/timestamp.h
@@ -39,11 +39,17 @@ typedef uint64_t timestamp_t;
 
 
 /* Obtient un horodatage initialisé au moment même. */
-void init_timestamp(timestamp_t *);
+bool init_timestamp(timestamp_t *);
+
+/* Obtient un horodatage initialisé avec une valeur donnée. */
+bool init_timestamp_from_value(timestamp_t *, uint64_t);
 
 /* Définit si un horodatage est plus récent qu'un autre ou non. */
 bool timestamp_is_younger(timestamp_t, timestamp_t);
 
+/* Effectue une copie d'horodatage. */
+void copy_timestamp(timestamp_t *, const timestamp_t *);
+
 /* Effectue la comparaison entre deux horodatages. */
 int cmp_timestamp(const timestamp_t *, const timestamp_t *);
 
diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h
index b42ad15..22f564a 100644
--- a/src/analysis/db/protocol.h
+++ b/src/analysis/db/protocol.h
@@ -127,8 +127,8 @@ typedef enum _DBCommand
      *    [ Statut d'exécution ; cf. DBError          ]
      *
      * Les traitements se réalisent dans :
-     *  - g_db_client_save() pour la partie client en émission.
-     *  - g_cdb_archive_process() pour la partie serveur.
+     *  - g_db_client_save() pour la partie client en émission ;
+     *  - g_cdb_archive_process() pour la partie serveur ;
      *  - g_db_client_update() pour la partie client en réception.
      *
      */
@@ -165,7 +165,7 @@ typedef enum _DBCommand
      *    [ marqueur de fin : octet 0x0                 ]
      *
      * Les traitements se réalisent dans :
-     *  - g_db_client_update() pour la partie client.
+     *  - g_db_client_update() pour la partie client ;
      *  - g_cdb_archive_process() pour la partie serveur.
      *
      */
@@ -179,7 +179,7 @@ typedef enum _DBCommand
      * Le client connecté envoie un paquet de la forme suivante :
      *
      *    [ Statut d'historique : DBC_SET_LAST_ACTIVE   ]
-     *    [ <horodatage du dernier élément actif        ]
+     *    [ <horodatage du dernier élément actif>       ]
      *
      * Le serveur s'exécute et notifie le client d'éventuels changements,
      * avec une série de paquets de la forme :
@@ -189,13 +189,61 @@ typedef enum _DBCommand
      *    [ <élément dont le statut a évolué>           ]
      *
      * Les traitements se réalisent dans :
-     *  - g_db_collection_disable_at() pour la partie serveur.
+     *  - g_db_collection_disable_at() pour la partie serveur ;
      *  - g_db_collection_update_item_state() pour la partie client.
      *
      */
 
     DBC_SET_LAST_ACTIVE,                    /* Définition du dernier actif */   // REMME
 
+    /* ------- Gestion des instantanés ------- */
+
+    DBC_SNAPSHOTS_UPDATED,                  /* Identification d'instantanés*/
+
+    /**
+     * Gestion de la commande 'DBC_GET_CUR_SNAPSHOT'.
+     *
+     * Le client connecté envoie un paquet de la forme suivante :
+     *
+     *    [ Gestion d'instantané : DBC_GET_CUR_SNAPSHOT         ]
+     *
+     * 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_CUR_SNAPSHOT,                   /* Demande d'identification    */
+
+    /**
+     * Gestion de la commande 'DBC_CUR_SNAPSHOT_UPDATED'.
+     *
+     * Le serveur envoie au client un paquet de la forme suivante :
+     *
+     *    [ Gestion d'instantané : DBC_CUR_SNAPSHOT_UPDATED     ]
+     *    [ <identifiant d'instantané>                          ]
+     *
+     * 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_CUR_SNAPSHOT_UPDATED,               /* Mise à jour de l'instantané */
+
+    /**
+     * Gestion de la commande 'DBC_SET_CUR_SNAPSHOT'.
+     *
+     * Le client connecté envoie un paquet de la forme suivante :
+     *
+     *    [ Gestion d'instantané : DBC_SET_CUR_SNAPSHOT         ]
+     *    [ <identifiant d'instantané>                          ]
+     *
+     * La définition d'un nouvel identifiant d'instantané courant se réalise dans :
+     *  - g_hub_client_set_current_snapshot() pour la partie client ;
+     *  - 
+     */
+
+    DBC_SET_CUR_SNAPSHOT,                   /* Définition de l'instantané  */
+
     DBC_COUNT
 
 } DBCommand;
diff --git a/src/common/packed.c b/src/common/packed.c
index 163ed3b..a8ce3c5 100644
--- a/src/common/packed.c
+++ b/src/common/packed.c
@@ -141,7 +141,7 @@ bool has_more_data_in_packed_buffer(const packed_buffer *pbuf)
 {
     bool result;                            /* Bilan à retourner           */
 
-    result = (pbuf->pos < pbuf->used);
+    result = (pbuf->pos < (pbuf->used + sizeof(uint32_t)));
 
     return result;
 
diff --git a/src/common/xml.c b/src/common/xml.c
index aaed2ea..3774359 100644
--- a/src/common/xml.c
+++ b/src/common/xml.c
@@ -25,6 +25,8 @@
 #include "xml.h"
 
 
+#include <inttypes.h>
+#include <malloc.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -453,6 +455,43 @@ bool qck_get_node_prop_long_value(xmlNodePtr node, const char *name, long *out)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : node = noeud dont une propriété est à lire.                  *
+*                name = nom de la propriété à lire.                           *
+*                out  = valeur entière lue depuis le contenu textuel. [OUT]   *
+*                                                                             *
+*  Description : Obtient la valeur entière d'une propriété d'un élément.      *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool qck_get_node_prop_uint64_value(xmlNodePtr node, const char *name, uint64_t *out)
+{
+    bool result;                            /* Bilan à retourner           */
+    char *value;                            /* Valeur brute lue            */
+
+    value = qck_get_node_prop_value(node, name);
+
+    if (value)
+    {
+        result = true;
+
+        *out = strtoull(value, NULL, 10);
+        free(value);
+
+    }
+    else
+        result = false;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : xpathCtx = contexte à utiliser pour les recherches.          *
 *                path     = chemin d'accès au noeud à traiter.                *
 *                name     = nom de la propriété à lire.                       *
@@ -522,6 +561,41 @@ bool get_node_prop_long_value(xmlXPathContextPtr xpathCtx, const char *path, con
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : xpathCtx = contexte à utiliser pour les recherches.          *
+*                path     = chemin d'accès au noeud à traiter.                *
+*                name     = nom de la propriété à lire.                       *
+*                out      = valeur entière obtenue via contenu textuel. [OUT] *
+*                                                                             *
+*  Description : Obtient la valeur entière d'une propriété d'un élément.      *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool get_node_prop_uint64_value(xmlXPathContextPtr xpathCtx, const char *path, const char *name, uint64_t *out)
+{
+    bool result;                            /* Bilan à retourner           */
+    xmlXPathObjectPtr xpathObj;             /* Point de départ XML         */
+
+    result = NULL;
+
+    xpathObj = get_node_xpath_object(xpathCtx, path);
+    if (xpathObj == NULL) return NULL;
+
+    if (xpathObj->nodesetval->nodeNr > 0)
+        result = qck_get_node_prop_uint64_value(xpathObj->nodesetval->nodeTab[0], name, out);
+
+    xmlXPathFreeObject(xpathObj);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : node = noeud de texte avec un lien avec le document XML.     *
 *                                                                             *
 *  Description : Construit un chemin d'accès complet selon le fichier XML.    *
@@ -1250,3 +1324,67 @@ bool add_long_attribute_to_node(xmlDocPtr xdoc, xmlXPathContextPtr context, cons
     return _add_long_attribute_to_node(node, name, value);
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : node  = noeud dont le contenu est à mettre à jour.           *
+*                name  = nom de la propriété à créer.                         *
+*                value = valeur numérique à placer.                           *
+*                                                                             *
+*  Description : Ajoute une propriété à un noeud existant donné.              *
+*                                                                             *
+*  Retour      : true en cas de succès, false sinon.                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool _add_uint64_attribute_to_node(xmlNodePtr node, const char *name, uint64_t value)
+{
+    bool result;                            /* Bilan à retourner           */
+    char *tmp;                              /* Stockage temporaire         */
+    int ret;                                /* Bilan de l'impression       */
+
+    ret = asprintf(&tmp, "%" PRIu64, value);
+
+    if (ret == -1)
+        result = false;
+
+    else
+    {
+        result = _add_string_attribute_to_node(node, name, tmp);
+        free(tmp);
+    }
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : xdoc    = structure XML chargée.                             *
+*                context = contexte à utiliser pour les recherches.           *
+*                path    = chemin d'accès au noeud visé.                      *
+*                name    = nom de la propriété à créer.                       *
+*                value   = valeur numérique à placer.                         *
+*                                                                             *
+*  Description : Ajoute une propriété à un noeud existant donné.              *
+*                                                                             *
+*  Retour      : true en cas de succès, false sinon.                          *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool add_uint64_attribute_to_node(xmlDocPtr xdoc, xmlXPathContextPtr context, const char *path, const char *name, uint64_t value)
+{
+    xmlNodePtr node;                        /* Noeud à modifier            */
+
+    node = ensure_node_exist(xdoc, context, path);
+    if (node == NULL) return false;
+
+    return _add_uint64_attribute_to_node(node, name, value);
+
+}
diff --git a/src/common/xml.h b/src/common/xml.h
index ed009c6..5ae7664 100644
--- a/src/common/xml.h
+++ b/src/common/xml.h
@@ -28,6 +28,7 @@
 
 #include <glib.h>
 #include <stdbool.h>
+#include <stdint.h>
 #include <libxml/tree.h>
 #include <libxml/xmlwriter.h>
 #include <libxml/xpath.h>
@@ -79,12 +80,18 @@ char *qck_get_node_prop_value(xmlNodePtr, const char *);
 /* Obtient la valeur entière d'une propriété d'un élément. */
 bool qck_get_node_prop_long_value(xmlNodePtr, const char *, long *);
 
+/* Obtient la valeur entière d'une propriété d'un élément. */
+bool qck_get_node_prop_uint64_value(xmlNodePtr, const char *, uint64_t *);
+
 /* Obtient la valeur d'une propriété d'un élément. */
 char *get_node_prop_value(xmlXPathContextPtr, const char *, const char *);
 
 /* Obtient la valeur entière d'une propriété d'un élément. */
 bool get_node_prop_long_value(xmlXPathContextPtr, const char *, const char *, long *);
 
+/* Obtient la valeur entière d'une propriété d'un élément. */
+bool get_node_prop_uint64_value(xmlXPathContextPtr, const char *, const char *, uint64_t *);
+
 /* Construit un chemin d'accès complet selon le fichier XML. */
 char *qck_build_filename_with_doc_url(xmlNodePtr);
 
@@ -158,6 +165,12 @@ bool _add_long_attribute_to_node(xmlNodePtr, const char *, long);
 /* Ajoute une propriété à un noeud existant donné. */
 bool add_long_attribute_to_node(xmlDocPtr, xmlXPathContextPtr, const char *, const char *, long);
 
+/* Ajoute une propriété à un noeud existant donné. */
+bool _add_uint64_attribute_to_node(xmlNodePtr, const char *, uint64_t);
+
+/* Ajoute une propriété à un noeud existant donné. */
+bool add_uint64_attribute_to_node(xmlDocPtr, xmlXPathContextPtr, const char *, const char *, uint64_t);
+
 
 
 #endif  /* _XML_H */
-- 
cgit v0.11.2-87-g4458