diff --git a/Makefile b/Makefile index 75642fe..6512da5 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ VERSION=1.1.10 -CFLAGS+= -O2 -Wall -Wextra -Wshadow -rdynamic\ - -D_FILE_OFFSET_BITS=64 -DVERSION=\"$(VERSION)\" \ +CFLAGS+= -O2 -Wall -Wextra -Wshadow\ + -rdynamic -D_XOPEN_SOURCE=700 -D_DEFAULT_SOURCE\ + -D_FILE_OFFSET_BITS=64 -DVERSION=\"$(VERSION)\"\ `pkg-config --cflags-only-I gumbo libcurl fuse uuid expat` LIBS = -pthread -lgumbo -lcurl -lfuse -lcrypto -luuid -lexpat COBJS = network.o fuse_local.o link.o cache.o util.o main.o diff --git a/src/cache.c b/src/cache.c index baff2ec..aaefd6a 100644 --- a/src/cache.c +++ b/src/cache.c @@ -9,21 +9,6 @@ #include #include -/** - * \brief Data file block size - * \details We set it to 1024*1024*8 = 8MiB - */ - -#define DEFAULT_DATA_BLK_SZ 8*1024*1024 - -/** - * \brief Maximum segment block count - * \details This is set to 128*1024 blocks, which uses 128KB. By default, - * this allows the user to store (128*1024)*(8*1024*1024) = 1TB of data - */ - -#define DEFAULT_MAX_SEGBC 128*1024 - /** * \brief error associated with metadata */ @@ -36,10 +21,7 @@ typedef enum { } MetaError; /* ---------------- External variables -----------------------*/ - int CACHE_SYSTEM_INIT = 0; -int DATA_BLK_SZ = 0; -int MAX_SEGBC = DEFAULT_MAX_SEGBC; char *META_DIR; /* ----------------- Static variables ----------------------- */ @@ -156,10 +138,6 @@ void CacheSystem_init(const char *path, int url_supplied) strerror(errno)); } - if (!DATA_BLK_SZ) { - DATA_BLK_SZ = DEFAULT_DATA_BLK_SZ; - } - CACHE_SYSTEM_INIT = 1; } @@ -202,12 +180,12 @@ blksz: %d, segbc: %ld\n", cf->path, cf->content_length, cf->blksz, cf->segbc); return EZERO; } - if (cf->blksz != DATA_BLK_SZ) { - fprintf(stderr, "Meta_read(): Warning: cf->blksz != DATA_BLK_SZ\n"); + if (cf->blksz != CONFIG.data_blksz) { + fprintf(stderr, "Meta_read(): Warning: cf->blksz != CONFIG.data_blksz\n"); } /* Allocate some memory for the segment */ - if (cf->segbc > MAX_SEGBC) { + if (cf->segbc > CONFIG.max_segbc) { fprintf(stderr, "Meta_read(): Error: segbc: %ld\n", cf->segbc); return EMEM; } @@ -671,7 +649,7 @@ int Cache_create(Link *this_link) cf->path = strndup(fn, MAX_PATH_LEN); cf->time = this_link->time; cf->content_length = this_link->content_length; - cf->blksz = DATA_BLK_SZ; + cf->blksz = CONFIG.data_blksz; cf->segbc = (cf->content_length / cf->blksz) + 1; cf->seg = CALLOC(cf->segbc, sizeof(Seg)); diff --git a/src/cache.h b/src/cache.h index 0a4e004..53d6d55 100644 --- a/src/cache.h +++ b/src/cache.h @@ -70,16 +70,6 @@ struct Cache { */ extern int CACHE_SYSTEM_INIT; -/** - * \brief The size of each download segment - */ -extern int DATA_BLK_SZ; - -/** - * \brief The maximum segment count for a single cache file - */ -extern int MAX_SEGBC; - /** * \brief The metadata directory */ diff --git a/src/link.c b/src/link.c index 81d9e7a..b3dffe5 100644 --- a/src/link.c +++ b/src/link.c @@ -49,9 +49,9 @@ LinkTable *LinkSystem_init(const char *url) } /* ----------- Enable cache system --------------------*/ - if (NETWORK_CONFIG.cache_enabled) { - if (NETWORK_CONFIG.cache_dir) { - CacheSystem_init(NETWORK_CONFIG.cache_dir, 0); + if (CONFIG.cache_enabled) { + if (CONFIG.cache_dir) { + CacheSystem_init(CONFIG.cache_dir, 0); } else { CacheSystem_init(url, 1); } @@ -62,7 +62,7 @@ LinkTable *LinkSystem_init(const char *url) return ROOT_LINK_TBL; } -static void LinkTable_add(LinkTable *linktbl, Link *link) +void LinkTable_add(LinkTable *linktbl, Link *link) { linktbl->num++; linktbl->links = realloc(linktbl->links, linktbl->num * sizeof(Link *)); @@ -139,7 +139,7 @@ static CURL *Link_to_curl(Link *link) fprintf(stderr, "Link_to_curl(): curl_easy_init() failed!\n"); } /* set up some basic curl stuff */ - curl_easy_setopt(curl, CURLOPT_USERAGENT, NETWORK_CONFIG.user_agent); + curl_easy_setopt(curl, CURLOPT_USERAGENT, CONFIG.user_agent); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); /* for following directories without the '/' */ curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 2); @@ -151,26 +151,26 @@ static CURL *Link_to_curl(Link *link) // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - if (NETWORK_CONFIG.username) { - curl_easy_setopt(curl, CURLOPT_USERNAME, NETWORK_CONFIG.username); + if (CONFIG.http_username) { + curl_easy_setopt(curl, CURLOPT_USERNAME, CONFIG.http_username); } - if (NETWORK_CONFIG.password) { - curl_easy_setopt(curl, CURLOPT_PASSWORD, NETWORK_CONFIG.password); + if (CONFIG.http_password) { + curl_easy_setopt(curl, CURLOPT_PASSWORD, CONFIG.http_password); } - if (NETWORK_CONFIG.proxy) { - curl_easy_setopt(curl, CURLOPT_PROXY, NETWORK_CONFIG.proxy); + if (CONFIG.proxy) { + curl_easy_setopt(curl, CURLOPT_PROXY, CONFIG.proxy); } - if (NETWORK_CONFIG.proxy_user) { + if (CONFIG.proxy_username) { curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, - NETWORK_CONFIG.proxy_user); + CONFIG.proxy_username); } - if (NETWORK_CONFIG.proxy_pass) { + if (CONFIG.proxy_password) { curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, - NETWORK_CONFIG.proxy_pass); + CONFIG.proxy_password); } return curl; @@ -355,7 +355,7 @@ DataStruct Link_to_DataStruct(Link *head_link) fprintf(stderr, "LinkTable_new(): URL: %s, HTTP %ld, retrying later.\n", url, http_resp); - sleep(HTTP_WAIT_SEC); + sleep(CONFIG.http_wait_sec); } else if (http_resp != HTTP_OK) { fprintf(stderr, "LinkTable_new(): cannot retrieve URL: %s, HTTP %ld\n", diff --git a/src/link.h b/src/link.h index 9f0ca75..b75b1ba 100644 --- a/src/link.h +++ b/src/link.h @@ -159,4 +159,8 @@ void LinkTable_free(LinkTable *linktbl); */ void LinkTable_print(LinkTable *linktbl); +/** + * \brief add a Link to a LinkTable + */ +void LinkTable_add(LinkTable *linktbl, Link *link); #endif diff --git a/src/main.c b/src/main.c index 3d399a3..3692af8 100644 --- a/src/main.c +++ b/src/main.c @@ -36,7 +36,7 @@ int main(int argc, char **argv) add_arg(&fuse_argv, &fuse_argc, argv[0]); /* initialise network configuration struct */ - NetworkConfig_init(); + Config_init(); /* initialise network subsystem */ NetworkSystem_init(); @@ -140,7 +140,10 @@ parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc) {"max-conns", required_argument, NULL, 'L'}, /* 11 */ {"user-agent", required_argument, NULL, 'L'}, /* 12 */ {"retry-wait", required_argument, NULL, 'L'}, /* 13 */ - {"cache-location", required_argument, NULL, 'L'}, /* 14 */ + {"cache-location", required_argument, NULL, 'L'}, /* 14 */ + {"sonic-username", required_argument, NULL, 'L'}, /* 15 */ + {"sonic-password", required_argument, NULL, 'L'}, /* 16 */ + {"sonic", no_argument, NULL, 'L'}, /* 17 */ {0, 0, 0, 0} }; while ((c = @@ -170,43 +173,52 @@ parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc) add_arg(fuse_argv, fuse_argc, "-s"); break; case 'u': - NETWORK_CONFIG.username = strdup(optarg); + CONFIG.http_username = strdup(optarg); break; case 'p': - NETWORK_CONFIG.password = strdup(optarg); + CONFIG.http_password = strdup(optarg); break; case 'P': - NETWORK_CONFIG.proxy = strdup(optarg); + CONFIG.proxy = strdup(optarg); break; case 'L': /* Long options */ switch (long_index) { case 6: - NETWORK_CONFIG.proxy_user = strdup(optarg); + CONFIG.proxy_username = strdup(optarg); break; case 7: - NETWORK_CONFIG.proxy_pass = strdup(optarg); + CONFIG.proxy_password = strdup(optarg); break; case 8: - NETWORK_CONFIG.cache_enabled = 1; + CONFIG.cache_enabled = 1; break; case 9: - DATA_BLK_SZ = atoi(optarg) * 1024 * 1024; + CONFIG.data_blksz = atoi(optarg) * 1024 * 1024; break; case 10: - MAX_SEGBC = atoi(optarg); + CONFIG.max_segbc = atoi(optarg); break; case 11: - NETWORK_CONFIG.max_conns = atoi(optarg); + CONFIG.max_conns = atoi(optarg); break; case 12: - NETWORK_CONFIG.user_agent = strdup(optarg); + CONFIG.user_agent = strdup(optarg); break; case 13: - HTTP_WAIT_SEC = atoi(optarg); + CONFIG.http_wait_sec = atoi(optarg); break; case 14: - NETWORK_CONFIG.cache_dir = strdup(optarg); + CONFIG.cache_dir = strdup(optarg); + break; + case 15: + CONFIG.sonic_username = strdup(optarg); + break; + case 16: + CONFIG.sonic_password = strdup(optarg); + break; + case 17: + CONFIG.sonic_mode = 1; break; default: fprintf(stderr, "see httpdirfs -h for usage\n"); diff --git a/src/network.c b/src/network.c index 3733a0a..b84bc74 100644 --- a/src/network.c +++ b/src/network.c @@ -9,13 +9,9 @@ #include #include -#define DEFAULT_NETWORK_MAX_CONNS 10 -#define DEFAULT_HTTP_WAIT_SEC 5 /* ----------------- External variables ---------------------- */ CURLSH *CURL_SHARE; -NetworkConfigStruct NETWORK_CONFIG; -int HTTP_WAIT_SEC = DEFAULT_HTTP_WAIT_SEC; /* ----------------- Static variable ----------------------- */ /** \brief curl multi interface handle */ @@ -123,8 +119,8 @@ static void curl_process_msgs(CURLMsg *curl_msg, int n_running_curl, if (!slept) { fprintf(stderr, "curl_process_msgs(): HTTP %ld, sleeping for %d sec\n", - http_resp, HTTP_WAIT_SEC); - sleep(HTTP_WAIT_SEC); + http_resp, CONFIG.http_wait_sec); + sleep(CONFIG.http_wait_sec); slept = 1; } } else { @@ -231,19 +227,6 @@ int curl_multi_perform_once(void) return n_running_curl; } -void NetworkConfig_init(void) -{ - NETWORK_CONFIG.username = NULL; - NETWORK_CONFIG.password = NULL; - NETWORK_CONFIG.proxy = NULL; - NETWORK_CONFIG.proxy_user = NULL; - NETWORK_CONFIG.proxy_pass = NULL; - NETWORK_CONFIG.max_conns = DEFAULT_NETWORK_MAX_CONNS; - NETWORK_CONFIG.user_agent = DEFAULT_USER_AGENT; - NETWORK_CONFIG.cache_enabled = 0; - NETWORK_CONFIG.cache_dir = NULL; -} - void NetworkSystem_init(void) { /* ------- Global related ----------*/ @@ -277,9 +260,9 @@ void NetworkSystem_init(void) exit_failure(); } curl_multi_setopt(curl_multi, CURLMOPT_MAX_TOTAL_CONNECTIONS, - NETWORK_CONFIG.max_conns); + CONFIG.max_conns); curl_multi_setopt(curl_multi, CURLMOPT_MAX_HOST_CONNECTIONS, - NETWORK_CONFIG.max_conns); + CONFIG.max_conns); /* ------------ Initialise locks ---------*/ if (pthread_mutex_init(&transfer_lock, NULL)) { diff --git a/src/network.h b/src/network.h index ac76ad7..92b632e 100644 --- a/src/network.h +++ b/src/network.h @@ -8,24 +8,6 @@ #include "link.h" -/** - * \brief the default user agent string - */ -#define DEFAULT_USER_AGENT "HTTPDirFS-" VERSION - -typedef struct { - char *username; - char *password; - char *proxy; - char *proxy_user; - char *proxy_pass; - long max_conns; - char *user_agent; - int http_429_wait; - char *cache_dir; - int cache_enabled; -} NetworkConfigStruct; - /** \brief HTTP response codes */ typedef enum { HTTP_OK = 200, @@ -36,21 +18,12 @@ typedef enum { HTTP_CLOUDFLARE_TIMEOUT = 524 } HTTPResponseCode; -/** \brief The waiting time after getting HTTP 429 */ -extern int HTTP_WAIT_SEC; - -/** \brief CURL configuration */ -extern NetworkConfigStruct NETWORK_CONFIG; - /** \brief curl shared interface */ extern CURLSH *CURL_SHARE; /** \brief perform one transfer cycle */ int curl_multi_perform_once(void); -/** \brief initialise network config struct */ -void NetworkConfig_init(void); - /** \brief initialise the network module */ void NetworkSystem_init(void); diff --git a/src/sonic.c b/src/sonic.c index 4d07666..d20d39d 100644 --- a/src/sonic.c +++ b/src/sonic.c @@ -8,6 +8,8 @@ #include #include +#include + typedef struct { char *server; @@ -31,8 +33,8 @@ void sonic_config_init(const char *server, const char *username, 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.http_username = strndup(username, MAX_FILENAME_LEN); + SONIC_CONFIG.http_password = strndup(password, MAX_FILENAME_LEN); SONIC_CONFIG.client = DEFAULT_USER_AGENT; /* * API 1.13.0 is the minimum version that supports @@ -47,16 +49,16 @@ void sonic_config_init(const char *server, const char *username, static char *sonic_gen_auth_str() { char *salt = generate_salt(); - size_t password_len = strnlen(SONIC_CONFIG.password, MAX_FILENAME_LEN); + size_t password_len = strnlen(SONIC_CONFIG.http_password, MAX_FILENAME_LEN); size_t password_salt_len = password_len + strnlen(salt, MAX_FILENAME_LEN); char *password_salt = CALLOC(password_salt_len + 1, sizeof(char)); - strncat(password_salt, SONIC_CONFIG.password, MAX_FILENAME_LEN); + strncat(password_salt, SONIC_CONFIG.http_password, MAX_FILENAME_LEN); strncat(password_salt + password_len, salt, MAX_FILENAME_LEN); char *token = generate_md5sum(password_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.http_username, token, salt, SONIC_CONFIG.api_version, SONIC_CONFIG.client); free(salt); free(token); @@ -76,27 +78,54 @@ static char *sonic_gen_url_first_part(char *method) return url; } +/** + * \brief generate a getMusicDirectory request URL + */ +static char *sonic_getMusicDirectory_link(const int id) +{ + 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=%d", first_part, id); + free(first_part); + return url; +} + +/** + * \brief generate a download request URL + */ +static char *sonic_download_link(const int id) +{ + char *first_part = sonic_gen_url_first_part("download"); + char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); + snprintf(url, MAX_PATH_LEN, "%s&id=%d", first_part, id); + free(first_part); + return url; +} + /** * \brief Process a single element output by the parser * \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. - * \param[in] element the name of this element, it should be either "child" or + * \param[in] elem the name of this element, it should be either "child" or * "artist" - * \param[in] attributes Each attribute seen in a start (or empty) tag occupies + * \param[in] attr Each attribute seen in a start (or empty) tag occupies * 2 consecutive places in this vector: the attribute name followed by the * attribute value. These pairs are terminated by a null pointer. + * \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. */ -static void XMLCALL XML_process_single_element(void *data, const char *element, - const char **attributes) +static void XMLCALL XML_process_single_element(void *data, const char *elem, + const char **attr) { LinkTable *linktbl = (LinkTable *) data; Link *link; - if (!strncmp(element, "child", 5)) { + if (!strcmp(elem, "child")) { /* Return from getMusicDirectory */ link = CALLOC(1, sizeof(Link)); link->type = LINK_INVALID; - } else if (!strncmp(element, "artist", 6)){ + } else if (!strcmp(elem, "artist")){ /* Return from getIndexes */ link = CALLOC(1, sizeof(Link)); link->type = LINK_DIR; @@ -105,9 +134,78 @@ static void XMLCALL XML_process_single_element(void *data, const char *element, return; } - for (int i = 0; attributes[i]; i += 2) { + int id_set = 0; + int linkname_set = 0; + for (int i = 0; attr[i]; i += 2) { + if (!strcmp("id", attr[i])) { + link->sonic_id = atoi(attr[i+1]); + id_set = 1; + continue; + } + + if (!strcmp("name", attr[i]) || !strcmp("title", attr[i])) { + strncpy(link->linkname, attr[i+1], MAX_FILENAME_LEN); + linkname_set = 1; + continue; + } + + if (!strcmp("isDir", attr[i])) { + if (!strcmp("true", attr[i+1])) { + link->type = LINK_DIR; + } else if (!strcmp("false", attr[i+1])) { + link->type = LINK_FILE; + } else { + link->type = LINK_DIR; + } + continue; + } + + 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; + } + + if (!strcmp("size", attr[i])) { + link->content_length = atoll(attr[i+1]); + continue; + } } + + /* Clean up if linkname is not set */ + if (!linkname_set) { + free(link); + return; + } + + /* Clean up if id is not set */ + if (!id_set) { + if (linkname_set) { + free(link->linkname); + } + free(link); + return; + } + + if (link->type == LINK_DIR) { + char *url = sonic_getMusicDirectory_link(link->sonic_id); + strncpy(link->f_url, url, MAX_PATH_LEN); + free(url); + } else if (link->type == LINK_FILE) { + char *url = sonic_download_link(link->sonic_id); + strncpy(link->f_url, url, MAX_PATH_LEN); + free(url); + } else { + /* Invalid link */ + free(link->linkname); + free(link); + return; + } + + LinkTable_add(linktbl, link); } /** @@ -127,14 +225,12 @@ static void sonic_XML_to_LinkTable(DataStruct ds, LinkTable *linktbl) XML_ParserFree(parser); } + LinkTable *sonic_LinkTable_new(const int id) { char *url; if (id > 0) { - char *first_part = sonic_gen_url_first_part("getMusicDirectory"); - url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); - snprintf(url, MAX_PATH_LEN, "%s&id=%d", first_part, id); - free(first_part); + url = sonic_getMusicDirectory_link(id); } else { url = sonic_gen_url_first_part("getIndexes"); } @@ -166,7 +262,7 @@ int main(int argc, char **argv) sonic_config_init(argv[1], argv[2], argv[3]); - NetworkConfig_init(); + Config_init(); NetworkSystem_init(); sonic_LinkTable_new(0); diff --git a/src/util.c b/src/util.c index 708b226..3278c8c 100644 --- a/src/util.c +++ b/src/util.c @@ -9,10 +9,86 @@ #include #include +/** + * \brief Backtrace buffer size + */ #define BT_BUF_SIZE 100 + +/** + * \brief The length of a MD5SUM string + */ #define MD5_HASH_LEN 32 + +/** + * \brief The length of the salt + * \details This is basically the length of a UUID + */ #define SALT_LEN 36 +/** + * \brief The default maximum number of network connections + */ +#define DEFAULT_NETWORK_MAX_CONNS 10 + +/** + * \brief The default HTTP 429 (too many requests) wait time + */ +#define DEFAULT_HTTP_WAIT_SEC 5 +/** + * \brief Data file block size + * \details We set it to 1024*1024*8 = 8MiB + */ +#define DEFAULT_DATA_BLKSZ 8*1024*1024 + +/** + * \brief Maximum segment block count + * \details This is set to 128*1024 blocks, which uses 128KB. By default, + * this allows the user to store (128*1024)*(8*1024*1024) = 1TB of data + */ +#define DEFAULT_MAX_SEGBC 128*1024 + +ConfigStruct CONFIG; + +/** + * \note The opening curly bracket should be at line 39, so the code lines up + * with the definition code in util.h. + */ +void Config_init(void) +{ + /*---------------- Network related --------------*/ + CONFIG.http_username = NULL; + + CONFIG.http_password = NULL; + + CONFIG.proxy = NULL; + + CONFIG.proxy_username = NULL; + + CONFIG.proxy_password = NULL; + + CONFIG.max_conns = DEFAULT_NETWORK_MAX_CONNS; + + CONFIG.user_agent = DEFAULT_USER_AGENT; + + CONFIG.http_wait_sec = DEFAULT_HTTP_WAIT_SEC; + + /*--------------- Cache related ---------------*/ + CONFIG.cache_enabled = 0; + + CONFIG.cache_dir = NULL; + + CONFIG.data_blksz = DEFAULT_DATA_BLKSZ; + + CONFIG.max_segbc = DEFAULT_MAX_SEGBC; + + /*-------------- Subsonic related -------------*/ + CONFIG.sonic_mode = 0; + + CONFIG.sonic_username = NULL; + + CONFIG.sonic_password = NULL; +} + char *path_append(const char *path, const char *filename) { int needs_separator = 0; @@ -123,3 +199,4 @@ void *CALLOC(size_t nmemb, size_t size) } return ptr; } + diff --git a/src/util.h b/src/util.h index 7a5a260..b002e49 100644 --- a/src/util.h +++ b/src/util.h @@ -21,6 +21,61 @@ */ #define MAX_FILENAME_LEN 255 +/** + * \brief the default user agent string + */ +#define DEFAULT_USER_AGENT "HTTPDirFS-" VERSION + + + + + + +/** + * \brief configuration data structure + * \note The opening curly bracket should be at line 39, so the code belong + * lines up with the initialisation code in util.c + */ +typedef struct { + /** \brief HTTP username */ + char *http_username; + /** \brief HTTP password */ + char *http_password; + /** \brief HTTP proxy URL */ + char *proxy; + /** \brief HTTP proxy username */ + char *proxy_username; + /** \brief HTTP proxy password */ + char *proxy_password; + /** \brief HTTP maximum connection count */ + long max_conns; + /** \brief HTTP user agent*/ + char *user_agent; + /** \brief The waiting time after getting HTTP 429 (too many requests) */ + int http_wait_sec; + + /** \brief Whether cache mode is enabled */ + int cache_enabled; + /** \brief The cache location*/ + char *cache_dir; + /** \brief The size of each download segment for cache mode */ + int data_blksz; + /** \brief The maximum segment count for a single cache file */ + int max_segbc; + + /** \brief Whether we are using the Subsonic mode */ + int sonic_mode; + /** \brief The Subsonic server username */ + char *sonic_username; + /** \brief The Subsonic server password */ + char *sonic_password; +} ConfigStruct; + +/** + * \brief The Configuration data structure + */ +extern ConfigStruct CONFIG; + /** * \brief append a path * \details This function appends a path with the next level, while taking the @@ -74,4 +129,9 @@ char *generate_md5sum(const char *str); */ void *CALLOC(size_t nmemb, size_t size); +/** + * \brief initialise the configuration data structure + */ +void Config_init(void); + #endif