packagekit/libpackagekit/pk-extra.c

692 lines
18 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Copyright (C) 2008 Richard Hughes <richard@hughsie.com>
*
* Licensed under the GNU General Public License Version 2
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/**
* SECTION:pk-extra
* @short_description: Client singleton access to extra metadata about a package
*
* Extra metadata such as icon name and localised summary may be stored here
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <glib/gi18n.h>
#include <sqlite3.h>
#include "pk-extra.h"
#include "pk-common.h"
#include "pk-debug.h"
static void pk_extra_class_init (PkExtraClass *klass);
static void pk_extra_init (PkExtra *extra);
static void pk_extra_finalize (GObject *object);
#define PK_EXTRA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PK_TYPE_EXTRA, PkExtraPrivate))
#define PK_EXTRA_DEFAULT_DATABASE_INTERNAL PK_DB_DIR "/extra-data.db"
/**
* PkExtraPrivate:
*
* Private #PkExtra data
**/
struct PkExtraPrivate
{
gchar *database;
gchar *locale;
gchar *locale_base;
gchar *icon;
gchar *exec;
gchar *summary;
sqlite3 *db;
};
G_DEFINE_TYPE (PkExtra, pk_extra, G_TYPE_OBJECT)
static gpointer pk_extra_object = NULL;
/**
* pk_extra_set_locale:
* @extra: a valid #PkExtra instance
* @locale: a correct locale
*
* Return value: %TRUE if set correctly
**/
gboolean
pk_extra_set_locale (PkExtra *extra, const gchar *locale)
{
guint i;
g_free (extra->priv->locale);
extra->priv->locale = g_strdup (locale);
extra->priv->locale_base = g_strdup (locale);
/* we only want the first section to compare */
for (i=0; i<10; i++) {
if (extra->priv->locale_base[i] == '_') {
extra->priv->locale_base[i] = '\0';
pk_debug ("locale_base is '%s'", extra->priv->locale_base);
break;
}
}
/* no point doing it twice if they are the same */
if (pk_strequal (extra->priv->locale_base, extra->priv->locale) == TRUE) {
g_free (extra->priv->locale_base);
extra->priv->locale_base = NULL;
}
return TRUE;
}
/**
* pk_extra_get_locale:
* @extra: a valid #PkExtra instance
*
* Return value: the current locale
**/
const gchar *
pk_extra_get_locale (PkExtra *extra)
{
return extra->priv->locale;
}
/**
* pk_extra_detail_localised_callback:
**/
static gint
pk_extra_detail_localised_callback (void *data, gint argc, gchar **argv, gchar **col_name)
{
PkExtra *extra = PK_EXTRA (data);
gint i;
gchar *col;
gchar *value;
g_return_val_if_fail (extra != NULL, 0);
g_return_val_if_fail (PK_IS_EXTRA (extra), 0);
for (i=0; i<argc; i++) {
col = col_name[i];
value = argv[i];
if (pk_strequal (col, "summary") == TRUE) {
extra->priv->summary = g_strdup (value);
} else if (pk_strequal (col, "locale") == TRUE) {
pk_debug ("locale: %s", value);
} else if (pk_strequal (col, "package") == TRUE) {
pk_debug ("package: %s", value);
} else {
pk_warning ("%s = %s\n", col, value);
}
}
return 0;
}
/**
* pk_extra_get_localised_detail_try:
* @extra: a valid #PkExtra instance
*
* Return value: the current locale
**/
static gboolean
pk_extra_get_localised_detail_try (PkExtra *extra, const gchar *package, const gchar *locale)
{
gchar *statement;
gchar *error_msg = NULL;
gint rc;
statement = g_strdup_printf ("SELECT package, summary, locale FROM localised "
"WHERE package = '%s' AND locale = '%s'",
package, locale);
rc = sqlite3_exec (extra->priv->db, statement, pk_extra_detail_localised_callback, extra, &error_msg);
g_free (statement);
if (rc != SQLITE_OK) {
pk_warning ("SQL error: %s\n", error_msg);
sqlite3_free (error_msg);
return FALSE;
}
return TRUE;
}
/**
* pk_extra_get_localised_detail:
* @extra: a valid #PkExtra instance
*
* Return value: the current locale
**/
gboolean
pk_extra_get_localised_detail (PkExtra *extra, const gchar *package, gchar **summary)
{
g_return_val_if_fail (extra != NULL, FALSE);
g_return_val_if_fail (PK_IS_EXTRA (extra), FALSE);
g_return_val_if_fail (extra->priv->locale != NULL, FALSE);
g_return_val_if_fail (extra->priv->database != NULL, FALSE);
/* do we have a connection */
if (extra->priv->db == NULL) {
pk_debug ("no database connection");
return FALSE;
}
/* try with default locale */
pk_extra_get_localised_detail_try (extra, package, extra->priv->locale);
/* try harder with a base locale */
if (extra->priv->summary == NULL && extra->priv->locale_base != NULL) {
pk_extra_get_localised_detail_try (extra, package, extra->priv->locale_base);
}
if (summary != NULL) {
*summary = extra->priv->summary;
} else {
g_free (extra->priv->summary);
}
extra->priv->summary = NULL;
return TRUE;
}
/**
* pk_extra_detail_package_callback:
**/
static gint
pk_extra_detail_package_callback (void *data, gint argc, gchar **argv, gchar **col_name)
{
PkExtra *extra = PK_EXTRA (data);
gint i;
gchar *col;
gchar *value;
g_return_val_if_fail (extra != NULL, 0);
g_return_val_if_fail (PK_IS_EXTRA (extra), 0);
for (i=0; i<argc; i++) {
col = col_name[i];
value = argv[i];
if (pk_strequal (col, "exec") == TRUE) {
extra->priv->exec = g_strdup (value);
} else if (pk_strequal (col, "icon") == TRUE) {
extra->priv->icon = g_strdup (value);
} else {
pk_warning ("%s = %s\n", col, value);
}
}
return 0;
}
/**
* pk_extra_get_package_detail:
* @extra: a valid #PkExtra instance
*
* Return value: the current locale
**/
gboolean
pk_extra_get_package_detail (PkExtra *extra, const gchar *package, gchar **icon, gchar **exec)
{
gchar *statement;
gchar *error_msg = NULL;
gint rc;
g_return_val_if_fail (extra != NULL, FALSE);
g_return_val_if_fail (PK_IS_EXTRA (extra), FALSE);
g_return_val_if_fail (extra->priv->locale != NULL, FALSE);
g_return_val_if_fail (extra->priv->database != NULL, FALSE);
/* do we have a connection */
if (extra->priv->db == NULL) {
pk_debug ("no database connection");
return FALSE;
}
statement = g_strdup_printf ("SELECT icon, exec FROM data WHERE package = '%s'", package);
rc = sqlite3_exec (extra->priv->db, statement, pk_extra_detail_package_callback, extra, &error_msg);
if (rc != SQLITE_OK) {
pk_warning ("SQL error: %s\n", error_msg);
sqlite3_free (error_msg);
return FALSE;
}
/* report back */
if (icon != NULL) {
*icon = extra->priv->icon;
} else {
g_free (extra->priv->icon);
}
if (exec != NULL) {
*exec = extra->priv->exec;
} else {
g_free (extra->priv->exec);
}
extra->priv->icon = NULL;
extra->priv->exec = NULL;
g_free (statement);
return TRUE;
}
/**
* pk_extra_set_localised_detail:
* @extra: a valid #PkExtra instance
*
* Return value: the current locale
**/
gboolean
pk_extra_set_localised_detail (PkExtra *extra, const gchar *package, const gchar *summary)
{
gchar *statement;
gchar *error_msg = NULL;
sqlite3_stmt *sql_statement = NULL;
gint rc;
g_return_val_if_fail (extra != NULL, FALSE);
g_return_val_if_fail (PK_IS_EXTRA (extra), FALSE);
g_return_val_if_fail (extra->priv->locale != NULL, FALSE);
g_return_val_if_fail (extra->priv->database != NULL, FALSE);
/* do we have a connection */
if (extra->priv->db == NULL) {
pk_debug ("no database connection");
return FALSE;
}
/* the row might already exist */
statement = g_strdup_printf ("DELETE FROM localised WHERE "
"package = '%s' AND locale = '%s'",
package, extra->priv->locale);
sqlite3_exec (extra->priv->db, statement, NULL, extra, NULL);
g_free (statement);
/* prepare the query, as we don't escape it */
rc = sqlite3_prepare_v2 (extra->priv->db,
"INSERT INTO localised (package, locale, summary) "
"VALUES (?, ?, ?)", -1, &sql_statement, NULL);
if (rc != SQLITE_OK) {
pk_warning ("SQL failed to prepare");
return FALSE;
}
/* add data */
sqlite3_bind_text (sql_statement, 1, package, -1, SQLITE_STATIC);
sqlite3_bind_text (sql_statement, 2, extra->priv->locale, -1, SQLITE_STATIC);
sqlite3_bind_text (sql_statement, 3, summary, -1, SQLITE_STATIC);
/* save this */
sqlite3_step (sql_statement);
rc = sqlite3_finalize (sql_statement);
if (rc != SQLITE_OK) {
pk_warning ("SQL error: %s\n", error_msg);
sqlite3_free (error_msg);
return FALSE;
}
return TRUE;
}
/**
* pk_extra_set_package_detail:
* @extra: a valid #PkExtra instance
*
* Return value: the current locale
**/
gboolean
pk_extra_set_package_detail (PkExtra *extra, const gchar *package, const gchar *icon, const gchar *exec)
{
gchar *statement;
gchar *error_msg = NULL;
sqlite3_stmt *sql_statement = NULL;
gint rc;
g_return_val_if_fail (extra != NULL, FALSE);
g_return_val_if_fail (PK_IS_EXTRA (extra), FALSE);
g_return_val_if_fail (extra->priv->locale != NULL, FALSE);
g_return_val_if_fail (extra->priv->database != NULL, FALSE);
/* do we have a connection */
if (extra->priv->db == NULL) {
pk_debug ("no database connection");
return FALSE;
}
/* the row might already exist */
statement = g_strdup_printf ("DELETE FROM data WHERE package = '%s'", package);
sqlite3_exec (extra->priv->db, statement, NULL, extra, NULL);
g_free (statement);
/* prepare the query, as we don't escape it */
rc = sqlite3_prepare_v2 (extra->priv->db, "INSERT INTO data (package, icon, exec) "
"VALUES (?, ?, ?)", -1, &sql_statement, NULL);
if (rc != SQLITE_OK) {
pk_warning ("SQL failed to prepare");
return FALSE;
}
/* add data */
sqlite3_bind_text (sql_statement, 1, package, -1, SQLITE_STATIC);
sqlite3_bind_text (sql_statement, 2, icon, -1, SQLITE_STATIC);
sqlite3_bind_text (sql_statement, 3, exec, -1, SQLITE_STATIC);
/* save this */
sqlite3_step (sql_statement);
rc = sqlite3_finalize (sql_statement);
if (rc != SQLITE_OK) {
pk_warning ("SQL error: %s\n", error_msg);
sqlite3_free (error_msg);
return FALSE;
}
return TRUE;
}
/**
* pk_extra_set_database:
* @extra: a valid #PkExtra instance
* @filename: a valid database
*
* Return value: %TRUE if set correctly
**/
gboolean
pk_extra_set_database (PkExtra *extra, const gchar *filename)
{
gboolean create_file;
const gchar *statement;
gint rc;
gchar *error_msg = NULL;
g_return_val_if_fail (extra != NULL, FALSE);
g_return_val_if_fail (PK_IS_EXTRA (extra), FALSE);
if (extra->priv->database != NULL) {
pk_warning ("cannot assign extra than once");
return FALSE;
}
/* if this is NULL, then assume default */
if (filename == NULL) {
filename = PK_EXTRA_DEFAULT_DATABASE_INTERNAL;
}
/* save for later */
extra->priv->database = g_strdup (filename);
/* if the database file was not installed (or was nuked) recreate it */
create_file = g_file_test (filename, G_FILE_TEST_EXISTS);
pk_debug ("trying to open database '%s'", filename);
rc = sqlite3_open (filename, &extra->priv->db);
if (rc) {
pk_warning ("Can't open database: %s\n", sqlite3_errmsg (extra->priv->db));
sqlite3_close (extra->priv->db);
extra->priv->db = NULL;
return FALSE;
} else {
if (create_file == FALSE) {
statement = "CREATE TABLE localised ("
"id INTEGER PRIMARY KEY,"
"package TEXT,"
"locale TEXT,"
"summary TEXT);";
rc = sqlite3_exec (extra->priv->db, statement, NULL, 0, &error_msg);
if (rc != SQLITE_OK) {
pk_warning ("SQL error: %s\n", error_msg);
sqlite3_free (error_msg);
}
statement = "CREATE TABLE data ("
"id INTEGER PRIMARY KEY,"
"package TEXT,"
"icon TEXT,"
"exec TEXT);";
rc = sqlite3_exec (extra->priv->db, statement, NULL, 0, &error_msg);
if (rc != SQLITE_OK) {
pk_warning ("SQL error: %s\n", error_msg);
sqlite3_free (error_msg);
}
}
}
return TRUE;
}
/**
* pk_extra_class_init:
**/
static void
pk_extra_class_init (PkExtraClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = pk_extra_finalize;
g_type_class_add_private (klass, sizeof (PkExtraPrivate));
}
/**
* pk_extra_init:
**/
static void
pk_extra_init (PkExtra *extra)
{
extra->priv = PK_EXTRA_GET_PRIVATE (extra);
extra->priv->database = NULL;
extra->priv->locale = NULL;
extra->priv->locale_base = NULL;
extra->priv->icon = NULL;
extra->priv->exec = NULL;
extra->priv->summary = NULL;
}
/**
* pk_extra_finalize:
**/
static void
pk_extra_finalize (GObject *object)
{
PkExtra *extra;
g_return_if_fail (object != NULL);
g_return_if_fail (PK_IS_EXTRA (object));
extra = PK_EXTRA (object);
g_return_if_fail (extra->priv != NULL);
g_free (extra->priv->icon);
g_free (extra->priv->exec);
g_free (extra->priv->summary);
g_free (extra->priv->locale);
g_free (extra->priv->locale_base);
sqlite3_close (extra->priv->db);
G_OBJECT_CLASS (pk_extra_parent_class)->finalize (object);
}
/**
* pk_extra_new:
**/
PkExtra *
pk_extra_new (void)
{
if (pk_extra_object != NULL) {
g_object_ref (pk_extra_object);
} else {
pk_extra_object = g_object_new (PK_TYPE_EXTRA, NULL);
g_object_add_weak_pointer (pk_extra_object, &pk_extra_object);
}
return PK_EXTRA (pk_extra_object);
}
/***************************************************************************
*** MAKE CHECK TESTS ***
***************************************************************************/
#ifdef PK_BUILD_TESTS
#include <libselftest.h>
#include <glib/gstdio.h>
void
libst_extra (LibSelfTest *test)
{
PkExtra *extra;
const gchar *text;
gboolean ret;
if (libst_start (test, "PkExtra", CLASS_AUTO) == FALSE) {
return;
}
g_unlink ("extra.db");
/************************************************************/
libst_title (test, "get extra");
extra = pk_extra_new ();
if (extra != NULL) {
libst_success (test, NULL);
} else {
libst_failed (test, NULL);
}
/************************************************************/
libst_title (test, "set database");
ret = pk_extra_set_database (extra, "extra.db");
if (ret == TRUE) {
libst_success (test, NULL);
} else {
libst_failed (test, NULL);
}
/************************************************************/
libst_title (test, "set database (again)");
ret = pk_extra_set_database (extra, "angry.db");
if (ret == FALSE) {
libst_success (test, NULL);
} else {
libst_failed (test, NULL);
}
/************************************************************/
libst_title (test, "set locale explicit en");
ret = pk_extra_set_locale (extra, "en");
if (ret == TRUE) {
libst_success (test, NULL);
} else {
libst_failed (test, NULL);
}
/************************************************************/
libst_title (test, "get locale");
text = pk_extra_get_locale (extra);
if (pk_strequal (text, "en") == TRUE) {
libst_success (test, NULL);
} else {
libst_failed (test, "locale was %s", text);
}
gchar *icon;
gchar *exec;
gchar *summary;
/************************************************************/
libst_title (test, "insert localised data");
ret = pk_extra_set_localised_detail (extra, "gnome-power-manager",
"Power manager for the GNOME's desktop");
if (ret == TRUE) {
libst_success (test, NULL);
} else {
libst_failed (test, "failed!");
}
/************************************************************/
libst_title (test, "retrieve localised data");
ret = pk_extra_get_localised_detail (extra, "gnome-power-manager", &summary);
if (ret == TRUE && summary != NULL) {
libst_success (test, "%s", summary);
} else {
libst_failed (test, "failed!");
}
/************************************************************/
libst_title (test, "set locale implicit en_GB");
ret = pk_extra_set_locale (extra, "en_GB");
if (ret == TRUE) {
libst_success (test, NULL);
} else {
libst_failed (test, NULL);
}
/************************************************************/
libst_title (test, "retrieve localised data");
ret = pk_extra_get_localised_detail (extra, "gnome-power-manager", &summary);
if (ret == TRUE && summary != NULL) {
libst_success (test, "%s", summary);
} else {
libst_failed (test, "failed!");
}
/************************************************************/
libst_title (test, "insert package data");
ret = pk_extra_set_package_detail (extra, "gnome-power-manager", "gpm-main.png", "gnome-power-manager");
if (ret == TRUE) {
libst_success (test, NULL);
} else {
libst_failed (test, "failed!");
}
/************************************************************/
libst_title (test, "retrieve package data");
ret = pk_extra_get_package_detail (extra, "gnome-power-manager", &icon, &exec);
if (ret == TRUE &&
pk_strequal (icon, "gpm-main.png") == TRUE &&
pk_strequal (exec, "gnome-power-manager") == TRUE) {
libst_success (test, "%s:%s", icon, exec);
} else {
libst_failed (test, "%s:%s", icon, exec);
}
/************************************************************/
libst_title (test, "insert new package data");
ret = pk_extra_set_package_detail (extra, "gnome-power-manager", "gpm-prefs.png", "gnome-power-preferences");
if (ret == TRUE) {
libst_success (test, NULL);
} else {
libst_failed (test, "failed!");
}
/************************************************************/
libst_title (test, "retrieve new package data");
ret = pk_extra_get_package_detail (extra, "gnome-power-manager", &icon, &exec);
if (ret == TRUE &&
pk_strequal (icon, "gpm-prefs.png") == TRUE &&
pk_strequal (exec, "gnome-power-preferences") == TRUE) {
libst_success (test, "%s:%s", icon, exec);
} else {
libst_failed (test, "%s:%s", icon, exec);
}
/************************************************************/
libst_title (test, "retrieve missing package data");
ret = pk_extra_get_package_detail (extra, "gnome-moo-manager", &icon, &exec);
if (ret == TRUE && icon == NULL && exec == NULL) {
libst_success (test, "passed");
} else {
libst_failed (test, "%s:%s", icon, exec);
}
g_object_unref (extra);
libst_end (test);
}
#endif