2019-10-21 04:28:57 +02:00
|
|
|
#include "sonic.h"
|
|
|
|
|
2021-08-22 01:51:37 +02:00
|
|
|
#include "config.h"
|
2021-08-22 03:26:09 +02:00
|
|
|
#include "log.h"
|
2021-09-03 22:39:31 +02:00
|
|
|
#include "link.h"
|
2021-09-04 13:40:37 +02:00
|
|
|
#include "memcache.h"
|
2021-09-03 22:39:31 +02:00
|
|
|
#include "util.h"
|
2019-10-21 04:28:57 +02:00
|
|
|
|
2019-10-22 02:13:28 +02:00
|
|
|
#include <expat.h>
|
2019-10-21 03:11:54 +02:00
|
|
|
|
2019-10-27 22:21:30 +01:00
|
|
|
#include <assert.h>
|
2019-10-21 03:11:54 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
2019-10-23 22:04:25 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
|
2019-10-21 04:28:57 +02:00
|
|
|
typedef struct {
|
2021-08-31 12:18:39 +02:00
|
|
|
char *server;
|
|
|
|
char *username;
|
|
|
|
char *password;
|
|
|
|
char *client;
|
|
|
|
char *api_version;
|
2019-10-21 04:28:57 +02:00
|
|
|
} SonicConfigStruct;
|
|
|
|
|
|
|
|
static SonicConfigStruct SONIC_CONFIG;
|
|
|
|
|
2019-10-21 03:11:54 +02:00
|
|
|
/**
|
2023-03-28 06:00:07 +02:00
|
|
|
* \brief initialise Sonic configuration struct
|
2019-10-21 03:11:54 +02:00
|
|
|
*/
|
2021-08-30 12:24:32 +02:00
|
|
|
void
|
|
|
|
sonic_config_init(const char *server, const char *username,
|
2021-08-31 12:15:00 +02:00
|
|
|
const char *password)
|
2019-10-21 03:11:54 +02:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
SONIC_CONFIG.server = strndup(server, MAX_PATH_LEN);
|
|
|
|
/*
|
|
|
|
* Correct for the extra '/'
|
|
|
|
*/
|
|
|
|
size_t server_url_len = strnlen(SONIC_CONFIG.server, MAX_PATH_LEN) - 1;
|
|
|
|
if (SONIC_CONFIG.server[server_url_len] == '/') {
|
|
|
|
SONIC_CONFIG.server[server_url_len] = '\0';
|
|
|
|
}
|
|
|
|
SONIC_CONFIG.username = strndup(username, MAX_FILENAME_LEN);
|
|
|
|
SONIC_CONFIG.password = strndup(password, MAX_FILENAME_LEN);
|
|
|
|
SONIC_CONFIG.client = DEFAULT_USER_AGENT;
|
|
|
|
|
|
|
|
if (!CONFIG.sonic_insecure) {
|
2021-08-31 12:15:00 +02:00
|
|
|
/*
|
2021-08-31 12:18:39 +02:00
|
|
|
* API 1.13.0 is the minimum version that supports
|
|
|
|
* salt authentication scheme
|
2021-08-31 12:15:00 +02:00
|
|
|
*/
|
2021-08-31 12:18:39 +02:00
|
|
|
SONIC_CONFIG.api_version = "1.13.0";
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* API 1.8.0 is the minimum version that supports ID3 mode
|
|
|
|
*/
|
|
|
|
SONIC_CONFIG.api_version = "1.8.0";
|
|
|
|
}
|
2019-10-21 03:11:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-10-21 04:28:57 +02:00
|
|
|
* \brief generate authentication string
|
2019-10-21 03:11:54 +02:00
|
|
|
*/
|
2019-10-24 04:15:30 +02:00
|
|
|
static char *sonic_gen_auth_str(void)
|
2019-10-21 03:11:54 +02:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!CONFIG.sonic_insecure) {
|
|
|
|
char *salt = generate_salt();
|
|
|
|
size_t pwd_len = strnlen(SONIC_CONFIG.password, MAX_FILENAME_LEN);
|
|
|
|
size_t pwd_salt_len = pwd_len + strnlen(salt, MAX_FILENAME_LEN);
|
|
|
|
char *pwd_salt = CALLOC(pwd_salt_len + 1, sizeof(char));
|
|
|
|
strncat(pwd_salt, SONIC_CONFIG.password, MAX_FILENAME_LEN);
|
|
|
|
strncat(pwd_salt + pwd_len, salt, MAX_FILENAME_LEN);
|
|
|
|
char *token = generate_md5sum(pwd_salt);
|
|
|
|
char *auth_str = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
|
|
|
|
snprintf(auth_str, MAX_PATH_LEN,
|
|
|
|
".view?u=%s&t=%s&s=%s&v=%s&c=%s",
|
|
|
|
SONIC_CONFIG.username, token, salt,
|
|
|
|
SONIC_CONFIG.api_version, SONIC_CONFIG.client);
|
|
|
|
FREE(salt);
|
|
|
|
FREE(token);
|
|
|
|
return auth_str;
|
|
|
|
} else {
|
|
|
|
char *pwd_hex = str_to_hex(SONIC_CONFIG.password);
|
|
|
|
char *auth_str = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
|
|
|
|
snprintf(auth_str, MAX_PATH_LEN,
|
|
|
|
".view?u=%s&p=enc:%s&v=%s&c=%s",
|
|
|
|
SONIC_CONFIG.username, pwd_hex,
|
|
|
|
SONIC_CONFIG.api_version, SONIC_CONFIG.client);
|
|
|
|
FREE(pwd_hex);
|
|
|
|
return auth_str;
|
|
|
|
}
|
2019-10-21 04:28:57 +02:00
|
|
|
}
|
2019-10-21 03:11:54 +02:00
|
|
|
|
2019-10-21 04:28:57 +02:00
|
|
|
/**
|
|
|
|
* \brief generate the first half of the request URL
|
|
|
|
*/
|
|
|
|
static char *sonic_gen_url_first_part(char *method)
|
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
char *auth_str = sonic_gen_auth_str();
|
|
|
|
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
|
|
|
|
snprintf(url, MAX_PATH_LEN, "%s/rest/%s%s", SONIC_CONFIG.server,
|
|
|
|
method, auth_str);
|
|
|
|
FREE(auth_str);
|
|
|
|
return url;
|
2019-10-21 04:28:57 +02:00
|
|
|
}
|
2019-10-21 03:11:54 +02:00
|
|
|
|
2019-10-23 22:04:25 +02:00
|
|
|
/**
|
|
|
|
* \brief generate a getMusicDirectory request URL
|
|
|
|
*/
|
2019-10-28 02:09:55 +01:00
|
|
|
static char *sonic_getMusicDirectory_link(const char *id)
|
2019-10-23 22:04:25 +02:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
char *first_part = sonic_gen_url_first_part("getMusicDirectory");
|
|
|
|
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
|
|
|
|
snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id);
|
|
|
|
FREE(first_part);
|
|
|
|
return url;
|
2019-10-23 22:04:25 +02:00
|
|
|
}
|
|
|
|
|
2019-10-27 22:21:30 +01:00
|
|
|
/**
|
|
|
|
* \brief generate a getArtist request URL
|
|
|
|
*/
|
2019-10-28 02:09:55 +01:00
|
|
|
static char *sonic_getArtist_link(const char *id)
|
2019-10-27 22:21:30 +01:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
char *first_part = sonic_gen_url_first_part("getArtist");
|
|
|
|
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
|
|
|
|
snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id);
|
|
|
|
FREE(first_part);
|
|
|
|
return url;
|
2019-10-27 22:21:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief generate a getAlbum request URL
|
|
|
|
*/
|
2019-10-28 02:09:55 +01:00
|
|
|
static char *sonic_getAlbum_link(const char *id)
|
2019-10-27 22:21:30 +01:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
char *first_part = sonic_gen_url_first_part("getAlbum");
|
|
|
|
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
|
|
|
|
snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id);
|
|
|
|
FREE(first_part);
|
|
|
|
return url;
|
2019-10-27 22:21:30 +01:00
|
|
|
}
|
|
|
|
|
2019-10-23 22:04:25 +02:00
|
|
|
/**
|
|
|
|
* \brief generate a download request URL
|
|
|
|
*/
|
2019-10-28 02:09:55 +01:00
|
|
|
static char *sonic_stream_link(const char *id)
|
2019-10-23 22:04:25 +02:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
char *first_part = sonic_gen_url_first_part("stream");
|
|
|
|
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
|
|
|
|
snprintf(url, MAX_PATH_LEN, "%s&format=raw&id=%s", first_part, id);
|
|
|
|
FREE(first_part);
|
|
|
|
return url;
|
2019-10-23 22:04:25 +02:00
|
|
|
}
|
|
|
|
|
2019-10-22 21:26:21 +02:00
|
|
|
/**
|
2019-10-25 19:52:53 +02:00
|
|
|
* \brief The parser for Sonic index mode
|
2019-10-22 21:26:21 +02:00
|
|
|
* \details This is the callback function called by the the XML parser.
|
|
|
|
* \param[in] data user supplied data, in this case it is the pointer to the
|
|
|
|
* LinkTable.
|
2019-10-23 22:04:25 +02:00
|
|
|
* \param[in] elem the name of this element, it should be either "child" or
|
2019-10-22 21:26:21 +02:00
|
|
|
* "artist"
|
2019-10-23 22:04:25 +02:00
|
|
|
* \param[in] attr Each attribute seen in a start (or empty) tag occupies
|
2019-10-22 21:26:21 +02:00
|
|
|
* 2 consecutive places in this vector: the attribute name followed by the
|
|
|
|
* attribute value. These pairs are terminated by a null pointer.
|
2019-10-23 22:04:25 +02:00
|
|
|
* \note we are using strcmp rather than strncmp, because we are assuming the
|
|
|
|
* parser terminates the strings properly, which is a fair assumption,
|
|
|
|
* considering how mature expat is.
|
2019-10-22 21:26:21 +02:00
|
|
|
*/
|
2021-08-30 12:24:32 +02:00
|
|
|
static void XMLCALL
|
|
|
|
XML_parser_general(void *data, const char *elem, const char **attr)
|
2019-10-22 21:26:21 +02:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
/*
|
|
|
|
* Error checking
|
|
|
|
*/
|
|
|
|
if (!strcmp(elem, "error")) {
|
|
|
|
lprintf(error, "error:\n");
|
|
|
|
for (int i = 0; attr[i]; i += 2) {
|
|
|
|
lprintf(error, "%s: %s\n", attr[i], attr[i + 1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LinkTable *linktbl = (LinkTable *) data;
|
|
|
|
Link *link;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Please refer to the documentation at the function prototype of
|
|
|
|
* sonic_LinkTable_new_id3()
|
|
|
|
*/
|
|
|
|
if (!strcmp(elem, "child")) {
|
|
|
|
link = CALLOC(1, sizeof(Link));
|
|
|
|
/*
|
|
|
|
* Initialise to LINK_DIR, as the LINK_FILE is set later.
|
|
|
|
*/
|
|
|
|
link->type = LINK_DIR;
|
|
|
|
} else if (!strcmp(elem, "artist")
|
2021-09-01 22:29:13 +02:00
|
|
|
&& linktbl->links[0]->sonic.depth != 3) {
|
2021-08-31 12:18:39 +02:00
|
|
|
/*
|
|
|
|
* We want to skip the first "artist" element in the album table
|
|
|
|
*/
|
|
|
|
link = CALLOC(1, sizeof(Link));
|
|
|
|
link->type = LINK_DIR;
|
|
|
|
} else if (!strcmp(elem, "album")
|
2021-09-01 22:29:13 +02:00
|
|
|
&& linktbl->links[0]->sonic.depth == 3) {
|
2021-08-31 12:18:39 +02:00
|
|
|
link = CALLOC(1, sizeof(Link));
|
|
|
|
link->type = LINK_DIR;
|
|
|
|
/*
|
|
|
|
* The new table should be a level 4 song table
|
|
|
|
*/
|
2021-09-01 22:29:13 +02:00
|
|
|
link->sonic.depth = 4;
|
2021-08-31 12:18:39 +02:00
|
|
|
} else if (!strcmp(elem, "song")
|
2021-09-01 22:29:13 +02:00
|
|
|
&& linktbl->links[0]->sonic.depth == 4) {
|
2021-08-31 12:18:39 +02:00
|
|
|
link = CALLOC(1, sizeof(Link));
|
|
|
|
link->type = LINK_FILE;
|
|
|
|
} else {
|
2021-08-31 12:15:00 +02:00
|
|
|
/*
|
2021-08-31 12:18:39 +02:00
|
|
|
* The element does not contain directory structural information
|
2021-08-31 12:15:00 +02:00
|
|
|
*/
|
2021-08-31 12:18:39 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int id_set = 0;
|
|
|
|
int linkname_set = 0;
|
|
|
|
|
|
|
|
int track = 0;
|
|
|
|
char *title = "";
|
|
|
|
char *suffix = "";
|
|
|
|
for (int i = 0; attr[i]; i += 2) {
|
|
|
|
if (!strcmp("id", attr[i])) {
|
2021-09-01 22:29:13 +02:00
|
|
|
link->sonic.id = CALLOC(MAX_FILENAME_LEN + 1, sizeof(char));
|
|
|
|
strncpy(link->sonic.id, attr[i + 1], MAX_FILENAME_LEN);
|
2021-08-31 12:18:39 +02:00
|
|
|
id_set = 1;
|
|
|
|
continue;
|
2021-08-31 12:15:00 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!strcmp("path", attr[i])) {
|
|
|
|
memset(link->linkname, 0, MAX_FILENAME_LEN);
|
|
|
|
/*
|
|
|
|
* Skip to the last '/' if it exists
|
|
|
|
*/
|
|
|
|
char *s = strrchr(attr[i + 1], '/');
|
|
|
|
if (s) {
|
|
|
|
strncpy(link->linkname, s + 1, MAX_FILENAME_LEN);
|
|
|
|
} else {
|
|
|
|
strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN);
|
|
|
|
}
|
|
|
|
linkname_set = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2021-08-31 12:15:00 +02:00
|
|
|
|
|
|
|
/*
|
2021-08-31 12:18:39 +02:00
|
|
|
* "title" is used for directory name,
|
|
|
|
* "name" is for top level directories
|
|
|
|
* N.B. "path" attribute is given the preference
|
2021-08-31 12:15:00 +02:00
|
|
|
*/
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!linkname_set) {
|
|
|
|
if (!strcmp("title", attr[i])
|
2021-09-03 15:56:11 +02:00
|
|
|
|| !strcmp("name", attr[i])) {
|
2021-08-31 12:18:39 +02:00
|
|
|
strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN);
|
|
|
|
linkname_set = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2021-08-31 12:15:00 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!strcmp("isDir", attr[i])) {
|
|
|
|
if (!strcmp("false", attr[i + 1])) {
|
|
|
|
link->type = LINK_FILE;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2021-08-31 12:15:00 +02:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!strcmp("created", attr[i])) {
|
|
|
|
struct tm *tm = CALLOC(1, sizeof(struct tm));
|
|
|
|
strptime(attr[i + 1], "%Y-%m-%dT%H:%M:%S.000Z", tm);
|
|
|
|
link->time = mktime(tm);
|
|
|
|
FREE(tm);
|
|
|
|
continue;
|
|
|
|
}
|
2021-08-31 12:15:00 +02:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!strcmp("size", attr[i])) {
|
|
|
|
link->content_length = atoll(attr[i + 1]);
|
|
|
|
continue;
|
2021-08-31 12:15:00 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!strcmp("track", attr[i])) {
|
|
|
|
track = atoi(attr[i + 1]);
|
|
|
|
continue;
|
2021-08-31 12:15:00 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!strcmp("title", attr[i])) {
|
|
|
|
title = (char *) attr[i + 1];
|
|
|
|
continue;
|
2021-08-31 12:15:00 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!strcmp("suffix", attr[i])) {
|
|
|
|
suffix = (char *) attr[i + 1];
|
|
|
|
continue;
|
2021-08-31 12:15:00 +02:00
|
|
|
}
|
2021-08-31 12:18:39 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 13:31:02 +02:00
|
|
|
if (!linkname_set && strnlen(title, MAX_PATH_LEN) > 0 &&
|
2021-09-03 15:56:11 +02:00
|
|
|
strnlen(suffix, MAX_PATH_LEN) > 0) {
|
2021-08-31 12:18:39 +02:00
|
|
|
snprintf(link->linkname, MAX_FILENAME_LEN, "%02d - %s.%s",
|
|
|
|
track, title, suffix);
|
|
|
|
linkname_set = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clean up if linkname or id is not set
|
|
|
|
*/
|
|
|
|
if (!linkname_set || !id_set) {
|
|
|
|
FREE(link);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (link->type == LINK_FILE) {
|
2021-09-01 22:29:13 +02:00
|
|
|
char *url = sonic_stream_link(link->sonic.id);
|
2021-08-31 12:18:39 +02:00
|
|
|
strncpy(link->f_url, url, MAX_PATH_LEN);
|
|
|
|
FREE(url);
|
|
|
|
}
|
2021-08-31 12:15:00 +02:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
LinkTable_add(linktbl, link);
|
2019-10-22 21:26:21 +02:00
|
|
|
}
|
|
|
|
|
2023-07-26 01:46:24 +02:00
|
|
|
static void sanitise_LinkTable(LinkTable *linktbl)
|
|
|
|
{
|
2022-09-23 07:49:36 +02:00
|
|
|
for (int i = 0; i < linktbl->num; i++) {
|
2023-07-26 01:46:24 +02:00
|
|
|
if (!strcmp(linktbl->links[i]->linkname, ".")) {
|
2022-11-07 00:45:13 +01:00
|
|
|
/* Note the super long sanitised name to avoid collision */
|
2023-07-26 01:46:24 +02:00
|
|
|
strcpy(linktbl->links[i]->linkname, "__DOT__");
|
|
|
|
}
|
2022-09-23 07:49:36 +02:00
|
|
|
|
2023-07-26 01:46:24 +02:00
|
|
|
if (!strcmp(linktbl->links[i]->linkname, "/")) {
|
2022-11-07 00:45:13 +01:00
|
|
|
/* Ditto */
|
2023-07-26 01:46:24 +02:00
|
|
|
strcpy(linktbl->links[i]->linkname, "__FORWARD-SLASH__");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t j = 0; j < strlen(linktbl->links[i]->linkname); j++) {
|
|
|
|
if (linktbl->links[i]->linkname[j] == '/') {
|
|
|
|
linktbl->links[i]->linkname[j] = '-';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (linktbl->links[i]->next_table != NULL) {
|
|
|
|
sanitise_LinkTable(linktbl->links[i]->next_table);
|
|
|
|
}
|
2022-09-23 07:49:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-22 21:26:21 +02:00
|
|
|
/**
|
|
|
|
* \brief parse a XML string in order to fill in the LinkTable
|
|
|
|
*/
|
2019-10-27 12:08:23 +01:00
|
|
|
static LinkTable *sonic_url_to_LinkTable(const char *url,
|
2021-09-03 17:29:00 +02:00
|
|
|
XML_StartElementHandler handler, int depth)
|
2019-10-22 21:26:21 +02:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
LinkTable *linktbl = LinkTable_alloc(url);
|
2021-09-01 22:29:13 +02:00
|
|
|
linktbl->links[0]->sonic.depth = depth;
|
2019-10-27 12:08:23 +01:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
/*
|
|
|
|
* start downloading the base URL
|
|
|
|
*/
|
2021-09-02 16:36:53 +02:00
|
|
|
TransferStruct xml = Link_download_full(linktbl->links[0]);
|
2021-09-03 17:58:08 +02:00
|
|
|
if (xml.curr_size == 0) {
|
2021-08-31 12:18:39 +02:00
|
|
|
LinkTable_free(linktbl);
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-10-27 12:08:23 +01:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
XML_Parser parser = XML_ParserCreate(NULL);
|
|
|
|
XML_SetUserData(parser, linktbl);
|
2019-10-25 19:52:53 +02:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
XML_SetStartElementHandler(parser, handler);
|
2019-10-25 19:52:53 +02:00
|
|
|
|
2021-09-03 17:58:08 +02:00
|
|
|
if (XML_Parse(parser, xml.data, xml.curr_size, 1) == XML_STATUS_ERROR) {
|
2021-08-31 12:18:39 +02:00
|
|
|
lprintf(error,
|
|
|
|
"Parse error at line %lu: %s\n",
|
|
|
|
XML_GetCurrentLineNumber(parser),
|
|
|
|
XML_ErrorString(XML_GetErrorCode(parser)));
|
|
|
|
}
|
2019-10-27 12:08:23 +01:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
XML_ParserFree(parser);
|
2019-10-27 12:08:23 +01:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
FREE(xml.data);
|
2019-10-27 12:08:23 +01:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
LinkTable_print(linktbl);
|
2019-10-27 12:08:23 +01:00
|
|
|
|
2022-11-07 00:45:13 +01:00
|
|
|
sanitise_LinkTable(linktbl);
|
2022-09-23 07:49:36 +02:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
return linktbl;
|
2019-10-27 12:08:23 +01:00
|
|
|
|
2019-10-22 21:26:21 +02:00
|
|
|
}
|
|
|
|
|
2019-10-28 02:09:55 +01:00
|
|
|
LinkTable *sonic_LinkTable_new_index(const char *id)
|
2019-10-21 04:28:57 +02:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
char *url;
|
|
|
|
if (strcmp(id, "0")) {
|
|
|
|
url = sonic_getMusicDirectory_link(id);
|
|
|
|
} else {
|
|
|
|
url = sonic_gen_url_first_part("getIndexes");
|
|
|
|
}
|
|
|
|
LinkTable *linktbl =
|
|
|
|
sonic_url_to_LinkTable(url, XML_parser_general, 0);
|
|
|
|
FREE(url);
|
|
|
|
return linktbl;
|
2019-10-27 12:08:23 +01:00
|
|
|
}
|
2019-10-21 04:28:57 +02:00
|
|
|
|
2021-08-30 12:24:32 +02:00
|
|
|
static void XMLCALL
|
|
|
|
XML_parser_id3_root(void *data, const char *elem, const char **attr)
|
2019-10-27 12:08:23 +01:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!strcmp(elem, "error")) {
|
|
|
|
lprintf(error, "\n");
|
|
|
|
for (int i = 0; attr[i]; i += 2) {
|
|
|
|
lprintf(error, "%s: %s\n", attr[i], attr[i + 1]);
|
2021-08-31 12:15:00 +02:00
|
|
|
}
|
2021-08-31 12:18:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
LinkTable *root_linktbl = (LinkTable *) data;
|
|
|
|
LinkTable *this_linktbl = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the current linktbl, if we have more than head link.
|
|
|
|
*/
|
|
|
|
if (root_linktbl->num > 1) {
|
|
|
|
this_linktbl =
|
|
|
|
root_linktbl->links[root_linktbl->num - 1]->next_table;
|
|
|
|
}
|
|
|
|
|
|
|
|
int id_set = 0;
|
|
|
|
int linkname_set = 0;
|
|
|
|
Link *link;
|
|
|
|
if (!strcmp(elem, "index")) {
|
2021-08-31 12:15:00 +02:00
|
|
|
/*
|
2021-08-31 12:18:39 +02:00
|
|
|
* Add a subdirectory
|
2021-08-31 12:15:00 +02:00
|
|
|
*/
|
2021-08-31 12:18:39 +02:00
|
|
|
link = CALLOC(1, sizeof(Link));
|
|
|
|
link->type = LINK_DIR;
|
|
|
|
for (int i = 0; attr[i]; i += 2) {
|
|
|
|
if (!strcmp("name", attr[i])) {
|
|
|
|
strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN);
|
|
|
|
linkname_set = 1;
|
2021-08-31 12:15:00 +02:00
|
|
|
/*
|
2021-08-31 12:18:39 +02:00
|
|
|
* Allocate a new LinkTable
|
2021-08-31 12:15:00 +02:00
|
|
|
*/
|
2021-08-31 12:18:39 +02:00
|
|
|
link->next_table = LinkTable_alloc("/");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Make sure we don't add an empty directory
|
|
|
|
*/
|
|
|
|
if (linkname_set) {
|
|
|
|
LinkTable_add(root_linktbl, link);
|
|
|
|
} else {
|
|
|
|
FREE(link);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
} else if (!strcmp(elem, "artist")) {
|
|
|
|
link = CALLOC(1, sizeof(Link));
|
|
|
|
link->type = LINK_DIR;
|
|
|
|
/*
|
|
|
|
* The new table should be a level 3 album table
|
|
|
|
*/
|
2021-09-01 22:29:13 +02:00
|
|
|
link->sonic.depth = 3;
|
2021-08-31 12:18:39 +02:00
|
|
|
for (int i = 0; attr[i]; i += 2) {
|
|
|
|
if (!strcmp("name", attr[i])) {
|
|
|
|
strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN);
|
|
|
|
linkname_set = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp("id", attr[i])) {
|
2021-09-01 22:29:13 +02:00
|
|
|
link->sonic.id =
|
2021-08-31 12:18:39 +02:00
|
|
|
CALLOC(MAX_FILENAME_LEN + 1, sizeof(char));
|
2021-09-01 22:29:13 +02:00
|
|
|
strncpy(link->sonic.id, attr[i + 1], MAX_FILENAME_LEN);
|
2021-08-31 12:18:39 +02:00
|
|
|
id_set = 1;
|
|
|
|
continue;
|
|
|
|
}
|
2021-08-31 12:15:00 +02:00
|
|
|
}
|
2021-08-31 12:18:39 +02:00
|
|
|
|
2021-08-31 12:15:00 +02:00
|
|
|
/*
|
2021-08-31 12:18:39 +02:00
|
|
|
* Clean up if linkname is not set
|
2021-08-31 12:15:00 +02:00
|
|
|
*/
|
2021-08-31 12:18:39 +02:00
|
|
|
if (!linkname_set || !id_set) {
|
|
|
|
FREE(link);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LinkTable_add(this_linktbl, link);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If we reach here, then this element does not contain directory structural
|
|
|
|
* information
|
|
|
|
*/
|
2019-10-27 22:21:30 +01:00
|
|
|
}
|
|
|
|
|
2019-10-28 02:09:55 +01:00
|
|
|
LinkTable *sonic_LinkTable_new_id3(int depth, const char *id)
|
2019-10-27 22:21:30 +01:00
|
|
|
{
|
2021-08-31 12:18:39 +02:00
|
|
|
char *url;
|
|
|
|
LinkTable *linktbl = ROOT_LINK_TBL;
|
|
|
|
switch (depth) {
|
2021-09-03 15:56:11 +02:00
|
|
|
/*
|
|
|
|
* Root table
|
|
|
|
*/
|
2021-08-31 12:18:39 +02:00
|
|
|
case 0:
|
|
|
|
url = sonic_gen_url_first_part("getArtists");
|
|
|
|
linktbl = sonic_url_to_LinkTable(url, XML_parser_id3_root, 0);
|
|
|
|
FREE(url);
|
|
|
|
break;
|
2021-09-03 15:56:11 +02:00
|
|
|
/*
|
|
|
|
* Album table - get all the albums of an artist
|
|
|
|
*/
|
2021-08-31 12:18:39 +02:00
|
|
|
case 3:
|
|
|
|
url = sonic_getArtist_link(id);
|
|
|
|
linktbl = sonic_url_to_LinkTable(url, XML_parser_general, depth);
|
|
|
|
FREE(url);
|
|
|
|
break;
|
2021-09-03 15:56:11 +02:00
|
|
|
/*
|
|
|
|
* Song table - get all the songs of an album
|
|
|
|
*/
|
2021-08-31 12:18:39 +02:00
|
|
|
case 4:
|
|
|
|
url = sonic_getAlbum_link(id);
|
|
|
|
linktbl = sonic_url_to_LinkTable(url, XML_parser_general, depth);
|
|
|
|
FREE(url);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* We shouldn't reach here.
|
|
|
|
*/
|
|
|
|
lprintf(fatal, "case %d.\n", depth);
|
|
|
|
break;
|
|
|
|
}
|
2022-09-23 07:49:36 +02:00
|
|
|
|
2021-08-31 12:18:39 +02:00
|
|
|
return linktbl;
|
2019-10-25 19:52:53 +02:00
|
|
|
}
|