Rev 31 | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*
* Copyright (C) 2015 by Andreas Theofilu <andreas@theosys.at>
*
* All rights reserved. No warranty, explicit or implicit, provided.
*
* NOTICE: All information contained herein is, and remains
* the property of Andreas Theofilu and his suppliers, if any.
* The intellectual and technical concepts contained
* herein are proprietary to Andreas Theofilu and its suppliers and
* may be covered by European and Foreign Patents, patents in process,
* and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Andreas Theofilu.
*/
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <stdlib.h>
#include <libgen.h>
#include <ctype.h>
#include <math.h>
#include <syslog.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sqlite3.h>
#include <id3.h>
#include "config.h"
#include "helplib.h"
#include "list.h"
#include "user.h"
#include "play.h"
struct callPars
{
int s1;
int start;
int length;
char *type;
};
int globalLine;
int globalPos;
static int listCallback(int s1, char *type, int line, sqlite3_stmt *res);
static int folderCallback(void *hint, int argc, char **argv, char **azColName);
int countPlaylists();
int listSongs(int s1, char *p_type, int start, int length)
{
char query[1024], hv0[128];
char fname[256];
sqlite3 *db;
sqlite3_stmt *res;
int rc, pos, line;
strcpy(fname, configs.home);
strcat(fname, MUSICDB);
rc = sqlite3_open(fname, &db);
if (rc)
{
syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
strcpy(query, "ERROR:LIST:Error opening database;");
write (s1, query, strlen(query));
return FALSE;
}
if (configs.debug)
syslog(LOG_DEBUG, "Listing %s ...", p_type);
/* First count the future result set */
if (strcmp(p_type, "QUEUE") && strcmp(p_type, "PLAYLIST"))
{
strcpy (query, "select count(*) as cnt from musicdb");
if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
strcpy(query, "ERROR:USER:Error preparing a SQL statement;");
write(s1, query, strlen(query));
return FALSE;
}
if (sqlite3_step(res) == SQLITE_ROW)
pos = sqlite3_column_int(res, 0);
else
pos = 0;
sqlite3_finalize(res);
}
else if (!strcmp(p_type, "QUEUE"))
{
if (queueTotal <= 0)
{
int fd;
strcpy(fname, configs.home);
strcat(fname, NOWPLAY);
sleep(1);
if (queueTotal <= 0)
{
if ((fd = open(fname, O_RDONLY)) == -1)
{
syslog(LOG_WARNING, "Error opening queue %s: %s", fname, strerror(errno));
strcpy(query, "ERROR:LIST:Error opening queue file;");
write(s1, query, strlen(query));
return FALSE;
}
scanQueue(fd);
close(fd);
}
}
pos = queueTotal;
}
else
pos = countPlaylists();
playQuiet = TRUE;
sprintf(hv0, "TOTAL:%d;", pos);
write(s1, hv0, strlen(hv0));
strcpy (query, "select id, title, interpret, album, genre, cover from \"main\".\"musicdb\" ");
strcat (query, "order by ");
if (strcmp(p_type, "TITLE") == 0)
strcat (query, "title");
else if (strcmp(p_type, "ARTIST") == 0)
strcat (query, "interpret");
else if (strcmp(p_type, "ALBUM") == 0)
strcat (query, "album");
else if (strcmp(p_type, "GENRE") == 0)
strcat (query, "genre");
else if (strcmp(p_type, "QUEUE") == 0)
{
sqlite3_close(db);
playQuiet = FALSE;
return listQueue(s1, start, length);
}
else if (strcmp(p_type, "PLAYLIST") == 0)
{
sqlite3_close(db);
playQuiet = FALSE;
return listPlaylists(s1, start, length);
}
else if (strcmp(p_type, "USERS") == 0)
{
sqlite3_close(db);
playQuiet = FALSE;
return listUsers(s1, start, length);
}
else /* No or unknown type */
{
strcpy (query, "ERROR:LIST:Missing type;");
write(s1, query, strlen(query));
sqlite3_close(db);
playQuiet = FALSE;
return FALSE;
}
/* Tell client which page to show */
sprintf(hv0, "PAGE:%s;", p_type);
write (s1, hv0, strlen (hv0));
/* Retrieve data from database */
line = pos = 0;
if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
strcpy(query, "ERROR:USER:Error preparing a SQL statement;");
write(s1, query, strlen(query));
playQuiet = FALSE;
return FALSE;
}
while ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
pos++;
if (pos >= start && pos < (start + length))
{
line++;
listCallback(s1, p_type, line, res);
}
if (pos >= (start + length))
break;
}
sqlite3_finalize(res);
sqlite3_close(db);
playQuiet = FALSE;
return TRUE;
}
int listQueue(int s1, int start, int length)
{
int fd, line;
char fname[256], hv0[256], buffer[8192];
char *title, *artist, *album, *cover;
int pos;
QUEUE *act;
if (pqueue == NULL)
{
strcpy (fname, configs.home);
strcat (fname, NOWPLAY);
if (access(fname, R_OK))
{
strcpy (hv0, "ERROR:LIST:No or empty queue;");
write (s1, hv0, strlen(hv0));
return FALSE;
}
if ((fd = open(fname, O_RDONLY)) <= 0)
{
syslog(LOG_WARNING, "Error opening file %s: %s", fname, strerror(errno));
strcpy(hv0, "ERROR:LIST:Error opening queue;");
write(s1, hv0, strlen(hv0));
return FALSE;
}
scanQueue(fd);
close(fd);
}
sprintf(hv0, "PAGE:QUEUE;TOTAL:%d;", queueTotal);
write (s1, hv0, strlen(hv0));
act = pqueue;
pos = line = 1;
playQuiet = TRUE;
while (act)
{
if (pos < start)
{
act = act->next;
pos++;
continue;
}
else if (pos >= (start + length))
break;
title = urlencode(act->title);
artist = urlencode(act->artist);
album = urlencode(act->album);
cover = urlencode(act->cover);
sprintf(buffer, "LINE:QUEUE:%d:%d:", act->id, line);
if (title != NULL)
{
strcat(buffer, title);
free(title);
}
else
strcat(buffer, act->title);
strcat(buffer, ":");
if (artist != NULL)
{
strcat(buffer, artist);
free(artist);
}
else
strcat(buffer, act->artist);
strcat(buffer, ":");
if (album != NULL)
{
strcat(buffer, album);
free(album);
}
else
strcat(buffer, act->album);
strcat(buffer, ":");
strcat(buffer, act->genre);
strcat(buffer, ":");
if (cover != NULL)
{
strcat(buffer, cover);
free(cover);
}
else
strcat(buffer, act->cover);
strcat(buffer, ";");
write(s1, buffer, strlen(buffer));
act = act->next;
pos++;
line++;
}
playQuiet = FALSE;
return TRUE;
}
int listFolders(int s1, char *p_type, int start, int length)
{
char query[1024], field[16];
char fname[256];
sqlite3 *db;
char *zErrMsg = 0;
int rc;
struct callPars cp;
strcpy(fname, configs.home);
strcat(fname, MUSICDB);
rc = sqlite3_open(fname, &db);
if (rc)
{
syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
strcpy(query, "ERROR:FOLDER:Error opening database;");
write (s1, query, strlen(query));
return FALSE;
}
strcpy (query, "select distinct ");
if (strcmp(p_type, "TITLE") == 0)
strcpy (field, "title");
else if (strcmp(p_type, "ARTIST") == 0)
strcpy (field, "interpret");
else if (strcmp(p_type, "ALBUM") == 0)
strcpy (field, "album");
else if (strcmp(p_type, "GENRE") == 0)
strcpy (field, "genre");
else /* No or unknown type */
{
strcpy (query, "ERROR:FOLDER:Missing type;");
write(s1, query, strlen(query));
sqlite3_close(db);
return FALSE;
}
strcat (query, field);
strcat (query, " from \"main\".\"musicdb\" order by ");
strcat (query, field);
cp.s1 = s1;
cp.type = p_type;
cp.start = start;
cp.length = length;
globalLine = 0;
globalPos = 0;
playQuiet = TRUE;
if ((rc = sqlite3_exec(db, query, folderCallback, (void *)&cp, &zErrMsg)) != SQLITE_OK)
{
syslog(LOG_WARNING, "SQL error [%s]: %s", query, zErrMsg);
sqlite3_free(zErrMsg);
sqlite3_close(db);
strcpy(query, "ERROR:FOLDER:SQL error;");
write (s1, query, strlen(query));
return FALSE;
}
if (globalPos >= 0)
{
sprintf(query, "TOTAL:%d;", globalPos + 1);
write (s1, query, strlen(query));
}
sqlite3_close(db);
playQuiet = FALSE;
return TRUE;
}
static int listCallback(int s1, char *type, int line, sqlite3_stmt *res)
{
int id;
char id3_title[256], id3_artist[256], id3_album[256], id3_genre[256], id3_cover[256];
char buffer[8192];
char *title, *artist, *album;
memset(id3_title, 0, sizeof(id3_title));
memset(id3_artist, 0, sizeof(id3_artist));
memset(id3_album, 0, sizeof(id3_album));
memset(id3_genre, 0, sizeof(id3_genre));
memset(id3_cover, 0, sizeof(id3_cover));
id = -1;
id = sqlite3_column_int(res, 0);
strncpy(id3_title, (const char *)sqlite3_column_text(res, 1), sizeof(id3_title));
strncpy(id3_artist, (const char *)sqlite3_column_text(res, 2), sizeof(id3_artist));
strncpy(id3_album, (const char *)sqlite3_column_text(res, 3), sizeof(id3_album));
strncpy(id3_genre, (const char *)sqlite3_column_text(res, 4), sizeof(id3_genre));
strncpy(id3_cover, (const char *)sqlite3_column_text(res, 5), sizeof(id3_cover));
title = urlencode(id3_title);
artist = urlencode(id3_artist);
album = urlencode(id3_album);
sprintf(buffer, "LINE:%s:%d:%d:", type, id, line);
if (title != NULL)
{
strcat(buffer, title);
free(title);
}
else
strcat(buffer, id3_title);
strcat(buffer, ":");
if (artist != NULL)
{
strcat(buffer, artist);
free(artist);
}
else
strcat(buffer, id3_artist);
strcat(buffer, ":");
if (album != NULL)
{
strcat(buffer, album);
free(album);
}
else
strcat(buffer, id3_album);
strcat(buffer, ":");
strcat(buffer, id3_genre);
strcat(buffer, ":");
strcat(buffer, id3_cover);
strcat(buffer, ";");
write(s1, buffer, strlen(buffer));
return 0;
}
static int folderCallback(void *hint, int argc, char **argv, char **azColName)
{
int i;
char id3_buffer[256];
char buffer[8192];
char *buf;
struct callPars *cp;
globalPos++;
cp = (struct callPars *)hint;
memset(id3_buffer, 0, sizeof(id3_buffer));
if (globalPos < cp->start || globalPos >= (cp->start + cp->length))
return 0;
for(i = 0; i < argc; i++)
{
if (strcasecmp(azColName[i], "title") == 0 || strcasecmp(azColName[i], "interpret") == 0 ||
strcasecmp(azColName[i], "album") == 0 || strcasecmp(azColName[i], "genre") == 0)
if (argv[i])
strncpy(id3_buffer, argv[i], sizeof(id3_buffer));
}
buf = urlencode(id3_buffer);
globalLine++;
sprintf(buffer, "FOLDER:%s:%d:", cp->type, globalLine);
if (buf != NULL)
{
strcat(buffer, buf);
free(buf);
}
else
strcat(buffer, id3_buffer);
strcat(buffer, ";");
write(cp->s1, buffer, strlen(buffer));
return 0;
}
int listFolderContent(int s1, char *p_type, char *name, int start, int length)
{
char query[1024], hv0[128], field[32];
char fname[256];
sqlite3 *db;
sqlite3_stmt *res;
int rc, pos, line;
strcpy(fname, configs.home);
strcat(fname, MUSICDB);
if (!strcasecmp(p_type, "TITLE"))
strcpy (field, "title");
else if (!strcasecmp(p_type, "ARTIST"))
strcpy (field, "interpret");
else if (!strcasecmp(p_type, "ALBUM"))
strcpy (field, "album");
else if (!strcasecmp(p_type, "GENRE"))
strcpy (field, "genre");
else
{
strcpy (hv0, "ERROR:LIST:Missing valid type;");
write(s1, hv0, strlen(hv0));
return FALSE;
}
rc = sqlite3_open(fname, &db);
if (rc)
{
syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
strcpy(query, "ERROR:LIST:Error opening database;");
write (s1, query, strlen(query));
return FALSE;
}
/* First count the future result set */
sprintf (query, "select count(*) as cnt from musicdb where %s = \"%s\"", field, name);
if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
strcpy(query, "ERROR:USER:Error preparing a SQL statement;");
write(s1, query, strlen(query));
return FALSE;
}
if (sqlite3_step(res) == SQLITE_ROW)
pos = sqlite3_column_int(res, 0);
else
pos = 0;
playQuiet = TRUE;
sprintf(hv0, "TOTAL:%d;", pos);
write(s1, hv0, strlen(hv0));
sqlite3_finalize(res);
sprintf (query, "select id, title, interpret, album, genre, cover from \"main\".\"musicdb\" where %s = \"%s\" order by title", field, name);
/* Tell client which page to show */
sprintf(hv0, "PAGE:%s;FOLDERNAME:%s;", p_type, name);
write (s1, hv0, strlen (hv0));
/* Retrieve data from database */
line = pos = 1;
if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
strcpy(query, "ERROR:LIST:Error preparing a SQL statement;");
write(s1, query, strlen(query));
playQuiet = FALSE;
return FALSE;
}
while ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
if (pos >= start && pos < (start + length))
{
listCallback(s1, p_type, line, res);
line++;
}
if (pos >= (start + length))
break;
pos++;
}
sqlite3_finalize(res);
sqlite3_close(db);
playQuiet = FALSE;
return TRUE;
}
/*
* Count the playlists of the users and return the result.
*/
int countPlaylists()
{
USERS *act;
int count;
count = 0;
act = userchain;
while (act)
{
count++;
act = act->next;
}
return count;
}
/*
* List the playlists of the current user.
*/
int listPlaylists(int s1, int start, int length)
{
USERS *act;
char hv0[512], *user, *playlist;
int pos, line;
if (userchain == NULL)
{
strcpy(hv0, "ERROR:PLAYLIST:No user selected;");
write(s1, hv0, strlen(hv0));
return FALSE;
}
playQuiet = TRUE;
strcpy(hv0, "PAGE:PLAYLIST;");
write(s1, hv0, strlen(hv0));
act = userchain;
pos = line = 1;
while (act)
{
if (pos < start)
{
act = act->next;
continue;
}
if (pos >= (start + length))
break;
user = urlencode(act->uname);
playlist = urlencode(act->playlist);
sprintf(hv0, "PLAYLIST:%d:%d:%s:%s;", line, act->id, user, playlist);
write (s1, hv0, strlen(hv0));
if (user)
free (user);
if (playlist)
free (playlist);
act = act->next;
line++;
}
playQuiet = FALSE;
return TRUE;
}
int listUserPlaylist(int s1, const char *user, const char *playlist, int start, int length)
{
char query[1024], hv0[128], buffer[8192];
char fname[256];
sqlite3 *db;
int rc, id, total, pos, line;
sqlite3_stmt *res;
char id3_title[256], id3_artist[256], id3_album[256], id3_genre[256], id3_cover[512];
char *title, *artist, *album, *cover;
USERS *act;
if (playlist == NULL)
{
strcpy(hv0, "ERROR:LIST:Missing name of playlist;");
write (s1, hv0, strlen(hv0));
return FALSE;
}
/* Check current user */
if (user != NULL)
{
if (userchain == NULL || strcmp(userchain->uname, user))
{
if (!selectUser(s1, user))
return FALSE;
}
}
/* Find the playlist */
act = userchain;
while (act)
{
if (!strcmp(act->playlist, playlist))
break;
act = act->next;
}
if (act == NULL)
{
strcpy(hv0, "ERROR:LIST:Playlist not found;");
write (s1, hv0, strlen(hv0));
return FALSE;
}
strcpy(fname, configs.home);
strcat(fname, MUSICDB);
rc = sqlite3_open(fname, &db);
if (rc)
{
syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
strcpy(query, "ERROR:USER:Error opening database;");
write (s1, query, strlen(query));
return FALSE;
}
playQuiet = TRUE;
strcpy(hv0, "PAGE:PLAYLIST;");
write(s1, hv0, strlen(hv0));
sprintf(query, "select count(*) from \"main\".\"playlists\" where userid = %d", act->id);
if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
strcpy(hv0, "ERROR:USER:Error preparing a SQL statement;");
write(s1, hv0, strlen(hv0));
playQuiet = FALSE;
return FALSE;
}
if ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
total = sqlite3_column_int(res, 0);
sprintf(hv0, "TOTAL:%d;", total);
write (s1, hv0, strlen(hv0));
}
sqlite3_finalize(res);
strcpy (query, "select id, title, interpret, album, genre, cover from musicdb as a where ");
strcat (query, "(select musicid from playlists as b where a.id = b.musicid and b.userid = ");
sprintf(hv0, "%d", act->id);
strcat (query, hv0);
strcat (query, ")");
if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
strcpy(hv0, "ERROR:USER:Error preparing a SQL statement;");
write(s1, hv0, strlen(hv0));
playQuiet = FALSE;
return FALSE;
}
pos = 1;
line = 0;
while ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
if (pos < start)
{
pos++;
continue;
}
if (pos >= (start + length))
break;
line++;
memset(id3_title, 0, sizeof(id3_title));
memset(id3_artist, 0, sizeof(id3_artist));
memset(id3_album, 0, sizeof(id3_album));
memset(id3_genre, 0, sizeof(id3_genre));
memset(id3_cover, 0, sizeof(id3_cover));
id = sqlite3_column_int(res, 0);
strncpy(id3_title, (const char *)sqlite3_column_text(res, 1), sizeof(id3_title));
strncpy(id3_artist, (const char *)sqlite3_column_text(res, 2), sizeof(id3_artist));
strncpy(id3_album, (const char *)sqlite3_column_text(res, 3), sizeof(id3_album));
strncpy(id3_genre, (const char *)sqlite3_column_text(res, 4), sizeof(id3_genre));
strncpy(id3_cover, (const char *)sqlite3_column_text(res, 5), sizeof(id3_cover));
title = urlencode(id3_title);
artist = urlencode(id3_artist);
album = urlencode(id3_album);
cover = urlencode(id3_cover);
if (title != NULL)
{
strncpy(id3_title, title, sizeof(id3_title));
free(title);
}
if (artist != NULL)
{
strncpy(id3_artist, artist, sizeof(id3_artist));
free(artist);
}
if (album != NULL)
{
strncpy(id3_album, album, sizeof(id3_album));
free(album);
}
if (cover != NULL)
{
strncpy(id3_cover, cover, sizeof(id3_cover));
free(cover);
}
sprintf (buffer, "LINE:PLAYLIST:%d:%d:%s:%s:%s:%s:%s;", id, line, id3_title, id3_artist, id3_album, id3_genre, id3_cover);
write (s1, buffer, strlen(buffer));
pos++;
}
sqlite3_finalize(res);
sqlite3_close(db);
playQuiet = FALSE;
return TRUE;
}
int listUsers(int s1, int start, int length)
{
char query[1024], hv0[256];
char fname[256], uname[128], *un;
sqlite3 *db;
int rc, id, total, pos;
sqlite3_stmt *res;
rc = sqlite3_open(fname, &db);
if (rc)
{
syslog(LOG_WARNING, "Error opening database %s: %s", fname, sqlite3_errmsg(db));
strcpy(query, "ERROR:LIST:Error opening database;");
write (s1, query, strlen(query));
return FALSE;
}
sprintf(query, "select distinct count(*) from users order by uname");
if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
strcpy(hv0, "ERROR:USER:Error preparing a SQL statement;");
write(s1, hv0, strlen(hv0));
return FALSE;
}
if ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
total = sqlite3_column_int(res, 0);
sprintf(hv0, "TOTAL:%d;", total);
write (s1, hv0, strlen(hv0));
}
sqlite3_finalize(res);
strcpy (query, "select distinct uname, id from users order by users");
if (sqlite3_prepare(db, query, -1, &res, NULL) != SQLITE_OK)
{
syslog(LOG_DAEMON, "Error preparing SQL statement [%s]: %s", query, sqlite3_errmsg(db));
strcpy(hv0, "ERROR:USER:Error preparing a SQL statement;");
write(s1, hv0, strlen(hv0));
return FALSE;
}
pos = 0;
playQuiet = TRUE;
while ((rc = sqlite3_step(res)) == SQLITE_ROW)
{
pos++;
if (pos < start)
continue;
if (pos >= (start + length))
break;
strncpy(uname, (const char *)sqlite3_column_text(res, 0), sizeof(uname));
id = sqlite3_column_int(res, 1);
un = urlencode(uname);
if (un)
{
strncpy(uname, un, sizeof(uname));
free(un);
}
sprintf(hv0, "USERS:%d:%d:%s;", id, pos+1, uname);
write(s1, hv0, strlen(hv0));
}
sqlite3_finalize(res);
sqlite3_close(db);
playQuiet = FALSE;
return TRUE;
}