Subversion Repositories mdb

Rev

Rev 58 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * Copyright (C) 2015 by Andreas Theofilu <andreas@theosys.at>
 *
 * All rights reserved. No warranty, explicit or implicit, provided.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Andreas Theofilu and his suppliers, if any.
 * The intellectual and technical concepts contained
 * herein are proprietary to Andreas Theofilu and its suppliers and
 * may be covered by European and Foreign Patents, patents in process,
 * and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Andreas Theofilu.
 */
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <stdlib.h>
#include <libgen.h>
#include <ctype.h>
#include <math.h>
#include <syslog.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>
#include <sqlite3.h>
#include <ao/ao.h>
#include <mpg123.h>
#include "config.h"
#include "mdb.h"
#include "helplib.h"
#include "play.h"
#include "play_flac.h"
#include "user.h"

#define CMD_SET_NONE    0
#define CMD_SET1                1
#define CMD_SET2                2

#define NOWPLAY                 "/nowplaying.list"
#define BITS                    8

int nextCommand = PLAY_NONE;
int playStatus;
int playerActive = FALSE;
int playerRepeat = FALSE;
int playerRandom = FALSE;
int playQuiet = FALSE;
ST_PLAYING playCurrent;
QUEUE *pqueue = NULL;
QUEUE *qstack = NULL;
int queueTotal = 0;

char aoOutPlayers[15][16] =
{
        "aixs",
        "alsa",
        "arts",
        "esd",
        "irix",
        "macosx",
        "nas",
        "null",
        "oss",
        "pulse",
        "roar",
        "sndio",
        "sun",
        "wmm",
        "\0"
};

/* Prototypes */
static int playCallback(void *hint, int argc, char **argv, char **azColName);
void playMP3(int s1, char *file);
int checkQueueDouble(int id);
void freeQueue();
void setCurrentQueue(QUEUE *q);
QUEUE *getRandomQueue();
QUEUE *getQueue(int idx);
void setCurrentQueue(QUEUE *q);
QUEUE *addToQueue(int id, char *path, int fType, char *title, char *artist, char *album, char *genre, char *cover);

/*
 * This function is called as a thread.
 * It uses the libmpg123 library to decode a MP3 file and the library libao
 * to play the decoded samples.
 * TODO: Implement a player for the formats OGG and WAV
 * TODO: Implement an internet radio
 */
void *pthr_playfile(void *pV_data)
{
char fname[256], query[8192], hv0[256];
int rc, flag, start;
sqlite3 *db;
char *zErrMsg = 0;
struct ST_PlayPars *_playPars;
sqlite3_stmt *res;

        if (playerActive)
        {
                syslog(LOG_DAEMON, "Internal error: Thread \"pthr_playfile\" was called although it is already running!");
                pthread_exit(NULL);
        }

        playerActive = TRUE;                            /* Kind of locking :-) */
        _playPars = (struct ST_PlayPars *)pV_data;
        start = 0;
        nextCommand = PLAY_NONE;                        /* Make sure that there is no command left */

        if (_playPars == NULL)
        {
                syslog(LOG_DAEMON, "Internal error: Invalid or no parameters for thread \"pthr_playfile\"!");
                playerActive = FALSE;
                pthread_exit(NULL);
                return NULL;
        }

        /* retrieve the data from the database */
        strcpy(fname, configs.home);                    /* Base file name */
        strcat(fname, MUSICDB);                                 /* File name of database */

        rc = sqlite3_open(fname, &db);

        if (rc)
        {
                syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
                strcpy(query, "ERROR:PLAY:Error opening database;");
                write (_playPars->s1, query, strlen(query));
                playerActive = FALSE;
                pthread_exit(NULL);
                return NULL;
        }

        strcpy (query, "select id, path, type, title, interpret, album, genre, cover from \"main\".\"musicdb\" where ");
        flag = FALSE;

        if (strcasecmp(_playPars->type, "ID") == 0)
        {
                strcat(query, "id = ");
                strcat(query, _playPars->what);
                strcpy(fname, configs.home);
                strcat(fname, NOWPLAY);
                unlink(fname);
                freeQueue();
                flag = TRUE;
        }
        else if (strcasecmp(_playPars->type, "DIRECT") == 0)
        {
                if (configs.debug)
                        syslog(LOG_DEBUG, "pthr_playfile(): Playing ID %s direct.", _playPars->what);

                strcat(query, "id = ");
                strcat(query, _playPars->what);
                flag = TRUE;
        }
        else if (strcasecmp(_playPars->type, "TITLE") == 0)
        {
                strcat(query, "title = \"");
                strcat(query, _playPars->what);
                strcat(query, "\" order by title");
                flag = TRUE;
        }
        else if (strcasecmp(_playPars->type, "ARTIST") == 0)
        {
                strcat(query, "interpret = \"");
                strcat(query, _playPars->what);
                strcat(query, "\" order by interpret");
                flag = TRUE;
        }
        else if (strcasecmp(_playPars->type, "ALBUM") == 0)
        {
                strcat(query, "album = \"");
                strcat(query, _playPars->what);
                strcat(query, "\" order by album");
                flag = TRUE;
        }
        else if (strcasecmp(_playPars->type, "GENRE") == 0)
        {
                strcat(query, "genre = \"");
                strcat(query, _playPars->what);
                strcat(query, "\" order by genre");
                flag = TRUE;
        }
        else if (strcasecmp(_playPars->type, "QUEUE") == 0)
        {
                strcpy(fname, configs.home);                            /* Base path */
                strcat(fname, NOWPLAY);                                         /* Append file name of queue to path */

                if (access(fname, R_OK))                                        /* In case we've no access or the file doesn't exist, quit this thread. */
                {
                        strcpy (hv0, "ERROR:PLAY:No or empty queue;");
                        write (_playPars->s1, hv0, strlen(hv0));
                        sqlite3_close(db);
                        playerActive = FALSE;
                        pthread_exit(NULL);
                        return NULL;
                }

                start = atoi(_playPars->what);
        }
        else if (strcasecmp(_playPars->type, "PLAYLIST") == 0)
        {
                USERS *act;

                if (userchain == NULL)
                {
                        strcpy (hv0, "ERROR:PLAY:No user selected;");
                        write (_playPars->s1, hv0, strlen(hv0));
                        sqlite3_close(db);
                        playerActive = FALSE;
                        pthread_exit(NULL);
                        return NULL;
                }

                /* find uid */
                if ((act = findPlaylist(userchain->uname, _playPars->what)) != NULL)
                {
                        if (!playlistToQueue(act->id, TRUE))
                        {
                                sqlite3_close(db);
                                playerActive = FALSE;
                                pthread_exit(NULL);
                                return NULL;
                        }
                }
                else if (isnumeric(_playPars->what))
                {
                        int id;

                        id = atoi(_playPars->what);

                        if (!playlistToQueue(id, TRUE))
                        {
                                sqlite3_close(db);
                                playerActive = FALSE;
                                pthread_exit(NULL);
                                return NULL;
                        }
                }
                else
                {
                        strcpy (hv0, "ERROR:PLAY:No playlist found;");
                        write (_playPars->s1, hv0, strlen(hv0));
                        sqlite3_close(db);
                        playerActive = FALSE;
                        pthread_exit(NULL);
                        return NULL;
                }
        }
        else
        {
                strcpy(hv0, "ERROR:PLAY:Missing type of argument;");
                write(_playPars->s1, hv0, strlen(hv0));
                sqlite3_close(db);
                playerActive = FALSE;
                pthread_exit(NULL);
                return NULL;
        }

        if (configs.debug)
        {
                syslog(LOG_DEBUG, "Player: SQL: %s", query);
                syslog(LOG_DEBUG, "Player: FLAG: %d, TYPE: %s", flag, _playPars->type);
        }

        if (flag && strcasecmp(_playPars->type, "DIRECT") == 0)
        {
                char *p, path[1024], title[255], artist[255], album[255], genre[128], cover[255];
                int id, type, err;

                if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
                {
                        syslog(LOG_WARNING, "SQL error [%s]: %s", query, sqlite3_errmsg(db));
                        sqlite3_close(db);
                        strcpy(query, "ERROR:PLAY:SQL error;");
                        write (_playPars->s1, query, strlen(query));
                        playerActive = FALSE;
                        pthread_exit(NULL);
                        return NULL;
                }

                err = 0;
                memset(path, 0, sizeof(path));
                memset(title, 0, sizeof(title));
                memset(artist, 0, sizeof(artist));
                memset(album, 0, sizeof(album));
                memset(genre, 0, sizeof(genre));
                memset(cover, 0, sizeof(cover));

                rc = sqlite3_step(res);

                id = sqlite3_column_int(res, 0);

                if (configs.debug)
                        syslog(LOG_DEBUG, "Playing directly ID: %d", id);

                if ((p = (char *)sqlite3_column_text(res, 1)) == NULL)
                {
                        syslog(LOG_WARNING, "Error getting path from database!");
                        sqlite3_finalize(res);
                        sqlite3_close(db);
                        strcpy(query, "ERROR:PLAY:SQL error;");
                        write (_playPars->s1, query, strlen(query));
                        playerActive = FALSE;
                        pthread_exit(NULL);
                        return NULL;
                }
                else
                        strncpy(path, p, sizeof(path));

                type = sqlite3_column_int(res, 2);

                if ((p = (char *)sqlite3_column_text(res, 3)) == NULL)
                {
                        syslog(LOG_WARNING, "Error getting title from database!");
                        sqlite3_finalize(res);
                        sqlite3_close(db);
                        strcpy(query, "ERROR:PLAY:SQL error;");
                        write (_playPars->s1, query, strlen(query));
                        playerActive = FALSE;
                        pthread_exit(NULL);
                        return NULL;
                }
                else
                        strncpy(title, p, sizeof(title));

                if ((p = (char *)sqlite3_column_text(res, 4)) == NULL)
                {
                        syslog(LOG_WARNING, "Error getting artist from database!");
                        sqlite3_finalize(res);
                        sqlite3_close(db);
                        strcpy(query, "ERROR:PLAY:SQL error;");
                        write (_playPars->s1, query, strlen(query));
                        playerActive = FALSE;
                        pthread_exit(NULL);
                        return NULL;
                }
                else
                        strncpy(artist, p, sizeof(artist));

                if ((p = (char *)sqlite3_column_text(res, 5)) == NULL)
                {
                        syslog(LOG_WARNING, "Error getting album from database!");
                        sqlite3_finalize(res);
                        sqlite3_close(db);
                        strcpy(query, "ERROR:PLAY:SQL error;");
                        write (_playPars->s1, query, strlen(query));
                        playerActive = FALSE;
                        pthread_exit(NULL);
                        return NULL;
                }
                else
                        strncpy(album, p, sizeof(album));

                if ((p = (char *)sqlite3_column_text(res, 6)) == NULL)
                {
                        syslog(LOG_WARNING, "Error getting genre from database!");
                        sqlite3_finalize(res);
                        sqlite3_close(db);
                        strcpy(query, "ERROR:PLAY:SQL error;");
                        write (_playPars->s1, query, strlen(query));
                        playerActive = FALSE;
                        pthread_exit(NULL);
                        return NULL;
                }
                else
                        strncpy(genre, p, sizeof(genre));

                if ((p = (char *)sqlite3_column_text(res, 7)) != NULL)
                        strncpy(cover, p, sizeof(cover));

                sqlite3_finalize(res);
                sprintf(query, "PLAYING:%d:", id);

                if ((p = urlencode(title)) != NULL)
                {
                        strcat(query, p);
                        free(p);
                }

                strcat(query, ":");

                if ((p = urlencode(artist)) != NULL)
                {
                        strcat(query, p);
                        free(p);
                }

                strcat(query, ":");

                if ((p = urlencode(album)) != NULL)
                {
                        strcat(query, p);
                        free(p);
                }

                strcat(query, ":");

                if ((p = urlencode(genre)) != NULL)
                {
                        strcat(query, p);
                        free(p);
                }

                strcat(query, ":");

                if (strlen(cover) != 0)
                {
                        if ((p = urlencode(cover)) != NULL)
                        {
                                strcat(query, p);
                                free(p);
                        }
                }

                strcat(query, ";");
                write(_playPars->s1, query, strlen(query));

                while (strlen(path))
                {
                        QUEUE *act;

                        if (configs.debug)
                                syslog(LOG_DEBUG, "Player: Trying to play file: %s", path);

                        if (type == FILE_TYPE_MP3)
                                playMP3(_playPars->s1, path);
                        else
                                playFlac(_playPars->s1, path);

                        path[0] = 0;

                        if (playStatus == PLAY_STATUS_STACK || playStatus == PLAY_STATUS_STPLAY)
                        {
                                act = qstackNext();
                                
                                if (act)
                                {
                                        playStatus = PLAY_STATUS_STPLAY;
                                        nextCommand = PLAY_NONE;
                                        strncpy(path, act->path, sizeof(path));
                                }
                                else
                                {
                                        nextCommand = PLAY_NONE;
                                        playStatus = PLAY_STATUS_STOP;
                                }
                        }
                }

                sqlite3_close(db);
                playerActive = FALSE;
                pthread_exit(NULL);
                return NULL;
        }
        else if (flag && (rc = sqlite3_exec(db, query, playCallback, (void *)_playPars->what, &zErrMsg)) != SQLITE_OK)
        {
                syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
                sqlite3_free(zErrMsg);
                sqlite3_close(db);
                strcpy(query, "ERROR:PLAY:SQL error;");
                write (_playPars->s1, query, strlen(query));
                playerActive = FALSE;
                pthread_exit(NULL);
                return NULL;
        }

        sqlite3_close(db);

        if (configs.debug)
                syslog(LOG_DEBUG, "Playing from current queue starting at entry %d", start);

        strcpy (fname, configs.home);
        strcat (fname, NOWPLAY);
        /* start the player and play the file(s) */
        if (!access(fname, R_OK))
        {
                int fd, iflag, num, nextNum;
                QUEUE *act;
                
                if ((fd = open(fname, O_RDONLY)) < 0)
                {
                        syslog(LOG_WARNING, "Error opening file %s: %s", fname, strerror(errno));
                        playerActive = FALSE;
                        pthread_exit(NULL);
                        return NULL;
                }

                iflag = FALSE;
                num = 0;
                nextNum = -1;

                if (start > 0 && start <= queueTotal)
                        num = start - 1;

                scanQueue(fd);
                close(fd);

                if (playerRandom && !flag && start <= 0)
                        act = getRandomQueue();
                else
                        act = getQueue(num);

                while (act)
                {
                        char *title, *artist, *album, *cover;

                        if (nextNum >= 0)
                        {
                                if (num == nextNum)
                                        nextNum = -1;
                                else
                                {
                                        num++;
                                        act = getQueue(num);
                                        continue;
                                }
                        }

                        iflag = TRUE;
                        title = urlencode(act->title);
                        artist = urlencode(act->artist);
                        album = urlencode(act->album);
                        cover = urlencode(act->cover);
                        sprintf(query, "PLAYING:%d:", act->id);

                        if (title != NULL)
                        {
                                strcat(query, title);
                                free(title);
                        }
                        else
                                strcat(query, act->title);

                        strcat(query, ":");

                        if (artist != NULL)
                        {
                                strcat(query, artist);
                                free(artist);
                        }
                        else
                                strcat(query, act->artist);

                        strcat(query, ":");

                        if (album != NULL)
                        {
                                strcat(query, album);
                                free(album);
                        }
                        else
                                strcat(query, act->album);

                        strcat(query, ":");
                        strcat(query, act->genre);
                        strcat(query, ":");
                        
                        if (cover != NULL)
                        {
                                strcat(query, cover);
                                free(cover);
                        }
                        else
                                strcat(query, act->cover);

                        strcat(query, ";");
                        write(_playPars->s1, query, strlen(query));
                        setCurrentQueue(act);
                        act->played = TRUE;

                        if (act->fType == FILE_TYPE_MP3)
                                playMP3(_playPars->s1, act->path);
                        else
                                playFlac(_playPars->s1, act->path);

                        if (configs.debug)
                                syslog(LOG_DEBUG, "Current play status: %d", playStatus);

                        if (playStatus == PLAY_STATUS_SKIPF)
                                playStatus = PLAY_STATUS_STOP;
                        else if (playStatus == PLAY_STATUS_SKIPR && !playerRandom)
                        {
                                if (num > 0)
                                {
                                        nextNum = num - 1;
                                        num = 0;
                                        act = pqueue;
                                        continue;
                                }

                                playStatus = PLAY_STATUS_STOP;
                        }
                        else if (playStatus == PLAY_STATUS_STACK || playStatus == PLAY_STATUS_STPLAY)
                        {
                                act = qstackNext();

                                if (act)
                                {
                                        playStatus = PLAY_STATUS_STPLAY;
                                        nextCommand = PLAY_NONE;
                                        continue;
                                }
                                else
                                {
                                        nextCommand = PLAY_NONE;
                                        playStatus = PLAY_STATUS_PLAY;
                                }
                        }
                        else if (playStatus == PLAY_STATUS_STOP)
                                break;

                        num++;

                        if (playerRandom)
                                act = getRandomQueue();
                        else
                                act = getQueue(num);

                        if (playerRepeat && act == NULL)
                        {
                                num = 0;
                                act = pqueue;
                        }

                        if (configs.debug)
                                syslog(LOG_DEBUG, "Do we have a next file to play? >>%s<<", (act == NULL) ? "NO" : "YES");
                }

                if (!iflag)
                        syslog(LOG_WARNING, "Found no files to play!");

                close (fd);
        }
        else
                syslog(LOG_WARNING, "Error accessing file %s: %s", fname, strerror(errno));

        playerActive = FALSE;
        pthread_exit(NULL);
        return NULL;
}

static int playCallback(void *hint, int argc, char **argv, char **azColName)
{
        int i, id, fd, fType;
        char path[512], id3_title[256], id3_artist[256], id3_album[256], id3_genre[256], id3_cover[512];
        char buffer[8192], *what;
        
        what = (char *)hint;
        memset(path, 0, sizeof(path));
        memset(id3_title, 0, sizeof(id3_title));
        memset(id3_artist, 0, sizeof(id3_artist));
        memset(id3_album, 0, sizeof(id3_album));
        memset(id3_genre, 0, sizeof(id3_genre));
        memset(id3_cover, 0, sizeof(id3_cover));
        id = -1;

        for(i = 0; i < argc; i++)
        {
                if (strcasecmp(azColName[i], "id") == 0)
                        if (argv[i])
                                id = atoi(argv[i]);

                if (strcasecmp(azColName[i], "path") == 0)
                        if (argv[i])
                                strncpy(path, argv[i], sizeof(path));

                if (strcasecmp(azColName[i], "type") == 0)
                        if (argv[i])
                                fType = atoi(argv[i]);

                if (strcasecmp(azColName[i], "title") == 0)
                        if (argv[i])
                                strncpy(id3_title, argv[i], sizeof(id3_title));

                if (strcasecmp(azColName[i], "interpret") == 0)
                        if (argv[i])
                                strncpy(id3_artist, argv[i], sizeof(id3_artist));

                if (strcasecmp(azColName[i], "album") == 0)
                        if (argv[i])
                                strncpy(id3_album, argv[i], sizeof(id3_album));

                if (strcasecmp(azColName[i], "genre") == 0)
                        if (argv[i])
                                strncpy(id3_genre, argv[i], sizeof(id3_genre));

                        if (strcasecmp(azColName[i], "cover") == 0)
                                if (argv[i])
                                        strncpy(id3_cover, argv[i], sizeof(id3_cover));
        }

        if (id != -1 && strlen(path) > 0)
        {
                char fname[512];

                strcpy(fname, configs.home);
                strcat(fname, NOWPLAY);

                if (checkQueueDouble(id) == TRUE)       /* Make sure every ID in the queue is unique */
                        return 0;

                if ((fd = open(fname, O_RDWR | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP)) < 0)
                {
                        syslog(LOG_WARNING, "Error creating or writing to file %s: %s", fname, strerror(errno));
                        return 1;
                }

                sprintf (buffer, "%s\t%d\t%d\t%s\t%s\t%s\t%s\t%s\n", path, fType, id, id3_title, id3_artist, id3_album, id3_genre, id3_cover);
                write (fd, buffer, strlen(buffer));
                addToQueue(id, path, fType, id3_title, id3_artist, id3_album, id3_genre, id3_cover);
                close (fd);
        }
        else
                syslog(LOG_WARNING, "Found no data to play");

        return 0;
}

void playMP3(int s1, char *file)
{
mpg123_handle *mh;
unsigned char *buffer;
size_t buffer_size;
size_t done;
off_t frame;
int err, i, rc, flag;
char hv0[128];

int driver;
ao_device *dev;
ao_option ao_opts;

ao_sample_format format;
int channels, encoding;
long rate;
double current_seconds, seconds_left, act_sec;
off_t current_frame, frames_left;

        if(file == NULL || strlen(file) == 0)
        {
                syslog(LOG_DAEMON, "No or invalid file was passed to player!");
                return;
        }

        /* Check if we've a valid sound driver defined */
        flag = FALSE;
        i = 0;

        while (aoOutPlayers[i][0])
        {
                if (!strcasecmp(aoOutPlayers[i], configs.player))
                {
                        flag = TRUE;

                        if (configs.debug)
                                syslog(LOG_DEBUG, "Found sound driver %s to use for playback.", aoOutPlayers[i]);

                        break;
                }

                i++;
        }

        if (configs.debug)
                syslog(LOG_DEBUG, "playMP3: Try to play file >>%s<<", file);

        /* initializations */
        ao_initialize();

        if (flag)
        {
                if ((driver = ao_driver_id(configs.player)) == -1)
                {
                        syslog(LOG_DAEMON, "Error finding the audio out driver %s!", configs.player);
                        ao_shutdown();
                        return;
                }
        }
        else if ((driver = ao_default_driver_id()) == -1)
        {
                syslog(LOG_DAEMON, "Error finding a default audio driver!");
                ao_shutdown();
                return;
        }

        mpg123_init();

        if (configs.debug)
                syslog(LOG_DEBUG, "MPG123 initialized.");

        if ((mh = mpg123_new(NULL, &err)) == NULL)
        {
                syslog(LOG_DAEMON, "Error creating new MP3 handle: %s", mpg123_plain_strerror(err));
                ao_shutdown();
                return;
        }

        if (configs.debug)
                syslog(LOG_DEBUG, "MPG123 opened.");

        buffer_size = mpg123_outblock(mh);

        if ((buffer = (unsigned char*)malloc(buffer_size * sizeof(unsigned char))) == NULL)
        {
                syslog(LOG_DAEMON, "Error allocating memory for MP3 player: %s", strerror(errno));
                mpg123_close(mh);
                mpg123_delete(mh);
                mpg123_exit();
                ao_shutdown();
                return;
        }
        
        /* open the file and get the decoding format */
        if (mpg123_open(mh, file) != MPG123_OK)
        {
                syslog(LOG_DAEMON, "Error opening MP3 file %s: %s", file, mpg123_strerror(mh));
                free(buffer);
                mpg123_close(mh);
                mpg123_delete(mh);
                mpg123_exit();
                ao_shutdown();
                return;
        }

        if (mpg123_scan(mh) != MPG123_OK)
                syslog(LOG_WARNING, "Error scanning MP3 file %s: %s", file, mpg123_strerror(mh));

        if (mpg123_getformat(mh, &rate, &channels, &encoding) != MPG123_OK)
        {
                syslog(LOG_DAEMON, "Error getting the format of MP3 file %s: %s", file, mpg123_strerror(mh));
                free(buffer);
                mpg123_close(mh);
                mpg123_delete(mh);
                mpg123_exit();
                ao_shutdown();
                return;
        }
        
        /* set the output format and open the output device */
        format.bits = mpg123_encsize(encoding) * BITS;
        format.rate = rate;
        format.channels = channels;
        format.byte_format = AO_FMT_NATIVE;
        format.matrix = 0;

        if (configs.debug)
        {
                syslog(LOG_DEBUG, "Format: Bits:     %d", format.bits);
                syslog(LOG_DEBUG, "Format: Rate:     %ld", rate);
                syslog(LOG_DEBUG, "Format: Channels: %d", channels);
        }

        memset(&ao_opts, 0, sizeof(ao_option));

        if (configs.debug)
        {
                syslog(LOG_DEBUG, "Switching on debugging for AO library.");
                strcpy(hv0, "debug");
                ao_opts.key = strdup(hv0);
                strcpy(hv0, "1");
                ao_opts.value = strdup(hv0);
                dev = ao_open_live(driver, &format, &ao_opts);
                syslog(LOG_DEBUG, "AO library opened.");
        }
        else
                dev = ao_open_live(driver, &format, NULL);

        if (dev == NULL)
        {
                switch (errno)
                {
                        case AO_ENODRIVER:      sprintf(hv0, "No driver corresponds to \"driver_id\"."); break;
                        case AO_ENOTLIVE:       sprintf(hv0, "This driver (%s) is not a live output device.", configs.player); break;
                        case AO_EBADOPTION:     sprintf(hv0, "A valid option key has an invalid value."); break;
                        case AO_EOPENDEVICE:sprintf(hv0, "Cannot open the device."); break;
                        default:
                                sprintf(hv0, "%s", strerror(errno));
                }

                syslog(LOG_DAEMON, "Error opening live playback device: %s", hv0);
                free(buffer);
                mpg123_close(mh);
                mpg123_delete(mh);
                mpg123_exit();
                ao_shutdown();

                if (configs.debug)
                {
                        free(ao_opts.key);
                        free(ao_opts.value);
                }

                return;
        }

        if (playStatus != PLAY_STATUS_STPLAY)
                playStatus = PLAY_STATUS_PLAY;

/*      strcpy (hv0, "PLAYER:PLAY;");
        write(s1, hv0, strlen(hv0)); */
        handleWrite("PLAYER:PLAY;");
        act_sec = 0.0;
        flag = FALSE;
        current_seconds = seconds_left = 0;
        current_frame = frames_left = 0;
        nextCommand = PLAY_NONE;                        /* Reset commands to be sure there is no lost command left */
        frame = mpg123_tellframe(mh);

        /* decode and play */
        while ((rc = mpg123_read(mh, buffer, buffer_size, &done)) == MPG123_OK)
        {
                int todo;
                char hv1[32], hv2[32], hv3[32];

                todo = check_command(s1);

                if (todo == PLAY_STATUS_STOP)
                        break;
                else if (todo == PLAY_STATUS_FWD)
                {
                        mpg123_seek_frame(mh, 100, SEEK_CUR);

                        if (playStatus != PLAY_STATUS_STPLAY)
                                playStatus = PLAY_STATUS_PLAY;

                        continue;
                }
                else if (todo == PLAY_STATUS_REW)
                {
                        off_t fr;
                        
                        fr =  mpg123_tellframe(mh);
                        
                        if (fr > 100)
                                fr -= 100;
                        else
                                fr = 0;

                        mpg123_seek_frame(mh, fr, SEEK_SET);

                        if (playStatus != PLAY_STATUS_STPLAY)
                                playStatus = PLAY_STATUS_PLAY;

                        continue;
                }

                /* get position */
                if (!flag)
                {
                        mpg123_position(mh, mpg123_tellframe(mh), done, &current_frame, &frames_left, &current_seconds, &seconds_left);
                        act_sec = current_seconds;
                        flag = TRUE;
                }
                else if (flag && frame != 0)
                {
                        double cs, sl;
                        mpg123_position(mh, frame, done, &current_frame, &frames_left, &cs, &sl);
                        frame = mpg123_tellframe(mh) / 100;
                        act_sec = cs;
                }
                else if (frame == 0)
                        frame = mpg123_tellframe(mh) / 100;

                if (!playQuiet)
                {
                        sprintf(hv0, "POSITION:%s:%s:%s;", secondsToString(act_sec, &hv1[0]), secondsToString((current_seconds + seconds_left) - act_sec, &hv2[0]), secondsToString(current_seconds + seconds_left, &hv3[0]));
/*                      write(s1, hv0, strlen(hv0)); */
                        handleWrite(hv0);
                }

                ao_play(dev, (char *)buffer, done);
        }

        if (configs.debug && rc != MPG123_OK)
                syslog(LOG_DEBUG, "MPG123 Error: %s", mpg123_plain_strerror(rc));

        /* clean up */
/*      strcpy(hv0, "PLAYER:STOP;");
        write (s1, hv0, strlen(hv0)); */
        handleWrite("PLAYER:STOP;");

        free(buffer);
        ao_close(dev);
        mpg123_close(mh);
        mpg123_delete(mh);
        mpg123_exit();

        if (configs.debug)
                syslog(LOG_DEBUG, "MPG123 closed");

        ao_shutdown();

        if (configs.debug)
        {
                free(ao_opts.key);
                free(ao_opts.value);
                syslog(LOG_DEBUG, "AO library closed.");
        }
}

int check_command(int s1)
{
/*      char hv0[64]; */

        if (nextCommand == PLAY_STOP_NOW)
        {
                playStatus = PLAY_STATUS_STOP;
                return playStatus;
        }

        if (nextCommand == PLAY_STACK)
        {
                playStatus = PLAY_STATUS_STACK;
                return PLAY_STATUS_STOP;
        }

        if (nextCommand == PLAY_PLAY && (playStatus == PLAY_STATUS_FWD || playStatus == PLAY_STATUS_REW))
        {
                playStatus = PLAY_STATUS_PLAY;
                return playStatus;
        }

        if (nextCommand == PLAY_STOP && (playStatus == PLAY_STATUS_PLAY || playStatus == PLAY_STATUS_PAUSE))
        {
                nextCommand = PLAY_NONE;
                playStatus = PLAY_STATUS_STOP;
                return playStatus;
        }
        else if (nextCommand == PLAY_STOP && playStatus == PLAY_STATUS_STPLAY)
        {
                nextCommand = PLAY_NONE;
                playStatus = PLAY_STATUS_PLAY;
                return PLAY_STATUS_STOP;
        }

        if ((nextCommand == PLAY_PAUSE || nextCommand == PLAY_PLAYPAUSE) && playStatus == PLAY_STATUS_PLAY)
        {
                nextCommand = PLAY_NONE;
                playStatus = PLAY_STATUS_PAUSE;
                handleWrite ("PLAYER:PAUSE;");
/*              write (s1, hv0, strlen(hv0)); */

                while (nextCommand != PLAY_PLAY && nextCommand != PLAY_PLAYPAUSE)
                        sleep(1);

                playStatus = PLAY_STATUS_PLAY;
                handleWrite ("PLAYER:PLAY;");
/*              write (s1, hv0, strlen(hv0)); */
                nextCommand = PLAY_NONE;
        }

        if (nextCommand == PLAY_FWD)
        {
                nextCommand = PLAY_NONE;
                playStatus = PLAY_STATUS_FWD;
                return PLAY_STATUS_FWD;
        }

        if (nextCommand == PLAY_REW)
        {
                nextCommand = PLAY_NONE;
                playStatus = PLAY_STATUS_REW;
                return PLAY_STATUS_REW;
        }

        if (nextCommand == PLAY_SKIP_FWD)
        {
                nextCommand = PLAY_NONE;
                playStatus = PLAY_STATUS_SKIPF;
                return PLAY_STATUS_STOP;
        }

        if (nextCommand == PLAY_SKIP_REW)
        {
                nextCommand = PLAY_NONE;
                playStatus = PLAY_STATUS_SKIPR;
                return PLAY_STATUS_STOP;
        }

        return 0;
}

int checkQueueDouble(int id)
{
        QUEUE *act;

        act = pqueue;
        
        while (act)
        {
                if (act->id == id)
                        return TRUE;
                
                act = act->next;
        }
        
        return FALSE;
}

void appendToQueue(int s1, char *type, char *what)
{
        char fname[256], query[1024];
        int rc;
        sqlite3 *db;
        char *zErrMsg = 0;
        
        /* retrieve the data from the database */
        strcpy(fname, configs.home);
        strcat(fname, MUSICDB);
        
        rc = sqlite3_open(fname, &db);
        
        if (rc)
        {
                syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
                strcpy(query, "ERROR:PLAY:Error opening database;");
                write (s1, query, strlen(query));
                playerActive = FALSE;
                return;
        }
        
        strcpy (query, "select id, path, type, title, interpret, album, genre, cover from \"main\".\"musicdb\" where ");

        if (strcasecmp(type, "ID") == 0)
        {
                strcat(query, "id = ");
                strcat(query, what);
        }
        else if (strcasecmp(type, "TITLE") == 0)
        {
                strcat(query, "title = \"");
                strcat(query, what);
                strcat(query, "\" order by title");
        }
        else if (strcasecmp(type, "ARTIST") == 0)
        {
                strcat(query, "interpret = \"");
                strcat(query, what);
                strcat(query, "\" order by interpret");
        }
        else if (strcasecmp(type, "ALBUM") == 0)
        {
                strcat(query, "album = \"");
                strcat(query, what);
                strcat(query, "\" order by album");
        }
        else if (strcasecmp(type, "GENRE") == 0)
        {
                strcat(query, "genre = \"");
                strcat(query, what);
                strcat(query, "\" order by genre");
        }
        else if (strcasecmp(type, "PLAYLIST") == 0)
        {
                USERS *act;

                sqlite3_close(db);
                act = userchain;

                while (act)
                {
                        if (strcmp(act->playlist, what) == 0)
                        {
                                playlistToQueue(act->id, FALSE);
                                break;
                        }

                        act = act->next;
                }

                if (act == NULL)
                {
                        strcpy(query, "ERROR:PLAY:Error transfering playlist entries to queue;");
                        write (s1, query, strlen(query));
                }

                return;
        }

        if ((rc = sqlite3_exec(db, query, playCallback, (void *)what, &zErrMsg)) != SQLITE_OK)
        {
                syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
                sqlite3_free(zErrMsg);
                sqlite3_close(db);
                strcpy(query, "ERROR:PLAY:SQL error;");
                write (s1, query, strlen(query));
        }

        readQueue();
}

QUEUE *addToQueue(int id, char *path, int fType, char *title, char *artist, char *album, char *genre, char *cover)
{
        QUEUE *act, *neu;
        /* find the end of the chain */
        act = pqueue;

        while (act && act->next)
        {
                if (act->id == id)                      /* If the ID is already in the chain, return the pointer to it */
                        return act;

                act = act->next;
        }
        /* Allocate memory for a new element in the chain */
        if ((neu = (QUEUE *)malloc(sizeof(QUEUE))) == NULL)
        {
                syslog(LOG_DAEMON, "Error allocating %lu bytes for an entry in the queue: %s", sizeof(QUEUE), strerror(errno));
                return NULL;
        }

        memset(neu, 0, sizeof(QUEUE));
        /* Copy the data into the new element */
        neu->id = id;
        neu->fType = fType;

        if (path)
                strncpy (neu->path, path, sizeof(neu->path)-1);

        if (title)
                strncpy (neu->title, title, sizeof(neu->title)-1);

        if (artist)
                strncpy (neu->artist, artist, sizeof(neu->artist)-1);

        if (album)
                strncpy (neu->album, album, sizeof(neu->album)-1);

        if (genre)
                strncpy (neu->genre, genre, sizeof(neu->genre)-1);

        if (cover)
                strncpy (neu->cover, cover, sizeof(neu->cover)-1);
        /* Append the element to the end of the chain */
        if (act == NULL)
                act = pqueue = neu;
        else
                act->next = neu;

        return neu;
}

/*
 * This function copies the content of a playlist into the queue.
 * The parameter defines whether the current queue should be deleted or not.
 * If the queue should not be deleted, the playlist will be appended.
 */
int playlistToQueue(int uid, int del)
{
        char fname[256], query[1024], hv0[256], buffer[8192];
        int rc;
        sqlite3 *db;
        sqlite3_stmt *res;
        char path[512], id3_title[256], id3_artist[256], id3_album[256], id3_genre[256];
        int id, fd, fType;
        unsigned int mode;

        strcpy(fname, configs.home);
        strcat(fname, MUSICDB);
        
        rc = sqlite3_open(fname, &db);
        
        if (rc)
        {
                syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
                return FALSE;
        }
        
        strcpy (query, "select id, path, type, title, interpret, album, genre from \"main\".\"musicdb\" as a where ");
        strcat (query, "(select musicid from \"main\".\"playlists\"  as b where a.id = b.musicid and b.userid = ");
        sprintf(hv0, "%d", uid);
        strcat (query, hv0);
        strcat (query, ")");

        if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
        {
                syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
                sqlite3_close(db);
                return FALSE;
        }

        /* Open the queue */
        strcpy(fname, configs.home);
        strcat(fname, NOWPLAY);

        if (!del)
                mode = O_RDWR | O_APPEND | O_CREAT;
        else
        {
                mode = O_RDWR | O_TRUNC | O_CREAT;

                if (playerActive)
                        nextCommand = PLAY_STOP_NOW;
        }

        if ((fd = open(fname, mode, S_IWUSR | S_IRUSR | S_IRGRP)) <= 0)
        {
                syslog(LOG_WARNING, "Error opening file %s: %s", fname, strerror(errno));
                sqlite3_finalize(res);
                sqlite3_close(db);
                return FALSE;
        }

        while ((rc = sqlite3_step(res)) == SQLITE_ROW)
        {
                memset(path, 0, sizeof(path));
                memset(id3_title, 0, sizeof(id3_title));
                memset(id3_artist, 0, sizeof(id3_artist));
                memset(id3_album, 0, sizeof(id3_album));
                memset(id3_genre, 0, sizeof(id3_genre));
                id = sqlite3_column_int(res, 0);
                strncpy(path, (const char *)sqlite3_column_text(res, 1), sizeof(path));
                fType = sqlite3_column_int(res, 2);
                strncpy(id3_title, (const char *)sqlite3_column_text(res, 3), sizeof(id3_title));
                strncpy(id3_artist, (const char *)sqlite3_column_text(res, 4), sizeof(id3_artist));
                strncpy(id3_album, (const char *)sqlite3_column_text(res, 5), sizeof(id3_album));
                strncpy(id3_genre, (const char *)sqlite3_column_text(res, 6), sizeof(id3_genre));
                sprintf (buffer, "%s\t%d\t%d\t%s\t%s\t%s\t%s\n", path, fType, id, id3_title, id3_artist, id3_album, id3_genre);
                write (fd, buffer, strlen(buffer));
        }

        scanQueue(fd);
        close(fd);
        sqlite3_finalize(res);
        sqlite3_close(db);
        return TRUE;
}

int QueueToPlaylist(int s1, char *user, char *playlist)
{
        char fname[256], query[1024], hv0[256];
        int rc;
        sqlite3 *db;
        char *zErrMsg = 0;
        sqlite3_stmt *res;
        int num;
        USERS *usp;
        QUEUE *act;

        strcpy(fname, configs.home);
        strcat(fname, MUSICDB);
        
        rc = sqlite3_open(fname, &db);
        
        if (rc)
        {
                syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
                return FALSE;
        }

        if ((usp = findPlaylist(user, playlist)) != NULL)
        {
                sprintf(query, "delete from playlists where userid = %d", usp->id);

                if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
                {
                        syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
                        sqlite3_close(db);
                        return FALSE;
                }

                sqlite3_step(res);
        }
        else if (createUser(s1, user, playlist))
        {
                if ((usp = findPlaylist(user, playlist)) == NULL)
                        return FALSE;
        }
        else
                return FALSE;

        /* Update the queue in case there is none in memory*/
        if (pqueue == NULL)
                readQueue();

        act = pqueue;
        num = 0;

        while (act)
        {
                strcpy (query, "insert into playlists (userid, musicid) values (");
                sprintf(hv0, "%d", usp->id);
                strcat (query, hv0);
                strcat (query, ",");
                sprintf(hv0, "%d", act->id);
                strcat (query, hv0);
                strcat (query, ")");

                if ((rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg)) != SQLITE_OK)
                {
                        syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
                        sqlite3_free(zErrMsg);
                        sqlite3_close(db);
                        return FALSE;
                }

                num++;
                act = act->next;
        }

        sqlite3_close(db);
        sprintf(hv0, "TRANSFERED:%d;", num);
        write (s1, hv0, strlen(hv0));
        return TRUE;
}

void freeQueue()
{
        QUEUE *act;

        act = pqueue;

        while (act)
        {
                pqueue = act->next;
                free (act);
                act = pqueue;
        }

        pqueue = NULL;
        queueTotal = 0;
}

int readQueue()
{
        int fd;
        char fname[256];

        strcpy (fname, configs.home);
        strcat (fname, NOWPLAY);
        /* start the player and play the file(s) */
        if (!access(fname, R_OK))
        {
                if ((fd = open(fname, O_RDONLY)) < 0)
                {
                        syslog(LOG_WARNING, "Error opening file %s: %s", fname, strerror(errno));
                        return FALSE;
                }
                
                scanQueue(fd);
                close(fd);
        }
        else
                return FALSE;

        return TRUE;
}

int scanQueue(int fd)
{
        QUEUE *act;
        int count, x;
        char buffer[8192], *t;

        if (pqueue != NULL)
                freeQueue();

        count = 0;
        lseek (fd, 0L, SEEK_SET);

        while (readLine (fd, buffer, sizeof(buffer)) != NULL)
        {
                QUEUE *new;

                if ((new = (QUEUE *)malloc(sizeof(QUEUE))) == NULL)
                {
                        syslog(LOG_DAEMON, "Error allocating %lu bytes of memory: %s", sizeof(QUEUE), strerror(errno));
                        lseek(fd, 0L, SEEK_SET);
                        return -1;
                }

                memset(new, 0, sizeof(QUEUE));
                
                if (count > 0)
                        act->next = new;
                else
                        pqueue = new;

                act = new;
                x = 0;
                t = strsplitt(buffer, '\t');

                while (t)
                {
                        switch (x)
                        {
                                case 0: strncpy(act->path, t, sizeof(act->path)-1); break;
                                case 1: act->fType = atoi(t); break;
                                case 2: act->id = atoi(t); break;
                                case 3: strncpy(act->title, t, sizeof(act->title)-1); break;
                                case 4: strncpy(act->artist, t, sizeof(act->artist)-1); break;
                                case 5: strncpy(act->album, t, sizeof(act->album)-1); break;
                                case 6: strncpy(act->genre, t, sizeof(act->genre)-1); break;
                                case 7: strncpy(act->cover, t, sizeof(act->cover)-1); break;
                        }

                        t = strsplitt(NULL, '\t');
                        x++;
                }

                count++;
        }

        queueTotal = count;
        return count;
}

void setCurrentQueue(QUEUE *q)
{
        if (q == NULL)
                return;

        memset(&playCurrent, 0, sizeof(ST_PLAYING));
        playCurrent.id = q->id;
        strncpy (playCurrent.title, q->title, sizeof(playCurrent.title));
        strncpy (playCurrent.artist, q->artist, sizeof(playCurrent.artist));
        strncpy (playCurrent.album, q->album, sizeof(playCurrent.album));
        strncpy (playCurrent.genre, q->genre, sizeof(playCurrent.genre));
        strncpy (playCurrent.cover, q->cover, sizeof(playCurrent.cover));
}

QUEUE *getQueue(int idx)
{
        QUEUE *act;
        int pos;

        act = pqueue;
        pos = 0;

        while (act)
        {
                if (pos == idx)
                        return act;
                
                pos++;
                act = act->next;
        }

        return NULL;
}

QUEUE *getRandomQueue()
{
        QUEUE *act;
        int pos;
        long r;

        if (pqueue == NULL)
                return NULL;

        if (queueTotal > 1)
                r = random_at_most((long)queueTotal-1);
        else
        {
                if (pqueue->played)
                        return pqueue->next;
                else
                        return pqueue;
        }

        pos = 0;
        act = pqueue;
        /* Scan queue and check wheter all entries are allready played */
        while (act)
        {
                if (!act->played)
                        break;

                act = act->next;
        }

        if (act == NULL)                /* If all is played, we reset the status and start again */
        {
                act = pqueue;

                while (act)
                {
                        act->played = FALSE;
                        act = act->next;
                }
        }

        act = pqueue;

        while (act)
        {
                if (pos == r)                           /* have we reached the random position? */
                {                                                       /* Yes, then check if this entry was already played. */
                        if (act->played)                /* Was it played? */
                        {                                               /* Yes, then reset and try again */
                                r = random_at_most((long)queueTotal - 1);
                                act = pqueue;
                                pos = 0;
                                continue;
                        }
                        else                                    /* It was not played before so */
                                return act;                     /* return the pointer to it */
                }

                pos++;
                act = act->next;
        }

        return NULL;
}

/*
 * The following functions manage the STACK.
 * The stack contains one or more files to play immediately. Even if a file
 * of the normal queue is playing, the song is interrupted and the stack
 * takes precedence. After the stack has been played or stopped by the user,
 * the queue continues to play.
 * 
 * Adding a file to the stack does not alter the queue in any way. The stack
 * is a kind of second queue but with higher priority.
 */
QUEUE* qstackAdd(int ID)
{
QUEUE *q;
char query[1024], hv0[64], fname[512];
char *p;
sqlite3_stmt *res;
sqlite3 *db;
int rc;

        if (ID < 0)
                return NULL;

        if ((q = (QUEUE *)malloc(sizeof(QUEUE))) == NULL)
        {
                syslog(LOG_DAEMON, "Error allocating memory for a stack entry: %s", strerror(errno));
                return NULL;
        }

        memset(q, 0, sizeof(QUEUE));
        q->id = ID;

        if (qstack)
        {
                QUEUE *act;

                act = qstack;
                
                while (act->next)
                        act = act->next;

                act->next = q;
        }
        else
                qstack = q;

        /* retrieve the data from the database */
        strcpy(fname, configs.home);                    /* Base file name */
        strcat(fname, MUSICDB);                                 /* File name of database */
        rc = sqlite3_open(fname, &db);

        if (rc)
        {
                syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
                qstackDelete(ID);
                return NULL;
        }

        strcpy (query, "select id, path, type, title, interpret, album, genre, cover from \"main\".\"musicdb\" where ");
        strcat(query, "id = ");
        sprintf(hv0, "%d", ID);
        strcat(query, hv0);

        if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
        {
                syslog(LOG_WARNING, "SQL error [%s]: %s", query, sqlite3_errmsg(db));
                sqlite3_close(db);
                qstackDelete(ID);
                return NULL;
        }

        rc = sqlite3_step(res);

        if ((p = (char *)sqlite3_column_text(res, 1)) == NULL)
        {
                syslog(LOG_WARNING, "Error getting path from database!");
                sqlite3_finalize(res);
                sqlite3_close(db);
                qstackDelete(ID);
                return NULL;
        }
        else
                strncpy(q->path, p, sizeof(q->path));

        q->fType = sqlite3_column_int(res, 2);

        if ((p = (char *)sqlite3_column_text(res, 3)) == NULL)
        {
                syslog(LOG_WARNING, "Error getting title from database!");
                sqlite3_finalize(res);
                sqlite3_close(db);
                qstackDelete(ID);
                return NULL;
        }
        else
                strncpy(q->title, p, sizeof(q->title));

        if ((p = (char *)sqlite3_column_text(res, 4)) == NULL)
        {
                syslog(LOG_WARNING, "Error getting artist from database!");
                sqlite3_finalize(res);
                sqlite3_close(db);
                qstackDelete(ID);
                return NULL;
        }
        else
                strncpy(q->artist, p, sizeof(q->artist));

        if ((p = (char *)sqlite3_column_text(res, 5)) == NULL)
        {
                syslog(LOG_WARNING, "Error getting album from database!");
                sqlite3_finalize(res);
                sqlite3_close(db);
                qstackDelete(ID);
                return NULL;
        }
        else
                strncpy(q->album, p, sizeof(q->album));

        if ((p = (char *)sqlite3_column_text(res, 6)) == NULL)
        {
                syslog(LOG_WARNING, "Error getting genre from database!");
                sqlite3_finalize(res);
                sqlite3_close(db);
                qstackDelete(ID);
                return NULL;
        }
        else
                strncpy(q->genre, p, sizeof(q->genre));

        if ((p = (char *)sqlite3_column_text(res, 7)) != NULL)
                strncpy(q->cover, p, sizeof(q->cover));

        sqlite3_finalize(res);
        sqlite3_close(db);

        if (configs.debug)
                syslog(LOG_DEBUG, "qstackAdd(): Added file >>%d:%s<< to stack.", q->id, q->path);

        return q;
}

void qstackDelete(int ID)
{
QUEUE *q, *p;
int flag;

        if (qstack == NULL)
                return;

        q = p = qstack;
        flag = 0;

        while (q)
        {
                QUEUE *act;

                if (q->id == ID)
                {
                        act = q;
                        q = q->next;

                        if (p != act)
                                p->next = q;
                        else
                                qstack = q;                     /* if the the first entry is to delete, let qstack point to the second entry, if there is one. */

                        free(q);

                        if (configs.debug)
                                syslog(LOG_DEBUG, "qstackDelete(): Deleted ID %d from stack.", ID);

                        return;
                }

                q = q->next;

                if (flag)
                        p = p->next;
                else
                        flag = 1;
        }
}

void qstackClear()
{
QUEUE *act, *q;

        act = qstack;

        while (act)
        {
                q = act->next;
                free(act);
                act = q;
        }

        qstack = NULL;
}

QUEUE* qstackNext()
{
QUEUE *act, *p;
int flag;

        if (qstack == NULL)
                return NULL;

        act = p = qstack;
        flag = 0;

        while (act->next)
        {
                if (flag)
                        p = p->next;
                else
                        flag = 1;

                act = act->next;
        }

        if (act != p)
                p->next = NULL;
        else
                qstack = NULL;

        if ((p = (QUEUE *)malloc(sizeof(QUEUE))) == NULL)
        {
                syslog(LOG_DAEMON, "Error allocating memory to get a stack entry: %s", strerror(errno));
                free(act);
                return NULL;
        }

        if (configs.debug)
                syslog(LOG_DEBUG, "qstackNext(): Pulled file >>%d:%s<< from stack.", act->id, act->path);

        memmove(p, act, sizeof(QUEUE));
        free(act);
        return p;
}