Subversion Repositories mdb

Rev

Rev 40 | Rev 42 | Go to most recent revision | 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 <time.h>
#include <signal.h>
#include <syslog.h>
#include <errno.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <dirent.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <sqlite3.h>
#include <id3.h>
#include "config.h"
#include "helplib.h"
#include "mdb.h"
#include "list.h"
#include "play.h"
#include "user.h"
#include "delete.h"
#include "search.h"

static pthread_mutex_t fastmutex_proc = PTHREAD_MUTEX_INITIALIZER;
int musicFilter(const struct dirent *dir);
int grabMusicFiles(int s1, char *dir);
void evaluateMusicFile(char *file);
int cleanArchieve(int s1, char *fname);

/* Global variables */
int file_found = 0;
char cmd_error[512];
char cmd_message[512];
int currentPage = PAGE_NONE;
int grabFilesCount;                                             /* Temporary used to count the scanned files */
struct ST_PlayPars _playPars;

static pthread_t pthr_play;
/*
 * This is called from the main listening thread as a thread of its own.
 * The function gets the commands from the client, work with them and give
 * back the result.
 */
void *processCommands(void *pV_data)
{
        char ch, buf[512];
        int i, s1, s;
        struct SOCKETS *soc;

        soc = (struct SOCKETS *)pV_data;
        s1 = soc->newfd;
        s = soc->sockfd;
        i = 0;
        /* Report the actual status to the client */
/*      pthread_mutex_lock (&fastmutex_proc); */
        sprintf(buf, "# MDB v%s\n", VERSION);
        strcat (buf, "# (C) Copyright 2015 by Andreas Theofilu <andreas@theosys.at>\n");
        strcat (buf, "# All rights reserved. No warranty, explicit or implicit, provided.\n");
        write (s1, buf, strlen(buf));

        if (userchain != NULL)
        {
                sprintf(buf, "USER:%s;", userchain->uname);
                write (s1, buf, strlen(buf));
        }

        strcpy (buf, "PAGE:");

        switch (currentPage)
        {
                case PAGE_NONE:         strcat (buf, "NONE;"); break;
                case PAGE_TITLE:        strcat (buf, "TITLE;"); break;
                case PAGE_ARTIST:       strcat (buf, "ARTIST;"); break;
                case PAGE_ALBUM:        strcat (buf, "ALBUM;"); break;
                case PAGE_GENRE:        strcat (buf, "GENRE;"); break;
                case PAGE_QUEUE:        strcat (buf, "QUEUE;"); break;
                case PAGE_PLAYLIST:     strcat (buf, "PLAYLIST;"); break;
        }

        write (s1, buf, strlen(buf));
        sprintf(buf, "REPEAT:%s;", playerRepeat ? "TRUE" : "FALSE");
        write (s1, buf, strlen(buf));
        sprintf(buf, "RANDOM:%s;", playerRandom ? "TRUE" : "FALSE");
        write (s1, buf, strlen(buf));
        /* In case the player is playing, we send the data of the
         * current song */
        if (playerActive)
        {
                char buffer[8192];
                char *title, *artist, *album, *genre;

                title = urlencode(playCurrent.title);
                artist = urlencode(playCurrent.artist);
                album = urlencode(playCurrent.album);
                genre = urlencode(playCurrent.genre);
                sprintf(buffer, "PLAYING:%d:%s:%s:%s:%s:%s;", playCurrent.id, title, artist, album, genre, playCurrent.cover);
                write (s1, buffer, strlen(buffer));

                if (title != NULL) free(title);
                if (artist != NULL) free(artist);
                if (album != NULL) free(album);
                if (genre != NULL) free(genre);
        }
        /* Signal client, that we are finished the initial report */
        strcpy (buf, "DONE;");
        write (s1, buf, strlen(buf));
        
/*      pthread_mutex_unlock(&fastmutex_proc); */

        memset(buf, 0, sizeof(buf));
        memset(cmd_message, 0, sizeof(cmd_message));
        
        while (read(s1,&ch,1) > 0)
        {
                if (i < (int)(sizeof(buf) - 1))
                {
                        buf[i] = ch;

                        if (ch == ';' || ch == 0x0d)
                        {
                                int pstat;

/*                              pthread_mutex_lock (&fastmutex_proc); */

                                if (!strncmp(buf, "quit", 4))
                                        break;

                                if ((pstat = parseCommand(s1, buf)) == FALSE)
                                {
                                        char hv0[128];

                                        char *p = urlencode(cmd_error);

                                        if (p != NULL)
                                        {
                                                sprintf(&hv0[0], "INVALID COMMAND: %s: %s;", buf, p);
                                                free(p);
                                        }
                                        else
                                                sprintf(&hv0[0], "INVALID COMMAND: %s;", buf);

                                        write(s1, hv0, strlen(hv0));
                                }
                                else if (pstat == 2)
                                        write(s1, "NAK;", 4);
                                else
                                {
                                        if (strlen(cmd_message) > 0)
                                                write(s1, cmd_message, strlen(cmd_message));
                                        else
                                                write(s1, "OK;", 3);
                                }

                                memset(&buf[0], 0, sizeof(buf));
                                memset(cmd_message, 0, sizeof(cmd_message));
                                i = 0;
/*                              pthread_mutex_unlock(&fastmutex_proc); */
                                continue;
                        }
                }

                i++;
        }

        close(s1);
        return NULL;
}

/*
 * This function parses the commands given by a network client.
 */
int parseCommand(int s1, char *cmd)
{
        char bef[32],par[1024], *p;
        char hv0[256];
        int i;

        memset(bef, 0, sizeof(bef));
        memset(par, 0, sizeof(par));
        memset(cmd_error, 0, sizeof(cmd_error));

        if ((p = strchr(cmd, ':')) != NULL)                     /* do we have a parameter? */
        {
                strncpy(bef, cmd, p - cmd);
                strncpy(par, p+1, strlen(p) - 2);               /* Cut off the trailing semi colon */
        }
        else
                strncpy(bef, cmd, strlen(cmd) - 1);

        cmd_error[0] = 0;
        cmd_message[0] = 0;

        if (!strcasecmp(bef, "RESCAN"))                         /* Rescan all directories */
        {
                int fd;
                char fname[512];

                if (access(configs.Pathfile, R_OK))
                {
                        syslog(LOG_WARNING, "Error accessing file %s", configs.Pathfile);
                        sprintf(cmd_error, "Error accessing file >%s<", configs.Pathfile);
                        return FALSE;
                }

                if ((fd = open(configs.Pathfile, O_RDONLY)) < 0)
                {
                        syslog(LOG_WARNING, "Error opening file %s: %s", configs.Pathfile, strerror(errno));
                        sprintf(cmd_error, "Error opening file >%s<", configs.Pathfile);
                        return FALSE;
                }

                i = 0;

                /* Delete the database entries before we start to scan.
                 * This makes shure, that there is no double entry.
                 */
                strcpy(fname, configs.home);
                strcat(fname, MUSICDB);

                if (!access(fname, R_OK | W_OK))
                        cleanArchieve(s1, fname);

                grabFilesCount = 0;
                /* Scan all directories in the config file recursievly. */
                while (readLine(fd, &hv0[0], sizeof(hv0)) != NULL)
                {
                        grabMusicFiles(s1, hv0);
                        i++;
                }

                sprintf(cmd_message, "SCAN:%d:%d;", i, grabFilesCount);
                close(fd);
        }

        /*
         * Syntax: LIST:<type>:<start>:<length>;
         *         <type>   TITLE | ARTIST | ALBUM | GENRE | QUEUE | PLAYLIST | USERS
         *         <start>  The line to start from
         *         <length> The number of lines to report
         * 
         * Returns: LINE:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>:<cover>;
         *         <id>     The unique ID of the file. Needed to play the file!
         *         <line>   The line number counting from 1
         * 
         * In case the type is USERS, the following is returned:
         *          USERS:<id>:<line>:<name>;
         *         <name>   The name of the user
         */
        if (!strcasecmp(bef, "LIST"))                           /* List content */
        {
                char p_type[16];
                int start, length;

                memset(p_type, 0, sizeof(p_type));
                remove_string(par, ":", &hv0[0]);
                start = strlen(hv0);
                hv0[start-1] = 0;
                strncpy(p_type, hv0, sizeof(p_type)-1);
                start = atoi(par);
                remove_string(par, ":", &hv0[0]);
                length = atoi(par);
                return listSongs(s1, p_type, start, length);
        }

        /*
         * Syntax: PLIST:<user>:<playlist>:<start>:<length>;
         *         <user>   The name of the user.
         *         <playlist> The name of the playlist
         *         <start>  The line to start from
         *         <length> The number of lines to report
         * 
         * Returns: LINE:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>:<cover>;
         *         <id>     The unique ID of the file. Needed to play the file!
         *         <line>   The line number counting from 1
         */
        if (!strcasecmp(bef, "PLIST"))                          /* List content */
        {
                char user[64], playlist[64], *t;
                int start, length, x;

                memset(user, 0, sizeof(user));
                memset(playlist, 0, sizeof(playlist));
                x = 0;
                t = strtok (par, ":");

                while (t)
                {
                        switch(x)
                        {
                                case 0: strncpy (user, t, sizeof(user)-1); break;
                                case 1: strncpy (playlist, t, sizeof(playlist)-1); break;
                                case 2: start = atoi(t); break;
                                case 3: length = atoi(t); break;
                        }

                        x++;
                        t = strtok (NULL, ":");
                }

                t = urldecode(user);

                if (t)
                {
                        strncpy(user, t, sizeof(user)-1);
                        free(t);
                }

                t = urldecode(playlist);

                if (t)
                {
                        strncpy(playlist, t, sizeof(playlist)-1);
                        free(t);
                }

                return listUserPlaylist(s1, user, playlist, start, length);
        }

        /*
         * Syntax: LISTFOLDER:<type>:<name>:<start>:<length>;
         *         <type>   TITLE | ARTIST | ALBUM | GENRE
         *         <start>  The line to start from
         *         <length> The number of lines to report
         * 
         * Returns: LINE:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>:<cover>;
         *         <id>     The unique ID of the file. Needed to play the file!
         *         <line>   The line number counting from 1
         */
        if (!strcasecmp(bef, "LISTFOLDER"))                     /* List the content of a folder */
        {
                char p_type[64], name[256], *t, *type, *nm;
                int x, start, length;

                x = 0;
                t = strtok(par, ":");

                while (t)
                {
                        switch(x)
                        {
                                case 0: strncpy(p_type, t, sizeof(p_type)); break;
                                case 1: strncpy(name, t, sizeof(name)); break;
                                case 2: start = atoi(t); break;
                                case 3: length = atoi(t); break;
                        }

                        x++;
                        t = strtok(NULL, ":");
                }

                type = urldecode(p_type);
                nm = urldecode(name);

                if (type)
                {
                        strncpy(p_type, type, sizeof(p_type)-1);
                        free(type);
                }

                if (nm)
                {
                        strncpy(name, nm, sizeof(name)-1);
                        free(nm);
                }

                return listFolderContent(s1, p_type, name, start, length);
        }

        /*
         * Syntax: FOLDER:<type>:<start>:<length>;
         *         <type>   TITLE | ARTIST | ALBUM | GENRE
         *         <start>  The line to start from
         *         <length> The number of lines to report
         * 
         * Returns: FOLDER:<type>:<id>:<line>:<content>;
         *         <id>       The unique ID of the file. Needed to play the file!
         *         <line>     The line number counting from 1
         *         <content>  The name of the folder
         */
        if (!strcasecmp(bef, "FOLDER"))                         /* List folders */
        {
                char p_type[16];
                int start, length;
                
                memset(p_type, 0, sizeof(p_type));
                remove_string(par, ":", &hv0[0]);
                start = strlen(hv0);
                hv0[start-1] = 0;
                strncpy(p_type, hv0, sizeof(p_type)-1);
                start = atoi(par);
                remove_string(par, ":", &hv0[0]);
                length = atoi(par);
                return listFolders(s1, p_type, start, length);
        }

        /*
         * This command moves one or more files into the queue.
         * If the queue already contains some files, the files are appended to
         * the end of the queue. The command makes sure, that there are no
         * duplicate entries in the queue.
         * If the name of a folder is omitted, all files of the specified types
         * are appended.
         * If a folder name was specified, only the files of the folder are
         * appended.
         *
         * Syntax: ADD:<type>:<folder>;
         *          <type>     ID | TITLE | ARTIST | ALBUM | GENRE | PLAYLIST
         *          <folder>   The name of a folder. This is optional.
         */
        if (!strcasecmp(bef, "ADD"))
        {
                int x;
                char *t, p_type[32], folder[64], *type, *fld;
                
                memset(p_type, 0, sizeof(p_type));
                memset(folder, 0, sizeof(folder));
                x = 0;
                t = strtok(par, ":");
                
                while (t)
                {
                        switch (x)
                        {
                                case 0: strncpy(p_type, t, sizeof(p_type)-1); break;
                                case 1: strncpy(folder, t, sizeof(folder)-1); break;
                        }

                        x++;
                        t = strtok(NULL, ":");
                }
                
                type = urldecode(p_type);
                fld = urldecode(folder);
                
                if (type)
                {
                        strncpy(p_type, type, sizeof(p_type));
                        free(type);
                }

                if (fld)
                {
                        strncpy(folder, fld, sizeof(folder));
                        free(fld);
                }
                
                if (strlen(p_type) && strlen(folder))
                        appendToQueue(s1, p_type, folder);
                else
                {
                        strcpy(cmd_message, "ERROR:ADD:Missing type or folder name;");
                        return FALSE;
                }
        }

        /*
         * This command moves one or more files into the queue and starts to play
         * them.
         * If the player is already playing, the file(s) are appended to the queue.
         * If a play request for the queue is detected and the ID is 0, then the
         * first or, if random is selected a random entry of the que is played. If
         * the ID is grater than 0, the wanted ID is played even if random is
         * selected.
         *
         * Syntax: PLAY:<type>:<what>;
         *         <type>     ID | TITLE | ARTIST | ALBUM | GENRE | PLAYLIST | QUEUE
         *         <what>     <id> or name of folder or name of playlist
         * 
         * Returns: PLAYING:<id>:<title>:<artist>:<album>:<genre>;
         *          POSITION:<time>:<time left:<total>;
         */
        if (!strcasecmp(bef, "PLAY"))                           /* Plays one or more files */
        {
                char p_type[16];
                char what[256];
                int i;

                memset(p_type, 0, sizeof(p_type));
                memset(hv0, 0, sizeof(hv0));
                remove_string(par, ":", &hv0[0]);
                i = strlen(hv0);

                if (i > 0)
                {
                        hv0[i-1] = 0;
                        strncpy(p_type, hv0, sizeof(p_type) - 1);
                }
                else
                        strcpy(p_type, "QUEUE");

                if (strcasecmp(p_type, "ID"))           /* If the type is not "ID" then */
                {                                                                       /* decode the text */
                        char *w;

                        w = urldecode(par);
                        
                        if (w)
                        {
                                strncpy (what, w, sizeof(what));
                                i = sizeof(what) - 1;
                                what[i] = 0;
                                free (w);
                        }
                        else
                                strncpy(what, par, sizeof(what));
                }
                else if (strlen(par))
                        strncpy(what, par, sizeof(what));
                else
                        strcpy(what, "0");

                if (playerActive && playStatus == PLAY_STATUS_PAUSE)
                        nextCommand = PLAY_PLAY;
                else if (playerActive)
                        appendToQueue(s1, p_type, what);
                else
                {
                        _playPars.s1 = s1;
                        strcpy(_playPars.type, p_type);
                        strcpy(_playPars.what, what);
                        /* start a new thread to play the file(s) */
                        if (pthread_create(&pthr_play, NULL, pthr_playfile, (void *)&_playPars) != 0)
                        {
                                syslog (LOG_DAEMON,"Create of thread \"pthr_play\" failed!");
                                strcpy(cmd_error, "Error playing a file!");
                                return FALSE;
                        }
                }
        }

        if (!strcasecmp(bef, "STOP") && playerActive)                           /* Stop the currently playing file */
                nextCommand = PLAY_STOP;

        if (!strcasecmp(bef, "PAUSE") && playerActive)                          /* Pause the currently playing file */
                nextCommand = PLAY_PAUSE;

        if (!strcasecmp(bef, "PLAYPAUSE"))                                                      /* Play/Pause the currently playing file, */
        {                                                                                                                       /* or play one in the queue if there are any. */
                if (playerActive && (playStatus == PLAY_STATUS_PAUSE || playStatus == PLAY_STATUS_PLAY))
                        nextCommand = PLAY_PLAYPAUSE;
                else if (playerActive && playStatus == PLAY_STATUS_STOP)
                        nextCommand = PLAY_PLAY;
                else if (!playerActive)
                {
                        _playPars.s1 = s1;
                        strcpy(_playPars.type, "QUEUE");                                        /* Select the queue to play. */
                        strcpy(_playPars.what, "0");                                             /* Play the first or a random file, if random is selected, in the queue. */
                        /* start a new thread to play the file(s) */
                        if (pthread_create(&pthr_play, NULL, pthr_playfile, (void *)&_playPars) != 0)
                        {
                                syslog (LOG_DAEMON,"Create of thread \"pthr_play\" failed!");
                                strcpy(cmd_error, "Error playing a file!");
                                return FALSE;
                        }
                }
        }

        if (!strcasecmp(bef, "FORWARD") && playerActive)                        /* Fast forward */
                nextCommand = PLAY_FWD;

        if (!strcasecmp(bef, "REWIND") && playerActive)                         /* Fast rewind */
                nextCommand = PLAY_REW;

        if (!strcasecmp(bef, "SKIPFWD") && playerActive)                        /* Skip to next file in queue */
                nextCommand = PLAY_SKIP_FWD;

        if (!strcasecmp(bef, "SKIPREW") && playerActive)                        /* Skip to previous file in queue */
                nextCommand = PLAY_SKIP_REW;

        if (!strcasecmp(bef, "RANDOM"))                                                         /* Toggle random play */
        {
                playerRandom = !playerRandom;
                sprintf(cmd_message, "RANDOM:%s;", playerRandom ? "TRUE" : "FALSE");
        }

        if (!strcasecmp(bef, "REPEAT"))                                                         /* Toggle repeat */
        {
                playerRepeat = !playerRepeat;
                sprintf(cmd_message, "REPEAT:%s;", playerRepeat ? "TRUE" : "FALSE");
        }

        /*
         * This command loads a user. If the user doesn't exist it creates
         * the user. In this case the name of a playlist is needed.
         * If the user already exist, the name of a playlist is optional.
         * 
         * If this command is called without the name of a playlist and the
         * user doesn't exist, an error occurs.
         *
         * This command should only be called when a new playlist is to be
         * saved. After this command the command "SAVEQUEUE" should be called.
         *
         * Syntax: USER:<name>:[<playlist>];
         *         <name>     the name of the user.
         *         <playlist> the name of the playlist; This is optional.
         */
        if (!strcasecmp(bef, "USER"))                                                           /* In case the user doesn't exist, create a new one. Activate this user */
        {
                char user[64], playlist[64], *t;
                int x;

                memset(user, 0, sizeof(user));
                memset(playlist, 0, sizeof(playlist));
                x = 0;
                t = strtok (par, ":");
                
                while (t)
                {
                        char *hv;

                        switch(x)
                        {
                                case 0:
                                        strncpy (user, t, sizeof(user));
                                        user[63] = 0;
                                        hv = urldecode(user);

                                        if (hv != NULL)
                                        {
                                                strcpy(user, hv);
                                                free(hv);
                                        }
                                break;

                                case 1:
                                        strncpy (playlist, t, sizeof(playlist));
                                        playlist[63] = 0;
                                        hv = urldecode(playlist);

                                        if (hv != NULL)
                                        {
                                                strcpy(playlist, hv);
                                                free(hv);
                                        }
                                break;
                        }

                        x++;
                        t = strtok(NULL, ":");
                }
                
                if (strlen(user) && strlen(playlist))
                {
                        if (!createUser(s1, user, playlist))
                                return FALSE;

                        return selectUser(s1, user);
                }
                else if (strlen(user))
                        return selectUser(s1, user);
                else
                {
                        strcpy (cmd_error, "Missing the username and the name of the playlist");
                        return FALSE;
                }
        }

        /*
         * Syntax: SAVEQUEUE:<user>:<playlist>;
         *         <user>     The name of the user. If this is empty, the actual
         *                    user is taken, if any. If there's no user selected,
         *                    an error occurs.
         *         <playlist> The name of the playlist. If the playlist already
         *                    exist, it'll be overwritten.
         * 
         * Return: TRANSFERED:<num>;
         *         <num>      The number of entries transfered.
         */
        if (!strcasecmp(bef, "SAVEQUEUE"))                      /* Saves the content of the queue into a playlist */
        {
                char user[64], playlist[64];
                char *t;
                int x;

                if (!queueTotal)
                {
                        readQueue();

                        if (!queueTotal)
                        {
                                strcpy(cmd_error, "Queue is empty");
                                return FALSE;
                        }
                }

                x = 0;
                t = strtok(par, ":");

                while (t)
                {
                        char *hv;

                        switch(x)
                        {
                                case 0:
                                        strncpy (user, t, sizeof(user));
                                        user[63] = 0;
                                        hv = urldecode(user);

                                        if (hv != NULL)
                                        {
                                                strcpy(user, hv);
                                                free(hv);
                                        }
                                break;

                                case 1:
                                        strncpy (playlist, t, sizeof(playlist));
                                        playlist[63] = 0;
                                        hv = urldecode(playlist);
                                        
                                        if (hv != NULL)
                                        {
                                                strcpy(playlist, hv);
                                                free(hv);
                                        }
                                break;
                        }

                        x++;
                        t = strtok(NULL, ":");
                }

                if (!strlen(user) && userchain != NULL)
                        strcpy(user, userchain->uname);
                else if (!selectUser(s1, user) && strlen(user) && strlen(playlist))
                {
                        if (!createUser(s1, user, playlist))
                                return FALSE;

                        return QueueToPlaylist(s1, user, playlist);
                }
                else if (!selectUser(s1, user))
                {
                        strcpy (cmd_error, "No or invalid user");
                        return FALSE;
                }

                if (!strlen(playlist))
                {
                        strcpy (cmd_error, "No or invalid playlist");
                        return FALSE;
                }

                if (!QueueToPlaylist(s1, user, playlist))
                        return FALSE;
        }

        /*
         * Syntax: DELETE:<type>:<id>;
         *      <type>     PTITLE | QUEUE | PLAYLIST
         *                 PTITLE: Delete one title from a playlist
         *                 QUEUE: If the given ID is -1, then the whole
         *                        queue is deleted. Otherwise only a title
         *                        in the queue.
         *                 PLAYLIST Delete a whole playlist from a user
         * 
         *      <id>       -1 = Delete whole QUEUE or PLAYLIST
         *                 >= 0 Delete only a particular entry
         */
        if (!strcasecmp(bef, "DELETE"))
        {
                char p_type[32], *t;
                int id, x;

                x = 0;
                t = strtok(par, ":");

                while (t)
                {
                        switch (x)
                        {
                                case 0:
                                        strncpy(p_type, t, sizeof(p_type));
                                        p_type[31] = 0;
                                break;

                                case 1: id = atoi(t); break;
                        }

                        x++;
                        t = strtok(NULL, ":");
                }

                if (!strcasecmp(p_type, "QUEUE"))
                        return deleteQueue(s1, id);
                else if (!strcasecmp(p_type, "PTITLE"))
                        return deletePlaylistEntry(s1, id);
                else if (!strcasecmp(p_type, "PLAYLIST"))
                        return deletePlaylist(s1, id);
                else
                        return FALSE;
        }

        /*
         * Syntax: SEARCH:<type>:<expression>:<start>:<lines>;
         *            <type>   TITLE | ARTIST | ALBUM | GENRE
         *            <expression> This is what to search for
         *            <start>  the data to start to
         *            <lines>  number of lines to return
         * 
         * Return: SEARCH:<type>:<id>:<line>:<title>:<artist>:<album>:<genre>;
         */
        if (!strcasecmp(bef, "SEARCH"))
        {
                int start, lines, x;
                char *t, p_type[32], expr[128];

                memset(p_type, 0, sizeof(p_type));
                memset(expr, 0, sizeof(expr));
                start = lines = 0;
                x = 0;
                t = strtok (par, ":");

                while (t)
                {
                        char *hv;

                        switch(x)
                        {
                                case 0: strncpy(p_type, t, sizeof(p_type)-1); break;

                                case 1:
                                        strncpy(expr, t, sizeof(expr)-1);
                                        hv = urldecode(expr);

                                        if (hv != NULL)
                                        {
                                                strncpy(expr, hv, sizeof(expr) - 1);
                                                free(hv);
                                        }
                                break;

                                case 2: start = atoi(t); break;
                                case 3: lines = atoi(t); break;
                        }

                        x++;
                        t = strtok (NULL, ":");
                }

                if (x != 4 || !strlen(p_type) || !strlen(expr) || !lines || !start)
                {
                        strcpy (cmd_error, "Missing one ore more parameters");
                        return FALSE;
                }

                return searchTerm(s1, p_type, expr, start, lines);
        }

        /*
         * This command reread the config file.
         */
        if (!strcasecmp(bef, "RESET"))
        {
                if (playerActive)
                        nextCommand = PLAY_STOP;

                readConf();
        }

        return TRUE;            /* cmd was OK */
}

/*
 * This function opens the database and deletes the whole archive of scanned
 * MP3 files.
 * This is called when the command "RESCAN" was detected. When this function
 * is finished, the database is ready for new data.
 */
int cleanArchieve(int s1, char *fname)
{
        char query[1024];
        sqlite3 *db;
        char *zErrMsg = 0;
        int rc;

        rc = sqlite3_open(fname, &db);

        if (rc)
        {
                syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
                strcpy(query, "ERROR:MDB:Error opening database;");
                write (s1, query, strlen(query));
                return FALSE;
        }
        
        strcpy (query, "delete from \"main\".\"playlists\"");
        
        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);
                strcpy(query, "ERROR:MDB:SQL error;");
                write (s1, query, strlen(query));
                return FALSE;
        }

        strcpy (query, "delete from \"main\".\"musicdb\"");

        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);
                strcpy(query, "ERROR:MDB:SQL error;");
                write (s1, query, strlen(query));
                return FALSE;
        }

        sqlite3_close(db);
        return TRUE;
}

/*
 * Callback function of directory scan.
 * This function desides whether a directory entry will be treated as a
 * music file or not.
 */
int musicFilter(const struct dirent *dir)
{
        if (dir == NULL)
                return 0;

        if (dir->d_type == DT_REG)
        {
                char *p = getFileExtension(dir->d_name);
                
                if (p != NULL && (strcasecmp(p, "mp3") == 0 || strcasecmp(p, "flac") == 0))
                        return 1;
/*              char *p = strrchr(dir->d_name, '.');

                if (p != NULL && (strcasestr(p, ".mp3") != NULL || strcasestr(p, ".flac") != NULL))
                        return 1; */
        }
        else if (dir->d_type == DT_DIR)
        {
                if (strcmp(dir->d_name, ".") != 0 && strcmp(dir->d_name, "..") != 0)
                        return 1;
        }

        return 0;
}

/*
 * This function scans a directory for music files. If it finds some file name
 * who is a possible music file, it gives it to a function to evaluate and
 * store it.
 */
int grabMusicFiles(int s1, char *dir)
{
        struct dirent **namelist;
        int n;

        if (dir == NULL || strlen(dir) == 0)
                return FALSE;

        n = scandir(dir, &namelist, musicFilter, alphasort);

        if (n < 0)
        {
                syslog(LOG_DAEMON, "Error scanning directory %s: %s", dir, strerror(errno));
                return FALSE;
        }
        else
        { 
                while(n--)
                {
                        char hv0[512];
                        strcpy(hv0, dir);
                        strcat(hv0, "/");
                        strcat(hv0, namelist[n]->d_name);

                        if (namelist[n]->d_type == DT_DIR)
                                grabMusicFiles(s1, hv0);
                        else
                        {
                                evaluateMusicFile(hv0);

                                if (!(grabFilesCount % 100))
                                {
                                        sprintf(hv0, "SCANNING:%d;", grabFilesCount);
                                        write (s1, hv0, strlen(hv0));
                                }
                        }

                        free(namelist[n]); 
                } 

                free(namelist); 
        }
        
        return TRUE;
}

/*static int callback(void *hint, int argc, char **argv, char **azColName)
{
        if (hint != NULL && strcmp(hint, "EXIST"))
        {
                file_found = TRUE;
                return 0;
        }

        return 0;
}
*/
void evaluateMusicFile(char *file)
{
        sqlite3 *db;
        sqlite3_stmt *res;
        char *zErrMsg = 0;
        int rc, fType;
        char fname[256], hv0[64];
        char query[8192];
        ID3Tag *id3;
        ID3Frame *frame;
        ID3Field *field;
        char id3_title[256];
        char id3_singer[256];
        char id3_album[256];
        char id3_genre[256];
        char id3_cover[256];
        char *title, *singer, *album, *ext;

        if (access(file, R_OK))
                return;

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

        if (!access(fname, R_OK | W_OK))
                file_found = TRUE;
        else                                            /* create the directory, if it doesn't exitst */
        {
                if (access(configs.home, R_OK | W_OK))
                {
                        if (mkdir(configs.home, 0775) != 0)
                        {
                                syslog(LOG_WARNING, "Error creating directory %s: %s", configs.home, strerror(errno));
                                return;
                        }
                        else
                                syslog(LOG_INFO, "Directory %s was created.", configs.home);
                }

                sprintf(query, "%s/Covers", configs.home);

                if (access(query, R_OK | W_OK))
                {
                        if (mkdir(query, 0775) != 0)
                                syslog(LOG_WARNING, "Error creating directory %s: %s", query, strerror(errno));
                        else
                                syslog(LOG_INFO, "Directory %s was created.", query);
                }
        }

        rc = sqlite3_open(fname, &db);
        
        if (rc)
        {
                syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
                return;
        }

        if (!file_found)
        {
                /* id        autoincrement unique index
                 * path      path to file containing an MP3 or FLAC file
                 * type      1 = MP3, 2 = FLAC
                 * title     title of song
                 * interpret singer
                 * album     the album the is from
                 * genre     genre of the song
                 * cover     name of file containing the cover
                 */
                strcpy(query, "create table \"main\".\"musicdb\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,");
                strcat(query, "\"path\" TEXT NOT NULL, \"type\" INTEGER, \"title\" TEXT, \"interpret\" TEXT,");
                strcat(query, "\"album\" TEXT, \"genre\" TEXT, \"cover\" TEXT)");
                rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg);

                if (rc != SQLITE_OK)
                {
                        syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
                        sqlite3_free(zErrMsg);
                        sqlite3_close(db);
                        return;
                }
                else
                        syslog(LOG_INFO, "Database \"musicdb\" was successfully created.");
                
                strcpy(query, "CREATE TABLE \"main\".\"users\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT,");
                strcat(query, "\"uname\" TEXT NOT NULL, \"playlist\" TEXT)");
                rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg);

                if (rc != SQLITE_OK)
                {
                        syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
                        sqlite3_free(zErrMsg);
                        sqlite3_close(db);
                        return;
                }
                else
                        syslog(LOG_INFO, "Database \"users\" was successfully created.");
                
                strcpy (query, "CREATE TABLE \"main\".\"playlists\" (\"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,");
                strcat (query, "\"userid\" INTEGER NOT NULL, \"musicid\" INTEGER NOT NULL)");

                rc = sqlite3_exec(db, query, NULL, NULL, &zErrMsg);

                if (rc != SQLITE_OK)
                {
                        syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
                        sqlite3_free(zErrMsg);
                        sqlite3_close(db);
                        return;
                }
                else
                        syslog(LOG_INFO, "Database \"users\" was successfully created.");
        }

        /* Make sure, the file is not already in the database */
        sprintf(query, "select path from musicdb where path = \"%s\"", file);

        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;
        }
        
        rc = sqlite3_step(res);
        
        if (rc == SQLITE_ROW)                   /* If we've a row, the path exists and we'll not add it again */
        {
                sqlite3_close(db);
                return;
        }

        /* Check the file extension and decide whether it's an MP3 or FLAC file. */
        ext = getFileExtension(file);
        
        if (ext != NULL)
        {
                if (strcasecmp(ext, "flac") == 0)
                        fType = FILE_TYPE_FLAC;
                else
                        fType = FILE_TYPE_MP3;
        }
        else
                fType = FILE_TYPE_MP3;

        /* Open the file and get the ID3 tag. */
        id3 = ID3Tag_New();
        ID3Tag_Link(id3, file);
        memset(&id3_title, 0, sizeof(id3_title));
        memset(&id3_singer, 0, sizeof(id3_singer));
        memset(&id3_album, 0, sizeof(id3_album));
        memset(&id3_genre, 0, sizeof(id3_genre));
        memset(&id3_cover, 0, sizeof(id3_cover));
        /* Get the required frames */
        if ((frame = ID3Tag_FindFrameWithID(id3, ID3FID_TITLE)) != NULL)
        {
                field = ID3Frame_GetField(frame, ID3FN_TEXT);
                ID3Field_GetASCII(field, &id3_title[0], sizeof(id3_title));
                char_replace(id3_title, '"', '`');
                char_replace(id3_title, '\\', ' ');
                ID3Frame_Clear(frame);
        }

        if ((frame = ID3Tag_FindFrameWithID(id3, ID3FID_LEADARTIST)) != NULL)
        {
                field = ID3Frame_GetField(frame, ID3FN_TEXT);
                ID3Field_GetASCII(field, &id3_singer[0], sizeof(id3_singer));
                char_replace(id3_singer, '"', '`');
                char_replace(id3_singer, '\\', ' ');
                ID3Frame_Clear(frame);
        }

        if ((frame = ID3Tag_FindFrameWithID(id3, ID3FID_ALBUM)) != NULL)
        {
                field = ID3Frame_GetField(frame, ID3FN_TEXT);
                ID3Field_GetASCII(field, &id3_album[0], sizeof(id3_album));
                char_replace(id3_album, '"', '`');
                char_replace(id3_album, '\\', ' ');
                ID3Frame_Clear(frame);
        }

        if ((frame = ID3Tag_FindFrameWithID(id3, ID3FID_PICTURE)) != NULL)
        {
                char fpname[512], uid[128], hv0[256];
                ID3Frame *fpframe;

                memset(uid, 0, sizeof(uid));
                strcpy(fpname, configs.home);
                strcat(fpname, "/Covers");

                if (access(fpname, R_OK | W_OK | X_OK))
                        mkdir(fpname, 0775);

                if ((fpframe = ID3Tag_FindFrameWithID(id3, ID3FID_UNIQUEFILEID)) != NULL)
                {
                        field = ID3Frame_GetField(fpframe, ID3FN_TEXT);
                        ID3Field_GetASCII(field, &uid[0], sizeof(uid));
                        ID3Frame_Clear(fpframe);
                }

                
                if (fpframe == NULL || !strlen(uid))
                {
                        char *md5;

                        if ((md5 = str2hash(file, (int)strlen(file))) != NULL)
                        {
                                sprintf(uid, "Picture-%s", md5);
                                free(md5);
                        }
                        else
                                sprintf(uid, "Picture-%ld", random());
                }

                strcat(fpname, "/");
                /* Get the picture and store it into a file */
                field = ID3Frame_GetField(frame, ID3FN_MIMETYPE);
                ID3Field_GetASCII(field, &hv0[0], sizeof(hv0));

                if (strstr(hv0, "jpeg") != NULL)
                        strcat(uid, ".jpg");
                else if (strstr(hv0, "png") != NULL)
                        strcat(uid, ".png");
                else if (strstr(hv0, "gif") != NULL)
                        strcat(uid, ".gif");
                else if (strstr(hv0, "tiff") != NULL)
                        strcat(uid, ".tiff");
                else
                {
                        char *pos;

                        if ((pos = strrchr(hv0, '/')) != NULL)
                        {
                                strcat(uid, ".");
                                strcat(uid, pos+1);
                        }
                }

                strcat(fpname, uid);
                strcpy (id3_cover, uid);
                field = ID3Frame_GetField(frame, ID3FN_DATA);
                ID3Field_ToFile(field, fpname);
                ID3Frame_Clear(frame);
        }

        if ((frame = ID3Tag_FindFrameWithID(id3, ID3FID_CONTENTTYPE)) != NULL)
        {
                field = ID3Frame_GetField(frame, ID3FN_TEXT);
                ID3Field_GetASCII(field, &id3_genre[0], sizeof(id3_genre));

                if (strchr(id3_genre, '(') && strchr(id3_genre, ')') && isdigit(id3_genre[1]))
                {
                        uint32 gen;

                        gen = ID3Field_GetINT(field);

                        if (gen < ID3_NR_OF_V1_GENRES)
                                strncpy (id3_genre, ID3_v1_genre_description[gen], sizeof(id3_genre));
                        else
                                strcpy (id3_genre, "Pop");
                }
                else
                {
                        char *genre;

                        if ((genre = ASCIItoUTF8(id3_genre)) != NULL)
                        {
                                strncpy (id3_genre, genre, sizeof(id3_genre));
                                free(genre);
                        }
                }

                ID3Frame_Clear(frame);
                char_replace(id3_genre, '"', '`');
                char_replace(id3_genre, '\\', ' ');
        }

        ID3Tag_Clear(id3);
        ID3Tag_Delete(id3);

        if (!strlen(id3_title) && !strlen(id3_singer))          /* grab title and artist from file name */
        {
                char hv0[256], *p;

                memset(hv0, 0, sizeof(hv0));
                p = strrchr(file, '/');

                if (p)
                        strncpy(hv0, p+1, sizeof(hv0)-1);
                else
                        strncpy(hv0, file, sizeof(hv0)-1);

                if ((p = strchr(hv0, '-')) != NULL)
                {
                        *p = 0;
                        p++;
                        strncpy(id3_singer, hv0, sizeof(id3_singer) - 1);
                        strncpy(id3_title, p, sizeof(id3_title) - 1);

                        if ((p = strrchr(id3_title, '.')) != NULL)
                                *p = 0;

                        trim(id3_singer);
                        trim(id3_title);
                }
                else
                {
                        strncpy(id3_title, hv0, sizeof(hv0) - 1);

                        if ((p = strrchr(id3_title, '.')) != NULL)
                                *p = 0;

                        trim(id3_title);
                }
        }

        strcpy (query, "insert into musicdb (\"path\", \"type\", \"title\", \"interpret\", \"album\", \"genre\", \"cover\") values ");
        strcat (query, "(\"");
        strcat (query, file);
        strcat (query, "\", ");
        sprintf(hv0, "%d", fType);
        strcat (query, hv0);
        strcat (query, ", \"");
        
        if ((title = ASCIItoUTF8(id3_title)) != NULL)
        {
                strcat (query, title);
                free(title);
        }
        else
                strcat (query, id3_title);

        strcat (query, "\", \"");

        if ((singer = ASCIItoUTF8(id3_singer)) != NULL)
        {
                strcat (query, singer);
                free(singer);
        }
        else
                strcat (query, id3_singer);

        strcat (query, "\", \"");

        if ((album = ASCIItoUTF8(id3_album)) != NULL)
        {
                strcat (query, album);
                free(album);
        }
        else
                strcat (query, id3_album);

        strcat (query, "\", \"");
        strcat (query, id3_genre);
        strcat (query, "\", \"");
        strcat (query, id3_cover);
        strcat (query, "\")");

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

        sqlite3_close(db);
        grabFilesCount++;
}