summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2014-10-16 22:04:29 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2014-10-16 22:04:29 (GMT)
commit6d34dbbb00da0c276261d0e1ce4bf862f22fd8e0 (patch)
treea5d3c8644691934ba84a91919f7db177f70743f1
parente75f44a99c8f984af4c47fa9a2c8e7e9841700d8 (diff)
Stored a bookmark into the data base and saved the archive.
git-svn-id: svn://svn.gna.org/svn/chrysalide/trunk@414 abbe820e-26c8-41b2-8c08-b7b2b41f8b0a
-rw-r--r--ChangeLog39
-rw-r--r--src/analysis/binary.c6
-rw-r--r--src/analysis/db/cdb.c47
-rw-r--r--src/analysis/db/cdb.h2
-rw-r--r--src/analysis/db/client.c43
-rw-r--r--src/analysis/db/client.h3
-rw-r--r--src/analysis/db/collection.c162
-rw-r--r--src/analysis/db/collection.h6
-rw-r--r--src/analysis/db/item-int.h10
-rw-r--r--src/analysis/db/item.c93
-rw-r--r--src/analysis/db/item.h18
-rw-r--r--src/analysis/db/items/bookmark.c61
-rw-r--r--src/analysis/db/items/bookmark.h2
-rw-r--r--src/analysis/db/protocol.h27
-rw-r--r--src/arch/vmpa.c56
-rw-r--r--src/arch/vmpa.h9
-rwxr-xr-xsrc/common/Makefile.am1
-rw-r--r--src/common/sqlite.h56
-rw-r--r--src/core/collections.c2
-rw-r--r--src/gui/panels/bookmarks.c166
20 files changed, 694 insertions, 115 deletions
diff --git a/ChangeLog b/ChangeLog
index d621c81..fef9d17 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,42 @@
+14-10-16 Cyrille Bagard <nocbos@gmail.com>
+
+ * src/analysis/binary.c:
+ Update test code for bookmarks.
+
+ * src/analysis/db/cdb.c:
+ * src/analysis/db/cdb.h:
+ Write an archive on demand, and provide a final status.
+
+ * src/analysis/db/client.c:
+ * src/analysis/db/client.h:
+ Ask the server for saving its archive.
+
+ * src/analysis/db/collection.c:
+ * src/analysis/db/collection.h:
+ * src/analysis/db/item.c:
+ * src/analysis/db/item.h:
+ * src/analysis/db/item-int.h:
+ * src/analysis/db/items/bookmark.c:
+ * src/analysis/db/items/bookmark.h:
+ Store an item into the data base.
+
+ * src/analysis/db/protocol.h:
+ Define a 'save' command.
+
+ * src/arch/vmpa.c:
+ * src/arch/vmpa.h:
+ Provide a function to save the new 'vmpa_t' types into a SQLite DB.
+
+ * src/common/Makefile.am:
+ Add the 'sqlite.h' file to libcommon_la_SOURCES.
+
+ * src/common/sqlite.h:
+ New entry: extend SQLite with some internal definitions.
+
+ * src/core/collections.c:
+ * src/gui/panels/bookmarks.c:
+ Update code.
+
14-10-12 Cyrille Bagard <nocbos@gmail.com>
* src/arch/arm/v7/arm.c:
diff --git a/src/analysis/binary.c b/src/analysis/binary.c
index 5577bb8..0fcbe0d 100644
--- a/src/analysis/binary.c
+++ b/src/analysis/binary.c
@@ -309,7 +309,7 @@ GLoadedBinary *g_loaded_binary_new_from_xml(xmlXPathContextPtr context, const ch
bool status;
- init_vmpa(&addr, 123, 0x200);
+ init_vmpa(&addr, 0xaeb4, VMPA_NO_VIRTUAL);
bm = g_db_bookmark_new(&addr, "Premier commentaire");
@@ -323,6 +323,10 @@ GLoadedBinary *g_loaded_binary_new_from_xml(xmlXPathContextPtr context, const ch
printf("send nok\n");
+ g_db_client_save(result->local);
+
+
+
/*
safe_send(client->fd, (uint32_t []) { htobe32(DBC_COLLECTION) }, sizeof(uint32_t), MSG_MORE);
safe_send(client->fd, (uint32_t []) { htobe32(DBF_BOOKMARKS) }, sizeof(uint32_t), MSG_MORE);
diff --git a/src/analysis/db/cdb.c b/src/analysis/db/cdb.c
index 563aa40..55d1b2d 100644
--- a/src/analysis/db/cdb.c
+++ b/src/analysis/db/cdb.c
@@ -34,7 +34,6 @@
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
-#include <sqlite3.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
@@ -283,6 +282,8 @@ GCdbArchive *g_cdb_archive_new(const char *owner, const rle_string *hash, const
result->filename = get_xdg_config_dir(suffix);
+ printf("dealing with '%s'...\n", result->filename);
+
free(suffix);
if (!mkpath(result->filename))
@@ -318,7 +319,7 @@ GCdbArchive *g_cdb_archive_new(const char *owner, const rle_string *hash, const
g_cdb_archive_create_xml_desc(result, user);
g_cdb_archive_create_db(result, NULL);
- if (!g_cdb_archive_write(result))
+ if (g_cdb_archive_write(result) != DBE_NONE)
goto gcan_error;
}
@@ -473,13 +474,13 @@ static bool g_cdb_archive_read(GCdbArchive *archive)
* *
******************************************************************************/
-bool g_cdb_archive_write(const GCdbArchive *archive)
+DBError g_cdb_archive_write(const GCdbArchive *archive)
{
- bool result; /* Conclusion à retourner */
+ DBError result; /* Conclusion à retourner */
struct archive *out; /* Archive à constituer */
int ret; /* Bilan d'un appel */
- result = false;
+ result = DBE_ARCHIVE_ERROR;
out = archive_write_new();
archive_write_add_filter_xz(out);
@@ -488,16 +489,19 @@ bool g_cdb_archive_write(const GCdbArchive *archive)
ret = archive_write_open_filename(out, archive->filename);
if (ret != ARCHIVE_OK) goto gcaw_exit;
- bool add_file_to_archive(struct archive *out, const char *src, const char *path)
+ DBError add_file_to_archive(struct archive *out, const char *src, const char *path)
{
+ DBError status; /* Bilan à renvoyer */
struct stat info; /* Informations d'origine */
struct archive_entry *entry; /* Elément de l'archive */
int fd; /* Flux ouvert en lecture */
char buffer[ARCHIVE_RBUF_SIZE]; /* Tampon pour les transferts */
ssize_t len; /* Quantité de données lues */
+ status = DBE_ARCHIVE_ERROR;
+
ret = stat(src, &info);
- if (ret != 0) return false;
+ if (ret != 0) return DBE_SYS_ERROR;
entry = archive_entry_new();
@@ -508,7 +512,11 @@ bool g_cdb_archive_write(const GCdbArchive *archive)
if (ret != 0) goto afta_error;
fd = open(src, O_RDONLY);
- if (fd == -1) goto afta_error;
+ if (fd == -1)
+ {
+ status = DBE_SYS_ERROR;
+ goto afta_error;
+ }
for (len = safe_read(fd, buffer, ARCHIVE_RBUF_SIZE);
len > 0;
@@ -522,18 +530,20 @@ bool g_cdb_archive_write(const GCdbArchive *archive)
archive_entry_free(entry);
- return true;
+ return DBE_NONE;
afta_error:
archive_entry_free(entry);
- return false;
+ return status;
}
result = add_file_to_archive(out, archive->xml_desc, "desc.xml");
- result &= add_file_to_archive(out, archive->sql_db, "sql.db");
+
+ if (result == DBE_NONE)
+ result = add_file_to_archive(out, archive->sql_db, "sql.db");
gcaw_exit:
@@ -781,6 +791,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
uint32_t val32; /* Valeur sur 32 bits */
bool status; /* Bilan de lecture initiale */
uint32_t command; /* Commande de la requête */
+ DBError error; /* Bilan d'une opération */
GDbCollection *collec; /* Collection visée au final */
void interrupt_poll_with_sigusr1(int sig) { };
@@ -843,6 +854,18 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
switch (command)
{
+ case DBC_SAVE:
+
+ error = g_cdb_archive_write(archive);
+
+ if (!safe_send(fds[i].fd, (uint32_t []) { htobe32(DBC_SAVE) }, sizeof(uint32_t), 0))
+ goto gcap_bad_exchange;
+
+ if (!safe_send(fds[i].fd, (uint32_t []) { htobe32(error) }, sizeof(uint32_t), 0))
+ goto gcap_bad_exchange;
+
+ break;
+
case DBC_COLLECTION:
status = safe_recv(fds[i].fd, &val32, sizeof(uint32_t), 0);
@@ -851,7 +874,7 @@ static void *g_cdb_archive_process(GCdbArchive *archive)
collec = find_collection_in_list(archive->collections, be32toh(val32));
if (collec == NULL) goto gcap_bad_exchange;
- status = g_db_collection_recv(collec, fds[i].fd);
+ status = g_db_collection_recv(collec, fds[i].fd, archive->db);
if (!status) goto gcap_bad_exchange;
printf("## CDB ## Got something for collection %p...\n", collec);
diff --git a/src/analysis/db/cdb.h b/src/analysis/db/cdb.h
index 17327f5..e9abb02 100644
--- a/src/analysis/db/cdb.h
+++ b/src/analysis/db/cdb.h
@@ -60,7 +60,7 @@ GType g_cdb_archive_get_type(void);
GCdbArchive *g_cdb_archive_new(const char *, const rle_string *, const rle_string *, DBError *);
/* Enregistre une archive avec tous les éléments à conserver. */
-bool g_cdb_archive_write(const GCdbArchive *);
+DBError g_cdb_archive_write(const GCdbArchive *);
/* Détermine si une empreinte correspond à celle d'une archive. */
int g_cdb_archive_compare_hash(const GCdbArchive *, const rle_string *);
diff --git a/src/analysis/db/client.c b/src/analysis/db/client.c
index 4b807ae..71df99c 100644
--- a/src/analysis/db/client.c
+++ b/src/analysis/db/client.c
@@ -328,6 +328,7 @@ static void *g_db_client_update(GDbClient *client)
uint32_t val32; /* Valeur sur 32 bits */
bool status; /* Bilan d'une opération */
uint32_t command; /* Commande de la requête */
+ DBError error; /* Bilan d'une commande passée */
GDbCollection *collec; /* Collection visée au final */
fds.fd = client->fd;
@@ -353,6 +354,19 @@ static void *g_db_client_update(GDbClient *client)
switch (command)
{
+ case DBC_SAVE:
+
+ status = safe_recv(fds.fd, &val32, sizeof(uint32_t), 0);
+ if (!status) goto gdcu_bad_exchange;
+
+ error = be32toh(val32);
+
+ printf("## CLIENT ## Saved ? %d\n", error);
+
+
+
+ break;
+
case DBC_COLLECTION:
status = safe_recv(fds.fd, &val32, sizeof(uint32_t), 0);
@@ -361,7 +375,7 @@ static void *g_db_client_update(GDbClient *client)
collec = find_collection_in_list(client->collections, be32toh(val32));
if (collec == NULL) goto gdcu_bad_exchange;
- status = g_db_collection_recv(collec, fds.fd);
+ status = g_db_collection_recv(collec, fds.fd, NULL);
if (!status) goto gdcu_bad_exchange;
@@ -459,3 +473,30 @@ void g_db_client_put_fd(GDbClient *client)
g_mutex_unlock(&client->sending_lock);
}
+
+
+/******************************************************************************
+* *
+* Paramètres : client = client pour les accès distants à manipuler. *
+* *
+* Description : Effectue une demande de sauvegarde de l'état courant. *
+* *
+* Retour : true si la commande a bien été envoyée, false sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_db_client_save(GDbClient *client)
+{
+ bool result; /* Bilan partiel à remonter */
+
+ g_db_client_get_fd(client);
+
+ result = safe_send(client->fd, (uint32_t []) { htobe32(DBC_SAVE) }, sizeof(uint32_t), 0);
+
+ g_db_client_put_fd(client);
+
+ return result;
+
+}
diff --git a/src/analysis/db/client.h b/src/analysis/db/client.h
index a1a9f8e..963744b 100644
--- a/src/analysis/db/client.h
+++ b/src/analysis/db/client.h
@@ -65,6 +65,9 @@ int g_db_client_get_fd(GDbClient *);
/* Marque le canal de communication comme disponible. */
void g_db_client_put_fd(GDbClient *);
+/* Effectue une demande de sauvegarde de l'état courant. */
+bool g_db_client_save(GDbClient *);
+
#endif /* _ANALYSIS_DB_CLIENT_H */
diff --git a/src/analysis/db/collection.c b/src/analysis/db/collection.c
index 20b1ff3..5c25720 100644
--- a/src/analysis/db/collection.c
+++ b/src/analysis/db/collection.c
@@ -24,6 +24,11 @@
#include "collection.h"
+#include <stdio.h>
+#include <string.h>
+
+
+#include "../../common/extstr.h"
#include "../../common/io.h"
#include "../../glibext/chrysamarshal.h"
@@ -36,6 +41,7 @@ struct _GDbCollection
uint32_t featuring; /* Fonctionnalité représentée */
GType type; /* Identifiant GLib équivalent */
+ const char *name; /* Nom en base de données */
GList *items; /* Eléments rassemblés */
GList *sorted; /* Eléments triés */
@@ -71,6 +77,15 @@ static void g_db_collection_finalize(GDbCollection *);
+/* --------------------- MANIPULATIONS AVEC UNE BASE DE DONNEES --------------------- */
+
+
+/* Enregistre un élément de collection dans une base de données. */
+static bool g_db_collection_store_item(const GDbCollection *, const GDbItem *, sqlite3 *);
+
+
+
+
/* Indique le type défini pour une collection générique d'éléments. */
G_DEFINE_TYPE(GDbCollection, g_db_collection, G_TYPE_OBJECT);
@@ -170,7 +185,8 @@ static void g_db_collection_finalize(GDbCollection *collec)
/******************************************************************************
* *
* Paramètres : id = identifiant réseau des éléments à traiter. *
-* type = type GLib des éléments à intégrer dans la collection. *
+* type = type GLib des éléments à placer dans la collection. *
+* name = indique le nom désignant la table associée. *
* *
* Description : Prépare la mise en place d'une nouvelle collection. *
* *
@@ -180,7 +196,7 @@ static void g_db_collection_finalize(GDbCollection *collec)
* *
******************************************************************************/
-GDbCollection *g_db_collection_new(uint32_t id, GType type)
+GDbCollection *g_db_collection_new(uint32_t id, GType type, const char *name)
{
GDbCollection *result; /* Adresse à retourner */
@@ -188,6 +204,7 @@ GDbCollection *g_db_collection_new(uint32_t id, GType type)
result->featuring = id;
result->type = type;
+ result->name = name;
return result;
@@ -233,6 +250,7 @@ uint32_t g_db_collection_get_feature(const GDbCollection *collec)
* *
* Paramètres : collec = ensemble d'éléments à considérer. *
* fd = flux ouvert en lecture pour la réception de données.*
+* db = base de données à mettre à jour. *
* *
* Description : Réceptionne et traite une requête réseau pour collection. *
* *
@@ -244,7 +262,7 @@ uint32_t g_db_collection_get_feature(const GDbCollection *collec)
* *
******************************************************************************/
-bool g_db_collection_recv(GDbCollection *collec, int fd)
+bool g_db_collection_recv(GDbCollection *collec, int fd, sqlite3 *db)
{
bool result; /* Bilan à faire remonter */
uint32_t val32; /* Valeur sur 32 bits */
@@ -269,6 +287,8 @@ bool g_db_collection_recv(GDbCollection *collec, int fd)
{
case DBA_ADD_ITEM:
result = g_db_collection_add_item(collec, item);
+ if (result && db != NULL)
+ result = g_db_collection_store_item(collec, item, db);
break;
case DBA_REM_ITEM:
@@ -430,6 +450,10 @@ bool g_db_collection_add_item(GDbCollection *collec, GDbItem *item)
g_signal_emit_by_name(collec, "content-changed", DBA_ADD_ITEM, item);
+
+ printf(" ==== CONTENT CHANGED !!!\n");
+
+
result = true;
}
@@ -478,6 +502,138 @@ bool g_db_collection_modify_item(GDbCollection *collec, GDbItem *item)
/* ---------------------------------------------------------------------------------- */
/* CREATION DE L'ABSTRACTION POUR COLLECTIONS */
+/* MANIPULATIONS AVEC UNE BASE DE DONNEES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : collec = ensemble d'éléments à considérer. *
+* item = élément de collection à enregistrer. *
+* db = base de données à mettre à jour. *
+* *
+* Description : Enregistre un élément de collection dans une base de données.*
+* *
+* Retour : Bilan de l'exécution de l'opération. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_db_collection_store_item(const GDbCollection *collec, const GDbItem *item, sqlite3 *db)
+{
+ bool result; /* Conclusion à faire remonter */
+ bound_value *values; /* Champs de table à inclure */
+ size_t count; /* Nombre de ces champs */
+ char *sql; /* Requête SQL à construire */
+ size_t i; /* Boucle de parcours */
+ sqlite3_stmt *stmt; /* Déclaration mise en place */
+ int ret; /* Bilan d'un appel à SQLite */
+ int index; /* Indice de valeur attachée */
+
+ if (!g_db_item_prepare_db_statement(item, true, &values, &count))
+ return false;
+
+ result = false;
+
+ /* Préparation de la requête */
+
+ sql = strdup("INSERT INTO ");
+ sql = stradd(sql, collec->name);
+ sql = stradd(sql, " (");
+
+ for (i = 0; i < count; i++)
+ {
+ if (i > 0) sql = stradd(sql, ", ");
+
+ sql = stradd(sql, values[i].name);
+
+ }
+
+ sql = stradd(sql, ") VALUES (");
+
+ for (i = 0; i < count; i++)
+ {
+ if (i > 0) sql = stradd(sql, ", ");
+
+ if (values[i].type == SQLITE_RAW)
+ sql = stradd(sql, values[i].cstring);
+ else
+ sql = stradd(sql, "?");
+
+ }
+
+ sql = stradd(sql, ");");
+
+ ret = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
+ if (ret != SQLITE_OK)
+ {
+ fprintf(stderr, "Can't prepare INSERT statment '%s' (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
+ goto gdcsi_exit;
+ }
+
+ /* Attribution des valeurs */
+
+ index = 1;
+
+ for (i = 0; i < count; i++)
+ {
+ switch (values[i].type)
+ {
+ case SQLITE_INT64:
+ ret = sqlite3_bind_int64(stmt, index, values[i].integer64);
+ index++;
+ break;
+
+ case SQLITE_TEXT:
+ ret = sqlite3_bind_text(stmt, index, values[i].string, -1, values[i].delete);
+ index++;
+ break;
+
+ default:
+ ret = SQLITE_OK;
+ break;
+
+ }
+
+ if (ret != SQLITE_OK)
+ {
+ fprintf(stderr, "Can't bind value for parameter nb %d in '%s' (ret=%d): %s\n",
+ index - 1, sql, ret, sqlite3_errmsg(db));
+ goto gdcsi_exit;
+ }
+
+ }
+
+ /* Exécution finale */
+
+ ret = sqlite3_step(stmt);
+
+ if (ret != SQLITE_DONE)
+ {
+ fprintf(stderr, "INSERT statement '%s' didn't return DONE (ret=%d): %s\n", sql, ret, sqlite3_errmsg(db));
+ goto gdcsi_exit;
+ }
+
+ sqlite3_finalize(stmt);
+
+ result = true;
+
+ gdcsi_exit:
+
+ free(sql);
+
+ printf("INSERT ? %d\n", result);
+
+ return result;
+
+}
+
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* CREATION DE L'ABSTRACTION POUR COLLECTIONS */
/* ---------------------------------------------------------------------------------- */
diff --git a/src/analysis/db/collection.h b/src/analysis/db/collection.h
index 75ee7df..b400820 100644
--- a/src/analysis/db/collection.h
+++ b/src/analysis/db/collection.h
@@ -26,6 +26,8 @@
#include <glib-object.h>
+#include <sqlite3.h>
+#include <stdbool.h>
#include <stdint.h>
@@ -52,7 +54,7 @@ typedef struct _GDbCollectionClass GDbCollectionClass;
GType g_db_collection_get_type(void);
/* Prépare la mise en place d'une nouvelle collection. */
-GDbCollection *g_db_collection_new(uint32_t, GType);
+GDbCollection *g_db_collection_new(uint32_t, GType, const char *);
@@ -63,7 +65,7 @@ uint32_t g_db_collection_get_feature(const GDbCollection *);
/* Réceptionne et traite une requête réseau pour collection. */
-bool g_db_collection_recv(GDbCollection *, int);
+bool g_db_collection_recv(GDbCollection *, int, sqlite3 *);
/* Envoie pour traitement une requête réseau pour collection. */
bool g_db_collection_send(GDbCollection *, int, DBAction, GDbItem *);
diff --git a/src/analysis/db/item-int.h b/src/analysis/db/item-int.h
index b6d8f9c..0f75ed4 100644
--- a/src/analysis/db/item-int.h
+++ b/src/analysis/db/item-int.h
@@ -32,12 +32,20 @@
+
+
+
+
+
/* Importe la définition d'une base d'éléments pour collection. */
typedef bool (* recv_db_item_fc) (GDbItem *, int, int);
/* Exporte la définition d'une base d'éléments pour collection. */
typedef bool (* send_db_item_fc) (const GDbItem *, int, int);
+/* Constitue les champs destinés à une insertion / modification. */
+typedef bool (* prepare_db_statement) (const GDbItem *, bool, bound_value **, size_t *);
+
/* Base d'un élément pour collection générique (instance) */
struct _GDbItem
@@ -61,6 +69,8 @@ struct _GDbItemClass
recv_db_item_fc recv; /* Réception depuis le réseau */
send_db_item_fc send; /* Emission depuis le réseau */
+ prepare_db_statement prepare_stmt; /* Préparation d'une requête */
+
};
diff --git a/src/analysis/db/item.c b/src/analysis/db/item.c
index e2fd7f3..7fa1a52 100644
--- a/src/analysis/db/item.c
+++ b/src/analysis/db/item.c
@@ -24,8 +24,13 @@
#include "item.h"
+#include <malloc.h>
+#include <sqlite3.h>
+
+
#include "item-int.h"
#include "../../common/io.h"
+#include "../../core/params.h"
@@ -52,6 +57,13 @@ static bool g_db_item_send_to_fd(const GDbItem *, int, int);
+/* --------------------- MANIPULATIONS AVEC UNE BASE DE DONNEES --------------------- */
+
+
+/* Constitue les champs destinés à une insertion / modification. */
+static bool _g_db_item_prepare_db_statement(const GDbItem *, bool, bound_value **, size_t *);
+
+
/* Indique le type défini pour une base d'élément de collection générique. */
G_DEFINE_TYPE(GDbItem, g_db_item, G_TYPE_OBJECT);
@@ -83,6 +95,8 @@ static void g_db_item_class_init(GDbItemClass *klass)
klass->recv = (recv_db_item_fc)g_db_item_recv_from_fd;
klass->send = (send_db_item_fc)g_db_item_send_to_fd;
+ klass->prepare_stmt = (prepare_db_statement)_g_db_item_prepare_db_statement;
+
}
@@ -354,3 +368,82 @@ bool g_db_item_is_volatile(const GDbItem *item)
return item->is_volatile;
}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* MANIPULATIONS AVEC UNE BASE DE DONNEES */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+* *
+* Paramètres : item = base d'éléments sur laquelle s'appuyer. *
+* create = indique si la préparation vise une création ou non. *
+* values = couples de champs et de valeurs à lier. [OUT] *
+* count = nombre de ces couples. [OUT] *
+* *
+* Description : Constitue les champs destinés à une insertion / modification.*
+* *
+* Retour : Bilan de l'opération : succès ou non. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool _g_db_item_prepare_db_statement(const GDbItem *item, bool create, bound_value **values, size_t *count)
+{
+ char *author; /* Identification à diffuser */
+ bound_value *value; /* Valeur à éditer / définir */
+
+ if (!g_generic_config_get_value(get_main_configuration(), MPK_AUTHOR_NAME, &author))
+ return false;
+
+ *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
+ value = &(*values)[*count - 1];
+
+ value->name = "user";
+ value->type = SQLITE_TEXT;
+ value->string = author;
+ value->delete = free;
+
+ if (!create)
+ {
+ *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
+ value = &(*values)[*count - 1];
+
+ value->name = "modified";
+ value->type = SQLITE_RAW;
+ value->string = "CURRENT_TIMESTAMP";
+ value->delete = SQLITE_STATIC;
+
+ }
+
+ return true;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : item = base d'éléments sur laquelle s'appuyer. *
+* create = indique si la préparation vise une création ou non. *
+* values = couples de champs et de valeurs à lier. [OUT] *
+* count = nombre de ces couples. [OUT] *
+* *
+* Description : Constitue les champs destinés à une insertion / modification.*
+* *
+* Retour : Bilan de l'opération : succès ou non. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool g_db_item_prepare_db_statement(const GDbItem *item, bool create, bound_value **values, size_t *count)
+{
+ *values = NULL;
+ *count = 0;
+
+ return G_DB_ITEM_GET_CLASS(item)->prepare_stmt(item, create, values, count);
+
+}
diff --git a/src/analysis/db/item.h b/src/analysis/db/item.h
index 3938610..2f8ee60 100644
--- a/src/analysis/db/item.h
+++ b/src/analysis/db/item.h
@@ -29,6 +29,9 @@
#include <stdbool.h>
+#include "../../common/sqlite.h"
+
+
#define G_TYPE_DB_ITEM g_db_item_get_type()
#define G_DB_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_db_item_get_type(), GDbItem))
@@ -65,4 +68,19 @@ bool g_db_item_is_volatile(const GDbItem *);
+/* --------------------- MANIPULATIONS AVEC UNE BASE DE DONNEES --------------------- */
+
+
+/* Définition du tronc commun pour les créations SQLite */
+#define SQLITE_DB_ITEM_CREATE \
+ "user TEXT, " \
+ "created TIMESTAMP DEFAULT CURRENT_TIMESTAMP, " \
+ "modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "
+
+
+/* Constitue les champs destinés à une insertion / modification. */
+bool g_db_item_prepare_db_statement(const GDbItem *, bool, bound_value **, size_t *);
+
+
+
#endif /* _ANALYSIS_DB_ITEM_H */
diff --git a/src/analysis/db/items/bookmark.c b/src/analysis/db/items/bookmark.c
index c28a837..6ff386c 100644
--- a/src/analysis/db/items/bookmark.c
+++ b/src/analysis/db/items/bookmark.c
@@ -75,6 +75,9 @@ static bool g_db_bookmark_recv_from_fd(GDbBookmark *, int, int);
/* Exporte la définition d'un signet dans un flux réseau. */
static bool g_db_bookmark_send_to_fd(const GDbBookmark *, int, int);
+/* Constitue les champs destinés à une insertion / modification. */
+static bool g_db_bookmark_prepare_db_statement(const GDbBookmark *, bool, bound_value **, size_t *);
+
@@ -97,10 +100,8 @@ bool create_bookmark_db_table(sqlite3 *db)
char *msg; /* Message d'erreur */
sql = "CREATE TABLE Bookmarks (" \
- "id INT PRIMARY KEY NOT NULL, " \
- "user TEXT NOT NULL, " \
- "created INT NOT NULL, " \
- "address INT NOT NULL, " \
+ SQLITE_DB_ITEM_CREATE \
+ SQLITE_VMPA_CREATE \
"comment TEXT" \
");";
@@ -157,6 +158,8 @@ static void g_db_bookmark_class_init(GDbBookmarkClass *klass)
item->recv = (recv_db_item_fc)g_db_bookmark_recv_from_fd;
item->send = (send_db_item_fc)g_db_bookmark_send_to_fd;
+ item->prepare_stmt = (prepare_db_statement)g_db_bookmark_prepare_db_statement;
+
}
@@ -235,13 +238,7 @@ GDbBookmark *g_db_bookmark_new(const vmpa2t *addr, const char *comment)
result = g_object_new(G_TYPE_DB_BOOKMARK, NULL);
-
-
-
- /* TODO */
-
- //dup addr;
-
+ copy_vmpa(&result->addr, addr);
g_db_bookmark_set_comment(result, comment);
@@ -351,6 +348,46 @@ static bool g_db_bookmark_send_to_fd(const GDbBookmark *bookmark, int fd, int fl
}
+/******************************************************************************
+* *
+* Paramètres : item = base d'éléments sur laquelle s'appuyer. *
+* create = indique si la préparation vise une création ou non. *
+* values = couples de champs et de valeurs à lier. [OUT] *
+* count = nombre de ces couples. [OUT] *
+* *
+* Description : Constitue les champs destinés à une insertion / modification.*
+* *
+* Retour : Etat du besoin en sauvegarde. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static bool g_db_bookmark_prepare_db_statement(const GDbBookmark *bookmark, bool create, bound_value **values, size_t *count)
+{
+ bool status; /* Bilan d'opération initiale */
+ bound_value *value; /* Valeur à éditer / définir */
+
+ status = G_DB_ITEM_CLASS(g_db_bookmark_parent_class)->prepare_stmt(G_DB_ITEM(bookmark), create, values, count);
+ if (!status) return false;
+
+ status = prepare_vmpa_db_statement(&bookmark->addr, create, values, count);
+ if (!status) return false;
+
+ *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
+ value = &(*values)[*count - 1];
+
+ value->name = "comment";
+ value->type = SQLITE_TEXT;
+ value->cstring = g_db_bookmark_get_comment(bookmark);
+ value->delete = SQLITE_STATIC;
+
+ return true;
+
+}
+
+
+
@@ -370,7 +407,7 @@ static bool g_db_bookmark_send_to_fd(const GDbBookmark *bookmark, int fd, int fl
* *
******************************************************************************/
-vmpa2t *g_db_bookmark_get_address(GDbBookmark *bookmark)
+const vmpa2t *g_db_bookmark_get_address(GDbBookmark *bookmark)
{
return &bookmark->addr;
diff --git a/src/analysis/db/items/bookmark.h b/src/analysis/db/items/bookmark.h
index d1b073c..cd5e202 100644
--- a/src/analysis/db/items/bookmark.h
+++ b/src/analysis/db/items/bookmark.h
@@ -66,7 +66,7 @@ GType g_db_bookmark_get_type(void);
GDbBookmark *g_db_bookmark_new(const vmpa2t *, const char *);
/* Fournit l'adresse associée à un signet. */
-vmpa2t *g_db_bookmark_get_address(GDbBookmark *);
+const vmpa2t *g_db_bookmark_get_address(GDbBookmark *);
/* Fournit le commentaire associé à un signet. */
const char *g_db_bookmark_get_comment(const GDbBookmark *);
diff --git a/src/analysis/db/protocol.h b/src/analysis/db/protocol.h
index 05b559e..85d44cb 100644
--- a/src/analysis/db/protocol.h
+++ b/src/analysis/db/protocol.h
@@ -113,6 +113,7 @@ typedef enum _DBCommand
{
DBC_HELO, /* Connexion initiale C -> S */
DBC_WELCOME, /* Réponse initiale S -> C */
+ DBC_SAVE, /* Enregistrement de l'archive */
DBC_COLLECTION, /* Implication d'une collection*/
DBC_COUNT
@@ -132,6 +133,8 @@ typedef enum _DBError
{
DBE_NONE, /* Succès d'une opération */
DBE_WRONG_VERSION, /* Proto Client != Serveur */
+ DBE_SYS_ERROR, /* Erreur suite à un appel sys.*/
+ DBE_ARCHIVE_ERROR, /* Soucis du côté libarchive */
DBE_COUNT
@@ -141,6 +144,30 @@ typedef enum _DBError
+/**
+ * Gestion de la commande 'DBC_SAVE'.
+ *
+ * Le client connecté envoie un paquet de la forme suivante :
+ *
+ * [ Ordre de sauvegarde : DBC_SAVE ]
+ *
+ * Le serveur s'exécute et renvoie un bilan :
+ *
+ * [ Ordre de sauvegarde : DBC_SAVE ]
+ * [ 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_update() pour la partie client en réception.
+ *
+ */
+
+
+
+
+
+
diff --git a/src/arch/vmpa.c b/src/arch/vmpa.c
index 6f305fb..024161c 100644
--- a/src/arch/vmpa.c
+++ b/src/arch/vmpa.c
@@ -482,6 +482,62 @@ vmpa2t *string_to_vmpa_virt(const char *buffer)
}
+/******************************************************************************
+* *
+* Paramètres : addr = adresse virtuelle ou physique à traiter. *
+* create = indique si la préparation vise une création ou non. *
+* values = couples de champs et de valeurs à lier. [OUT] *
+* count = nombre de ces couples. [OUT] *
+* *
+* Description : Constitue les champs destinés à une insertion / modification.*
+* *
+* Retour : Bilan de l'opération : succès ou non. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+bool prepare_vmpa_db_statement(const vmpa2t *addr, bool create, bound_value **values, size_t *count)
+{
+ bound_value *value; /* Valeur à éditer / définir */
+
+ *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
+ value = &(*values)[*count - 1];
+
+ value->name = "phys";
+
+ if (addr->physical != VMPA_NO_PHYSICAL)
+ {
+ value->type = SQLITE_INT64;
+ value->integer64 = addr->physical;
+ }
+ else
+ {
+ value->type = SQLITE_RAW;
+ value->cstring = "NULL";
+ }
+
+ *values = (bound_value *)realloc(*values, ++(*count) * sizeof(bound_value));
+ value = &(*values)[*count - 1];
+
+ value->name = "virt";
+
+ if (addr->virtual != VMPA_NO_VIRTUAL)
+ {
+ value->type = SQLITE_INT64;
+ value->integer64 = addr->virtual;
+ }
+ else
+ {
+ value->type = SQLITE_RAW;
+ value->cstring = "NULL";
+ }
+
+ return true;
+
+}
+
+
/* ---------------------------------------------------------------------------------- */
/* DEFINITION D'UNE ZONE EN MEMOIRE */
diff --git a/src/arch/vmpa.h b/src/arch/vmpa.h
index 52c5d6a..f0a8faf 100644
--- a/src/arch/vmpa.h
+++ b/src/arch/vmpa.h
@@ -34,6 +34,7 @@
#include "archbase.h"
#include "../common/cpp.h"
+#include "../common/sqlite.h"
@@ -117,6 +118,14 @@ vmpa2t *string_to_vmpa_phy(const char *);
/* Transforme une chaîne de caractères en adresse virtuelle. */
vmpa2t *string_to_vmpa_virt(const char *);
+/* Définition du tronc commun pour les créations SQLite */
+#define SQLITE_VMPA_CREATE \
+ "phys INTEGER, " \
+ "virt INTEGER, "
+
+/* Constitue les champs destinés à une insertion / modification. */
+bool prepare_vmpa_db_statement(const vmpa2t *, bool, bound_value **, size_t *);
+
/* ------------------------ DEFINITION D'UNE ZONE EN MEMOIRE ------------------------ */
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 7ec85ab..198de32 100755
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -13,6 +13,7 @@ libcommon_la_SOURCES = \
leb128.h leb128.c \
macros.h \
net.h net.c \
+ sqlite.h \
xdg.h xdg.c \
xml.h xml.c
diff --git a/src/common/sqlite.h b/src/common/sqlite.h
new file mode 100644
index 0000000..66fb773
--- /dev/null
+++ b/src/common/sqlite.h
@@ -0,0 +1,56 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * sqlite.h - prototypes pour une extension des définitions propres à SQLite
+ *
+ * Copyright (C) 2014 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * OpenIDA is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * OpenIDA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Foobar. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _COMMON_SQLITE_H
+#define _COMMON_SQLITE_H
+
+
+#include <stdint.h>
+
+
+/* Type pour les insertions brutes */
+#define SQLITE_RAW 0
+#define SQLITE_INT64 10
+
+
+/* Description des champs et de leur valeur associée */
+typedef struct _bound_value
+{
+ const char *name; /* Nom du champ à manipuler */
+ unsigned int type; /* Type de valeur à associer */
+
+ union
+ {
+ int64_t integer64; /* Nombre sur 64 bits */
+ char *string; /* Chaîne de caractères #1 */
+ const char *cstring; /* Chaîne de caractères #2 */
+
+ };
+
+ void (* delete) (void *); /* Suppression éventuelle */
+
+} bound_value;
+
+
+
+#endif /* _COMMON_SQLITE_H */
diff --git a/src/core/collections.c b/src/core/collections.c
index 860b7f6..80d2ae3 100644
--- a/src/core/collections.c
+++ b/src/core/collections.c
@@ -164,7 +164,7 @@ GList *create_collections_list(void)
for (i = 0; i < _collection_definitions_count; i++)
{
def = &_collection_definitions[i];
- collec = g_db_collection_new(i, def->items);
+ collec = g_db_collection_new(i, def->items, "Bookmarks");
result = g_list_append(result, collec);
diff --git a/src/gui/panels/bookmarks.c b/src/gui/panels/bookmarks.c
index 56e7119..5d2be18 100644
--- a/src/gui/panels/bookmarks.c
+++ b/src/gui/panels/bookmarks.c
@@ -110,6 +110,8 @@ static void g_bookmarks_panel_finalize(GBookmarksPanel *);
/* Recharge une collection de signets à l'affichage. */
static void reload_bookmarks_into_treeview(GBookmarksPanel *, GLoadedBinary *);
+/* Réagit au changement de sélection des signets. */
+static void on_bookmarks_selection_change(GtkTreeSelection *, GBookmarksPanel *);
@@ -234,6 +236,7 @@ static void g_bookmarks_panel_init(GBookmarksPanel *panel)
GtkCellRenderer *renderer; /* Moteur de rendu de colonne */
GtkTreeViewColumn *column; /* Colonne de la liste */
GtkTreeSortable *sortable; /* Autre vision de la liste */
+ GtkTreeSelection *select; /* Sélection dans la liste */
base = G_EDITOR_ITEM(panel);
@@ -294,7 +297,7 @@ static void g_bookmarks_panel_init(GBookmarksPanel *panel)
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Physical address"), renderer,
- "text", BMC_VIRTUAL,
+ "text", BMC_PHYSICAL,
NULL);
gtk_tree_view_column_set_sort_column_id(column, BMC_PHYSICAL);
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
@@ -330,6 +333,12 @@ static void g_bookmarks_panel_init(GBookmarksPanel *panel)
gtk_tree_sortable_set_sort_column_id(sortable, BMC_COMMENT, GTK_SORT_ASCENDING);
+ /* Prise en compte de la sélection */
+
+ select = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
+ gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
+ g_signal_connect(G_OBJECT(select), "changed", G_CALLBACK(on_bookmarks_selection_change), panel);
+
/* Préparation du menu contextuel */
panel->menu = build_bookmarks_panel_menu(panel);
@@ -464,14 +473,17 @@ static void reload_bookmarks_into_treeview(GBookmarksPanel *panel, GLoadedBinary
{
GtkTreeStore *store; /* Modèle de gestion */
GDbCollection *collec; /* Collection à lister ici */
+ GExeFormat *format; /* Format du fichier binaire */
+ GArchProcessor *proc; /* Architecture du binaire */
+ MemoryDataSize msize; /* Taille par défaut */
GList *items; /* Liste des éléments groupés */
GList *b; /* Boucle de parcours */
GDbBookmark *bookmark; /* Signet en cours d'étude */
- vmpa2t *addr; /* Adressse associée au signet */
+ const vmpa2t *addr; /* Adressse associée au signet */
+ VMPA_BUFFER(phys); /* Position physique */
+ VMPA_BUFFER(virt); /* Adresse virtuelle */
GtkTreeIter iter; /* Point d'insertion */
- printf("RELOAD :: %p\n", binary);
-
/* Basculement du binaire utilisé */
if (panel->binary != NULL)
@@ -491,7 +503,10 @@ static void reload_bookmarks_into_treeview(GBookmarksPanel *panel, GLoadedBinary
/* Actualisation de l'affichage */
- sleep(1);
+ format = g_loaded_binary_get_format(binary);
+ proc = get_arch_processor_from_format(format);
+
+ msize = g_arch_processor_get_memory_size(proc);
collec = g_loaded_binary_find_collection(binary, DBF_BOOKMARKS);
@@ -499,107 +514,65 @@ static void reload_bookmarks_into_treeview(GBookmarksPanel *panel, GLoadedBinary
items = g_db_collection_list_items(collec);
-
- printf(" ... items = %p\n", items);
-
- /*
- gtk_tree_store_append(store, &iter, NULL);
- gtk_tree_store_set(store, &iter,
- BMC_BOOKMARK, bookmark,
- BMC_PICTURE, G_BOOKMARKS_PANEL_GET_CLASS(panel)->bookmark_img,
- BMC_PHYSICAL, "0x01",
- BMC_VIRTUAL, "0x02",
- BMC_COMMENT, "desc",
- -1);
- */
-
-
for (b = g_list_first(items); b != NULL; b = g_list_next(b))
{
bookmark = G_DB_BOOKMARK(b->data);
-
- printf("Adding // %p\n", bookmark);
-
- //printf("add.virt = %s\n", vmpa2_virt_to_string(&addr, MDS_32_BITS));
-
- fflush(NULL);
-
-
addr = g_db_bookmark_get_address(bookmark);
+ vmpa2_phys_to_string(addr, msize, phys, NULL);
+ vmpa2_virt_to_string(addr, msize, virt, NULL);
+
gtk_tree_store_append(store, &iter, NULL);
gtk_tree_store_set(store, &iter,
BMC_BOOKMARK, bookmark,
BMC_PICTURE, G_BOOKMARKS_PANEL_GET_CLASS(panel)->bookmark_img,
- BMC_PHYSICAL, "vmpa2_phy_to_string(addr, MDS_32_BITS)", /* FIXME : pareil qu'en bas */
- BMC_VIRTUAL, "vmpa2_virt_to_string(&addr, MDS_32_BITS)", /* FIXME : choisir en fonction de l'architecture */
- BMC_COMMENT, "g_db_bookmark_get_comment(bookmark)",
+ BMC_PHYSICAL, phys,
+ BMC_VIRTUAL, virt,
+ BMC_COMMENT, g_db_bookmark_get_comment(bookmark),
-1);
}
g_db_collection_runlock(collec);
+}
-#if 0
- GtkTreeStore *store; /* Modèle de gestion */
- GList *params; /* Paramètres de configuration */
- GCfgParam *param; /* Paramètre en cours d'étude */
- GList *p; /* Boucle de parcours */
- char *type_desc; /* Type de paramètre */
- GtkTreeIter iter; /* Point d'insertion */
-
- store = GTK_TREE_STORE(gtk_tree_view_get_model(panel->treeview));
- gtk_tree_store_clear(store);
-
- g_generic_config_rlock(config);
+/******************************************************************************
+* *
+* Paramètres : selection = sélection modifiée. *
+* panel = structure contenant les informations maîtresses. *
+* *
+* Description : Réagit au changement de sélection des signets. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
- params = g_generic_config_list_params(config);
+static void on_bookmarks_selection_change(GtkTreeSelection *selection, GBookmarksPanel *panel)
+{
+ GtkTreeIter iter; /* Point de sélection */
+ GtkTreeModel *model; /* Modèle de gestion */
+ GDbBookmark *bookmark; /* Signet en cours d'étude */
+ const vmpa2t *addr; /* Adressse associée au signet */
+ GtkViewPanel *vpanel; /* Afficheur effectif de code */
- for (p = g_list_first(params); p != NULL; p = g_list_next(p))
+ if (gtk_tree_selection_get_selected(selection, &model, &iter))
{
- param = G_CFG_PARAM(p->data);
+ gtk_tree_model_get(model, &iter, BMC_BOOKMARK, &bookmark, -1);
- if (is_param_filtered(panel, g_config_param_get_path(param)))
- continue;
-
- switch (g_config_param_get_ptype(param))
- {
- case CPT_BOOLEAN:
- type_desc = _("Boolean");
- break;
-
- case CPT_INTEGER:
- type_desc = _("Integer");
- break;
-
- case CPT_STRING:
- type_desc = _("String");
- break;
-
- default:
- type_desc = _("<Unknown type>");
- break;
-
- }
-
- gtk_tree_store_append(store, &iter, NULL);
- gtk_tree_store_set(store, &iter,
- CPC_BOOKMARK, param,
- CPC_PATH, g_config_param_get_path(param),
- CPC_TYPE, type_desc,
- -1);
+ addr = g_db_bookmark_get_address(bookmark);
- update_config_param_value(store, &iter);
+ vpanel = g_editor_item_get_current_view(G_EDITOR_ITEM(panel));
+ gtk_view_panel_scroll_to_address(vpanel, addr);
- g_signal_connect(param, "modified", G_CALLBACK(on_config_param_modified), panel);
+ g_object_unref(G_OBJECT(bookmark));
}
- g_generic_config_runlock(config);
-#endif
}
@@ -1008,8 +981,39 @@ static bool is_param_filtered(GBookmarksPanel *panel, const char *name)
static gboolean on_button_press_over_bookmarks(GtkWidget *widget, GdkEventButton *event, GBookmarksPanel *panel)
{
- if (event->button == 3)
- gtk_menu_popup(panel->menu, NULL, NULL, NULL, NULL, event->button, event->time);
+ GtkTreeSelection *selection; /* Sélection courante */
+ GtkTreeIter iter; /* Point de sélection */
+ GtkTreeModel *model; /* Modèle de gestion */
+ GDbBookmark *bookmark; /* Signet en cours d'étude */
+ const vmpa2t *addr; /* Adressse associée au signet */
+ GtkViewPanel *vpanel; /* Afficheur effectif de code */
+
+ switch (event->button)
+ {
+ case 1:
+
+ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
+
+ if (gtk_tree_selection_get_selected(selection, &model, &iter))
+ {
+ gtk_tree_model_get(model, &iter, BMC_BOOKMARK, &bookmark, -1);
+
+ addr = g_db_bookmark_get_address(bookmark);
+
+ vpanel = g_editor_item_get_current_view(G_EDITOR_ITEM(panel));
+ gtk_view_panel_scroll_to_address(vpanel, addr);
+
+ g_object_unref(G_OBJECT(bookmark));
+
+ }
+
+ break;
+
+ case 3:
+ gtk_menu_popup(panel->menu, NULL, NULL, NULL, NULL, event->button, event->time);
+ break;
+
+ }
return FALSE;