aboutsummaryrefslogblamecommitdiffstats
path: root/src/sticker.c
blob: edfb799b658d53cae9ae0ade1fa7e0e2093d3e31 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13












                                                                       



                                                                          


                    
                 







                              



                          













                                                                      


                                                                









                                                                       


                                                          








































                                                                             
                                                              




                                                                      

                                                                         












                                                               

                                            




























                                                                       
                            





                                                                    
                            





                                                                     
                            















                                                                                        
                            







                                                 

                                                                        


                           
 
                             

                             







                                                                      
                             





                                                                     
                             







                                                                                                 
                                                               








                                                              
                                     


                                     


                                                  
                    

 


























































                                                                         
                               



























































                                                                         

                               

























































                                                                        
                               

                    
 









                                                                       














































                                                                          





                                                                 
 


                                                        




                            
/*
 * Copyright (C) 2003-2009 The Music Player Daemon Project
 * http://www.musicpd.org
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "sticker.h"
#include "idle.h"

#include <glib.h>
#include <sqlite3.h>
#include <assert.h>

#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "sticker"

struct sticker {
	GHashTable *table;
};

static const char sticker_sql_create[] =
	"CREATE TABLE IF NOT EXISTS sticker("
	"  type VARCHAR NOT NULL, "
	"  uri VARCHAR NOT NULL, "
	"  name VARCHAR NOT NULL, "
	"  value VARCHAR NOT NULL"
	");"
	"CREATE UNIQUE INDEX IF NOT EXISTS"
	" sticker_value ON sticker(type, uri, name);"
	"";

static const char sticker_sql_get[] =
	"SELECT value FROM sticker WHERE type=? AND uri=? AND name=?";

static const char sticker_sql_list[] =
	"SELECT name,value FROM sticker WHERE type=? AND uri=?";

static const char sticker_sql_update[] =
	"UPDATE sticker SET value=? WHERE type=? AND uri=? AND name=?";

static const char sticker_sql_insert[] =
	"INSERT INTO sticker(type,uri,name,value) VALUES(?, ?, ?, ?)";

static const char sticker_sql_delete[] =
	"DELETE FROM sticker WHERE type=? AND uri=?";

static sqlite3 *sticker_db;
static sqlite3_stmt *sticker_stmt_get, *sticker_stmt_list,
	*sticker_stmt_update, *sticker_stmt_insert,
	*sticker_stmt_delete;

static sqlite3_stmt *
sticker_prepare(const char *sql)
{
	int ret;
	sqlite3_stmt *stmt;

	ret = sqlite3_prepare_v2(sticker_db, sql, -1, &stmt, NULL);
	if (ret != SQLITE_OK)
		g_error("sqlite3_prepare_v2() failed: %s",
			sqlite3_errmsg(sticker_db));

	return stmt;
}

void
sticker_global_init(const char *path)
{
	int ret;

	if (path == NULL)
		/* not configured */
		return;

	/* open/create the sqlite database */

	ret = sqlite3_open(path, &sticker_db);
	if (ret != SQLITE_OK)
		g_error("Failed to open sqlite database '%s': %s",
			path, sqlite3_errmsg(sticker_db));

	/* create the table and index */

	ret = sqlite3_exec(sticker_db, sticker_sql_create, NULL, NULL, NULL);
	if (ret != SQLITE_OK)
		g_error("Failed to create sticker table: %s",
			sqlite3_errmsg(sticker_db));

	/* prepare the statements we're going to use */

	sticker_stmt_get = sticker_prepare(sticker_sql_get);
	sticker_stmt_list = sticker_prepare(sticker_sql_list);
	sticker_stmt_update = sticker_prepare(sticker_sql_update);
	sticker_stmt_insert = sticker_prepare(sticker_sql_insert);
	sticker_stmt_delete = sticker_prepare(sticker_sql_delete);

	if (sticker_stmt_get == NULL || sticker_stmt_update == NULL ||
	    sticker_stmt_insert == NULL || sticker_stmt_delete == NULL ||
	    sticker_stmt_list == NULL)
		g_error("Failed to prepare sqlite statements");
}

void
sticker_global_finish(void)
{
	if (sticker_db == NULL)
		/* not configured */
		return;

	sqlite3_finalize(sticker_stmt_delete);
	sqlite3_finalize(sticker_stmt_update);
	sqlite3_finalize(sticker_stmt_insert);
	sqlite3_finalize(sticker_stmt_list);
	sqlite3_finalize(sticker_stmt_get);
	sqlite3_close(sticker_db);
}

bool
sticker_enabled(void)
{
	return sticker_db != NULL;
}

char *
sticker_load_value(const char *type, const char *uri, const char *name)
{
	int ret;
	char *value;

	assert(sticker_enabled());
	assert(type != NULL);
	assert(uri != NULL);
	assert(name != NULL);

	if (*name == 0)
		return NULL;

	sqlite3_reset(sticker_stmt_get);

	ret = sqlite3_bind_text(sticker_stmt_get, 1, type, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return NULL;
	}

	ret = sqlite3_bind_text(sticker_stmt_get, 2, uri, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return NULL;
	}

	ret = sqlite3_bind_text(sticker_stmt_get, 3, name, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return NULL;
	}

	do {
		ret = sqlite3_step(sticker_stmt_get);
	} while (ret == SQLITE_BUSY);

	if (ret == SQLITE_ROW) {
		/* record found */
		value = g_strdup((const char*)sqlite3_column_text(sticker_stmt_get, 0));
	} else if (ret == SQLITE_DONE) {
		/* no record found */
		value = NULL;
	} else {
		/* error */
		g_warning("sqlite3_step() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return NULL;
	}

	sqlite3_reset(sticker_stmt_get);
	sqlite3_clear_bindings(sticker_stmt_get);

	return value;
}

static bool
sticker_list_values(GHashTable *hash, const char *type, const char *uri)
{
	int ret;
	char *name, *value;

	assert(hash != NULL);
	assert(type != NULL);
	assert(uri != NULL);
	assert(sticker_enabled());

	sqlite3_reset(sticker_stmt_list);

	ret = sqlite3_bind_text(sticker_stmt_list, 1, type, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	ret = sqlite3_bind_text(sticker_stmt_list, 2, uri, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	do {
		ret = sqlite3_step(sticker_stmt_list);
		switch (ret) {
		case SQLITE_ROW:
			name = g_strdup((const char*)sqlite3_column_text(sticker_stmt_list, 0));
			value = g_strdup((const char*)sqlite3_column_text(sticker_stmt_list, 1));
			g_hash_table_insert(hash, name, value);
			break;
		case SQLITE_DONE:
			break;
		case SQLITE_BUSY:
			/* no op */
			break;
		default:
			g_warning("sqlite3_step() failed: %s",
				  sqlite3_errmsg(sticker_db));
			return false;
		}
	} while (ret != SQLITE_DONE);

	sqlite3_reset(sticker_stmt_list);
	sqlite3_clear_bindings(sticker_stmt_list);

	return true;
}

static bool
sticker_update_value(const char *type, const char *uri,
		     const char *name, const char *value)
{
	int ret;

	assert(type != NULL);
	assert(uri != NULL);
	assert(name != NULL);
	assert(*name != 0);
	assert(value != NULL);

	assert(sticker_enabled());

	sqlite3_reset(sticker_stmt_update);

	ret = sqlite3_bind_text(sticker_stmt_update, 1, value, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	ret = sqlite3_bind_text(sticker_stmt_update, 2, type, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	ret = sqlite3_bind_text(sticker_stmt_update, 3, uri, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	ret = sqlite3_bind_text(sticker_stmt_update, 4, name, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	do {
		ret = sqlite3_step(sticker_stmt_update);
	} while (ret == SQLITE_BUSY);

	if (ret != SQLITE_DONE) {
		g_warning("sqlite3_step() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	ret = sqlite3_changes(sticker_db);

	sqlite3_reset(sticker_stmt_update);
	sqlite3_clear_bindings(sticker_stmt_update);

	idle_add(IDLE_STICKER);
	return ret > 0;
}

static bool
sticker_insert_value(const char *type, const char *uri,
		     const char *name, const char *value)
{
	int ret;

	assert(type != NULL);
	assert(uri != NULL);
	assert(name != NULL);
	assert(*name != 0);
	assert(value != NULL);

	assert(sticker_enabled());

	sqlite3_reset(sticker_stmt_insert);

	ret = sqlite3_bind_text(sticker_stmt_insert, 1, type, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	ret = sqlite3_bind_text(sticker_stmt_insert, 2, uri, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	ret = sqlite3_bind_text(sticker_stmt_insert, 3, name, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	ret = sqlite3_bind_text(sticker_stmt_insert, 4, value, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	do {
		ret = sqlite3_step(sticker_stmt_insert);
	} while (ret == SQLITE_BUSY);

	if (ret != SQLITE_DONE) {
		g_warning("sqlite3_step() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	sqlite3_reset(sticker_stmt_insert);
	sqlite3_clear_bindings(sticker_stmt_insert);


	idle_add(IDLE_STICKER);
	return true;
}

bool
sticker_store_value(const char *type, const char *uri,
		    const char *name, const char *value)
{
	assert(sticker_enabled());
	assert(type != NULL);
	assert(uri != NULL);
	assert(name != NULL);
	assert(value != NULL);

	if (*name == 0)
		return false;

	return sticker_update_value(type, uri, name, value) ||
		sticker_insert_value(type, uri, name, value);
}

bool
sticker_delete(const char *type, const char *uri)
{
	int ret;

	assert(sticker_enabled());
	assert(type != NULL);
	assert(uri != NULL);

	sqlite3_reset(sticker_stmt_delete);

	ret = sqlite3_bind_text(sticker_stmt_delete, 1, type, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	ret = sqlite3_bind_text(sticker_stmt_delete, 2, uri, -1, NULL);
	if (ret != SQLITE_OK) {
		g_warning("sqlite3_bind_text() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	do {
		ret = sqlite3_step(sticker_stmt_delete);
	} while (ret == SQLITE_BUSY);

	if (ret != SQLITE_DONE) {
		g_warning("sqlite3_step() failed: %s",
			  sqlite3_errmsg(sticker_db));
		return false;
	}

	sqlite3_reset(sticker_stmt_delete);
	sqlite3_clear_bindings(sticker_stmt_delete);

	idle_add(IDLE_STICKER);
	return true;
}

static struct sticker *
sticker_new(void)
{
	struct sticker *sticker = g_new(struct sticker, 1);

	sticker->table = g_hash_table_new_full(g_str_hash, g_str_equal,
					       g_free, g_free);
	return sticker;
}

void
sticker_free(struct sticker *sticker)
{
	assert(sticker != NULL);
	assert(sticker->table != NULL);

	g_hash_table_destroy(sticker->table);
	g_free(sticker);
}

const char *
sticker_get_value(struct sticker *sticker, const char *name)
{
	return g_hash_table_lookup(sticker->table, name);
}

struct sticker_foreach_data {
	void (*func)(const char *name, const char *value,
		     gpointer user_data);
	gpointer user_data;
};

static void
sticker_foreach_func(gpointer key, gpointer value, gpointer user_data)
{
	struct sticker_foreach_data *data = user_data;

	data->func(key, value, data->user_data);
}

void
sticker_foreach(struct sticker *sticker,
		void (*func)(const char *name, const char *value,
			     gpointer user_data),
		gpointer user_data)
{
	struct sticker_foreach_data data = {
		.func = func,
		.user_data = user_data,
	};

	g_hash_table_foreach(sticker->table, sticker_foreach_func, &data);
}

struct sticker *
sticker_load(const char *type, const char *uri)
{
	struct sticker *sticker = sticker_new();
	bool success;

	success = sticker_list_values(sticker->table, type, uri);
	if (!success)
		return NULL;

	if (g_hash_table_size(sticker->table) == 0) {
		/* don't return empty sticker objects */
		sticker_free(sticker);
		return NULL;
	}

	return sticker;
}