diff --git a/README.md b/README.md index e87c8bc..b060266 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ much faster than ``rclone mount``. ## Usage - ./httpdirfs -f --cache -f $URL $YOUR_MOUNT_POINT + ./httpdirfs -f --cache $URL $YOUR_MOUNT_POINT An example URL would be [Debian CD Image Server](https://cdimage.debian.org/debian-cd/). The ``-f`` flag diff --git a/src/README.md b/src/README.md new file mode 100644 index 0000000..fa89bde --- /dev/null +++ b/src/README.md @@ -0,0 +1,5 @@ +## Coding Convention +- External variables: capital letters +- Static global variables: lower case letters +- Function names: TypeName_verb or verb_noun +- Type names: camel case with the first letter capitalised, e.g. CamelCase diff --git a/src/cache.c b/src/cache.c index 502c22c..baff2ec 100644 --- a/src/cache.c +++ b/src/cache.c @@ -211,11 +211,8 @@ blksz: %d, segbc: %ld\n", cf->path, cf->content_length, cf->blksz, cf->segbc); fprintf(stderr, "Meta_read(): Error: segbc: %ld\n", cf->segbc); return EMEM; } - cf->seg = calloc(cf->segbc, sizeof(Seg)); - if (!cf->seg) { - fprintf(stderr, "Meta_read(): calloc failure: %s\n", strerror(errno)); - return EMEM; - } + cf->seg = CALLOC(cf->segbc, sizeof(Seg)); + /* Read all the segment */ nmemb = fread(cf->seg, sizeof(Seg), cf->segbc, fp); @@ -474,11 +471,7 @@ int CacheDir_create(const char *dirn) */ static Cache *Cache_alloc() { - Cache *cf = calloc(1, sizeof(Cache)); - if (!cf) { - fprintf(stderr, "Cache_new(): calloc failure!\n"); - exit_failure(); - } + Cache *cf = CALLOC(1, sizeof(Cache)); if (pthread_mutex_init(&cf->seek_lock, NULL)) { fprintf(stderr, "Cache_alloc(): seek_lock initialisation failed!\n"); @@ -680,12 +673,7 @@ int Cache_create(Link *this_link) cf->content_length = this_link->content_length; cf->blksz = DATA_BLK_SZ; cf->segbc = (cf->content_length / cf->blksz) + 1; - cf->seg = calloc(cf->segbc, sizeof(Seg)); - - if (!cf->seg) { - fprintf(stderr, "Cache_create(): cf->seg calloc failure!\n"); - exit_failure(); - } + cf->seg = CALLOC(cf->segbc, sizeof(Seg)); if (Meta_create(cf)) { fprintf(stderr, "Cache_create(): cannot create metadata.\n"); @@ -914,7 +902,7 @@ static void *Cache_bgdl(void *arg) pthread_self()); #endif PTHREAD_MUTEX_LOCK(&cf->w_lock); - uint8_t *recv_buf = calloc(cf->blksz, sizeof(uint8_t)); + uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); fprintf(stderr, "Cache_bgdl(): thread %lu: ", pthread_self()); long recv = path_download(cf->path, (char *) recv_buf, cf->blksz, cf->next_dl_offset); @@ -976,7 +964,7 @@ long Cache_read(Cache *cf, char * const output_buf, const off_t len, /* ------------------------Download the segment -------------------------*/ - uint8_t *recv_buf = calloc(cf->blksz, sizeof(uint8_t)); + uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); fprintf(stderr, "Cache_read(): thread %lu: ", pthread_self()); long recv = path_download(cf->path, (char *) recv_buf, cf->blksz, dl_offset); diff --git a/src/link.c b/src/link.c index c7e4f6c..bf29513 100644 --- a/src/link.c +++ b/src/link.c @@ -48,11 +48,8 @@ static void LinkTable_add(LinkTable *linktbl, Link *link) static Link *Link_new(const char *linkname, LinkType type) { - Link *link = calloc(1, sizeof(Link)); - if (!link) { - fprintf(stderr, "Link_new(): calloc failure!\n"); - exit_failure(); - } + Link *link = CALLOC(1, sizeof(Link)); + strncpy(link->linkname, linkname, MAX_FILENAME_LEN); link->type = type; @@ -172,11 +169,8 @@ void Link_req_file_stat(Link *this_link) * * It gets freed in curl_multi_perform_once(); */ - TransferStruct *transfer = calloc(1, sizeof(TransferStruct)); - if (!transfer) { - fprintf(stderr, "Link_get_size(): malloc failed!\n"); - exit_failure(); - } + TransferStruct *transfer = CALLOC(1, sizeof(TransferStruct)); + transfer->link = this_link; transfer->type = FILESTAT; curl_easy_setopt(curl, CURLOPT_PRIVATE, transfer); @@ -326,11 +320,7 @@ LinkTable *LinkTable_new(const char *url) pthread_self()); #endif PTHREAD_MUTEX_LOCK(&link_lock); - LinkTable *linktbl = calloc(1, sizeof(LinkTable)); - if (!linktbl) { - fprintf(stderr, "LinkTable_new(): calloc failure!\n"); - exit_failure(); - } + LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); /* populate the base URL */ LinkTable_add(linktbl, Link_new("/", LINK_HEAD)); @@ -507,19 +497,12 @@ LinkTable *LinkTable_disk_open(const char *dirn) return NULL; } - LinkTable *linktbl = calloc(1, sizeof(LinkTable)); - if (!linktbl) { - fprintf(stderr, "LinkTable_disk_open(): calloc linktbl failed!\n"); - return NULL; - } + LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); fread(&linktbl->num, sizeof(int), 1, fp); - linktbl->links = calloc(linktbl->num, sizeof(Link *)); + linktbl->links = CALLOC(linktbl->num, sizeof(Link *)); for (int i = 0; i < linktbl->num; i++) { - linktbl->links[i] = calloc(1, sizeof(Link)); - if (!linktbl->links[i]) { - fprintf(stderr, "LinkTable_disk_open(): calloc links[i] failed!\n"); - } + linktbl->links[i] = CALLOC(1, sizeof(Link)); fread(linktbl->links[i]->linkname, sizeof(char), MAX_FILENAME_LEN, fp); fread(linktbl->links[i]->f_url, sizeof(char), MAX_PATH_LEN, fp); fread(&linktbl->links[i]->type, sizeof(LinkType), 1, fp); diff --git a/src/link.h b/src/link.h index 0d2dd7d..a47523b 100644 --- a/src/link.h +++ b/src/link.h @@ -50,6 +50,11 @@ struct Link { int cache_opened; /** \brief The pointer associated with the cache file */ Cache *cache_ptr; + /** + * \brief Subsonic Music Directory ID + * \details We use linkname to store filename + */ + int sonic_id; }; struct LinkTable { diff --git a/src/main.c b/src/main.c index e8843ec..2adcba4 100644 --- a/src/main.c +++ b/src/main.c @@ -241,7 +241,7 @@ static void print_help(char *program_name, int long_help) static void print_version() { fprintf(stderr, - "HTTPDirFS version %s\n", VERSION); + "HTTPDirFS version " VERSION "\n"); } static void print_long_help() diff --git a/src/network.c b/src/network.c index 407c0ac..0f6f8fe 100644 --- a/src/network.c +++ b/src/network.c @@ -239,7 +239,7 @@ void network_config_init() NETWORK_CONFIG.proxy_user = NULL; NETWORK_CONFIG.proxy_pass = NULL; NETWORK_CONFIG.max_conns = DEFAULT_NETWORK_MAX_CONNS; - NETWORK_CONFIG.user_agent = "HTTPDirFS"; + NETWORK_CONFIG.user_agent = DEFAULT_USER_AGENT; NETWORK_CONFIG.cache_enabled = 0; NETWORK_CONFIG.cache_dir = NULL; } diff --git a/src/network.h b/src/network.h index d6d6f4c..5be2039 100644 --- a/src/network.h +++ b/src/network.h @@ -8,6 +8,11 @@ #include "link.h" +/** + * \brief the default user agent string + */ +#define DEFAULT_USER_AGENT "HTTPDirFS-" VERSION + typedef enum { FILESTAT = 's', DATA = 'd' diff --git a/src/sonic.c b/src/sonic.c index 91815a8..4c6013b 100644 --- a/src/sonic.c +++ b/src/sonic.c @@ -1,48 +1,96 @@ -#include "util.h" +#include "sonic.h" + +#include "util.h" +#include "network.h" + -#include -#include #include #include -#define MD5_HASH_LEN 32 -#define SALT_LEN 36 +typedef struct { + char *server; + char *username; + char *password; + char *client; + char *api_version; +} SonicConfigStruct; + +static SonicConfigStruct SONIC_CONFIG; + /** - * \brief generate the salt for authentication string - * \details this effectively generates a UUID string, which we use as the salt - * \return a pointer to a 37-char array with the salt. + * \brief initalise Subsonic configuration struct */ -char *generate_salt() +void sonic_config_init(const char *server, const char *username, + const char *password) { - char *out; - out = calloc(SALT_LEN + 1, sizeof(char)); - uuid_t uu; - uuid_generate(uu); - uuid_unparse(uu, out); - return out; + 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; + /* + * API 1.13.0 is the minimum version that supports + * salt authentication scheme + */ + SONIC_CONFIG.api_version = "1.13.0"; } /** - * \brief generate the md5sum of a string - * \param[in] str a character array for the input string - * \return a pointer to a 33-char array with the salt + * \brief generate authentication string */ -char *generate_md5sum(const char *str) +static char *sonic_gen_auth_str() { - MD5_CTX c; - unsigned char md5[MD5_DIGEST_LENGTH]; - size_t len = strnlen(str, MAX_PATH_LEN); - char *out = calloc(MD5_HASH_LEN + 1, sizeof(char)); + char *salt = generate_salt(); + size_t password_len = strnlen(SONIC_CONFIG.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 + 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.api_version, SONIC_CONFIG.client); + free(salt); + free(token); + return auth_str; +} - MD5_Init(&c); - MD5_Update(&c, str, len); - MD5_Final(md5, &c); +/** + * \brief generate the first half of the request URL + */ +static char *sonic_gen_url_first_part(char *method) +{ + 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; +} - for(int i = 0; i < MD5_DIGEST_LENGTH; i++) { - sprintf(out + 2 * i, "%02x", md5[i]); +LinkTable *sonic_LinkTable_new(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); + } else { + url = sonic_gen_url_first_part("getIndexes"); } - return out; + + printf("%s\n", url); + LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); + + return NULL; } int main(int argc, char **argv) @@ -50,8 +98,7 @@ int main(int argc, char **argv) (void) argc; (void) argv; - char *salt = generate_salt(); - char *md5sum = generate_md5sum(salt); - - printf("%s\n%s\n", salt, md5sum); + sonic_config_init(argv[1], argv[2], argv[3]); + sonic_LinkTable_new(0); + sonic_LinkTable_new(3); } diff --git a/src/sonic.h b/src/sonic.h index 546a530..b92bfc2 100644 --- a/src/sonic.h +++ b/src/sonic.h @@ -1,4 +1,15 @@ #ifndef SONIC_H #define SONIC_H +/** + * \file sonic.h + * \brief Subsonic related function + */ +#include "link.h" + +/** + * \brief Initialise SubSonic configuration. + */ +void sonic_config_init(const char *server, const char *username, + const char *password); #endif diff --git a/src/util.c b/src/util.c index e65fd37..708b226 100644 --- a/src/util.c +++ b/src/util.c @@ -1,12 +1,17 @@ #include "util.h" +#include +#include + #include #include - #include #include +#include #define BT_BUF_SIZE 100 +#define MD5_HASH_LEN 32 +#define SALT_LEN 36 char *path_append(const char *path, const char *filename) { @@ -18,11 +23,7 @@ char *path_append(const char *path, const char *filename) char *str; size_t ul = strnlen(path, MAX_PATH_LEN); size_t sl = strnlen(filename, MAX_FILENAME_LEN); - str = calloc(ul + sl + needs_separator + 1, sizeof(char)); - if (!str) { - fprintf(stderr, "path_append(): calloc failure!\n"); - exit_failure(); - } + str = CALLOC(ul + sl + needs_separator + 1, sizeof(char)); strncpy(str, path, ul); if (needs_separator) { str[ul] = '/'; @@ -85,3 +86,40 @@ void erase_string(FILE *file, size_t max_len, char *s) fprintf(file, "\b"); } } + +char *generate_salt() +{ + char *out; + out = CALLOC(SALT_LEN + 1, sizeof(char)); + uuid_t uu; + uuid_generate(uu); + uuid_unparse(uu, out); + return out; +} + +char *generate_md5sum(const char *str) +{ + MD5_CTX c; + unsigned char md5[MD5_DIGEST_LENGTH]; + size_t len = strnlen(str, MAX_PATH_LEN); + char *out = CALLOC(MD5_HASH_LEN + 1, sizeof(char)); + + MD5_Init(&c); + MD5_Update(&c, str, len); + MD5_Final(md5, &c); + + for(int i = 0; i < MD5_DIGEST_LENGTH; i++) { + sprintf(out + 2 * i, "%02x", md5[i]); + } + return out; +} + +void *CALLOC(size_t nmemb, size_t size) +{ + void *ptr = calloc(nmemb, size); + if (!ptr) { + fprintf(stderr, "calloc() failed, %s!\n", strerror(errno)); + exit_failure(); + } + return ptr; +} diff --git a/src/util.h b/src/util.h index eefcb95..7a5a260 100644 --- a/src/util.h +++ b/src/util.h @@ -36,17 +36,17 @@ char *path_append(const char *path, const char *filename); int64_t round_div(int64_t a, int64_t b); /** - * \brief wrapper for pthread_mutex_lock() + * \brief wrapper for pthread_mutex_lock(), with error handling */ void PTHREAD_MUTEX_LOCK(pthread_mutex_t *x); /** - * \brief wrapper for pthread_mutex_unlock() + * \brief wrapper for pthread_mutex_unlock(), with error handling */ void PTHREAD_MUTEX_UNLOCK(pthread_mutex_t *x); /** - * \brief wrapper for exit(EXIT_FAILURE) + * \brief wrapper for exit(EXIT_FAILURE), with error handling */ void exit_failure(void); @@ -55,4 +55,23 @@ void exit_failure(void); */ void erase_string(FILE *file, size_t max_len, char *s); +/** + * \brief generate the salt for authentication string + * \details this effectively generates a UUID string, which we use as the salt + * \return a pointer to a 37-char array with the salt. + */ +char *generate_salt(); + +/** + * \brief generate the md5sum of a string + * \param[in] str a character array for the input string + * \return a pointer to a 33-char array with the salt + */ +char *generate_md5sum(const char *str); + +/** + * \brief wrapper for calloc(), with error handling + */ +void *CALLOC(size_t nmemb, size_t size); + #endif