Subversion Repositories mdb

Rev

Rev 59 | 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 <iconv.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 <id3tag.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"

#define ID3_NR_OF_V1_GENRES 148

static const char *ID3_v1_genre_description[ID3_NR_OF_V1_GENRES] =
{
        "Blues",             /* 0 */
        "Classic Rock",      /* 1 */
        "Country",           /* 2 */
        "Dance",             /* 3 */
        "Disco",             /* 4 */
        "Funk",              /* 5 */
        "Grunge",            /* 6 */
        "Hip-Hop",           /* 7 */
        "Jazz",              /* 8 */
        "Metal",             /* 9 */
        "New Age",           /* 10 */
        "Oldies",            /* 11 */
        "Other",             /* 12 */
        "Pop",               /* 13 */
        "R&B",               /* 14 */
        "Rap",               /* 15 */
        "Reggae",            /* 16 */
        "Rock",              /* 17 */
        "Techno",            /* 18 */
        "Industrial",        /* 19 */
        "Alternative",       /* 20 */
        "Ska",               /* 21 */
        "Death Metal",       /* 22 */
        "Pranks",            /* 23 */
        "Soundtrack",        /* 24 */
        "Euro-Techno",       /* 25 */
        "Ambient",           /* 26 */
        "Trip-Hop",          /* 27 */
        "Vocal",             /* 28 */
        "Jazz+Funk",         /* 29 */
        "Fusion",            /* 30 */
        "Trance",            /* 31 */
        "Classical",         /* 32 */
        "Instrumental",      /* 33 */
        "Acid",              /* 34 */
        "House",             /* 35 */
        "Game",              /* 36 */
        "Sound Clip",        /* 37 */
        "Gospel",            /* 38 */
        "Noise",             /* 39 */
        "AlternRock",        /* 40 */
        "Bass",              /* 41 */
        "Soul",              /* 42 */
        "Punk",              /* 43 */
        "Space",             /* 44 */
        "Meditative",        /* 45 */
        "Instrumental Pop",  /* 46 */
        "Instrumental Rock", /* 47 */
        "Ethnic",            /* 48 */
        "Gothic",            /* 49 */
        "Darkwave",          /* 50 */
        "Techno-Industrial", /* 51 */
        "Electronic",        /* 52 */
        "Pop-Folk",          /* 53 */
        "Eurodance",         /* 54 */
        "Dream",             /* 55 */
        "Southern Rock",     /* 56 */
        "Comedy",            /* 57 */
        "Cult",              /* 58 */
        "Gangsta",           /* 59 */
        "Top 40",            /* 60 */
        "Christian Rap",     /* 61 */
        "Pop/Funk",          /* 62 */
        "Jungle",            /* 63 */
        "Native American",   /* 64 */
        "Cabaret",           /* 65 */
        "New Wave",          /* 66 */
        "Psychedelic",       /* 67 */
        "Rave",              /* 68 */
        "Showtunes",         /* 69 */
        "Trailer",           /* 70 */
        "Lo-Fi",             /* 71 */
        "Tribal",            /* 72 */
        "Acid Punk",         /* 73 */
        "Acid Jazz",         /* 74 */
        "Polka",             /* 75 */
        "Retro",             /* 76 */
        "Musical",           /* 77 */
        "Rock & Roll",       /* 78 */
        "Hard Rock",         /* 79 */
        /*  following are winamp extentions */
        "Folk",                  /* 80 */
        "Folk-Rock",             /* 81 */
        "National Folk",         /* 82 */
        "Swing",                 /* 83 */
        "Fast Fusion",           /* 84 */
        "Bebob",                 /* 85 */
        "Latin",                 /* 86 */
        "Revival",               /* 87 */
        "Celtic",                /* 88 */
        "Bluegrass",             /* 89 */
        "Avantgarde",            /* 90 */
        "Gothic Rock",           /* 91 */
        "Progressive Rock",      /* 92 */
        "Psychedelic Rock",      /* 93 */
        "Symphonic Rock",        /* 94 */
        "Slow Rock",             /* 95 */
        "Big Band",              /* 96 */
        "Chorus",                /* 97 */
        "Easy Listening",        /* 98 */
        "Acoustic",              /* 99 */
        "Humour",                /* 100 */
        "Speech",                /* 101 */
        "Chanson",               /* 102 */
        "Opera",                 /* 103 */
        "Chamber Music",         /* 104 */
        "Sonata",                /* 105 */
        "Symphony",              /* 106 */
        "Booty Bass",            /* 107 */
        "Primus",                /* 108 */
        "Porn Groove",           /* 109 */
        "Satire",                /* 110 */
        "Slow Jam",              /* 111 */
        "Club",                  /* 112 */
        "Tango",                 /* 113 */
        "Samba",                 /* 114 */
        "Folklore",              /* 115 */
        "Ballad",                /* 116 */
        "Power Ballad",          /* 117 */
        "Rhythmic Soul",         /* 118 */
        "Freestyle",             /* 119 */
        "Duet",                  /* 120 */
        "Punk Rock",             /* 121 */
        "Drum Solo",             /* 122 */
        "A capella",             /* 123 */
        "Euro-House",            /* 124 */
        "Dance Hall",            /* 125 */
        "Goa",                   /* 126 */
        "Drum & Bass",           /* 127 */
        "Club-House",            /* 128 */
        "Hardcore",              /* 129 */
        "Terror",                /* 130 */
        "Indie",                 /* 131 */
        "Britpop",               /* 132 */
        "Negerpunk",             /* 133 */
        "Polsk Punk",            /* 134 */
        "Beat",                  /* 135 */
        "Christian Gangsta Rap", /* 136 */
        "Heavy Metal",           /* 137 */
        "Black Metal",           /* 138 */
        "Crossover",             /* 139 */
        "Contemporary Christian",/* 140 */
        "Christian Rock ",       /* 141 */
        "Merengue",              /* 142 */
        "Salsa",                 /* 143 */
        "Thrash Metal",          /* 144 */
        "Anime",                 /* 145 */
        "JPop",                  /* 146 */
        "Synthpop"               /* 147 */
};

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);
char *getID3_Field(union id3_field *field, enum id3_field_textencoding te, char *ret, int len);
int checkID3FieldType(enum id3_field_type ft);

/* 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;
int _s1[MAX_HANDLES];

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));
        handleAdd(s1);

        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++;
        }

        handleDelete(s1);
        close(s1);
        pthread_exit(NULL);
        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.
         * The type DIRECT means to play a particular file without change the current
         * queue. If something is already playing, it's stopped and the file the
         * parameter <what> points to is played. With DIRECT the parameter <what>
         * has to contain a valid ID number.
         *
         * Syntax: PLAY:<type>:<what>;
         *         <type>     ID | TITLE | ARTIST | ALBUM | GENRE | PLAYLIST | QUEUE | DIRECT
         *         <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") && strcasecmp(p_type, "DIRECT"))   /* If the type is not "ID" and not "DIRECT" 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 && strcasecmp(p_type, "DIRECT"))
                        nextCommand = PLAY_PLAY;
                else if (playerActive && strcasecmp(p_type, "DIRECT"))          /* Only if type is not DIRECT */
                        appendToQueue(s1, p_type, what);
                else
                {
                        _playPars.s1 = s1;
                        strcpy(_playPars.type, p_type);
                        strcpy(_playPars.what, what);

                        if (!strcasecmp(p_type, "DIRECT") && playerActive)
                        {
                                qstackAdd(atoi(what));
                                nextCommand = PLAY_STACK;
                        }
                        else if (!playerActive)
                        {
                                int e;

                                /* start a new thread to play the file(s) */
                                if ((e = pthread_create(&pthr_play, &pattr, pthr_playfile, (void *)&_playPars)) != 0)
                                {
                                        switch (e)
                                        {
                                                case EAGAIN: strcpy (hv0, "Insufficient resources to create another thread."); break;
                                                case EINVAL: strcpy (hv0, "Invalid settings in \"attr\"."); break;
                                                case EPERM:  strcpy (hv0, "No permission to set the scheduling policy and parameters specified in \"attr\"."); break;
                                                default:     sprintf (hv0, "Unknown error %d.", e);
                                        }

                                        syslog (LOG_DAEMON,"Creation of thread \"pthr_play\" failed: %s", hv0);
                                        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, &pattr, 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;
        }
        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[1024];
                        int len;

                        strcpy(hv0, dir);
                        strcat(hv0, "/");
                        len = sizeof(hv0) - strlen(hv0) - 1;

                        if (len > 0)
                                strncat(hv0, namelist[n]->d_name, len);
                        else
                        {
                                free(namelist[n]);
                                continue;
                        }

                        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;
}

void evaluateMusicFile(char *file)
{
sqlite3 *db;
sqlite3_stmt *res;
char *zErrMsg = 0;
int rc, fType, update, id;
char fname[256], hv0[64];
char query[8192];
struct id3_tag *id3;
struct id3_file *ifile;
struct id3_frame *frame;
union id3_field *field;
id3_byte_t const *bt;
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;
enum id3_field_type enc;

        if (access(file, R_OK))
                return;

        bt = NULL;

        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 id, path, cover 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);
        id3_cover[0] = 0;

        if (rc == SQLITE_ROW)                   /* If we've a row, the path exists and we'll not add it again */
        {                                                               /* Instead we'll update the information */
                update = 1;
                id = sqlite3_column_int(res, 0);

                if ((ext = (char *)sqlite3_column_text(res, 2)) == NULL)
                {
                        syslog(LOG_WARNING, "Error getting path of picture from database!");
                        id3_cover[0] = 0;
                }
                else
                        strncpy(&id3_cover[0], ext, sizeof(id3_cover));
        }
        else
                update = 0;

        sqlite3_finalize(res);
        /* 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. */
        if ((ifile = id3_file_open(file, ID3_FILE_MODE_READONLY)) == NULL)
        {
                syslog(LOG_WARNING, "Error opening file %s!", file);
                sqlite3_close(db);
                return;
        }

        if ((id3 = id3_file_tag(ifile)) == NULL)
        {
                syslog(LOG_WARNING, "Error initializing MP3 file %s", file);
                sqlite3_close(db);
                return;
        }

        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));

        if (!strlen(id3_cover))
                memset(&id3_cover, 0, sizeof(id3_cover));

        /* Get the required frames */
        if ((frame = id3_tag_findframe(id3, ID3_FRAME_TITLE, 0)) != NULL)
        {
                int i;

                for (i = 0; i < (int)frame->nfields; i++)
                {
                        field = id3_frame_field(frame, i);

                        if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
                                enc = id3_field_gettextencoding(field);

                        if (field && checkID3FieldType(field->type))
                                break;
                }

                getID3_Field(field, enc, &id3_title[0], sizeof(id3_title));
                char_replace(id3_title, '"', '`');
                char_replace(id3_title, '\\', ' ');
        }

        if ((frame = id3_tag_findframe(id3, ID3_FRAME_ARTIST, 0)) != NULL)
        {
                int i;

                for (i = 0; i < (int)frame->nfields; i++)
                {
                        field = id3_frame_field(frame, i);

                        if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
                                enc = id3_field_gettextencoding(field);

                        if (field && checkID3FieldType(field->type))
                                break;
                }

                getID3_Field(field, enc, &id3_singer[0], sizeof(id3_singer));
                char_replace(id3_singer, '"', '`');
                char_replace(id3_singer, '\\', ' ');
        }

        if ((frame = id3_tag_findframe(id3, ID3_FRAME_ALBUM, 0)) != NULL)
        {
                int i;

                for (i = 0; i < (int)frame->nfields; i++)
                {
                        field = id3_frame_field(frame, i);

                        if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
                                enc = id3_field_gettextencoding(field);

                        if (field && checkID3FieldType(field->type))
                                break;
                }

                getID3_Field(field, enc, &id3_album[0], sizeof(id3_album));
                char_replace(id3_album, '"', '`');
                char_replace(id3_album, '\\', ' ');
        }

        if ((frame = id3_tag_findframe(id3, "APIC", 0)) != NULL)
        {
                char fpname[512], uid[128], hv0[256];
/*              struct id3_frame *fpframe; */
                int i, fd, np;
                long unsigned len;

                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 (!strlen(id3_cover))
                {
                        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, "/");
                len = 0;
                np = 0;                 /* 1 = a new picture was found */
                bt = NULL;
                /* Get the picture and store it into a file */
                for (i = 0; i < (int)frame->nfields; i++)
                {
                        if ((field = id3_frame_field(frame, i)) != NULL)
                        {
                                id3_latin1_t const *lt1;

                                switch (field->type)
                                {
                                        case ID3_FIELD_TYPE_LATIN1:                     /* MIME type */
                                                if ((lt1 = id3_field_getlatin1(field)) != NULL)
                                                        strncpy (&hv0[0], (char *)lt1, sizeof(hv0)-1);
                                        break;

                                        case ID3_FIELD_TYPE_LATIN1FULL:
                                                if ((lt1 = id3_field_getfulllatin1(field)) != NULL)
                                                        strncpy (&hv0[0], (char *)lt1, sizeof(hv0)-1);
                                        break;

                                        case ID3_FIELD_TYPE_BINARYDATA:         /* The picture */
                                                bt = id3_field_getbinarydata(field, &len);
                                                np = 1;
                                        break;
                                }
                        }
                }

                if (!strlen(id3_cover) && np)
                {
                        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);
                }
                else if (strlen(id3_cover) && !np)
                {
                        strcat (fpname, id3_cover);
                        unlink(fpname);
                }
                else if (strlen(id3_cover))
                        strcat (fpname, id3_cover);

                if (np)
                {
                        if ((fd = open(fpname, O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
                                syslog(LOG_WARNING, "Error opening file %s for saving a picture: %s", fpname, strerror(errno));
                        else
                        {
                                write(fd, bt, len);
                                close(fd);
                        }
                }
        }

        if ((frame = id3_tag_findframe(id3, ID3_FRAME_GENRE, 0)) != NULL)
        {
                int i;
                
                for (i = 0; i < (int)frame->nfields; i++)
                {
                        field = id3_frame_field(frame, i);

                        if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
                                enc = id3_field_gettextencoding(field);

                        if (field && checkID3FieldType(field->type))
                                break;
                }

                if (field != NULL)
                        getID3_Field(field, enc, &id3_genre[0], sizeof(id3_genre));
                else
                        id3_genre[0] = 0;

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

                        gen = atoi(&id3_genre[1]);

                        if (gen >= 0 && gen < ID3_NR_OF_V1_GENRES)
                                strncpy (id3_genre, ID3_v1_genre_description[gen], sizeof(id3_genre));
                        else
                                strcpy (id3_genre, "Pop");
                }

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

        id3_tag_delete(id3);
        id3_file_close(ifile);

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

                memset(hv0, 0, sizeof(hv0));
                p = strrchr(file, '/');                                                 /* find the last directory separator */

                if (p)                                                                                  /* If there is a seperator ... */
                        strncpy(hv0, p+1, sizeof(hv0)-1);                       /* Copy the part after it (this is the file name) */
                else                                                                                    /* There seems to be no path ... */
                        strncpy(hv0, file, sizeof(hv0)-1);                      /* Copy the whole file name (this IS the file name) */

                if ((p = strchr(hv0, '-')) != NULL)                             /* Do we have a dash as seperator? */
                {                                                                                               /* Yes, so look how many components the file name have */
                        char *p2;

                        *p = 0;
                        p++;

                        if ((p2 = strchr(p, '-')) != NULL)
                        {
                                *p2 = 0;
                                p2++;
                                strncpy(id3_album, hv0, sizeof(id3_album) - 1);
                                strncpy(id3_singer, p, sizeof(id3_singer) - 1);
                                strncpy(id3_title, p2, sizeof(id3_title) - 1);
                                trim(id3_album);
                        }
                        else
                        {
                                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);
                        /* Make upper / lower case */
                        flag = 1;

                        for (x = 0; x < (int)strlen(id3_singer); x++)
                        {
                                if (id3_singer[x] == ' ')
                                        flag = 1;

                                if (flag && id3_singer[x] != ' ')
                                {
                                        id3_singer[x] = (char)toupper((int)id3_singer[x]);
                                        flag = 0;
                                }
                                else
                                        id3_singer[x] = (char)tolower((int)id3_singer[x]);
                        }

                        flag = 1;

                        for (x = 0; x < (int)strlen(id3_title); x++)
                        {
                                if (id3_title[x] == ' ')
                                        flag = 1;

                                if (flag && id3_title[x] != ' ')
                                {
                                        id3_title[x] = (char)toupper((int)id3_title[x]);
                                        flag = 0;
                                }
                                else
                                        id3_title[x] = (char)tolower((int)id3_title[x]);
                        }

                        if (p2)
                        {
                                flag = 1;

                                for (x = 0; x < (int)strlen(id3_album); x++)
                                {
                                        if (id3_album[x] == ' ')
                                                flag = 1;

                                        if (flag && id3_album[x] != ' ')
                                        {
                                                id3_album[x] = (char)toupper((int)id3_album[x]);
                                                flag = 0;
                                        }
                                        else
                                                id3_album[x] = (char)tolower((int)id3_album[x]);
                                }
                        }
                }
                else
                {
                        strncpy(id3_title, hv0, sizeof(hv0) - 1);

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

                        trim(id3_title);
                        flag = 0;

                        for (x = 0; x < (int)strlen(id3_title); x++)
                        {
                                if (id3_title[x] == ' ')
                                        flag = 1;

                                if (flag && id3_title[x] != ' ')
                                {
                                        id3_title[x] = (char)toupper((int)id3_title[x]);
                                        flag = 0;
                                }
                                else
                                        id3_title[x] = (char)tolower((int)id3_title[x]);
                        }
                }
        }

        if (!update)
        {
                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, ", \"");
                strcat (query, id3_title);
                strcat (query, "\", \"");
                strcat (query, id3_singer);
                strcat (query, "\", \"");
                strcat (query, id3_album);
                strcat (query, "\", \"");
                strcat (query, id3_genre);
                strcat (query, "\", \"");
                strcat (query, id3_cover);
                strcat (query, "\")");
        }
        else
        {
                strcpy(query, "update musicdb set \"title\" = \"");
                strcat(query, id3_title);
                strcat(query, "\", \"interpret\" = \"");
                strcat(query, id3_singer);
                strcat(query, "\", \"album\" = \"");
                strcat(query, id3_album);
                strcat(query, "\", \"genre\" = \"");
                strcat(query, id3_genre);
                strcat(query, "\", \"cover\" = \"");
                strcat(query, id3_cover);
                strcat(query, "\" where id = ");
                sprintf(hv0, "%d", id);
                strcat(query, hv0);
        }

        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++;
}

int checkID3FieldType(enum id3_field_type ft)
{
        switch (ft)
        {
                case ID3_FIELD_TYPE_LATIN1:
                case ID3_FIELD_TYPE_LATIN1FULL:
                case ID3_FIELD_TYPE_LATIN1LIST:
                case ID3_FIELD_TYPE_STRING:
                case ID3_FIELD_TYPE_STRINGFULL:
                case ID3_FIELD_TYPE_STRINGLIST:
                        return 1;

                case ID3_FIELD_TYPE_INT8:
                case ID3_FIELD_TYPE_INT16:
                case ID3_FIELD_TYPE_INT24:
                case ID3_FIELD_TYPE_INT32:
                case ID3_FIELD_TYPE_INT32PLUS:
                        return 1;
        }

        return 0;
}

char *getID3_Field(union id3_field *field, enum id3_field_textencoding te, char *ret, int len)
{
char *p, *pg;
char wc[256];
id3_utf8_t *uc;
id3_ucs4_t const *ucs;
id3_latin1_t const *lt1;
iconv_t ic;
int i;
size_t length;
long num;

        if (field == NULL)
        {
                if (configs.debug)
                        syslog(LOG_DEBUG, "Missing valid field pointer!");

                *ret = 0;
                return NULL;
        }

        *ret = 0;

        switch (te)
        {
                case ID3_FIELD_TEXTENCODING_ISO_8859_1:
                        if (field->type == ID3_FIELD_TYPE_LATIN1)
                        {
                                if ((lt1 = id3_field_getlatin1(field)) == NULL)
                                {
                                        syslog(LOG_WARNING, "Couldn't get latin1 field!");
                                        return NULL;
                                }
                        }
                        else if (field->type == ID3_FIELD_TYPE_LATIN1FULL)
                        {
                                if ((lt1 = id3_field_getfulllatin1(field)) == NULL)
                                {
                                        syslog(LOG_WARNING, "Couldn't get full latin1 field!");
                                        return NULL;
                                }
                        }
                        else if (field->type == ID3_FIELD_TYPE_LATIN1LIST)
                        {
                                int n;

                                n = id3_field_getnstrings(field);

                                for (i = 0; i < n; i++)
                                {
                                        if ((ucs = id3_field_getstrings(field, i)) != NULL)
                                                break;
                                }

                                if (ucs == NULL)
                                {
                                        syslog(LOG_WARNING, "Couldn't get Latin1 field out of a list of %d", n);
                                        return NULL;
                                }

                                uc = id3_ucs4_utf8duplicate(ucs);

                                if (uc)
                                {
                                        strncpy(ret, (char *)uc, len);
                                        free (uc);
                                        return ret;
                                }

                                return NULL;
                        }
                        else
                        {
                                syslog(LOG_WARNING, "No parseable field type found!");
                                return NULL;
                        }

                        if ((ic = iconv_open("UTF-8", "ISO-8859-1")) == (iconv_t)-1)
                        {
                                syslog(LOG_WARNING, "Error initializing ICONV to convert ASCII into UTF-8: %s", strerror(errno));
                                return ret;
                        }

                        length = sizeof(wc);
                        p = wc;
                        pg = (char *)lt1;
                        i = strlen((char *)lt1);

                        iconv(ic, &pg, (size_t *)&i, &p, &length);
                        iconv_close(ic);
                        strncpy (ret, wc, len);
                break;

                case ID3_FIELD_TEXTENCODING_UTF_16:
                case ID3_FIELD_TEXTENCODING_UTF_16BE:
                case ID3_FIELD_TEXTENCODING_UTF_8:
                        if (field->type == ID3_FIELD_TYPE_STRINGFULL)
                        {
                                if ((ucs = id3_field_getfullstring(field)) == NULL)
                                {
                                        syslog(LOG_WARNING, "Couldn't get full UTF-? field!");
                                        return NULL;
                                }
                        }
                        else if (field->type == ID3_FIELD_TYPE_STRING)
                        {
                                if ((ucs = id3_field_getstring(field)) == NULL)
                                {
                                        syslog(LOG_WARNING, "Couldn't get UTF-? field!");
                                        return NULL;
                                }
                        }
                        else if (field->type == ID3_FIELD_TYPE_STRINGLIST)
                        {
                                int n;

                                n = id3_field_getnstrings(field);

                                for (i = 0; i < n; i++)
                                {
                                        if ((ucs = id3_field_getstrings(field, i)) != NULL)
                                                break;
                                }

                                if (ucs == NULL)
                                {
                                        syslog(LOG_WARNING, "Couldn't get UTF-? field out of a list of %d", n);
                                        return NULL;
                                }
                        }
                        else
                        {
                                syslog(LOG_WARNING, "No parseable field type found!");
                                return NULL;
                        }

                        uc = id3_ucs4_utf8duplicate(ucs);

                        if (uc)
                        {
                                strncpy(ret, (char *)uc, len);
                                free (uc);
                        }
                break;

                case ID3_FIELD_TYPE_INT8:
                case ID3_FIELD_TYPE_INT16:
                case ID3_FIELD_TYPE_INT24:
                case ID3_FIELD_TYPE_INT32:
                case ID3_FIELD_TYPE_INT32PLUS:
                        num = id3_field_getint(field);
                        sprintf(ret, "(%ld)", num);
                break;

                default:
                        if (configs.debug)
                                syslog(LOG_DEBUG, "Unkown field encoding: %d", te);

                        return NULL;
        }

        return ret;
}

/*
 * This are functions to handle all the handles for connected clients.
 */
void handleInit()
{
int i;

        for (i = 0; i < MAX_HANDLES; i++)
                _s1[i] = -1;
}

void handleAdd(int fd)
{
int i;

        for (i = 0; i < MAX_HANDLES; i++)
        {
                if (_s1[i] == fd)
                        return;
        }

        for (i = 0; i < MAX_HANDLES; i++)
        {
                if (_s1[i] < 0)
                {
                        _s1[i] = fd;
                        return;
                }
        }
}

void handleDelete(int fd)
{
int i;

        for (i = 0; i < MAX_HANDLES; i++)
        {
                if (_s1[i] == fd)
                {
                        _s1[i] = -1;
                        return;
                }
        }
}

void handleWrite(char *txt)
{
        int i;

        for (i = 0; i < MAX_HANDLES; i++)
        {
                if (_s1[i] >= 0)
                        write(_s1[i], txt, strlen(txt));
        }
}