/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2008 Richard Hughes * * 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 #include #include #include #ifdef HAVE_UNISTD_H #include #endif /* HAVE_UNISTD_H */ #include #include #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; ipriv->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; ipriv->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 #include 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