diff --git a/src/cache.c b/src/cache.c index 254faae..844d779 100644 --- a/src/cache.c +++ b/src/cache.c @@ -37,193 +37,183 @@ static char *DATA_DIR; /** * \brief Calculate cache system directory path */ -static char *CacheSystem_calc_dir(const char *url) -{ - char *xdg_cache_home = getenv("XDG_CACHE_HOME"); - if (!xdg_cache_home) { - char *home = getenv("HOME"); - char *xdg_cache_home_default = "/.cache"; - xdg_cache_home = path_append(home, xdg_cache_home_default); - } - if (mkdir - (xdg_cache_home, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } - char *cache_dir_root = path_append(xdg_cache_home, "/httpdirfs/"); - if (mkdir - (cache_dir_root, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } +static char *CacheSystem_calc_dir(const char *url) { + char *xdg_cache_home = getenv("XDG_CACHE_HOME"); + if (!xdg_cache_home) { + char *home = getenv("HOME"); + char *xdg_cache_home_default = "/.cache"; + xdg_cache_home = path_append(home, xdg_cache_home_default); + } + if (mkdir(xdg_cache_home, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && + (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } + char *cache_dir_root = path_append(xdg_cache_home, "/httpdirfs/"); + if (mkdir(cache_dir_root, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && + (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } - char *fn = path_append(cache_dir_root, "/CACHEDIR.TAG"); - FILE *fp = fopen(fn, "w"); - if (fp) { - fprintf(fp, "Signature: 8a477f597d28d172789f06886806bc55\n\ + char *fn = path_append(cache_dir_root, "/CACHEDIR.TAG"); + FILE *fp = fopen(fn, "w"); + if (fp) { + fprintf(fp, "Signature: 8a477f597d28d172789f06886806bc55\n\ # This file is a cache directory tag created by httpdirfs.\n\ # For information about cache directory tags, see:\n\ # http://www.brynosaurus.com/cachedir/\n"); - } else { - lprintf(fatal, "fopen(%s): %s", fn, strerror(errno)); - } - if (ferror(fp)) { - lprintf(fatal, "fwrite(): encountered error!\n"); - } - if (fclose(fp)) { - lprintf(fatal, "fclose(%s): %s\n", fn, strerror(errno)); - } - CURL *c = curl_easy_init(); - char *escaped_url = curl_easy_escape(c, url, 0); - char *full_path = path_append(cache_dir_root, escaped_url); - if (mkdir(full_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } - FREE(fn); - FREE(cache_dir_root); - curl_free(escaped_url); - curl_easy_cleanup(c); - return full_path; + } else { + lprintf(fatal, "fopen(%s): %s", fn, strerror(errno)); + } + if (ferror(fp)) { + lprintf(fatal, "fwrite(): encountered error!\n"); + } + if (fclose(fp)) { + lprintf(fatal, "fclose(%s): %s\n", fn, strerror(errno)); + } + CURL *c = curl_easy_init(); + char *escaped_url = curl_easy_escape(c, url, 0); + char *full_path = path_append(cache_dir_root, escaped_url); + if (mkdir(full_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && + (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } + FREE(fn); + FREE(cache_dir_root); + curl_free(escaped_url); + curl_easy_cleanup(c); + return full_path; } -void CacheSystem_init(const char *path, int url_supplied) -{ - lprintf(cache_lock_debug, - "thread %x: initialise cf_lock;\n", pthread_self()); - if (pthread_mutex_init(&cf_lock, NULL)) { - lprintf(fatal, "cf_lock initialisation failed!\n"); - } +void CacheSystem_init(const char *path, int url_supplied) { + lprintf(cache_lock_debug, "thread %x: initialise cf_lock;\n", pthread_self()); + if (pthread_mutex_init(&cf_lock, NULL)) { + lprintf(fatal, "cf_lock initialisation failed!\n"); + } - if (url_supplied) { - path = CacheSystem_calc_dir(path); - } + if (url_supplied) { + path = CacheSystem_calc_dir(path); + } - lprintf(debug, "%s\n", path); + lprintf(debug, "%s\n", path); - META_DIR = path_append(path, "meta/"); - DATA_DIR = path_append(path, "data/"); + META_DIR = path_append(path, "meta/"); + DATA_DIR = path_append(path, "data/"); + /* + * Check if directories exist, if not, create them + */ + if (mkdir(META_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && + (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } + + if (mkdir(DATA_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && + (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } + + if (CONFIG.mode == SONIC) { + char *sonic_path; /* - * Check if directories exist, if not, create them + * Create "rest" sub-directory for META_DIR */ - if (mkdir(META_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + sonic_path = path_append(META_DIR, "rest/"); + if (mkdir(sonic_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && + (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } + FREE(sonic_path); - if (mkdir(DATA_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + /* + * Create "rest" sub-directory for DATA_DIR + */ + sonic_path = path_append(DATA_DIR, "rest/"); + if (mkdir(sonic_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) && + (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); } + FREE(sonic_path); + } - if (CONFIG.mode == SONIC) { - char *sonic_path; - /* - * Create "rest" sub-directory for META_DIR - */ - sonic_path = path_append(META_DIR, "rest/"); - if (mkdir - (sonic_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } - FREE(sonic_path); - - /* - * Create "rest" sub-directory for DATA_DIR - */ - sonic_path = path_append(DATA_DIR, "rest/"); - if (mkdir - (sonic_path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) - && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } - FREE(sonic_path); - } - - CACHE_SYSTEM_INIT = 1; + CACHE_SYSTEM_INIT = 1; } /** * \brief read a metadata file * \return 0 on success, errno on error. */ -static int Meta_read(Cache *cf) -{ - FILE *fp = cf->mfp; - rewind(fp); +static int Meta_read(Cache *cf) { + FILE *fp = cf->mfp; + rewind(fp); - int nmemb = 0; - - if (!fp) { - /* - * The metadata file does not exist - */ - lprintf(error, "fopen(): %s\n", strerror(errno)); - return EIO; - } - - if (sizeof(long) != fread(&cf->time, sizeof(long), 1, fp) || - sizeof(off_t) != fread(&cf->content_length, sizeof(off_t), 1, fp) || - sizeof(int) != fread(&cf->blksz, sizeof(int), 1, fp) || - sizeof(long) != fread(&cf->segbc, sizeof(long), 1, fp) || - ferror(fp)) { - lprintf(error, "error reading core metadata!\n"); - return EIO; - } - - /* These things really should not be zero!!! */ - if (!cf->content_length || !cf->blksz || !cf->segbc) { - lprintf(error, - "corruption: content_length: %ld, blksz: %d, segbc: %ld\n", - cf->content_length, cf->blksz, cf->segbc); - return EBADMSG; - } - - if (cf->blksz != CONFIG.data_blksz) { - lprintf(warning, "Warning: cf->blksz != CONFIG.data_blksz\n"); - } - - if (cf->segbc > CONFIG.max_segbc) { - lprintf(error, "Error: segbc: %ld\n", cf->segbc); - return EFBIG; - } + int nmemb = 0; + if (!fp) { /* - * Allocate memory for all segments, and read them in + * The metadata file does not exist */ - cf->seg = CALLOC(cf->segbc, sizeof(Seg)); - nmemb = fread(cf->seg, sizeof(Seg), cf->segbc, fp); + lprintf(error, "fopen(): %s\n", strerror(errno)); + return EIO; + } + if (sizeof(long) != fread(&cf->time, sizeof(long), 1, fp) || + sizeof(off_t) != fread(&cf->content_length, sizeof(off_t), 1, fp) || + sizeof(int) != fread(&cf->blksz, sizeof(int), 1, fp) || + sizeof(long) != fread(&cf->segbc, sizeof(long), 1, fp) || ferror(fp)) { + lprintf(error, "error reading core metadata!\n"); + return EIO; + } + + /* These things really should not be zero!!! */ + if (!cf->content_length || !cf->blksz || !cf->segbc) { + lprintf(error, "corruption: content_length: %ld, blksz: %d, segbc: %ld\n", + cf->content_length, cf->blksz, cf->segbc); + return EBADMSG; + } + + if (cf->blksz != CONFIG.data_blksz) { + lprintf(warning, "Warning: cf->blksz != CONFIG.data_blksz\n"); + } + + if (cf->segbc > CONFIG.max_segbc) { + lprintf(error, "Error: segbc: %ld\n", cf->segbc); + return EFBIG; + } + + /* + * Allocate memory for all segments, and read them in + */ + cf->seg = CALLOC(cf->segbc, sizeof(Seg)); + nmemb = fread(cf->seg, sizeof(Seg), cf->segbc, fp); + + /* + * We shouldn't have gone past the end of the file + */ + if (feof(fp)) { /* - * We shouldn't have gone past the end of the file + * reached EOF */ - if (feof(fp)) { - /* - * reached EOF - */ - lprintf(error, "attempted to read past the end of the \ + lprintf(error, "attempted to read past the end of the \ file!\n"); - return EBADMSG; - } + return EBADMSG; + } - /* - * Error checking for fread - */ - if (ferror(fp)) { - lprintf(error, "error reading bitmap!\n"); - return EIO; - } + /* + * Error checking for fread + */ + if (ferror(fp)) { + lprintf(error, "error reading bitmap!\n"); + return EIO; + } - /* - * Check for inconsistent metadata file - */ - if (nmemb != cf->segbc) { - lprintf(error, "corrupted metadata!\n"); - return EBADMSG; - } + /* + * Check for inconsistent metadata file + */ + if (nmemb != cf->segbc) { + lprintf(error, "corrupted metadata!\n"); + return EBADMSG; + } - return 0; + return 0; } /** @@ -232,42 +222,41 @@ file!\n"); * - -1 on error, * - 0 on success */ -static int Meta_write(Cache *cf) -{ - FILE *fp = cf->mfp; - rewind(fp); - - if (!fp) { - /* - * Cannot create the metadata file - */ - lprintf(error, "fopen(): %s\n", strerror(errno)); - return -1; - } +static int Meta_write(Cache *cf) { + FILE *fp = cf->mfp; + rewind(fp); + if (!fp) { /* - * These things really should not be zero!!! + * Cannot create the metadata file */ - if (!cf->content_length || !cf->blksz || !cf->segbc) { - lprintf(error, "content_length: %ld, blksz: %d, segbc: %ld\n", - cf->content_length, cf->blksz, cf->segbc); - } + lprintf(error, "fopen(): %s\n", strerror(errno)); + return -1; + } - fwrite(&cf->time, sizeof(long), 1, fp); - fwrite(&cf->content_length, sizeof(off_t), 1, fp); - fwrite(&cf->blksz, sizeof(int), 1, fp); - fwrite(&cf->segbc, sizeof(long), 1, fp); - fwrite(cf->seg, sizeof(Seg), cf->segbc, fp); + /* + * These things really should not be zero!!! + */ + if (!cf->content_length || !cf->blksz || !cf->segbc) { + lprintf(error, "content_length: %ld, blksz: %d, segbc: %ld\n", + cf->content_length, cf->blksz, cf->segbc); + } - /* - * Error checking for fwrite - */ - if (ferror(fp)) { - lprintf(error, "fwrite(): encountered error!\n"); - return -1; - } + fwrite(&cf->time, sizeof(long), 1, fp); + fwrite(&cf->content_length, sizeof(off_t), 1, fp); + fwrite(&cf->blksz, sizeof(int), 1, fp); + fwrite(&cf->segbc, sizeof(long), 1, fp); + fwrite(cf->seg, sizeof(Seg), cf->segbc, fp); - return 0; + /* + * Error checking for fwrite + */ + if (ferror(fp)) { + lprintf(error, "fwrite(): encountered error!\n"); + return -1; + } + + return 0; } /** @@ -275,41 +264,39 @@ static int Meta_write(Cache *cf) * \details We use sparse creation here * \return exit on failure */ -static void Data_create(Cache *cf) -{ - int fd; - int mode; +static void Data_create(Cache *cf) { + int fd; + int mode; - mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - char *datafn = path_append(DATA_DIR, cf->path); - fd = open(datafn, O_WRONLY | O_CREAT, mode); - FREE(datafn); - if (fd == -1) { - lprintf(fatal, "open(): %s\n", strerror(errno)); - } - if (ftruncate(fd, cf->content_length)) { - lprintf(warning, "ftruncate(): %s\n", strerror(errno)); - } - if (close(fd)) { - lprintf(fatal, "close:(): %s\n", strerror(errno)); - } + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + char *datafn = path_append(DATA_DIR, cf->path); + fd = open(datafn, O_WRONLY | O_CREAT, mode); + FREE(datafn); + if (fd == -1) { + lprintf(fatal, "open(): %s\n", strerror(errno)); + } + if (ftruncate(fd, cf->content_length)) { + lprintf(warning, "ftruncate(): %s\n", strerror(errno)); + } + if (close(fd)) { + lprintf(fatal, "close:(): %s\n", strerror(errno)); + } } /** * \brief obtain the data file size * \return file size on success, -1 on error */ -static long Data_size(const char *fn) -{ - char *datafn = path_append(DATA_DIR, fn); - struct stat st; - int s = stat(datafn, &st); - FREE(datafn); - if (!s) { - return st.st_size; - } - lprintf(error, "stat(): %s\n", strerror(errno)); - return -1; +static long Data_size(const char *fn) { + char *datafn = path_append(DATA_DIR, fn); + struct stat st; + int s = stat(datafn, &st); + FREE(datafn); + if (!s) { + return st.st_size; + } + lprintf(error, "stat(): %s\n", strerror(errno)); + return -1; } /** @@ -322,65 +309,62 @@ static long Data_size(const char *fn) * - negative values on error, * - otherwise, the number of bytes read. */ -static long Data_read(Cache *cf, uint8_t *buf, off_t len, off_t offset) -{ - if (len == 0) { - lprintf(error, "requested to read 0 byte!\n"); - return -EINVAL; - } +static long Data_read(Cache *cf, uint8_t *buf, off_t len, off_t offset) { + if (len == 0) { + lprintf(error, "requested to read 0 byte!\n"); + return -EINVAL; + } - lprintf(cache_lock_debug, - "thread %x: locking seek_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&cf->seek_lock); + lprintf(cache_lock_debug, "thread %x: locking seek_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&cf->seek_lock); - long byte_read = 0; + long byte_read = 0; + /* + * Seek to the right location + */ + if (fseeko(cf->dfp, offset, SEEK_SET)) { /* - * Seek to the right location + * fseeko failed */ - if (fseeko(cf->dfp, offset, SEEK_SET)) { - /* - * fseeko failed - */ - lprintf(error, "fseeko(): %s\n", strerror(errno)); - byte_read = -EIO; - goto end; - } + lprintf(error, "fseeko(): %s\n", strerror(errno)); + byte_read = -EIO; + goto end; + } - /* - * Calculate how much to read - */ - if (offset + len > cf->content_length) { - len -= offset + len - cf->content_length; - if (len < 0) { - goto end; - } + /* + * Calculate how much to read + */ + if (offset + len > cf->content_length) { + len -= offset + len - cf->content_length; + if (len < 0) { + goto end; } + } - byte_read = fread(buf, sizeof(uint8_t), len, cf->dfp); - if (byte_read != len) { - lprintf(debug, - "fread(): requested %ld, returned %ld!\n", len, byte_read); - if (feof(cf->dfp)) { - /* - * reached EOF - */ - lprintf(error, "fread(): reached the end of the file!\n"); - } - if (ferror(cf->dfp)) { - /* - * filesystem error - */ - lprintf(error, "fread(): encountered error!\n"); - } + byte_read = fread(buf, sizeof(uint8_t), len, cf->dfp); + if (byte_read != len) { + lprintf(debug, "fread(): requested %ld, returned %ld!\n", len, byte_read); + if (feof(cf->dfp)) { + /* + * reached EOF + */ + lprintf(error, "fread(): reached the end of the file!\n"); } + if (ferror(cf->dfp)) { + /* + * filesystem error + */ + lprintf(error, "fread(): encountered error!\n"); + } + } end: - lprintf(cache_lock_debug, - "thread %x: unlocking seek_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->seek_lock); - return byte_read; + lprintf(cache_lock_debug, "thread %x: unlocking seek_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->seek_lock); + return byte_read; } /** @@ -393,139 +377,131 @@ end: * - -1 when the data file does not exist * - otherwise, the number of bytes written. */ -static long Data_write(Cache *cf, const uint8_t *buf, off_t len, - off_t offset) -{ - if (len == 0) { - /* - * We should permit empty files - */ - return 0; - } +static long Data_write(Cache *cf, const uint8_t *buf, off_t len, off_t offset) { + if (len == 0) { + /* + * We should permit empty files + */ + return 0; + } - lprintf(cache_lock_debug, - "thread %x: locking seek_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&cf->seek_lock); + lprintf(cache_lock_debug, "thread %x: locking seek_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&cf->seek_lock); - long byte_written = 0; + long byte_written = 0; - if (fseeko(cf->dfp, offset, SEEK_SET)) { - /* - * fseeko failed - */ - lprintf(error, "fseeko(): %s\n", strerror(errno)); - byte_written = -EIO; - goto end; - } + if (fseeko(cf->dfp, offset, SEEK_SET)) { + /* + * fseeko failed + */ + lprintf(error, "fseeko(): %s\n", strerror(errno)); + byte_written = -EIO; + goto end; + } - byte_written = fwrite(buf, sizeof(uint8_t), len, cf->dfp); + byte_written = fwrite(buf, sizeof(uint8_t), len, cf->dfp); - if (byte_written != len) { - lprintf(error, - "fwrite(): requested %ld, returned %ld!\n", - len, byte_written); - } + if (byte_written != len) { + lprintf(error, "fwrite(): requested %ld, returned %ld!\n", len, + byte_written); + } - if (ferror(cf->dfp)) { - /* - * filesystem error - */ - lprintf(error, "fwrite(): encountered error!\n"); - } + if (ferror(cf->dfp)) { + /* + * filesystem error + */ + lprintf(error, "fwrite(): encountered error!\n"); + } end: - lprintf(cache_lock_debug, - "thread %x: unlocking seek_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->seek_lock); - return byte_written; + lprintf(cache_lock_debug, "thread %x: unlocking seek_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->seek_lock); + return byte_written; } -int CacheDir_create(const char *dirn) -{ - char *metadirn = path_append(META_DIR, dirn); - char *datadirn = path_append(DATA_DIR, dirn); - int i; +int CacheDir_create(const char *dirn) { + char *metadirn = path_append(META_DIR, dirn); + char *datadirn = path_append(DATA_DIR, dirn); + int i; - i = -mkdir(metadirn, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); - if (i && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } + i = -mkdir(metadirn, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (i && (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } - i |= -mkdir(datadirn, - S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) << 1; - if (i && (errno != EEXIST)) { - lprintf(fatal, "mkdir(): %s\n", strerror(errno)); - } - FREE(datadirn); - FREE(metadirn); - return -i; + i |= -mkdir(datadirn, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) << 1; + if (i && (errno != EEXIST)) { + lprintf(fatal, "mkdir(): %s\n", strerror(errno)); + } + FREE(datadirn); + FREE(metadirn); + return -i; } /** * \brief Allocate a new cache data structure */ -static Cache *Cache_alloc() -{ - Cache *cf = CALLOC(1, sizeof(Cache)); +static Cache *Cache_alloc() { + Cache *cf = CALLOC(1, sizeof(Cache)); - if (pthread_mutex_init(&cf->seek_lock, NULL)) { - lprintf(fatal, "seek_lock initialisation failed!\n"); - } + if (pthread_mutex_init(&cf->seek_lock, NULL)) { + lprintf(fatal, "seek_lock initialisation failed!\n"); + } - if (pthread_mutex_init(&cf->w_lock, NULL)) { - lprintf(fatal, "w_lock initialisation failed!\n"); - } + if (pthread_mutex_init(&cf->w_lock, NULL)) { + lprintf(fatal, "w_lock initialisation failed!\n"); + } - if (pthread_mutexattr_init(&cf->bgt_lock_attr)) { - lprintf(fatal, "bgt_lock_attr initialisation failed!\n"); - } + if (pthread_mutexattr_init(&cf->bgt_lock_attr)) { + lprintf(fatal, "bgt_lock_attr initialisation failed!\n"); + } - if (pthread_mutexattr_setpshared(&cf->bgt_lock_attr, - PTHREAD_PROCESS_SHARED)) { - lprintf(fatal, "could not set bgt_lock_attr!\n"); - } + if (pthread_mutexattr_setpshared(&cf->bgt_lock_attr, + PTHREAD_PROCESS_SHARED)) { + lprintf(fatal, "could not set bgt_lock_attr!\n"); + } - if (pthread_mutex_init(&cf->bgt_lock, &cf->bgt_lock_attr)) { - lprintf(fatal, "bgt_lock initialisation failed!\n"); - } + if (pthread_mutex_init(&cf->bgt_lock, &cf->bgt_lock_attr)) { + lprintf(fatal, "bgt_lock initialisation failed!\n"); + } - return cf; + return cf; } /** * \brief free a cache data structure */ -static void Cache_free(Cache *cf) -{ - if (pthread_mutex_destroy(&cf->seek_lock)) { - lprintf(fatal, "could not destroy seek_lock!\n"); - } +static void Cache_free(Cache *cf) { + if (pthread_mutex_destroy(&cf->seek_lock)) { + lprintf(fatal, "could not destroy seek_lock!\n"); + } - if (pthread_mutex_destroy(&cf->w_lock)) { - lprintf(fatal, "could not destroy w_lock!\n"); - } + if (pthread_mutex_destroy(&cf->w_lock)) { + lprintf(fatal, "could not destroy w_lock!\n"); + } - if (pthread_mutex_destroy(&cf->bgt_lock)) { - lprintf(fatal, "could not destroy bgt_lock!\n"); - } + if (pthread_mutex_destroy(&cf->bgt_lock)) { + lprintf(fatal, "could not destroy bgt_lock!\n"); + } - if (pthread_mutexattr_destroy(&cf->bgt_lock_attr)) { - lprintf(fatal, "could not destroy bgt_lock_attr!\n"); - } + if (pthread_mutexattr_destroy(&cf->bgt_lock_attr)) { + lprintf(fatal, "could not destroy bgt_lock_attr!\n"); + } - if (cf->path) { - FREE(cf->path); - } + if (cf->path) { + FREE(cf->path); + } - if (cf->seg) { - FREE(cf->seg); - } + if (cf->seg) { + FREE(cf->seg); + } - if (cf->fs_path) { - FREE(cf->fs_path); - } + if (cf->fs_path) { + FREE(cf->fs_path); + } - FREE(cf); + FREE(cf); } /** @@ -538,61 +514,59 @@ static void Cache_free(Cache *cf) * - 0, if both metadata and cache file exist * - -1, otherwise */ -static int Cache_exist(const char *fn) -{ - char *metafn = path_append(META_DIR, fn); - char *datafn = path_append(DATA_DIR, fn); - /* - * access() returns 0 on success - */ - int no_meta = access(metafn, F_OK); - int no_data = access(datafn, F_OK); +static int Cache_exist(const char *fn) { + char *metafn = path_append(META_DIR, fn); + char *datafn = path_append(DATA_DIR, fn); + /* + * access() returns 0 on success + */ + int no_meta = access(metafn, F_OK); + int no_data = access(datafn, F_OK); - if (no_meta ^ no_data) { - if (no_meta) { - lprintf(warning, "Cache file partially missing.\n"); - if (unlink(datafn)) { - lprintf(error, "unlink(): %s\n", strerror(errno)); - } - } - if (no_data) { - if (unlink(metafn)) { - lprintf(error, "unlink(): %s\n", strerror(errno)); - } - } + if (no_meta ^ no_data) { + if (no_meta) { + lprintf(warning, "Cache file partially missing.\n"); + if (unlink(datafn)) { + lprintf(error, "unlink(): %s\n", strerror(errno)); + } } + if (no_data) { + if (unlink(metafn)) { + lprintf(error, "unlink(): %s\n", strerror(errno)); + } + } + } - FREE(metafn); - FREE(datafn); + FREE(metafn); + FREE(datafn); - return no_meta | no_data; + return no_meta | no_data; } /** * \brief delete a cache file set */ -void Cache_delete(const char *fn) -{ - if (CONFIG.mode == SONIC) { - Link *link = path_to_Link(fn); - fn = link->sonic.id; - } +void Cache_delete(const char *fn) { + if (CONFIG.mode == SONIC) { + Link *link = path_to_Link(fn); + fn = link->sonic.id; + } - char *metafn = path_append(META_DIR, fn); - char *datafn = path_append(DATA_DIR, fn); - if (!access(metafn, F_OK)) { - if (unlink(metafn)) { - lprintf(error, "unlink(): %s\n", strerror(errno)); - } + char *metafn = path_append(META_DIR, fn); + char *datafn = path_append(DATA_DIR, fn); + if (!access(metafn, F_OK)) { + if (unlink(metafn)) { + lprintf(error, "unlink(): %s\n", strerror(errno)); } + } - if (!access(datafn, F_OK)) { - if (unlink(datafn)) { - lprintf(error, "unlink(): %s\n", strerror(errno)); - } + if (!access(datafn, F_OK)) { + if (unlink(datafn)) { + lprintf(error, "unlink(): %s\n", strerror(errno)); } - FREE(metafn); - FREE(datafn); + } + FREE(metafn); + FREE(datafn); } /** @@ -601,20 +575,19 @@ void Cache_delete(const char *fn) * - 0 on success * - -1 on failure, with appropriate errno set. */ -static int Data_open(Cache *cf) -{ - char *datafn = path_append(DATA_DIR, cf->path); - cf->dfp = fopen(datafn, "r+"); - if (!cf->dfp) { - /* - * Failed to open the data file - */ - lprintf(error, "fopen(%s): %s\n", datafn, strerror(errno)); - FREE(datafn); - return -1; - } +static int Data_open(Cache *cf) { + char *datafn = path_append(DATA_DIR, cf->path); + cf->dfp = fopen(datafn, "r+"); + if (!cf->dfp) { + /* + * Failed to open the data file + */ + lprintf(error, "fopen(%s): %s\n", datafn, strerror(errno)); FREE(datafn); - return 0; + return -1; + } + FREE(datafn); + return 0; } /** @@ -623,293 +596,278 @@ static int Data_open(Cache *cf) * - 0 on success * - -1 on failure, with appropriate errno set. */ -static int Meta_open(Cache *cf) -{ - char *metafn = path_append(META_DIR, cf->path); - cf->mfp = fopen(metafn, "r+"); - if (!cf->mfp) { - /* - * Failed to open the data file - */ - lprintf(error, "fopen(%s): %s\n", metafn, strerror(errno)); - FREE(metafn); - return -1; - } +static int Meta_open(Cache *cf) { + char *metafn = path_append(META_DIR, cf->path); + cf->mfp = fopen(metafn, "r+"); + if (!cf->mfp) { + /* + * Failed to open the data file + */ + lprintf(error, "fopen(%s): %s\n", metafn, strerror(errno)); FREE(metafn); - return 0; + return -1; + } + FREE(metafn); + return 0; } /** * \brief Create a metafile * \return exit on error */ -static void Meta_create(Cache *cf) -{ - char *metafn = path_append(META_DIR, cf->path); - cf->mfp = fopen(metafn, "w"); - if (!cf->mfp) { - /* - * Failed to open the data file - */ - lprintf(fatal, "fopen(%s): %s\n", metafn, strerror(errno)); - } - if (fclose(cf->mfp)) { - lprintf(error, - "cannot close metadata after creation: %s.\n", - strerror(errno)); - } - FREE(metafn); +static void Meta_create(Cache *cf) { + char *metafn = path_append(META_DIR, cf->path); + cf->mfp = fopen(metafn, "w"); + if (!cf->mfp) { + /* + * Failed to open the data file + */ + lprintf(fatal, "fopen(%s): %s\n", metafn, strerror(errno)); + } + if (fclose(cf->mfp)) { + lprintf(error, "cannot close metadata after creation: %s.\n", + strerror(errno)); + } + FREE(metafn); } -int Cache_create(const char *path) -{ - Link *this_link = path_to_Link(path); +int Cache_create(const char *path) { + Link *this_link = path_to_Link(path); - char *fn = "__UNINITIALISED__"; - if (CONFIG.mode == NORMAL) { - fn = curl_easy_unescape(NULL, - this_link->f_url + ROOT_LINK_OFFSET, 0, - NULL); - } else if (CONFIG.mode == SINGLE) { - fn = curl_easy_unescape(NULL, this_link->linkname, 0, NULL); - } else if (CONFIG.mode == SONIC) { - fn = this_link->sonic.id; - } else { - lprintf(fatal, "Invalid CONFIG.mode\n"); + char *fn = "__UNINITIALISED__"; + if (CONFIG.mode == NORMAL) { + fn = curl_easy_unescape(NULL, this_link->f_url + ROOT_LINK_OFFSET, 0, NULL); + } else if (CONFIG.mode == SINGLE) { + fn = curl_easy_unescape(NULL, this_link->linkname, 0, NULL); + } else if (CONFIG.mode == SONIC) { + fn = this_link->sonic.id; + } else { + lprintf(fatal, "Invalid CONFIG.mode\n"); + } + lprintf(debug, "Creating cache files for %s.\n", fn); + + Cache *cf = Cache_alloc(); + cf->path = strndup(fn, MAX_PATH_LEN); + cf->time = this_link->time; + cf->content_length = this_link->content_length; + cf->blksz = CONFIG.data_blksz; + cf->segbc = (cf->content_length / cf->blksz) + 1; + cf->seg = CALLOC(cf->segbc, sizeof(Seg)); + + Meta_create(cf); + + if (Meta_open(cf)) { + Cache_free(cf); + lprintf(error, "cannot open metadata file, %s.\n", fn); + } + + if (Meta_write(cf)) { + lprintf(error, "Meta_write() failed!\n"); + } + + if (fclose(cf->mfp)) { + lprintf(error, "cannot close metadata after write, %s.\n", strerror(errno)); + } + + Data_create(cf); + + Cache_free(cf); + + int res = Cache_exist(fn); + + if (CONFIG.mode == NORMAL) { + curl_free(fn); + } + + return res; +} + +Cache *Cache_open(const char *fn) { + /* + * Obtain the link structure memory pointer + */ + Link *link = path_to_Link(fn); + if (!link) { + /* + * There is no associated link to the path + */ + return NULL; + } + + lprintf(cache_lock_debug, "thread %x: locking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&cf_lock); + + if (link->cache_ptr) { + link->cache_ptr->cache_opened++; + + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return link->cache_ptr; + } + + /* + * Check if both metadata and data file exist + */ + if (CONFIG.mode == NORMAL || CONFIG.mode == SINGLE) { + if (Cache_exist(fn)) { + + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; } - lprintf(debug, "Creating cache files for %s.\n", fn); + } else if (CONFIG.mode == SONIC) { + if (Cache_exist(link->sonic.id)) { - Cache *cf = Cache_alloc(); - cf->path = strndup(fn, MAX_PATH_LEN); - cf->time = this_link->time; - cf->content_length = this_link->content_length; - cf->blksz = CONFIG.data_blksz; - cf->segbc = (cf->content_length / cf->blksz) + 1; - cf->seg = CALLOC(cf->segbc, sizeof(Seg)); - - Meta_create(cf); - - if (Meta_open(cf)) { - Cache_free(cf); - lprintf(error, "cannot open metadata file, %s.\n", fn); + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; } + } else { + lprintf(fatal, "Invalid CONFIG.mode\n"); + } - if (Meta_write(cf)) { - lprintf(error, "Meta_write() failed!\n"); - } + /* + * Create the cache in-memory data structure + */ + Cache *cf = Cache_alloc(); - if (fclose(cf->mfp)) { - lprintf(error, - "cannot close metadata after write, %s.\n", - strerror(errno)); - } + /* + * Fill in the fs_path + */ + cf->fs_path = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); + strncpy(cf->fs_path, fn, MAX_PATH_LEN); - Data_create(cf); + /* + * Set the path for the local cache file, if we are in sonic mode + */ + if (CONFIG.mode == SONIC) { + fn = link->sonic.id; + } + cf->path = strndup(fn, MAX_PATH_LEN); + + /* + * Associate the cache structure with a link + */ + cf->link = link; + + if (Meta_open(cf)) { + Cache_free(cf); + lprintf(error, "cannot open metadata file %s.\n", fn); + + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + + /* + * Corrupt metadata + */ + if (Meta_read(cf)) { + Cache_free(cf); + lprintf(error, "metadata error: %s.\n", fn); + + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + + /* + * Inconsistency between metadata and data file, note that on disk file + * size might be bigger than content_length, due to on-disk filesystem + * allocation policy. + */ + if (cf->content_length > Data_size(fn)) { + lprintf(error, "metadata inconsistency %s, \ +cf->content_length: %ld, Data_size(fn): %ld.\n", + fn, cf->content_length, Data_size(fn)); Cache_free(cf); - int res = Cache_exist(fn); + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } - if (CONFIG.mode == NORMAL) { - curl_free(fn); - } + /* + * Check if the cache files are not outdated + */ + if (cf->time != cf->link->time) { + lprintf(warning, "outdated cache file: %s.\n", fn); + Cache_free(cf); - return res; + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + + if (Data_open(cf)) { + Cache_free(cf); + lprintf(error, "cannot open data file %s.\n", fn); + + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return NULL; + } + + cf->cache_opened = 1; + /* + * Yup, we just created a circular loop. ;) + */ + cf->link->cache_ptr = cf; + + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return cf; } -Cache *Cache_open(const char *fn) -{ - /* - * Obtain the link structure memory pointer - */ - Link *link = path_to_Link(fn); - if (!link) { - /* - * There is no associated link to the path - */ - return NULL; - } +void Cache_close(Cache *cf) { + lprintf(cache_lock_debug, "thread %x: locking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&cf_lock); - lprintf(cache_lock_debug, - "thread %x: locking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&cf_lock); + cf->cache_opened--; - if (link->cache_ptr) { - link->cache_ptr->cache_opened++; + if (cf->cache_opened > 0) { - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return link->cache_ptr; - } - - /* - * Check if both metadata and data file exist - */ - if (CONFIG.mode == NORMAL || CONFIG.mode == SINGLE) { - if (Cache_exist(fn)) { - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - } else if (CONFIG.mode == SONIC) { - if (Cache_exist(link->sonic.id)) { - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - } else { - lprintf(fatal, "Invalid CONFIG.mode\n"); - } - - /* - * Create the cache in-memory data structure - */ - Cache *cf = Cache_alloc(); - - /* - * Fill in the fs_path - */ - cf->fs_path = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); - strncpy(cf->fs_path, fn, MAX_PATH_LEN); - - /* - * Set the path for the local cache file, if we are in sonic mode - */ - if (CONFIG.mode == SONIC) { - fn = link->sonic.id; - } - - cf->path = strndup(fn, MAX_PATH_LEN); - - /* - * Associate the cache structure with a link - */ - cf->link = link; - - if (Meta_open(cf)) { - Cache_free(cf); - lprintf(error, "cannot open metadata file %s.\n", fn); - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - - /* - * Corrupt metadata - */ - if (Meta_read(cf)) { - Cache_free(cf); - lprintf(error, "metadata error: %s.\n", fn); - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - - /* - * Inconsistency between metadata and data file, note that on disk file - * size might be bigger than content_length, due to on-disk filesystem - * allocation policy. - */ - if (cf->content_length > Data_size(fn)) { - lprintf(error, "metadata inconsistency %s, \ -cf->content_length: %ld, Data_size(fn): %ld.\n", fn, cf->content_length, - Data_size(fn)); - Cache_free(cf); - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - - /* - * Check if the cache files are not outdated - */ - if (cf->time != cf->link->time) { - lprintf(warning, "outdated cache file: %s.\n", fn); - Cache_free(cf); - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - - if (Data_open(cf)) { - Cache_free(cf); - lprintf(error, "cannot open data file %s.\n", fn); - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return NULL; - } - - cf->cache_opened = 1; - /* - * Yup, we just created a circular loop. ;) - */ - cf->link->cache_ptr = cf; - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", + pthread_self()); PTHREAD_MUTEX_UNLOCK(&cf_lock); - return cf; -} + return; + } -void Cache_close(Cache *cf) -{ - lprintf(cache_lock_debug, - "thread %x: locking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&cf_lock); + if (Meta_write(cf)) { + lprintf(error, "Meta_write() error."); + } - cf->cache_opened--; + if (fclose(cf->mfp)) { + lprintf(error, "cannot close metadata: %s.\n", strerror(errno)); + } - if (cf->cache_opened > 0) { + if (fclose(cf->dfp)) { + lprintf(error, "cannot close data file %s.\n", strerror(errno)); + } - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return; - } + cf->link->cache_ptr = NULL; - if (Meta_write(cf)) { - lprintf(error, "Meta_write() error."); - } - - if (fclose(cf->mfp)) { - lprintf(error, "cannot close metadata: %s.\n", strerror(errno)); - } - - if (fclose(cf->dfp)) { - lprintf(error, "cannot close data file %s.\n", strerror(errno)); - } - - cf->link->cache_ptr = NULL; - - lprintf(cache_lock_debug, - "thread %x: unlocking cf_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf_lock); - return Cache_free(cf); + lprintf(cache_lock_debug, "thread %x: unlocking cf_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf_lock); + return Cache_free(cf); } /** * \brief Check if a segment exists. * \return 1 if the segment exists */ -static int Seg_exist(Cache *cf, off_t offset) -{ - off_t byte = offset / cf->blksz; - return cf->seg[byte]; +static int Seg_exist(Cache *cf, off_t offset) { + off_t byte = offset / cf->blksz; + return cf->seg[byte]; } /** @@ -919,10 +877,9 @@ static int Seg_exist(Cache *cf, off_t offset) * \param[in] i 1 for exist, 0 for doesn't exist * \note Call this after downloading a segment. */ -static void Seg_set(Cache *cf, off_t offset, int i) -{ - off_t byte = offset / cf->blksz; - cf->seg[byte] = i; +static void Seg_set(Cache *cf, off_t offset, int i) { + off_t byte = offset / cf->blksz; + cf->seg[byte] = i; } /** @@ -931,145 +888,136 @@ static void Seg_set(Cache *cf, off_t offset, int i) * segment, we can spawn a pthread using this function to download the next * segment. */ -static void *Cache_bgdl(void *arg) -{ - Cache *cf = (Cache *) arg; +static void *Cache_bgdl(void *arg) { + Cache *cf = (Cache *)arg; - lprintf(cache_lock_debug, "thread %x: locking w_lock;\n", - pthread_self()); + lprintf(cache_lock_debug, "thread %x: locking w_lock;\n", pthread_self()); + PTHREAD_MUTEX_LOCK(&cf->w_lock); + + uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); + lprintf(debug, "thread %x spawned.\n ", pthread_self()); + long recv = + Link_download(cf->link, (char *)recv_buf, cf->blksz, cf->next_dl_offset); + if (recv < 0) { + lprintf(error, "thread %x received %ld bytes, \ +which doesn't make sense\n", + pthread_self(), recv); + } + + if ((recv == cf->blksz) || + (cf->next_dl_offset == (cf->content_length / cf->blksz * cf->blksz))) { + Data_write(cf, recv_buf, recv, cf->next_dl_offset); + Seg_set(cf, cf->next_dl_offset, 1); + } else { + lprintf(error, "received %ld rather than %ld, possible network \ +error.\n", + recv, cf->blksz); + } + + FREE(recv_buf); + + lprintf(cache_lock_debug, "thread %x: unlocking bgt_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->bgt_lock); + + lprintf(cache_lock_debug, "thread %x: unlocking w_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->w_lock); + + if (pthread_detach(pthread_self())) { + lprintf(error, "%s\n", strerror(errno)); + }; + pthread_exit(NULL); +} + +long Cache_read(Cache *cf, char *const output_buf, const off_t len, + const off_t offset_start) { + long send; + + /* + * The offset of the segment to be downloaded + */ + off_t dl_offset = (offset_start + len) / cf->blksz * cf->blksz; + + /* + * ------------- Check if the segment already exists -------------- + */ + if (Seg_exist(cf, dl_offset)) { + send = Data_read(cf, (uint8_t *)output_buf, len, offset_start); + goto bgdl; + } else { + /* + * Wait for any other download thread to finish + */ + + lprintf(cache_lock_debug, "thread %ld: locking w_lock;\n", pthread_self()); PTHREAD_MUTEX_LOCK(&cf->w_lock); - uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); - lprintf(debug, "thread %x spawned.\n ", pthread_self()); - long recv = Link_download(cf->link, (char *) recv_buf, cf->blksz, - cf->next_dl_offset); - if (recv < 0) { - lprintf(error, "thread %x received %ld bytes, \ -which doesn't make sense\n", pthread_self(), recv); - } - - if ((recv == cf->blksz) || - (cf->next_dl_offset == - (cf->content_length / cf->blksz * cf->blksz))) { - Data_write(cf, recv_buf, recv, cf->next_dl_offset); - Seg_set(cf, cf->next_dl_offset, 1); - } else { - lprintf(error, "received %ld rather than %ld, possible network \ -error.\n", recv, cf->blksz); - } - - FREE(recv_buf); - - lprintf(cache_lock_debug, - "thread %x: unlocking bgt_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->bgt_lock); - - lprintf(cache_lock_debug, - "thread %x: unlocking w_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->w_lock); - - if (pthread_detach(pthread_self())) { - lprintf(error, "%s\n", strerror(errno)); - }; - pthread_exit(NULL); -} - -long -Cache_read(Cache *cf, char *const output_buf, const off_t len, - const off_t offset_start) -{ - long send; - - /* - * The offset of the segment to be downloaded - */ - off_t dl_offset = (offset_start + len) / cf->blksz * cf->blksz; - - /* - * ------------- Check if the segment already exists -------------- - */ if (Seg_exist(cf, dl_offset)) { - send = Data_read(cf, (uint8_t *) output_buf, len, offset_start); - goto bgdl; - } else { - /* - * Wait for any other download thread to finish - */ + /* + * The segment now exists - it was downloaded by another + * download thread. Send it off and unlock the I/O + */ + send = Data_read(cf, (uint8_t *)output_buf, len, offset_start); - lprintf(cache_lock_debug, - "thread %ld: locking w_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&cf->w_lock); + lprintf(cache_lock_debug, "thread %x: unlocking w_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->w_lock); - if (Seg_exist(cf, dl_offset)) { - /* - * The segment now exists - it was downloaded by another - * download thread. Send it off and unlock the I/O - */ - send = - Data_read(cf, (uint8_t *) output_buf, len, offset_start); - - lprintf(cache_lock_debug, - "thread %x: unlocking w_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->w_lock); - - goto bgdl; - } + goto bgdl; } + } + /* + * ------------------ Download the segment --------------------- + */ + + uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); + lprintf(debug, "thread %x: spawned.\n ", pthread_self()); + long recv = Link_download(cf->link, (char *)recv_buf, cf->blksz, dl_offset); + if (recv < 0) { + lprintf(error, "thread %x received %ld bytes, \ +which doesn't make sense\n", + pthread_self(), recv); + } + /* + * check if we have received enough data, write it to the disk + * + * Condition 1: received the exact amount as the segment size. + * Condition 2: offset is the last segment + */ + if ((recv == cf->blksz) || + (dl_offset == (cf->content_length / cf->blksz * cf->blksz))) { + Data_write(cf, recv_buf, recv, dl_offset); + Seg_set(cf, dl_offset, 1); + } else { + lprintf(error, "received %ld rather than %ld, possible network \ +error.\n", + recv, cf->blksz); + } + FREE(recv_buf); + send = Data_read(cf, (uint8_t *)output_buf, len, offset_start); + + lprintf(cache_lock_debug, "thread %x: unlocking w_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&cf->w_lock); + + /* + * ----------- Download the next segment in background ----------------- + */ +bgdl: {} + off_t next_dl_offset = round_div(offset_start, cf->blksz) * cf->blksz; + if ((next_dl_offset > dl_offset) && !Seg_exist(cf, next_dl_offset) && + next_dl_offset < cf->content_length) { /* - * ------------------ Download the segment --------------------- + * Stop the spawning of multiple background pthreads */ - - uint8_t *recv_buf = CALLOC(cf->blksz, sizeof(uint8_t)); - lprintf(debug, "thread %x: spawned.\n ", pthread_self()); - long recv = Link_download(cf->link, (char *) recv_buf, cf->blksz, - dl_offset); - if (recv < 0) { - lprintf(error, "thread %x received %ld bytes, \ -which doesn't make sense\n", pthread_self(), recv); + if (!pthread_mutex_trylock(&cf->bgt_lock)) { + lprintf(cache_lock_debug, "thread %x: trylocked bgt_lock;\n", + pthread_self()); + cf->next_dl_offset = next_dl_offset; + if (pthread_create(&cf->bgt, NULL, Cache_bgdl, cf)) { + lprintf(error, "Error creating background download thread\n"); + } } - /* - * check if we have received enough data, write it to the disk - * - * Condition 1: received the exact amount as the segment size. - * Condition 2: offset is the last segment - */ - if ((recv == cf->blksz) || - (dl_offset == (cf->content_length / cf->blksz * cf->blksz))) { - Data_write(cf, recv_buf, recv, dl_offset); - Seg_set(cf, dl_offset, 1); - } else { - lprintf(error, "received %ld rather than %ld, possible network \ -error.\n", recv, cf->blksz); - } - FREE(recv_buf); - send = Data_read(cf, (uint8_t *) output_buf, len, offset_start); + } - lprintf(cache_lock_debug, - "thread %x: unlocking w_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&cf->w_lock); - - /* - * ----------- Download the next segment in background ----------------- - */ -bgdl: { - } - off_t next_dl_offset = round_div(offset_start, cf->blksz) * cf->blksz; - if ((next_dl_offset > dl_offset) && !Seg_exist(cf, next_dl_offset) - && next_dl_offset < cf->content_length) { - /* - * Stop the spawning of multiple background pthreads - */ - if (!pthread_mutex_trylock(&cf->bgt_lock)) { - lprintf(cache_lock_debug, - "thread %x: trylocked bgt_lock;\n", pthread_self()); - cf->next_dl_offset = next_dl_offset; - if (pthread_create(&cf->bgt, NULL, Cache_bgdl, cf)) { - lprintf(error, - "Error creating background download thread\n"); - } - } - } - - return send; + return send; } diff --git a/src/cache.h b/src/cache.h index 797279c..c4d77f4 100644 --- a/src/cache.h +++ b/src/cache.h @@ -14,9 +14,9 @@ typedef struct Cache Cache; #include "link.h" #include "network.h" -#include -#include #include +#include +#include /** * \brief Type definition for a cache segment @@ -27,48 +27,48 @@ typedef uint8_t Seg; * \brief cache data type in-memory data structure */ struct Cache { - /** \brief How many times the cache has been opened */ - int cache_opened; + /** \brief How many times the cache has been opened */ + int cache_opened; - /** \brief the FILE pointer for the data file*/ - FILE *dfp; - /** \brief the FILE pointer for the metadata */ - FILE *mfp; - /** \brief the path to the local cache file */ - char *path; - /** \brief the Link associated with this cache data set */ - Link *link; - /** \brief the modified time of the file */ - long time; - /** \brief the size of the file */ - off_t content_length; - /** \brief the block size of the data file */ - int blksz; - /** \brief segment array byte count */ - long segbc; - /** \brief the detail of each segment */ - Seg *seg; + /** \brief the FILE pointer for the data file*/ + FILE *dfp; + /** \brief the FILE pointer for the metadata */ + FILE *mfp; + /** \brief the path to the local cache file */ + char *path; + /** \brief the Link associated with this cache data set */ + Link *link; + /** \brief the modified time of the file */ + long time; + /** \brief the size of the file */ + off_t content_length; + /** \brief the block size of the data file */ + int blksz; + /** \brief segment array byte count */ + long segbc; + /** \brief the detail of each segment */ + Seg *seg; - /** \brief mutex lock for seek operation */ - pthread_mutex_t seek_lock; - /** \brief mutex lock for write operation */ - pthread_mutex_t w_lock; + /** \brief mutex lock for seek operation */ + pthread_mutex_t seek_lock; + /** \brief mutex lock for write operation */ + pthread_mutex_t w_lock; - /** \brief background download pthread */ - pthread_t bgt; - /** - * \brief mutex lock for the background download thread - * \note This lock is locked by the foreground thread, but unlocked by the - * background thread! - */ - pthread_mutex_t bgt_lock; - /** \brief mutex attributes for bgt_lock */ - pthread_mutexattr_t bgt_lock_attr; - /** \brief the offset of the next segment to be downloaded in background*/ - off_t next_dl_offset; + /** \brief background download pthread */ + pthread_t bgt; + /** + * \brief mutex lock for the background download thread + * \note This lock is locked by the foreground thread, but unlocked by the + * background thread! + */ + pthread_mutex_t bgt_lock; + /** \brief mutex attributes for bgt_lock */ + pthread_mutexattr_t bgt_lock_attr; + /** \brief the offset of the next segment to be downloaded in background*/ + off_t next_dl_offset; - /** \brief the FUSE filesystem path to the remote file*/ - char *fs_path; + /** \brief the FUSE filesystem path to the remote file*/ + char *fs_path; }; /** diff --git a/src/config.c b/src/config.c index b3fcd2b..b13cc86 100644 --- a/src/config.c +++ b/src/config.c @@ -6,19 +6,19 @@ /** * \brief The default HTTP 429 (too many requests) wait time */ -#define DEFAULT_HTTP_WAIT_SEC 5 +#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 +#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 +#define DEFAULT_MAX_SEGBC 128 * 1024 ConfigStruct CONFIG; @@ -26,50 +26,49 @@ 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) -{ - CONFIG.mode = NORMAL; +void Config_init(void) { + CONFIG.mode = NORMAL; - CONFIG.log_type = log_level_init(); + CONFIG.log_type = log_level_init(); - /*---------------- Network related --------------*/ - CONFIG.http_username = NULL; + /*---------------- Network related --------------*/ + CONFIG.http_username = NULL; - CONFIG.http_password = NULL; + CONFIG.http_password = NULL; - CONFIG.proxy = NULL; + CONFIG.proxy = NULL; - CONFIG.proxy_username = NULL; + CONFIG.proxy_username = NULL; - CONFIG.proxy_password = NULL; + CONFIG.proxy_password = NULL; - CONFIG.max_conns = DEFAULT_NETWORK_MAX_CONNS; + CONFIG.max_conns = DEFAULT_NETWORK_MAX_CONNS; - CONFIG.user_agent = DEFAULT_USER_AGENT; + CONFIG.user_agent = DEFAULT_USER_AGENT; - CONFIG.http_wait_sec = DEFAULT_HTTP_WAIT_SEC; + CONFIG.http_wait_sec = DEFAULT_HTTP_WAIT_SEC; - CONFIG.no_range_check = 0; + CONFIG.no_range_check = 0; - CONFIG.insecure_tls = 0; + CONFIG.insecure_tls = 0; - CONFIG.refresh_timeout = DEFAULT_REFRESH_TIMEOUT; + CONFIG.refresh_timeout = DEFAULT_REFRESH_TIMEOUT; - /*--------------- Cache related ---------------*/ - CONFIG.cache_enabled = 0; + /*--------------- Cache related ---------------*/ + CONFIG.cache_enabled = 0; - CONFIG.cache_dir = NULL; + CONFIG.cache_dir = NULL; - CONFIG.data_blksz = DEFAULT_DATA_BLKSZ; + CONFIG.data_blksz = DEFAULT_DATA_BLKSZ; - CONFIG.max_segbc = DEFAULT_MAX_SEGBC; + CONFIG.max_segbc = DEFAULT_MAX_SEGBC; - /*-------------- Sonic related -------------*/ - CONFIG.sonic_username = NULL; + /*-------------- Sonic related -------------*/ + CONFIG.sonic_username = NULL; - CONFIG.sonic_password = NULL; + CONFIG.sonic_password = NULL; - CONFIG.sonic_id3 = 0; + CONFIG.sonic_id3 = 0; - CONFIG.sonic_insecure = 0; + CONFIG.sonic_insecure = 0; } diff --git a/src/config.h b/src/config.h index df56c2d..416a0f9 100644 --- a/src/config.h +++ b/src/config.h @@ -5,13 +5,13 @@ * \brief the maximum length of a path and a URL. * \details This corresponds the maximum path length under Ext4. */ -#define MAX_PATH_LEN 4096 +#define MAX_PATH_LEN 4096 /** * \brief the maximum length of a filename. * \details This corresponds the filename length under Ext4. */ -#define MAX_FILENAME_LEN 255 +#define MAX_FILENAME_LEN 255 /** * \brief the default user agent string @@ -21,20 +21,20 @@ /** * \brief The default maximum number of network connections */ -#define DEFAULT_NETWORK_MAX_CONNS 10 +#define DEFAULT_NETWORK_MAX_CONNS 10 /** * \brief The default refresh_timeout */ -#define DEFAULT_REFRESH_TIMEOUT 86400 +#define DEFAULT_REFRESH_TIMEOUT 86400 /** * \brief Operation modes */ typedef enum { - NORMAL = 1, - SONIC = 2, - SINGLE = 3, + NORMAL = 1, + SONIC = 2, + SINGLE = 3, } OperationMode; /** @@ -43,55 +43,55 @@ typedef enum { * lines up with the initialisation code in util.c */ typedef struct { - /** \brief Operation Mode */ - OperationMode mode; - /** \brief Current log level */ - int log_type; - /*---------------- Network related --------------*/ - /** \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 proxy certificate file */ - char *proxy_cafile; - /** \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 Disable check for the server's support of HTTP range request */ - int no_range_check; - /** \brief Disable TLS certificate verification */ - int insecure_tls; - /** \brief Server certificate file */ - char *cafile; - /** \brief Refresh directory listing after refresh_timeout seconds*/ - int refresh_timeout; - /*--------------- Cache related ---------------*/ - /** \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; - /*-------------- Sonic related -------------*/ - /** \brief The Sonic server username */ - char *sonic_username; - /** \brief The Sonic server password */ - char *sonic_password; - /** \brief Whether we are using sonic mode ID3 extension */ - int sonic_id3; - /** \brief Whether we use the legacy sonic authentication mode */ - int sonic_insecure; + /** \brief Operation Mode */ + OperationMode mode; + /** \brief Current log level */ + int log_type; + /*---------------- Network related --------------*/ + /** \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 proxy certificate file */ + char *proxy_cafile; + /** \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 Disable check for the server's support of HTTP range request */ + int no_range_check; + /** \brief Disable TLS certificate verification */ + int insecure_tls; + /** \brief Server certificate file */ + char *cafile; + /** \brief Refresh directory listing after refresh_timeout seconds*/ + int refresh_timeout; + /*--------------- Cache related ---------------*/ + /** \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; + /*-------------- Sonic related -------------*/ + /** \brief The Sonic server username */ + char *sonic_username; + /** \brief The Sonic server password */ + char *sonic_password; + /** \brief Whether we are using sonic mode ID3 extension */ + int sonic_id3; + /** \brief Whether we use the legacy sonic authentication mode */ + int sonic_insecure; } ConfigStruct; /** diff --git a/src/fuse_local.c b/src/fuse_local.c index b576d1d..00b23f5 100644 --- a/src/fuse_local.c +++ b/src/fuse_local.c @@ -13,110 +13,104 @@ #include #include -static void *fs_init(struct fuse_conn_info *conn) -{ - (void) conn; - return NULL; +static void *fs_init(struct fuse_conn_info *conn) { + (void)conn; + return NULL; } /** \brief release an opened file */ -static int fs_release(const char *path, struct fuse_file_info *fi) -{ - lprintf(info, "%s\n", path); - (void) path; - if (CACHE_SYSTEM_INIT) { - Cache_close((Cache *) fi->fh); - } - return 0; +static int fs_release(const char *path, struct fuse_file_info *fi) { + lprintf(info, "%s\n", path); + (void)path; + if (CACHE_SYSTEM_INIT) { + Cache_close((Cache *)fi->fh); + } + return 0; } /** \brief return the attributes for a single file indicated by path */ -static int fs_getattr(const char *path, struct stat *stbuf) -{ - int res = 0; - memset(stbuf, 0, sizeof(struct stat)); +static int fs_getattr(const char *path, struct stat *stbuf) { + int res = 0; + memset(stbuf, 0, sizeof(struct stat)); - if (!strcmp(path, "/")) { - stbuf->st_mode = S_IFDIR | 0755; - stbuf->st_nlink = 1; - } else { - Link *link = path_to_Link(path); - if (!link) { - return -ENOENT; - } - struct timespec spec = { 0 }; - spec.tv_sec = link->time; -#if defined(__APPLE__) && defined(__MACH__) - stbuf->st_mtimespec = spec; -#else - stbuf->st_mtim = spec; -#endif - switch (link->type) { - case LINK_DIR: - stbuf->st_mode = S_IFDIR | 0755; - stbuf->st_nlink = 1; - break; - case LINK_FILE: - stbuf->st_mode = S_IFREG | 0444; - stbuf->st_nlink = 1; - stbuf->st_size = link->content_length; - stbuf->st_blksize = 128 * 1024; - stbuf->st_blocks = (link->content_length) / 512; - break; - default: - return -ENOENT; - } + if (!strcmp(path, "/")) { + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 1; + } else { + Link *link = path_to_Link(path); + if (!link) { + return -ENOENT; } - stbuf->st_uid = getuid(); - stbuf->st_gid = getgid(); + struct timespec spec = {0}; + spec.tv_sec = link->time; +#if defined(__APPLE__) && defined(__MACH__) + stbuf->st_mtimespec = spec; +#else + stbuf->st_mtim = spec; +#endif + switch (link->type) { + case LINK_DIR: + stbuf->st_mode = S_IFDIR | 0755; + stbuf->st_nlink = 1; + break; + case LINK_FILE: + stbuf->st_mode = S_IFREG | 0444; + stbuf->st_nlink = 1; + stbuf->st_size = link->content_length; + stbuf->st_blksize = 128 * 1024; + stbuf->st_blocks = (link->content_length) / 512; + break; + default: + return -ENOENT; + } + } + stbuf->st_uid = getuid(); + stbuf->st_gid = getgid(); - return res; + return res; } /** \brief read a file */ -static int -fs_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *fi) -{ - long received; - if (CACHE_SYSTEM_INIT) { - received = Cache_read((Cache *) fi->fh, buf, size, offset); - } else { - received = path_download(path, buf, size, offset); - } - return received; +static int fs_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) { + long received; + if (CACHE_SYSTEM_INIT) { + received = Cache_read((Cache *)fi->fh, buf, size, offset); + } else { + received = path_download(path, buf, size, offset); + } + return received; } /** \brief open a file indicated by the path */ -static int fs_open(const char *path, struct fuse_file_info *fi) -{ - lprintf(info, "%s\n", path); - Link *link = path_to_Link(path); - if (!link) { +static int fs_open(const char *path, struct fuse_file_info *fi) { + lprintf(info, "%s\n", path); + Link *link = path_to_Link(path); + if (!link) { + return -ENOENT; + } + if ((fi->flags & O_RDWR) != O_RDONLY) { + return -EROFS; + } + if (CACHE_SYSTEM_INIT) { + fi->fh = (uint64_t)Cache_open(path); + if (!fi->fh) { + /* + * The link clearly exists, the cache cannot be opened, attempt + * cache creation + */ + Cache_delete(path); + Cache_create(path); + fi->fh = (uint64_t)Cache_open(path); + /* + * The cache definitely cannot be opened for some reason. + */ + if (!fi->fh) { return -ENOENT; + } } - if ((fi->flags & O_RDWR) != O_RDONLY) { - return -EROFS; - } - if (CACHE_SYSTEM_INIT) { - fi->fh = (uint64_t) Cache_open(path); - if (!fi->fh) { - /* - * The link clearly exists, the cache cannot be opened, attempt - * cache creation - */ - Cache_delete(path); - Cache_create(path); - fi->fh = (uint64_t) Cache_open(path); - /* - * The cache definitely cannot be opened for some reason. - */ - if (!fi->fh) { - return -ENOENT; - } - } - } - return 0; + } + return 0; } /** @@ -130,46 +124,40 @@ static int fs_open(const char *path, struct fuse_file_info *fi) * generate the LinkTables for previous level directories. We might * as well maintain our own tree structure. */ -static int -fs_readdir(const char *path, void *buf, fuse_fill_dir_t dir_add, - off_t offset, struct fuse_file_info *fi) -{ - (void) offset; - (void) fi; - LinkTable *linktbl; +static int fs_readdir(const char *path, void *buf, fuse_fill_dir_t dir_add, + off_t offset, struct fuse_file_info *fi) { + (void)offset; + (void)fi; + LinkTable *linktbl; - linktbl = path_to_Link_LinkTable_new(path); - if (!linktbl) { - return -ENOENT; + linktbl = path_to_Link_LinkTable_new(path); + if (!linktbl) { + return -ENOENT; + } + + /* + * start adding the links + */ + dir_add(buf, ".", NULL, 0); + dir_add(buf, "..", NULL, 0); + /* We skip the head link */ + for (int i = 1; i < linktbl->num; i++) { + Link *link = linktbl->links[i]; + if (link->type != LINK_INVALID) { + dir_add(buf, link->linkname, NULL, 0); } + } - - /* - * start adding the links - */ - dir_add(buf, ".", NULL, 0); - dir_add(buf, "..", NULL, 0); - /* We skip the head link */ - for (int i = 1; i < linktbl->num; i++) { - Link *link = linktbl->links[i]; - if (link->type != LINK_INVALID) { - dir_add(buf, link->linkname, NULL, 0); - } - } - - return 0; + return 0; } -static struct fuse_operations fs_oper = { - .getattr = fs_getattr, - .readdir = fs_readdir, - .open = fs_open, - .read = fs_read, - .init = fs_init, - .release = fs_release -}; +static struct fuse_operations fs_oper = {.getattr = fs_getattr, + .readdir = fs_readdir, + .open = fs_open, + .read = fs_read, + .init = fs_init, + .release = fs_release}; -int fuse_local_init(int argc, char **argv) -{ - return fuse_main(argc, argv, &fs_oper, NULL); +int fuse_local_init(int argc, char **argv) { + return fuse_main(argc, argv, &fs_oper, NULL); } diff --git a/src/link.c b/src/link.c index 0119f7a..d7679eb 100644 --- a/src/link.c +++ b/src/link.c @@ -32,188 +32,180 @@ static void make_link_relative(const char *page_url, char *link_url); /** * \brief create a new Link */ -static Link *Link_new(const char *linkname, LinkType type) -{ - Link *link = CALLOC(1, sizeof(Link)); +static Link *Link_new(const char *linkname, LinkType type) { + Link *link = CALLOC(1, sizeof(Link)); - strncpy(link->linkname, linkname, MAX_FILENAME_LEN); - strncpy(link->linkpath, linkname, MAX_FILENAME_LEN); - link->type = type; + strncpy(link->linkname, linkname, MAX_FILENAME_LEN); + strncpy(link->linkpath, linkname, MAX_FILENAME_LEN); + link->type = type; - /* - * remove the '/' from linkname if it exists - */ - char *c = &(link->linkname[strnlen(link->linkname, MAX_FILENAME_LEN) - 1]); - if (*c == '/') { - *c = '\0'; - } + /* + * remove the '/' from linkname if it exists + */ + char *c = &(link->linkname[strnlen(link->linkname, MAX_FILENAME_LEN) - 1]); + if (*c == '/') { + *c = '\0'; + } - return link; + return link; } -static CURL *Link_to_curl(Link *link) -{ - lprintf(debug, "%s\n", link->f_url); - CURL *curl = curl_easy_init(); - if (!curl) { - lprintf(fatal, "curl_easy_init() failed!\n"); - } +static CURL *Link_to_curl(Link *link) { + lprintf(debug, "%s\n", link->f_url); + CURL *curl = curl_easy_init(); + if (!curl) { + lprintf(fatal, "curl_easy_init() failed!\n"); + } + /* + * set up some basic curl stuff + */ + CURLcode ret = curl_easy_setopt(curl, CURLOPT_USERAGENT, CONFIG.user_agent); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + /* + * for following directories without the '/' + */ + ret = curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 2); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_URL, link->f_url); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_SHARE, CURL_SHARE); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + if (CONFIG.cafile) { /* - * set up some basic curl stuff - */ - CURLcode ret = - curl_easy_setopt(curl, CURLOPT_USERAGENT, CONFIG.user_agent); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - /* - * for following directories without the '/' - */ - ret = curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 2); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = curl_easy_setopt(curl, CURLOPT_URL, link->f_url); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = curl_easy_setopt(curl, CURLOPT_SHARE, CURL_SHARE); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_memory_callback); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - if (CONFIG.cafile) { - /* - * Having been given a certificate file, disable any search directory - * built into libcurl, so that we exclusively use the explicitly given - * certificate(s). - * - * If we ever add a CAPATH option, we should do the mirror for CAINFO, - * too: disable both and then enable whichever one(s) were given. - */ - ret = curl_easy_setopt(curl, CURLOPT_CAPATH, NULL); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - - ret = curl_easy_setopt(curl, CURLOPT_CAINFO, CONFIG.cafile); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - } - if (CONFIG.insecure_tls) { - ret = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - } - - if (CONFIG.log_type & libcurl_debug) { - ret = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - } - - if (CONFIG.http_username) { - ret = curl_easy_setopt(curl, CURLOPT_USERNAME, CONFIG.http_username); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - } - - if (CONFIG.http_password) { - ret = curl_easy_setopt(curl, CURLOPT_PASSWORD, CONFIG.http_password); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - } - - if (CONFIG.proxy) { - ret = curl_easy_setopt(curl, CURLOPT_PROXY, CONFIG.proxy); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - } - - if (CONFIG.proxy_username) { - ret = curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, - CONFIG.proxy_username); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - } - - if (CONFIG.proxy_password) { - ret = curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, - CONFIG.proxy_password); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - } - - if (CONFIG.proxy_cafile) { - /* See CONFIG.cafile above */ - ret = curl_easy_setopt(curl, CURLOPT_PROXY_CAPATH, NULL); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - - ret = curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO, - CONFIG.proxy_cafile); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - } - - return curl; -} - -static void Link_req_file_stat(Link *this_link) -{ - lprintf(debug, "%s\n", this_link->f_url); - CURL *curl = Link_to_curl(this_link); - CURLcode ret = curl_easy_setopt(curl, CURLOPT_NOBODY, 1); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - - /* - * We need to put the variable on the heap, because otherwise the - * variable gets popped from the stack as the function returns. + * Having been given a certificate file, disable any search directory + * built into libcurl, so that we exclusively use the explicitly given + * certificate(s). * - * It gets freed in curl_process_msgs(); + * If we ever add a CAPATH option, we should do the mirror for CAINFO, + * too: disable both and then enable whichever one(s) were given. */ - TransferStruct *transfer = CALLOC(1, sizeof(TransferStruct)); - - transfer->link = this_link; - transfer->type = FILESTAT; - ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, transfer); + ret = curl_easy_setopt(curl, CURLOPT_CAPATH, NULL); if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); + lprintf(error, "%s", curl_easy_strerror(ret)); } - transfer_nonblocking(curl); + ret = curl_easy_setopt(curl, CURLOPT_CAINFO, CONFIG.cafile); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + if (CONFIG.insecure_tls) { + ret = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + + if (CONFIG.log_type & libcurl_debug) { + ret = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + + if (CONFIG.http_username) { + ret = curl_easy_setopt(curl, CURLOPT_USERNAME, CONFIG.http_username); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + + if (CONFIG.http_password) { + ret = curl_easy_setopt(curl, CURLOPT_PASSWORD, CONFIG.http_password); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + + if (CONFIG.proxy) { + ret = curl_easy_setopt(curl, CURLOPT_PROXY, CONFIG.proxy); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + + if (CONFIG.proxy_username) { + ret = curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME, CONFIG.proxy_username); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + + if (CONFIG.proxy_password) { + ret = curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, CONFIG.proxy_password); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + + if (CONFIG.proxy_cafile) { + /* See CONFIG.cafile above */ + ret = curl_easy_setopt(curl, CURLOPT_PROXY_CAPATH, NULL); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + + ret = curl_easy_setopt(curl, CURLOPT_PROXY_CAINFO, CONFIG.proxy_cafile); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + } + + return curl; +} + +static void Link_req_file_stat(Link *this_link) { + lprintf(debug, "%s\n", this_link->f_url); + CURL *curl = Link_to_curl(this_link); + CURLcode ret = curl_easy_setopt(curl, CURLOPT_NOBODY, 1); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + + /* + * We need to put the variable on the heap, because otherwise the + * variable gets popped from the stack as the function returns. + * + * It gets freed in curl_process_msgs(); + */ + TransferStruct *transfer = CALLOC(1, sizeof(TransferStruct)); + + transfer->link = this_link; + transfer->type = FILESTAT; + ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, transfer); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + + transfer_nonblocking(curl); } /** @@ -221,164 +213,155 @@ static void Link_req_file_stat(Link *this_link) * \details Try and get the stats for each link in the link table. This will get * repeated until the uninitialised entry count drop to zero. */ -static void LinkTable_uninitialised_fill(LinkTable *linktbl) -{ - int u; - char s[STATUS_LEN]; - lprintf(debug, " ... "); - do { - u = 0; - for (int i = 0; i < linktbl->num; i++) { - Link *this_link = linktbl->links[i]; - if (this_link->type == LINK_UNINITIALISED_FILE) { - Link_req_file_stat(linktbl->links[i]); - u++; - } - } - /* - * Block until the gaps are filled - */ - int n = curl_multi_perform_once(); - int i = 0; - int j = 0; - while ((i = curl_multi_perform_once())) { - if (CONFIG.log_type & debug) { - if (j) { - erase_string(stderr, STATUS_LEN, s); - } - snprintf(s, STATUS_LEN, "%d / %d", n - i, n); - fprintf(stderr, "%s", s); - j++; - } - } - } while (u); - if (CONFIG.log_type & debug) { - erase_string(stderr, STATUS_LEN, s); - fprintf(stderr, "... Done!\n"); +static void LinkTable_uninitialised_fill(LinkTable *linktbl) { + int u; + char s[STATUS_LEN]; + lprintf(debug, " ... "); + do { + u = 0; + for (int i = 0; i < linktbl->num; i++) { + Link *this_link = linktbl->links[i]; + if (this_link->type == LINK_UNINITIALISED_FILE) { + Link_req_file_stat(linktbl->links[i]); + u++; + } } + /* + * Block until the gaps are filled + */ + int n = curl_multi_perform_once(); + int i = 0; + int j = 0; + while ((i = curl_multi_perform_once())) { + if (CONFIG.log_type & debug) { + if (j) { + erase_string(stderr, STATUS_LEN, s); + } + snprintf(s, STATUS_LEN, "%d / %d", n - i, n); + fprintf(stderr, "%s", s); + j++; + } + } + } while (u); + if (CONFIG.log_type & debug) { + erase_string(stderr, STATUS_LEN, s); + fprintf(stderr, "... Done!\n"); + } } /** * \brief Create the root linktable for single file mode */ -static LinkTable *single_LinkTable_new(const char *url) -{ - char *ptr = strrchr(url, '/') + 1; - LinkTable *linktbl = LinkTable_alloc(url); - Link *link = Link_new(ptr, LINK_UNINITIALISED_FILE); - strncpy(link->f_url, url, MAX_FILENAME_LEN); - LinkTable_add(linktbl, link); - LinkTable_uninitialised_fill(linktbl); - LinkTable_print(linktbl); - return linktbl; +static LinkTable *single_LinkTable_new(const char *url) { + char *ptr = strrchr(url, '/') + 1; + LinkTable *linktbl = LinkTable_alloc(url); + Link *link = Link_new(ptr, LINK_UNINITIALISED_FILE); + strncpy(link->f_url, url, MAX_FILENAME_LEN); + LinkTable_add(linktbl, link); + LinkTable_uninitialised_fill(linktbl); + LinkTable_print(linktbl); + return linktbl; } -LinkTable *LinkSystem_init(const char *url) -{ - if (pthread_mutex_init(&link_lock, NULL)) { - lprintf(error, "link_lock initialisation failed!\n"); - } - int url_len = strnlen(url, MAX_PATH_LEN) - 1; - /* - * --------- Set the length of the root link ----------- - */ - /* - * This is where the '/' should be - */ - ROOT_LINK_OFFSET = strnlen(url, MAX_PATH_LEN) - - ((url[url_len] == '/') ? 1 : 0); +LinkTable *LinkSystem_init(const char *url) { + if (pthread_mutex_init(&link_lock, NULL)) { + lprintf(error, "link_lock initialisation failed!\n"); + } + int url_len = strnlen(url, MAX_PATH_LEN) - 1; + /* + * --------- Set the length of the root link ----------- + */ + /* + * This is where the '/' should be + */ + ROOT_LINK_OFFSET = + strnlen(url, MAX_PATH_LEN) - ((url[url_len] == '/') ? 1 : 0); - /* - * --------------------- Enable cache system -------------------- - */ - if (CONFIG.cache_enabled) { - if (CONFIG.cache_dir) { - CacheSystem_init(CONFIG.cache_dir, 0); - } else { - CacheSystem_init(url, 1); - } - } - - /* - * ----------- Create the root link table -------------- - */ - if (CONFIG.mode == NORMAL) { - ROOT_LINK_TBL = LinkTable_new(url); - } else if (CONFIG.mode == SINGLE) { - ROOT_LINK_TBL = single_LinkTable_new(url); - } else if (CONFIG.mode == SONIC) { - sonic_config_init(url, CONFIG.sonic_username, - CONFIG.sonic_password); - if (!CONFIG.sonic_id3) { - ROOT_LINK_TBL = sonic_LinkTable_new_index("0"); - } else { - ROOT_LINK_TBL = sonic_LinkTable_new_id3(0, "0"); - } + /* + * --------------------- Enable cache system -------------------- + */ + if (CONFIG.cache_enabled) { + if (CONFIG.cache_dir) { + CacheSystem_init(CONFIG.cache_dir, 0); } else { - lprintf(fatal, "Invalid CONFIG.mode\n"); + CacheSystem_init(url, 1); } - return ROOT_LINK_TBL; + } + + /* + * ----------- Create the root link table -------------- + */ + if (CONFIG.mode == NORMAL) { + ROOT_LINK_TBL = LinkTable_new(url); + } else if (CONFIG.mode == SINGLE) { + ROOT_LINK_TBL = single_LinkTable_new(url); + } else if (CONFIG.mode == SONIC) { + sonic_config_init(url, CONFIG.sonic_username, CONFIG.sonic_password); + if (!CONFIG.sonic_id3) { + ROOT_LINK_TBL = sonic_LinkTable_new_index("0"); + } else { + ROOT_LINK_TBL = sonic_LinkTable_new_id3(0, "0"); + } + } else { + lprintf(fatal, "Invalid CONFIG.mode\n"); + } + return ROOT_LINK_TBL; } -void LinkTable_add(LinkTable *linktbl, Link *link) -{ - linktbl->num++; - linktbl->links = - realloc(linktbl->links, linktbl->num * sizeof(Link *)); - if (!linktbl->links) { - lprintf(fatal, "realloc() failure!\n"); - } - linktbl->links[linktbl->num - 1] = link; +void LinkTable_add(LinkTable *linktbl, Link *link) { + linktbl->num++; + linktbl->links = realloc(linktbl->links, linktbl->num * sizeof(Link *)); + if (!linktbl->links) { + lprintf(fatal, "realloc() failure!\n"); + } + linktbl->links[linktbl->num - 1] = link; } -static LinkType linkname_to_LinkType(const char *linkname) -{ - /* - * The link name has to start with alphanumerical character - */ - if (!isalnum(linkname[0]) && (linkname[0] != '%')) { - return LINK_INVALID; - } +static LinkType linkname_to_LinkType(const char *linkname) { + /* + * The link name has to start with alphanumerical character + */ + if (!isalnum(linkname[0]) && (linkname[0] != '%')) { + return LINK_INVALID; + } - /* - * Check for stray '/' - Linkname should not have '/' - */ - char *slash = strchr(linkname, '/'); - if (slash) { - int linkname_len = strnlen(linkname, MAX_FILENAME_LEN) - 1; - if (slash - linkname != linkname_len) { - return LINK_INVALID; - } + /* + * Check for stray '/' - Linkname should not have '/' + */ + char *slash = strchr(linkname, '/'); + if (slash) { + int linkname_len = strnlen(linkname, MAX_FILENAME_LEN) - 1; + if (slash - linkname != linkname_len) { + return LINK_INVALID; } + } - if (linkname[strnlen(linkname, MAX_FILENAME_LEN) - 1] == '/') { - return LINK_DIR; - } + if (linkname[strnlen(linkname, MAX_FILENAME_LEN) - 1] == '/') { + return LINK_DIR; + } - return LINK_UNINITIALISED_FILE; + return LINK_UNINITIALISED_FILE; } /** * \brief check if two link names are equal, after taking the '/' into account. */ -static int linknames_equal(char *linkname, const char *linkname_new) -{ - if (!strncmp(linkname, linkname_new, MAX_FILENAME_LEN)) { - return 1; - } +static int linknames_equal(char *linkname, const char *linkname_new) { + if (!strncmp(linkname, linkname_new, MAX_FILENAME_LEN)) { + return 1; + } - /* - * check if the link names differ by a single '/' - */ - if (!strncmp - (linkname, linkname_new, strnlen(linkname, MAX_FILENAME_LEN))) { - size_t linkname_new_len = strnlen(linkname_new, MAX_FILENAME_LEN); - if ((linkname_new_len - strnlen(linkname, MAX_FILENAME_LEN) == 1) - && (linkname_new[linkname_new_len - 1] == '/')) { - return 1; - } + /* + * check if the link names differ by a single '/' + */ + if (!strncmp(linkname, linkname_new, strnlen(linkname, MAX_FILENAME_LEN))) { + size_t linkname_new_len = strnlen(linkname_new, MAX_FILENAME_LEN); + if ((linkname_new_len - strnlen(linkname, MAX_FILENAME_LEN) == 1) && + (linkname_new[linkname_new_len - 1] == '/')) { + return 1; } - return 0; + } + return 0; } /** @@ -386,794 +369,741 @@ static int linknames_equal(char *linkname, const char *linkname_new) * https://github.com/google/gumbo-parser/blob/master/examples/find_links.cc */ static void HTML_to_LinkTable(const char *url, GumboNode *node, - LinkTable *linktbl) -{ - if (node->type != GUMBO_NODE_ELEMENT) { - return; - } - GumboAttribute *href; - if (node->v.element.tag == GUMBO_TAG_A && - (href = - gumbo_get_attribute(&node->v.element.attributes, "href"))) { - char *link_url = (char *) href->value; - make_link_relative(url, link_url); - /* - * if it is valid, copy the link onto the heap - */ - LinkType type = linkname_to_LinkType(link_url); - /* - * We also check if the link being added is the same as the last link. - * This is to prevent duplicated link, if an Apache server has the - * IconsAreLinks option. - */ - if (((type == LINK_DIR) || (type == LINK_UNINITIALISED_FILE)) && - !linknames_equal(linktbl->links[linktbl->num - 1]->linkname, - link_url)) { - LinkTable_add(linktbl, Link_new(link_url, type)); - } - } - /* - * Note the recursive call, lol. - */ - GumboVector *children = &node->v.element.children; - for (size_t i = 0; i < children->length; ++i) { - HTML_to_LinkTable(url, (GumboNode *) children->data[i], linktbl); - } + LinkTable *linktbl) { + if (node->type != GUMBO_NODE_ELEMENT) { return; + } + GumboAttribute *href; + if (node->v.element.tag == GUMBO_TAG_A && + (href = gumbo_get_attribute(&node->v.element.attributes, "href"))) { + char *link_url = (char *)href->value; + make_link_relative(url, link_url); + /* + * if it is valid, copy the link onto the heap + */ + LinkType type = linkname_to_LinkType(link_url); + /* + * We also check if the link being added is the same as the last link. + * This is to prevent duplicated link, if an Apache server has the + * IconsAreLinks option. + */ + if (((type == LINK_DIR) || (type == LINK_UNINITIALISED_FILE)) && + !linknames_equal(linktbl->links[linktbl->num - 1]->linkname, + link_url)) { + LinkTable_add(linktbl, Link_new(link_url, type)); + } + } + /* + * Note the recursive call, lol. + */ + GumboVector *children = &node->v.element.children; + for (size_t i = 0; i < children->length; ++i) { + HTML_to_LinkTable(url, (GumboNode *)children->data[i], linktbl); + } + return; } -void Link_set_file_stat(Link *this_link, CURL *curl) -{ - long http_resp; - CURLcode ret = - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); +void Link_set_file_stat(Link *this_link, CURL *curl) { + long http_resp; + CURLcode ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + if (http_resp == HTTP_OK) { + curl_off_t cl = 0; + ret = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl); if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); + lprintf(error, "%s", curl_easy_strerror(ret)); } - if (http_resp == HTTP_OK) { - curl_off_t cl = 0; - ret = - curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = - curl_easy_getinfo(curl, CURLINFO_FILETIME, &(this_link->time)); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - if (cl <= 0) { - this_link->type = LINK_INVALID; - } else { - this_link->type = LINK_FILE; - this_link->content_length = cl; - } + ret = curl_easy_getinfo(curl, CURLINFO_FILETIME, &(this_link->time)); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + if (cl <= 0) { + this_link->type = LINK_INVALID; } else { - lprintf(warning, "HTTP %ld", http_resp); - if (HTTP_temp_failure(http_resp)) { - lprintf(warning, ", retrying later.\n"); - } else { - this_link->type = LINK_INVALID; - lprintf(warning, ".\n"); - } + this_link->type = LINK_FILE; + this_link->content_length = cl; } + } else { + lprintf(warning, "HTTP %ld", http_resp); + if (HTTP_temp_failure(http_resp)) { + lprintf(warning, ", retrying later.\n"); + } else { + this_link->type = LINK_INVALID; + lprintf(warning, ".\n"); + } + } } -static void LinkTable_fill(LinkTable *linktbl) -{ - CURL *c = curl_easy_init(); - Link *head_link = linktbl->links[0]; - lprintf(debug, "Filling %s\n", head_link->f_url); - for (int i = 1; i < linktbl->num; i++) { - Link *this_link = linktbl->links[i]; - /* Some web sites use characters in their href attributes that really - shouldn't be in their href attributes, most commonly spaces. And - some web sites _do_ properly encode their href attributes. So we - first unescape the link path, and then we escape it, so that curl - will definitely be happy with it (e.g., curl won't accept URLs with - spaces in them!). If we only escaped it, and there were already - encoded characters in it, then that would break the link. */ - char *unescaped_path = curl_easy_unescape(c, this_link->linkpath, 0, - NULL); - char *escaped_path = curl_easy_escape(c, unescaped_path, 0); - curl_free(unescaped_path); - /* Our code does the wrong thing if there's a trailing slash that's been - replaced with %2F, which curl_easy_escape does, God bless it, so if - it did that then let's put it back. */ - int escaped_len = strlen(escaped_path); - if (escaped_len >= 3 && !strcmp(escaped_path + escaped_len - 3, "%2F")) - strcpy(escaped_path + escaped_len - 3, "/"); - char *url = path_append(head_link->f_url, escaped_path); - curl_free(escaped_path); - strncpy(this_link->f_url, url, MAX_PATH_LEN); - FREE(url); - char *unescaped_linkname; - unescaped_linkname = curl_easy_unescape(c, this_link->linkname, - 0, NULL); - strncpy(this_link->linkname, unescaped_linkname, MAX_FILENAME_LEN); - curl_free(unescaped_linkname); - } - LinkTable_uninitialised_fill(linktbl); - curl_easy_cleanup(c); +static void LinkTable_fill(LinkTable *linktbl) { + CURL *c = curl_easy_init(); + Link *head_link = linktbl->links[0]; + lprintf(debug, "Filling %s\n", head_link->f_url); + for (int i = 1; i < linktbl->num; i++) { + Link *this_link = linktbl->links[i]; + /* Some web sites use characters in their href attributes that really + shouldn't be in their href attributes, most commonly spaces. And + some web sites _do_ properly encode their href attributes. So we + first unescape the link path, and then we escape it, so that curl + will definitely be happy with it (e.g., curl won't accept URLs with + spaces in them!). If we only escaped it, and there were already + encoded characters in it, then that would break the link. */ + char *unescaped_path = curl_easy_unescape(c, this_link->linkpath, 0, NULL); + char *escaped_path = curl_easy_escape(c, unescaped_path, 0); + curl_free(unescaped_path); + /* Our code does the wrong thing if there's a trailing slash that's been + replaced with %2F, which curl_easy_escape does, God bless it, so if + it did that then let's put it back. */ + int escaped_len = strlen(escaped_path); + if (escaped_len >= 3 && !strcmp(escaped_path + escaped_len - 3, "%2F")) + strcpy(escaped_path + escaped_len - 3, "/"); + char *url = path_append(head_link->f_url, escaped_path); + curl_free(escaped_path); + strncpy(this_link->f_url, url, MAX_PATH_LEN); + FREE(url); + char *unescaped_linkname; + unescaped_linkname = curl_easy_unescape(c, this_link->linkname, 0, NULL); + strncpy(this_link->linkname, unescaped_linkname, MAX_FILENAME_LEN); + curl_free(unescaped_linkname); + } + LinkTable_uninitialised_fill(linktbl); + curl_easy_cleanup(c); } /** * \brief Reset invalid links in the link table */ -static void LinkTable_invalid_reset(LinkTable *linktbl) -{ +static void LinkTable_invalid_reset(LinkTable *linktbl) { + int j = 0; + for (int i = 0; i < linktbl->num; i++) { + Link *this_link = linktbl->links[i]; + if (this_link->type == LINK_INVALID) { + this_link->type = LINK_UNINITIALISED_FILE; + j++; + } + } + lprintf(debug, "%d invalid links\n", j); +} + +void LinkTable_free(LinkTable *linktbl) { + if (linktbl) { + for (int i = 0; i < linktbl->num; i++) { + LinkTable_free(linktbl->links[i]->next_table); + FREE(linktbl->links[i]); + } + FREE(linktbl->links); + FREE(linktbl); + } +} + +void LinkTable_print(LinkTable *linktbl) { + if (CONFIG.log_type & info) { int j = 0; + lprintf(info, "--------------------------------------------\n"); + lprintf(info, " LinkTable %p for %s\n", linktbl, linktbl->links[0]->f_url); + lprintf(info, "--------------------------------------------\n"); for (int i = 0; i < linktbl->num; i++) { - Link *this_link = linktbl->links[i]; - if (this_link->type == LINK_INVALID) { - this_link->type = LINK_UNINITIALISED_FILE; - j++; - } + Link *this_link = linktbl->links[i]; + lprintf(info, "%d %c %lu %s %s\n", i, this_link->type, + this_link->content_length, this_link->linkname, this_link->f_url); + if ((this_link->type != LINK_FILE) && (this_link->type != LINK_DIR) && + (this_link->type != LINK_HEAD)) { + j++; + } } - lprintf(debug, "%d invalid links\n", j); + lprintf(info, "--------------------------------------------\n"); + lprintf(info, " Invalid link count: %d\n", j); + lprintf(info, "--------------------------------------------\n"); + } } -void LinkTable_free(LinkTable *linktbl) -{ - if (linktbl) { - for (int i = 0; i < linktbl->num; i++) { - LinkTable_free(linktbl->links[i]->next_table); - FREE(linktbl->links[i]); - } - FREE(linktbl->links); - FREE(linktbl); - } +LinkTable *LinkTable_alloc(const char *url) { + LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); + linktbl->num = 0; + linktbl->index_time = 0; + linktbl->links = NULL; + + /* + * populate the base URL + */ + Link *head_link = Link_new("/", LINK_HEAD); + LinkTable_add(linktbl, head_link); + strncpy(head_link->f_url, url, MAX_PATH_LEN); + assert(linktbl->num == 1); + return linktbl; } -void LinkTable_print(LinkTable *linktbl) -{ - if (CONFIG.log_type & info) { - int j = 0; - lprintf(info, "--------------------------------------------\n"); - lprintf(info, " LinkTable %p for %s\n", linktbl, - linktbl->links[0]->f_url); - lprintf(info, "--------------------------------------------\n"); - for (int i = 0; i < linktbl->num; i++) { - Link *this_link = linktbl->links[i]; - lprintf(info, "%d %c %lu %s %s\n", - i, - this_link->type, - this_link->content_length, - this_link->linkname, this_link->f_url); - if ((this_link->type != LINK_FILE) - && (this_link->type != LINK_DIR) - && (this_link->type != LINK_HEAD)) { - j++; - } - } - lprintf(info, "--------------------------------------------\n"); - lprintf(info, " Invalid link count: %d\n", j); - lprintf(info, "--------------------------------------------\n"); - } -} +LinkTable *LinkTable_new(const char *url) { + LinkTable *linktbl = LinkTable_alloc(url); + linktbl->index_time = time(NULL); -LinkTable *LinkTable_alloc(const char *url) -{ - LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); - linktbl->num = 0; - linktbl->index_time = 0; - linktbl->links = NULL; + /* + * start downloading the base URL + */ + TransferStruct ts = Link_download_full(linktbl->links[0]); + if (ts.curr_size == 0) { + LinkTable_free(linktbl); + return NULL; + } + /* + * Otherwise parsed the received data + */ + GumboOutput *output = gumbo_parse(ts.data); + HTML_to_LinkTable(url, output->root, linktbl); + gumbo_destroy_output(&kGumboDefaultOptions, output); + FREE(ts.data); - /* - * populate the base URL - */ - Link *head_link = Link_new("/", LINK_HEAD); - LinkTable_add(linktbl, head_link); - strncpy(head_link->f_url, url, MAX_PATH_LEN); - assert(linktbl->num == 1); - return linktbl; -} - -LinkTable *LinkTable_new(const char *url) -{ - LinkTable *linktbl = LinkTable_alloc(url); - linktbl->index_time = time(NULL); - - /* - * start downloading the base URL - */ - TransferStruct ts = Link_download_full(linktbl->links[0]); - if (ts.curr_size == 0) { + int skip_fill = 0; + char *unescaped_path; + CURL *c = curl_easy_init(); + unescaped_path = curl_easy_unescape(c, url + ROOT_LINK_OFFSET, 0, NULL); + if (CACHE_SYSTEM_INIT) { + CacheDir_create(unescaped_path); + LinkTable *disk_linktbl; + disk_linktbl = LinkTable_disk_open(unescaped_path); + if (disk_linktbl) { + /* + * Check if we need to update the link table + */ + lprintf(debug, "disk_linktbl->num: %d, linktbl->num: %d\n", + disk_linktbl->num, linktbl->num); + if (disk_linktbl->num == linktbl->num) { LinkTable_free(linktbl); - return NULL; + linktbl = disk_linktbl; + skip_fill = 1; + } else { + LinkTable_free(disk_linktbl); + } } + } + if (!skip_fill) { /* - * Otherwise parsed the received data + * Fill in the link table */ - GumboOutput *output = gumbo_parse(ts.data); - HTML_to_LinkTable(url, output->root, linktbl); - gumbo_destroy_output(&kGumboDefaultOptions, output); - FREE(ts.data); - - int skip_fill = 0; - char *unescaped_path; - CURL *c = curl_easy_init(); - unescaped_path = - curl_easy_unescape(c, url + ROOT_LINK_OFFSET, 0, NULL); - if (CACHE_SYSTEM_INIT) { - CacheDir_create(unescaped_path); - LinkTable *disk_linktbl; - disk_linktbl = LinkTable_disk_open(unescaped_path); - if (disk_linktbl) { - /* - * Check if we need to update the link table - */ - lprintf(debug, - "disk_linktbl->num: %d, linktbl->num: %d\n", - disk_linktbl->num, linktbl->num); - if (disk_linktbl->num == linktbl->num) { - LinkTable_free(linktbl); - linktbl = disk_linktbl; - skip_fill = 1; - } else { - LinkTable_free(disk_linktbl); - } - } - } - - if (!skip_fill) { - /* - * Fill in the link table - */ - LinkTable_fill(linktbl); - } else { - /* - * Fill in the holes in the link table - */ - LinkTable_invalid_reset(linktbl); - LinkTable_uninitialised_fill(linktbl); - } - + LinkTable_fill(linktbl); + } else { /* - * Save the link table + * Fill in the holes in the link table */ - if (CACHE_SYSTEM_INIT) { - if (LinkTable_disk_save(linktbl, unescaped_path)) { - lprintf(error, "Failed to save the LinkTable!\n"); - } + LinkTable_invalid_reset(linktbl); + LinkTable_uninitialised_fill(linktbl); + } + + /* + * Save the link table + */ + if (CACHE_SYSTEM_INIT) { + if (LinkTable_disk_save(linktbl, unescaped_path)) { + lprintf(error, "Failed to save the LinkTable!\n"); } + } - curl_free(unescaped_path); - curl_easy_cleanup(c); + curl_free(unescaped_path); + curl_easy_cleanup(c); - LinkTable_print(linktbl); + LinkTable_print(linktbl); - return linktbl; + return linktbl; } -static void LinkTable_disk_delete(const char *dirn) -{ - char *metadirn = path_append(META_DIR, dirn); - char *path; - path = path_append(metadirn, "/.LinkTable"); - if (unlink(path)) { - lprintf(error, "unlink(%s): %s\n", path, strerror(errno)); - } - FREE(path); - FREE(metadirn); +static void LinkTable_disk_delete(const char *dirn) { + char *metadirn = path_append(META_DIR, dirn); + char *path; + path = path_append(metadirn, "/.LinkTable"); + if (unlink(path)) { + lprintf(error, "unlink(%s): %s\n", path, strerror(errno)); + } + FREE(path); + FREE(metadirn); } -int LinkTable_disk_save(LinkTable *linktbl, const char *dirn) -{ - char *metadirn = path_append(META_DIR, dirn); - char *path; - path = path_append(metadirn, "/.LinkTable"); - FILE *fp = fopen(path, "w"); - FREE(metadirn); +int LinkTable_disk_save(LinkTable *linktbl, const char *dirn) { + char *metadirn = path_append(META_DIR, dirn); + char *path; + path = path_append(metadirn, "/.LinkTable"); + FILE *fp = fopen(path, "w"); + FREE(metadirn); - if (!fp) { - lprintf(error, "fopen(%s): %s\n", path, strerror(errno)); - FREE(path); - return -1; - } + if (!fp) { + lprintf(error, "fopen(%s): %s\n", path, strerror(errno)); FREE(path); + return -1; + } + FREE(path); - fwrite(&linktbl->num, sizeof(int), 1, fp); - for (int i = 0; i < linktbl->num; i++) { - fwrite(linktbl->links[i]->linkname, sizeof(char), - MAX_FILENAME_LEN, fp); - fwrite(linktbl->links[i]->f_url, sizeof(char), MAX_PATH_LEN, fp); - fwrite(&linktbl->links[i]->type, sizeof(LinkType), 1, fp); - fwrite(&linktbl->links[i]->content_length, sizeof(size_t), 1, fp); - fwrite(&linktbl->links[i]->time, sizeof(long), 1, fp); - } + fwrite(&linktbl->num, sizeof(int), 1, fp); + for (int i = 0; i < linktbl->num; i++) { + fwrite(linktbl->links[i]->linkname, sizeof(char), MAX_FILENAME_LEN, fp); + fwrite(linktbl->links[i]->f_url, sizeof(char), MAX_PATH_LEN, fp); + fwrite(&linktbl->links[i]->type, sizeof(LinkType), 1, fp); + fwrite(&linktbl->links[i]->content_length, sizeof(size_t), 1, fp); + fwrite(&linktbl->links[i]->time, sizeof(long), 1, fp); + } - int res = 0; + int res = 0; - if (ferror(fp)) { - lprintf(error, "encountered ferror!\n"); - res = -1; - } + if (ferror(fp)) { + lprintf(error, "encountered ferror!\n"); + res = -1; + } - if (fclose(fp)) { - lprintf(error, - "cannot close the file pointer, %s\n", strerror(errno)); - res = -1; - } + if (fclose(fp)) { + lprintf(error, "cannot close the file pointer, %s\n", strerror(errno)); + res = -1; + } - return res; + return res; } /* This is necessary to get the compiler on some platforms to stop complaining about the fact that we're not using the return value of fread, when we know we aren't and that's fine. */ -static inline void ignore_value(int i) -{ - (void) i; -} +static inline void ignore_value(int i) { (void)i; } -LinkTable *LinkTable_disk_open(const char *dirn) -{ - char *metadirn = path_append(META_DIR, dirn); - char *path; - if (metadirn[strnlen(metadirn, MAX_PATH_LEN)] == '/') { - path = path_append(metadirn, ".LinkTable"); - } else { - path = path_append(metadirn, "/.LinkTable"); - } - FILE *fp = fopen(path, "r"); - FREE(metadirn); +LinkTable *LinkTable_disk_open(const char *dirn) { + char *metadirn = path_append(META_DIR, dirn); + char *path; + if (metadirn[strnlen(metadirn, MAX_PATH_LEN)] == '/') { + path = path_append(metadirn, ".LinkTable"); + } else { + path = path_append(metadirn, "/.LinkTable"); + } + FILE *fp = fopen(path, "r"); + FREE(metadirn); - if (!fp) { - FREE(path); - return NULL; - } - - LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); - - if (sizeof(int) != fread(&linktbl->num, sizeof(int), 1, fp)) { - /* - * reached EOF - */ - lprintf(error, "reached EOF!\n"); - LinkTable_free(linktbl); - LinkTable_disk_delete(dirn); - return NULL; - } - linktbl->links = CALLOC(linktbl->num, sizeof(Link *)); - for (int i = 0; i < linktbl->num; i++) { - linktbl->links[i] = CALLOC(1, sizeof(Link)); - /* The return values are safe to ignore here since we check them - immediately afterwards with feof() and ferror(). */ - ignore_value(fread(linktbl->links[i]->linkname, sizeof(char), - MAX_FILENAME_LEN, fp)); - ignore_value(fread(linktbl->links[i]->f_url, sizeof(char), - MAX_PATH_LEN, fp)); - ignore_value(fread(&linktbl->links[i]->type, sizeof(LinkType), 1, fp)); - ignore_value(fread(&linktbl->links[i]->content_length, - sizeof(size_t), 1, fp)); - ignore_value(fread(&linktbl->links[i]->time, sizeof(long), 1, fp)); - if (feof(fp)) { - /* - * reached EOF - */ - lprintf(error, "reached EOF!\n"); - LinkTable_free(linktbl); - LinkTable_disk_delete(dirn); - return NULL; - } - if (ferror(fp)) { - lprintf(error, "encountered ferror!\n"); - LinkTable_free(linktbl); - LinkTable_disk_delete(dirn); - return NULL; - } - } - if (fclose(fp)) { - lprintf(error, - "cannot close the file pointer, %s\n", strerror(errno)); - } - return linktbl; -} - -LinkTable *path_to_Link_LinkTable_new(const char *path) -{ - Link *link = NULL; - Link *tmp_link = NULL; - Link link_cpy = { 0 }; - LinkTable *next_table = NULL; - if (!strcmp(path, "/")) { - next_table = ROOT_LINK_TBL; - link_cpy = *next_table->links[0]; - tmp_link = &link_cpy; - } else { - link = path_to_Link(path); - tmp_link = link; - } - - if (next_table) { - time_t time_now = time(NULL); - if (time_now - next_table->index_time > CONFIG.refresh_timeout) { - /* refresh directory contents */ - LinkTable_free(next_table); - next_table = NULL; - if (link) { - link->next_table = NULL; - } - } - } - if (!next_table) { - if (CONFIG.mode == NORMAL) { - next_table = LinkTable_new(tmp_link->f_url); - } else if (CONFIG.mode == SINGLE) { - next_table = single_LinkTable_new(tmp_link->f_url); - } else if (CONFIG.mode == SONIC) { - if (!CONFIG.sonic_id3) { - next_table = sonic_LinkTable_new_index(tmp_link->sonic.id); - } else { - next_table = - sonic_LinkTable_new_id3(tmp_link->sonic.depth, - tmp_link->sonic.id); - } - } else { - lprintf(fatal, "Invalid CONFIG.mode: %d\n", CONFIG.mode); - } - } - if (link) { - link->next_table = next_table; - } else { - ROOT_LINK_TBL = next_table; - } - return next_table; -} - -static Link *path_to_Link_recursive(char *path, LinkTable *linktbl) -{ - /* - * skip the leading '/' if it exists - */ - if (*path == '/') { - path++; - } - - /* - * remove the last '/' if it exists - */ - char *slash = &(path[strnlen(path, MAX_PATH_LEN) - 1]); - if (*slash == '/') { - *slash = '\0'; - } - - slash = strchr(path, '/'); - if (slash == NULL) { - /* - * We cannot find another '/', we have reached the last level - */ - for (int i = 1; i < linktbl->num; i++) { - if (!strncmp - (path, linktbl->links[i]->linkname, MAX_FILENAME_LEN)) { - /* - * We found our link - */ - return linktbl->links[i]; - } - } - } else { - /* - * We can still find '/', time to consume the path and traverse - * the tree structure - */ - - /* - * add termination mark to the current string, - * effective create two substrings - */ - *slash = '\0'; - /* - * move the pointer past the '/' - */ - char *next_path = slash + 1; - for (int i = 1; i < linktbl->num; i++) { - if (!strncmp - (path, linktbl->links[i]->linkname, MAX_FILENAME_LEN)) { - /* - * The next sub-directory exists - */ - LinkTable *next_table = linktbl->links[i]->next_table; - if (!next_table) { - if (CONFIG.mode == NORMAL) { - next_table = - LinkTable_new(linktbl->links[i]->f_url); - } else if (CONFIG.mode == SONIC) { - if (!CONFIG.sonic_id3) { - next_table = - sonic_LinkTable_new_index - (linktbl->links[i]->sonic.id); - } else { - next_table = - sonic_LinkTable_new_id3 - (linktbl->links - [i]->sonic.depth, - linktbl->links[i]->sonic.id); - } - } else { - lprintf(fatal, "Invalid CONFIG.mode\n"); - } - } - linktbl->links[i]->next_table = next_table; - return path_to_Link_recursive(next_path, next_table); - } - } - } + if (!fp) { + FREE(path); return NULL; + } + + LinkTable *linktbl = CALLOC(1, sizeof(LinkTable)); + + if (sizeof(int) != fread(&linktbl->num, sizeof(int), 1, fp)) { + /* + * reached EOF + */ + lprintf(error, "reached EOF!\n"); + LinkTable_free(linktbl); + LinkTable_disk_delete(dirn); + return NULL; + } + linktbl->links = CALLOC(linktbl->num, sizeof(Link *)); + for (int i = 0; i < linktbl->num; i++) { + linktbl->links[i] = CALLOC(1, sizeof(Link)); + /* The return values are safe to ignore here since we check them + immediately afterwards with feof() and ferror(). */ + ignore_value( + fread(linktbl->links[i]->linkname, sizeof(char), MAX_FILENAME_LEN, fp)); + ignore_value( + fread(linktbl->links[i]->f_url, sizeof(char), MAX_PATH_LEN, fp)); + ignore_value(fread(&linktbl->links[i]->type, sizeof(LinkType), 1, fp)); + ignore_value( + fread(&linktbl->links[i]->content_length, sizeof(size_t), 1, fp)); + ignore_value(fread(&linktbl->links[i]->time, sizeof(long), 1, fp)); + if (feof(fp)) { + /* + * reached EOF + */ + lprintf(error, "reached EOF!\n"); + LinkTable_free(linktbl); + LinkTable_disk_delete(dirn); + return NULL; + } + if (ferror(fp)) { + lprintf(error, "encountered ferror!\n"); + LinkTable_free(linktbl); + LinkTable_disk_delete(dirn); + return NULL; + } + } + if (fclose(fp)) { + lprintf(error, "cannot close the file pointer, %s\n", strerror(errno)); + } + return linktbl; } -Link *path_to_Link(const char *path) -{ - lprintf(link_lock_debug, - "thread %x: locking link_lock;\n", pthread_self()); +LinkTable *path_to_Link_LinkTable_new(const char *path) { + Link *link = NULL; + Link *tmp_link = NULL; + Link link_cpy = {0}; + LinkTable *next_table = NULL; + if (!strcmp(path, "/")) { + next_table = ROOT_LINK_TBL; + link_cpy = *next_table->links[0]; + tmp_link = &link_cpy; + } else { + link = path_to_Link(path); + tmp_link = link; + } - PTHREAD_MUTEX_LOCK(&link_lock); - char *new_path = strndup(path, MAX_PATH_LEN); - if (!new_path) { - lprintf(fatal, "cannot allocate memory\n"); + if (next_table) { + time_t time_now = time(NULL); + if (time_now - next_table->index_time > CONFIG.refresh_timeout) { + /* refresh directory contents */ + LinkTable_free(next_table); + next_table = NULL; + if (link) { + link->next_table = NULL; + } } - Link *link = path_to_Link_recursive(new_path, ROOT_LINK_TBL); - FREE(new_path); - - lprintf(link_lock_debug, - "thread %x: unlocking link_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&link_lock); - return link; + } + if (!next_table) { + if (CONFIG.mode == NORMAL) { + next_table = LinkTable_new(tmp_link->f_url); + } else if (CONFIG.mode == SINGLE) { + next_table = single_LinkTable_new(tmp_link->f_url); + } else if (CONFIG.mode == SONIC) { + if (!CONFIG.sonic_id3) { + next_table = sonic_LinkTable_new_index(tmp_link->sonic.id); + } else { + next_table = + sonic_LinkTable_new_id3(tmp_link->sonic.depth, tmp_link->sonic.id); + } + } else { + lprintf(fatal, "Invalid CONFIG.mode: %d\n", CONFIG.mode); + } + } + if (link) { + link->next_table = next_table; + } else { + ROOT_LINK_TBL = next_table; + } + return next_table; } -TransferStruct Link_download_full(Link *link) -{ - char *url = link->f_url; - CURL *curl = Link_to_curl(link); +static Link *path_to_Link_recursive(char *path, LinkTable *linktbl) { + /* + * skip the leading '/' if it exists + */ + if (*path == '/') { + path++; + } - TransferStruct ts; - ts.curr_size = 0; - ts.data = NULL; - ts.type = DATA; - ts.transferring = 1; + /* + * remove the last '/' if it exists + */ + char *slash = &(path[strnlen(path, MAX_PATH_LEN) - 1]); + if (*slash == '/') { + *slash = '\0'; + } - CURLcode ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &ts); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, (void *) &ts); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); + slash = strchr(path, '/'); + if (slash == NULL) { + /* + * We cannot find another '/', we have reached the last level + */ + for (int i = 1; i < linktbl->num; i++) { + if (!strncmp(path, linktbl->links[i]->linkname, MAX_FILENAME_LEN)) { + /* + * We found our link + */ + return linktbl->links[i]; + } } + } else { + /* + * We can still find '/', time to consume the path and traverse + * the tree structure + */ /* - * If we get temporary HTTP failure, wait for 5 seconds before retry + * add termination mark to the current string, + * effective create two substrings */ - long http_resp = 0; - do { - transfer_blocking(curl); - ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); + *slash = '\0'; + /* + * move the pointer past the '/' + */ + char *next_path = slash + 1; + for (int i = 1; i < linktbl->num; i++) { + if (!strncmp(path, linktbl->links[i]->linkname, MAX_FILENAME_LEN)) { + /* + * The next sub-directory exists + */ + LinkTable *next_table = linktbl->links[i]->next_table; + if (!next_table) { + if (CONFIG.mode == NORMAL) { + next_table = LinkTable_new(linktbl->links[i]->f_url); + } else if (CONFIG.mode == SONIC) { + if (!CONFIG.sonic_id3) { + next_table = + sonic_LinkTable_new_index(linktbl->links[i]->sonic.id); + } else { + next_table = sonic_LinkTable_new_id3( + linktbl->links[i]->sonic.depth, linktbl->links[i]->sonic.id); + } + } else { + lprintf(fatal, "Invalid CONFIG.mode\n"); + } } - if (HTTP_temp_failure(http_resp)) { - lprintf(warning, - "URL: %s, HTTP %ld, retrying later.\n", - url, http_resp); - sleep(CONFIG.http_wait_sec); - } else if (http_resp != HTTP_OK) { - lprintf(warning, - "cannot retrieve URL: %s, HTTP %ld\n", url, http_resp); - ts.curr_size = 0; - free(ts.data); /* not FREE(); can be NULL on error path! */ - curl_easy_cleanup(curl); - return ts; - } - } while (HTTP_temp_failure(http_resp)); - - ret = curl_easy_getinfo(curl, CURLINFO_FILETIME, &(link->time)); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); + linktbl->links[i]->next_table = next_table; + return path_to_Link_recursive(next_path, next_table); + } } - curl_easy_cleanup(curl); - return ts; + } + return NULL; +} + +Link *path_to_Link(const char *path) { + lprintf(link_lock_debug, "thread %x: locking link_lock;\n", pthread_self()); + + PTHREAD_MUTEX_LOCK(&link_lock); + char *new_path = strndup(path, MAX_PATH_LEN); + if (!new_path) { + lprintf(fatal, "cannot allocate memory\n"); + } + Link *link = path_to_Link_recursive(new_path, ROOT_LINK_TBL); + FREE(new_path); + + lprintf(link_lock_debug, "thread %x: unlocking link_lock;\n", pthread_self()); + PTHREAD_MUTEX_UNLOCK(&link_lock); + return link; +} + +TransferStruct Link_download_full(Link *link) { + char *url = link->f_url; + CURL *curl = Link_to_curl(link); + + TransferStruct ts; + ts.curr_size = 0; + ts.data = NULL; + ts.type = DATA; + ts.transferring = 1; + + CURLcode ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, (void *)&ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + + /* + * If we get temporary HTTP failure, wait for 5 seconds before retry + */ + long http_resp = 0; + do { + transfer_blocking(curl); + ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + if (HTTP_temp_failure(http_resp)) { + lprintf(warning, "URL: %s, HTTP %ld, retrying later.\n", url, http_resp); + sleep(CONFIG.http_wait_sec); + } else if (http_resp != HTTP_OK) { + lprintf(warning, "cannot retrieve URL: %s, HTTP %ld\n", url, http_resp); + ts.curr_size = 0; + free(ts.data); /* not FREE(); can be NULL on error path! */ + curl_easy_cleanup(curl); + return ts; + } + } while (HTTP_temp_failure(http_resp)); + + ret = curl_easy_getinfo(curl, CURLINFO_FILETIME, &(link->time)); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + curl_easy_cleanup(curl); + return ts; } static CURL *Link_download_curl_setup(Link *link, size_t req_size, off_t offset, TransferStruct *header, - TransferStruct *ts) -{ - if (!link) { - lprintf(fatal, "Invalid supplied\n"); - } + TransferStruct *ts) { + if (!link) { + lprintf(fatal, "Invalid supplied\n"); + } - size_t start = offset; - size_t end = start + req_size - 1; + size_t start = offset; + size_t end = start + req_size - 1; - char range_str[64]; - snprintf(range_str, sizeof(range_str), "%lu-%lu", start, end); - lprintf(debug, "%s: %s\n", link->linkname, range_str); + char range_str[64]; + snprintf(range_str, sizeof(range_str), "%lu-%lu", start, end); + lprintf(debug, "%s: %s\n", link->linkname, range_str); - CURL *curl = Link_to_curl(link); - CURLcode ret = - curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *) header); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) ts); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, (void *) ts); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ret = curl_easy_setopt(curl, CURLOPT_RANGE, range_str); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } + CURL *curl = Link_to_curl(link); + CURLcode ret = curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)header); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_PRIVATE, (void *)ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + ret = curl_easy_setopt(curl, CURLOPT_RANGE, range_str); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } - return curl; + return curl; } -static curl_off_t Link_download_cleanup(CURL *curl, TransferStruct *header) -{ - /* - * Check for range seek support - */ - if (!CONFIG.no_range_check) { - if (!strcasestr((header->data), "Accept-Ranges: bytes")) { - fprintf(stderr, "This web server does not support HTTP \ +static curl_off_t Link_download_cleanup(CURL *curl, TransferStruct *header) { + /* + * Check for range seek support + */ + if (!CONFIG.no_range_check) { + if (!strcasestr((header->data), "Accept-Ranges: bytes")) { + fprintf(stderr, "This web server does not support HTTP \ range requests\n"); - exit(EXIT_FAILURE); - } + exit(EXIT_FAILURE); } + } - FREE(header->data); + FREE(header->data); - long http_resp; - CURLcode ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - if ((http_resp != HTTP_OK) && - (http_resp != HTTP_PARTIAL_CONTENT) && - (http_resp != HTTP_RANGE_NOT_SATISFIABLE)) { - char *url; - curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); - lprintf(warning, "Could not download %s, HTTP %ld\n", url, http_resp); - return -ENOENT; - } + long http_resp; + CURLcode ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + if ((http_resp != HTTP_OK) && (http_resp != HTTP_PARTIAL_CONTENT) && + (http_resp != HTTP_RANGE_NOT_SATISFIABLE)) { + char *url; + curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); + lprintf(warning, "Could not download %s, HTTP %ld\n", url, http_resp); + return -ENOENT; + } - curl_off_t recv; - ret = curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T, &recv); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } + curl_off_t recv; + ret = curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T, &recv); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } - curl_easy_cleanup(curl); + curl_easy_cleanup(curl); - return recv; + return recv; } -long Link_download(Link *link, char *output_buf, size_t req_size, off_t offset) -{ - TransferStruct ts; - ts.curr_size = 0; - ts.data = NULL; - ts.type = DATA; - ts.transferring = 1; +long Link_download(Link *link, char *output_buf, size_t req_size, + off_t offset) { + TransferStruct ts; + ts.curr_size = 0; + ts.data = NULL; + ts.type = DATA; + ts.transferring = 1; - TransferStruct header; - header.curr_size = 0; - header.data = NULL; + TransferStruct header; + header.curr_size = 0; + header.data = NULL; - if (offset + req_size > link->content_length) { - lprintf(error, - "requested size too large, req_size: %lu, recv: %ld, content-length: %ld\n", - req_size, recv, link->content_length); - req_size = link->content_length - offset; - } + if (offset + req_size > link->content_length) { + lprintf(error, + "requested size too large, req_size: %lu, recv: %ld, " + "content-length: %ld\n", + req_size, recv, link->content_length); + req_size = link->content_length - offset; + } - CURL *curl = Link_download_curl_setup(link, req_size, offset, &header, &ts); + CURL *curl = Link_download_curl_setup(link, req_size, offset, &header, &ts); - transfer_blocking(curl); + transfer_blocking(curl); - curl_off_t recv = Link_download_cleanup(curl, &header); + curl_off_t recv = Link_download_cleanup(curl, &header); - /* The extra 1 byte is probably for '\0' */ - if (recv - 1 == (long int) req_size) { - recv--; - } else if (offset + req_size < link->content_length) { - lprintf(error, "req_size: %lu, recv: %ld\n", req_size, recv); - } + /* The extra 1 byte is probably for '\0' */ + if (recv - 1 == (long int)req_size) { + recv--; + } else if (offset + req_size < link->content_length) { + lprintf(error, "req_size: %lu, recv: %ld\n", req_size, recv); + } - memmove(output_buf, ts.data, recv); - FREE(ts.data); + memmove(output_buf, ts.data, recv); + FREE(ts.data); - return recv; + return recv; } long path_download(const char *path, char *output_buf, size_t req_size, - off_t offset) -{ - if (!path) { - lprintf(fatal, "NULL path supplied\n"); - } + off_t offset) { + if (!path) { + lprintf(fatal, "NULL path supplied\n"); + } - Link *link; - link = path_to_Link(path); - if (!link) { - return -ENOENT; - } + Link *link; + link = path_to_Link(path); + if (!link) { + return -ENOENT; + } - return Link_download(link, output_buf, req_size, offset); + return Link_download(link, output_buf, req_size, offset); } -static void make_link_relative(const char *page_url, char *link_url) -{ - /* - Some servers make the links to subdirectories absolute (in URI terms: - path-absolute), but our code expects them to be relative (in URI terms: - path-noscheme), so change the contents of link_url as needed to - accommodate that. +static void make_link_relative(const char *page_url, char *link_url) { + /* + Some servers make the links to subdirectories absolute (in URI terms: + path-absolute), but our code expects them to be relative (in URI terms: + path-noscheme), so change the contents of link_url as needed to + accommodate that. - Also, some servers serve their links as `./name`. This is helpful to - them because it is the only way to express relative references when the - first new path segment of the target contains an unescaped colon (`:`), - eg in `./6:1-balun.png`. While stripping the ./ strictly speaking - reintroduces that ambiguity, it is of little practical concern in this - implementation, as full URI link targets are filtered by their number of - slashes anyway. In URI terms, this converts path-noscheme with a leading - `.` segment into path-noscheme or path-rootless without that segment. - */ + Also, some servers serve their links as `./name`. This is helpful to + them because it is the only way to express relative references when the + first new path segment of the target contains an unescaped colon (`:`), + eg in `./6:1-balun.png`. While stripping the ./ strictly speaking + reintroduces that ambiguity, it is of little practical concern in this + implementation, as full URI link targets are filtered by their number of + slashes anyway. In URI terms, this converts path-noscheme with a leading + `.` segment into path-noscheme or path-rootless without that segment. + */ - if (link_url[0] == '.' && link_url[1] == '/') { - memmove(link_url, link_url + 2, strlen(link_url) - 1); - return; - } + if (link_url[0] == '.' && link_url[1] == '/') { + memmove(link_url, link_url + 2, strlen(link_url) - 1); + return; + } - if (link_url[0] != '/') { - /* Already relative, nothing to do here! + if (link_url[0] != '/') { + /* Already relative, nothing to do here! - (Full URIs, eg. `http://example.com/path`, pass through here - unmodified, but those are classified in different LinkTypes later - anyway). - */ - return; - } + (Full URIs, eg. `http://example.com/path`, pass through here + unmodified, but those are classified in different LinkTypes later + anyway). + */ + return; + } - /* Find the slash after the host name. */ - int slashes_left_to_find = 3; - while (*page_url) { - if (*page_url == '/' && ! --slashes_left_to_find) - break; - /* N.B. This is here, rather than doing `while (*page_url++)`, because - when we're done we want the pointer to point at the final slash. */ - page_url++; - } - if (slashes_left_to_find) { - if (slashes_left_to_find == 1 && ! *page_url) - /* We're at the top level of the web site and the user entered the URL - without a trailing slash. */ - page_url = "/"; - else - /* Well, that's odd. Let's return rather than trying to dig ourselves - deeper into whatever hole we're in. */ - return; - } - /* The page URL is no longer the full page_url, it's just the part after - the host name. */ - /* The link URL should start with the page URL. */ - if (strstr(link_url, page_url) != link_url) - return; - int skip_len = strlen(page_url); - if (page_url[skip_len-1] != '/') { - if (page_url[skip_len] != '/') - /* Um, I'm not sure what to do here, so give up. */ - return; - skip_len++; - } - /* Move the part of the link URL after the parent page's pat to - the beginning of the link URL string, discarding what came - before it. */ - memmove(link_url, link_url + skip_len, strlen(link_url) - skip_len + 1); + /* Find the slash after the host name. */ + int slashes_left_to_find = 3; + while (*page_url) { + if (*page_url == '/' && !--slashes_left_to_find) + break; + /* N.B. This is here, rather than doing `while (*page_url++)`, because + when we're done we want the pointer to point at the final slash. */ + page_url++; + } + if (slashes_left_to_find) { + if (slashes_left_to_find == 1 && !*page_url) + /* We're at the top level of the web site and the user entered the URL + without a trailing slash. */ + page_url = "/"; + else + /* Well, that's odd. Let's return rather than trying to dig ourselves + deeper into whatever hole we're in. */ + return; + } + /* The page URL is no longer the full page_url, it's just the part after + the host name. */ + /* The link URL should start with the page URL. */ + if (strstr(link_url, page_url) != link_url) + return; + int skip_len = strlen(page_url); + if (page_url[skip_len - 1] != '/') { + if (page_url[skip_len] != '/') + /* Um, I'm not sure what to do here, so give up. */ + return; + skip_len++; + } + /* Move the part of the link URL after the parent page's pat to + the beginning of the link URL string, discarding what came + before it. */ + memmove(link_url, link_url + skip_len, strlen(link_url) - skip_len + 1); } diff --git a/src/link.h b/src/link.h index 73c30dc..dc94d7b 100644 --- a/src/link.h +++ b/src/link.h @@ -20,11 +20,11 @@ typedef struct LinkTable LinkTable; * \brief the link type */ typedef enum { - LINK_HEAD = 'H', - LINK_DIR = 'D', - LINK_FILE = 'F', - LINK_INVALID = 'I', - LINK_UNINITIALISED_FILE = 'U' + LINK_HEAD = 'H', + LINK_DIR = 'D', + LINK_FILE = 'F', + LINK_INVALID = 'I', + LINK_UNINITIALISED_FILE = 'U' } LinkType; /** @@ -32,32 +32,32 @@ typedef enum { * \details index 0 contains the Link for the base URL */ struct LinkTable { - int num; - time_t index_time; - Link **links; + int num; + time_t index_time; + Link **links; }; /** * \brief Link type data structure */ struct Link { - /** \brief The link name in the last level of the URL */ - char linkname[MAX_FILENAME_LEN + 1]; - char linkpath[MAX_FILENAME_LEN + 1]; - /** \brief The full URL of the file */ - char f_url[MAX_PATH_LEN + 1]; - /** \brief The type of the link */ - LinkType type; - /** \brief CURLINFO_CONTENT_LENGTH_DOWNLOAD of the file */ - size_t content_length; - /** \brief The next LinkTable level, if it is a LINK_DIR */ - LinkTable *next_table; - /** \brief CURLINFO_FILETIME obtained from the server */ - long time; - /** \brief The pointer associated with the cache file */ - Cache *cache_ptr; - /** \brief Stores *sonic related data */ - Sonic sonic; + /** \brief The link name in the last level of the URL */ + char linkname[MAX_FILENAME_LEN + 1]; + char linkpath[MAX_FILENAME_LEN + 1]; + /** \brief The full URL of the file */ + char f_url[MAX_PATH_LEN + 1]; + /** \brief The type of the link */ + LinkType type; + /** \brief CURLINFO_CONTENT_LENGTH_DOWNLOAD of the file */ + size_t content_length; + /** \brief The next LinkTable level, if it is a LINK_DIR */ + LinkTable *next_table; + /** \brief CURLINFO_FILETIME obtained from the server */ + long time; + /** \brief The pointer associated with the cache file */ + Cache *cache_ptr; + /** \brief Stores *sonic related data */ + Sonic sonic; }; /** @@ -96,8 +96,7 @@ long path_download(const char *path, char *output_buf, size_t size, * \brief Download a Link * \return the number of bytes downloaded */ -long Link_download(Link *link, char *output_buf, size_t req_size, - off_t offset); +long Link_download(Link *link, char *output_buf, size_t req_size, off_t offset); /** * \brief find the link associated with a path diff --git a/src/log.c b/src/log.c index 0010dd9..ec2a6f1 100644 --- a/src/log.c +++ b/src/log.c @@ -9,60 +9,55 @@ #include #include -int log_level_init() -{ - char *env = getenv("HTTPDIRFS_LOG_LEVEL"); - if (env) { - return atoi(env); +int log_level_init() { + char *env = getenv("HTTPDIRFS_LOG_LEVEL"); + if (env) { + return atoi(env); + } + return DEFAULT_LOG_LEVEL; +} + +void log_printf(LogType type, const char *file, const char *func, int line, + const char *format, ...) { + if (type & CONFIG.log_type) { + switch (type) { + case fatal: + fprintf(stderr, "Fatal:"); + break; + case error: + fprintf(stderr, "Error:"); + break; + case warning: + fprintf(stderr, "Warning:"); + break; + case info: + goto print_actual_message; + default: + fprintf(stderr, "Debug(%x):", type); + break; } - return DEFAULT_LOG_LEVEL; -} -void -log_printf(LogType type, const char *file, const char *func, int line, - const char *format, ...) -{ - if (type & CONFIG.log_type) { - switch (type) { - case fatal: - fprintf(stderr, "Fatal:"); - break; - case error: - fprintf(stderr, "Error:"); - break; - case warning: - fprintf(stderr, "Warning:"); - break; - case info: - goto print_actual_message; - default: - fprintf(stderr, "Debug(%x):", type); - break; - } + fprintf(stderr, "%s:%d:", file, line); - fprintf(stderr, "%s:%d:", file, line); + print_actual_message: {} + fprintf(stderr, "%s: ", func); + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); -print_actual_message: { - } - fprintf(stderr, "%s: ", func); - va_list args; - va_start(args, format); - vfprintf(stderr, format, args); - va_end(args); - - if (type == fatal) { - exit_failure(); - } + if (type == fatal) { + exit_failure(); } + } } -void print_version() -{ - /* FUSE prints its help to stderr */ - fprintf(stderr, "HTTPDirFS version " VERSION "\n"); - /* - * --------- Print off SSL engine version --------- - */ - curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); - fprintf(stderr, "libcurl SSL engine: %s\n", data->ssl_version); +void print_version() { + /* FUSE prints its help to stderr */ + fprintf(stderr, "HTTPDirFS version " VERSION "\n"); + /* + * --------- Print off SSL engine version --------- + */ + curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); + fprintf(stderr, "libcurl SSL engine: %s\n", data->ssl_version); } diff --git a/src/log.h b/src/log.h index 1bdfbfa..0cbdad6 100644 --- a/src/log.h +++ b/src/log.h @@ -5,16 +5,16 @@ * \brief Log types */ typedef enum { - fatal = 1 << 0, - error = 1 << 1, - warning = 1 << 2, - info = 1 << 3, - debug = 1 << 4, - link_lock_debug = 1 << 5, - network_lock_debug = 1 << 6, - cache_lock_debug = 1 << 7, - memcache_debug = 1 << 8, - libcurl_debug = 1 << 9, + fatal = 1 << 0, + error = 1 << 1, + warning = 1 << 2, + info = 1 << 3, + debug = 1 << 4, + link_lock_debug = 1 << 5, + network_lock_debug = 1 << 6, + cache_lock_debug = 1 << 7, + memcache_debug = 1 << 8, + libcurl_debug = 1 << 9, } LogType; /** @@ -38,8 +38,8 @@ void log_printf(LogType type, const char *file, const char *func, int line, * \brief Log type printf * \details This macro automatically prints out the filename and line number */ -#define lprintf(type, ...) \ - log_printf(type, __FILE__, __func__, __LINE__, __VA_ARGS__); +#define lprintf(type, ...) \ + log_printf(type, __FILE__, __func__, __LINE__, __VA_ARGS__); #endif /** diff --git a/src/main.c b/src/main.c index 06089d5..a633081 100644 --- a/src/main.c +++ b/src/main.c @@ -10,343 +10,330 @@ void add_arg(char ***fuse_argv_ptr, int *fuse_argc, char *opt_string); static void print_help(char *program_name, int long_help); static void print_long_help(); -static int -parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc); +static int parse_arg_list(int argc, char **argv, char ***fuse_argv, + int *fuse_argc); void parse_config_file(char ***argv, int *argc); static char *config_path = NULL; -int main(int argc, char **argv) -{ - /* - * Automatically print help if not enough arguments are supplied - */ - if (argc < 2) { - print_help(argv[0], 0); - fprintf(stderr, "For more information, run \"%s --help.\"\n", - argv[0]); - exit(EXIT_FAILURE); +int main(int argc, char **argv) { + /* + * Automatically print help if not enough arguments are supplied + */ + if (argc < 2) { + print_help(argv[0], 0); + fprintf(stderr, "For more information, run \"%s --help.\"\n", argv[0]); + exit(EXIT_FAILURE); + } + + /* + * These are passed into fuse initialiser + */ + char **fuse_argv = NULL; + int fuse_argc = 0; + /* + * These are the combined argument with the config file + */ + char **all_argv = NULL; + int all_argc = 0; + + /*--- Add the program's name to the combined argument list ---*/ + add_arg(&all_argv, &all_argc, argv[0]); + /*--- FUSE expects the first initialisation to be the program's name ---*/ + add_arg(&fuse_argv, &fuse_argc, argv[0]); + + /* + * initialise network configuration struct + */ + Config_init(); + + /* + * initialise network subsystem + */ + NetworkSystem_init(); + + /* + * Copy the command line argument list to the combined argument list + */ + for (int i = 1; i < argc; i++) { + add_arg(&all_argv, &all_argc, argv[i]); + if (!strcmp(argv[i], "--config")) { + config_path = strdup(argv[i + 1]); } + } + /* + * parse the config file, if it exists, store it in all_argv and + * all_argc + */ + parse_config_file(&all_argv, &all_argc); + + /* + * parse the combined argument list + */ + if (parse_arg_list(all_argc, all_argv, &fuse_argv, &fuse_argc)) { /* - * These are passed into fuse initialiser + * The user basically didn't supply enough arguments, if we reach here + * The point is to print some error messages */ - char **fuse_argv = NULL; - int fuse_argc = 0; - /* - * These are the combined argument with the config file - */ - char **all_argv = NULL; - int all_argc = 0; + goto fuse_start; + } - /*--- Add the program's name to the combined argument list ---*/ - add_arg(&all_argv, &all_argc, argv[0]); - /*--- FUSE expects the first initialisation to be the program's name ---*/ - add_arg(&fuse_argv, &fuse_argc, argv[0]); + /*--- Add the last remaining argument, which is the mountpoint ---*/ + add_arg(&fuse_argv, &fuse_argc, argv[argc - 1]); - /* - * initialise network configuration struct - */ - Config_init(); - - /* - * initialise network subsystem - */ - NetworkSystem_init(); - - /* - * Copy the command line argument list to the combined argument list - */ - for (int i = 1; i < argc; i++) { - add_arg(&all_argv, &all_argc, argv[i]); - if (!strcmp(argv[i], "--config")) { - config_path = strdup(argv[i + 1]); - } - } - - /* - * parse the config file, if it exists, store it in all_argv and - * all_argc - */ - parse_config_file(&all_argv, &all_argc); - - /* - * parse the combined argument list - */ - if (parse_arg_list(all_argc, all_argv, &fuse_argv, &fuse_argc)) { - /* - * The user basically didn't supply enough arguments, if we reach here - * The point is to print some error messages - */ - goto fuse_start; - } - - /*--- Add the last remaining argument, which is the mountpoint ---*/ - add_arg(&fuse_argv, &fuse_argc, argv[argc - 1]); - - /* - * The second last remaining argument is the URL - */ - char *base_url = argv[argc - 2]; - if (strncmp(base_url, "http://", 7) - && strncmp(base_url, "https://", 8)) { - fprintf(stderr, "Error: Please supply a valid URL.\n"); - print_help(argv[0], 0); - exit(EXIT_FAILURE); - } else { - if (CONFIG.sonic_username && CONFIG.sonic_password) { - CONFIG.mode = SONIC; - } else if (CONFIG.sonic_username || CONFIG.sonic_password) { - fprintf(stderr, - "Error: You have to supply both username and password to \ + /* + * The second last remaining argument is the URL + */ + char *base_url = argv[argc - 2]; + if (strncmp(base_url, "http://", 7) && strncmp(base_url, "https://", 8)) { + fprintf(stderr, "Error: Please supply a valid URL.\n"); + print_help(argv[0], 0); + exit(EXIT_FAILURE); + } else { + if (CONFIG.sonic_username && CONFIG.sonic_password) { + CONFIG.mode = SONIC; + } else if (CONFIG.sonic_username || CONFIG.sonic_password) { + fprintf(stderr, "Error: You have to supply both username and password to \ activate Sonic mode.\n"); - exit(EXIT_FAILURE); - } - if (!LinkSystem_init(base_url)) { - fprintf(stderr, "Network initialisation failed.\n"); - exit(EXIT_FAILURE); - } + exit(EXIT_FAILURE); } + if (!LinkSystem_init(base_url)) { + fprintf(stderr, "Network initialisation failed.\n"); + exit(EXIT_FAILURE); + } + } fuse_start: - fuse_local_init(fuse_argc, fuse_argv); + fuse_local_init(fuse_argc, fuse_argv); - return 0; + return 0; } -void parse_config_file(char ***argv, int *argc) -{ - char *full_path; - if (!config_path) { - char *xdg_config_home = getenv("XDG_CONFIG_HOME"); - if (!xdg_config_home) { - char *home = getenv("HOME"); - char *xdg_config_home_default = "/.config"; - xdg_config_home = path_append(home, xdg_config_home_default); - } - full_path = path_append(xdg_config_home, "/httpdirfs/config"); - } else { - full_path = config_path; +void parse_config_file(char ***argv, int *argc) { + char *full_path; + if (!config_path) { + char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + if (!xdg_config_home) { + char *home = getenv("HOME"); + char *xdg_config_home_default = "/.config"; + xdg_config_home = path_append(home, xdg_config_home_default); } + full_path = path_append(xdg_config_home, "/httpdirfs/config"); + } else { + full_path = config_path; + } - /* - * The buffer has to be able to fit a URL - */ - int buf_len = MAX_PATH_LEN; - char buf[buf_len]; - FILE *config = fopen(full_path, "r"); - if (config) { - while (fgets(buf, buf_len, config)) { - if (buf[0] == '-') { - (*argc)++; - buf[strnlen(buf, buf_len) - 1] = '\0'; - char *space; - space = strchr(buf, ' '); - if (!space) { - *argv = realloc(*argv, *argc * sizeof(char *)); - (*argv)[*argc - 1] = strndup(buf, buf_len); - } else { - (*argc)++; - *argv = realloc(*argv, *argc * sizeof(char *)); - /* - * Only copy up to the space character - */ - (*argv)[*argc - 2] = strndup(buf, space - buf); - /* - * Starts copying after the space - */ - (*argv)[*argc - 1] = strndup(space + 1, - buf_len - - (space + 1 - buf)); - } - } + /* + * The buffer has to be able to fit a URL + */ + int buf_len = MAX_PATH_LEN; + char buf[buf_len]; + FILE *config = fopen(full_path, "r"); + if (config) { + while (fgets(buf, buf_len, config)) { + if (buf[0] == '-') { + (*argc)++; + buf[strnlen(buf, buf_len) - 1] = '\0'; + char *space; + space = strchr(buf, ' '); + if (!space) { + *argv = realloc(*argv, *argc * sizeof(char *)); + (*argv)[*argc - 1] = strndup(buf, buf_len); + } else { + (*argc)++; + *argv = realloc(*argv, *argc * sizeof(char *)); + /* + * Only copy up to the space character + */ + (*argv)[*argc - 2] = strndup(buf, space - buf); + /* + * Starts copying after the space + */ + (*argv)[*argc - 1] = strndup(space + 1, buf_len - (space + 1 - buf)); } - fclose(config); + } } - FREE(full_path); + fclose(config); + } + FREE(full_path); } -static int -parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc) -{ - int c; - int long_index = 0; - const char *short_opts = "o:hVdfsp:u:P:"; - const struct option long_opts[] = { +static int parse_arg_list(int argc, char **argv, char ***fuse_argv, + int *fuse_argc) { + int c; + int long_index = 0; + const char *short_opts = "o:hVdfsp:u:P:"; + const struct option long_opts[] = { + /* + * Note that 'L' is returned for long options + */ + {"help", no_argument, NULL, 'h'}, /* 0 */ + {"version", no_argument, NULL, 'V'}, /* 1 */ + {"debug", no_argument, NULL, 'd'}, /* 2 */ + {"username", required_argument, NULL, 'u'}, /* 3 */ + {"password", required_argument, NULL, 'p'}, /* 4 */ + {"proxy", required_argument, NULL, 'P'}, /* 5 */ + {"proxy-username", required_argument, NULL, 'L'}, /* 6 */ + {"proxy-password", required_argument, NULL, 'L'}, /* 7 */ + {"cache", no_argument, NULL, 'L'}, /* 8 */ + {"dl-seg-size", required_argument, NULL, 'L'}, /* 9 */ + {"max-seg-count", required_argument, NULL, 'L'}, /* 10 */ + {"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 */ + {"sonic-username", required_argument, NULL, 'L'}, /* 15 */ + {"sonic-password", required_argument, NULL, 'L'}, /* 16 */ + {"sonic-id3", no_argument, NULL, 'L'}, /* 17 */ + {"no-range-check", no_argument, NULL, 'L'}, /* 18 */ + {"sonic-insecure", no_argument, NULL, 'L'}, /* 19 */ + {"insecure-tls", no_argument, NULL, 'L'}, /* 20 */ + {"config", required_argument, NULL, 'L'}, /* 21 */ + {"single-file-mode", required_argument, NULL, 'L'}, /* 22 */ + {"cacert", required_argument, NULL, 'L'}, /* 23 */ + {"proxy-cacert", required_argument, NULL, 'L'}, /* 24 */ + {"refresh-timeout", required_argument, NULL, 'L'}, /* 25 */ + {0, 0, 0, 0}}; + while ((c = getopt_long(argc, argv, short_opts, long_opts, &long_index)) != + -1) { + switch (c) { + case 'o': + add_arg(fuse_argv, fuse_argc, "-o"); + add_arg(fuse_argv, fuse_argc, optarg); + break; + case 'h': + print_help(argv[0], 1); + add_arg(fuse_argv, fuse_argc, "-ho"); + /* + * skip everything else to print the help + */ + return 1; + case 'V': + print_version(); + add_arg(fuse_argv, fuse_argc, "-V"); + return 1; + case 'd': + add_arg(fuse_argv, fuse_argc, "-d"); + CONFIG.log_type |= debug; + break; + case 'f': + add_arg(fuse_argv, fuse_argc, "-f"); + break; + case 's': + add_arg(fuse_argv, fuse_argc, "-s"); + break; + case 'u': + CONFIG.http_username = strdup(optarg); + break; + case 'p': + CONFIG.http_password = strdup(optarg); + break; + case 'P': + CONFIG.proxy = strdup(optarg); + break; + case 'L': + /* + * Long options + */ + switch (long_index) { + case 6: + CONFIG.proxy_username = strdup(optarg); + break; + case 7: + CONFIG.proxy_password = strdup(optarg); + break; + case 8: + CONFIG.cache_enabled = 1; + break; + case 9: + CONFIG.data_blksz = atoi(optarg) * 1024 * 1024; + break; + case 10: + CONFIG.max_segbc = atoi(optarg); + break; + case 11: + CONFIG.max_conns = atoi(optarg); + break; + case 12: + CONFIG.user_agent = strdup(optarg); + break; + case 13: + CONFIG.http_wait_sec = atoi(optarg); + break; + case 14: + 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_id3 = 1; + break; + case 18: + CONFIG.no_range_check = 1; + break; + case 19: + CONFIG.sonic_insecure = 1; + break; + case 20: + CONFIG.insecure_tls = 1; + break; + case 21: /* - * Note that 'L' is returned for long options + * This is for --config, we don't need to do anything */ - { "help", no_argument, NULL, 'h' }, /* 0 */ - { "version", no_argument, NULL, 'V' }, /* 1 */ - { "debug", no_argument, NULL, 'd' }, /* 2 */ - { "username", required_argument, NULL, 'u' }, /* 3 */ - { "password", required_argument, NULL, 'p' }, /* 4 */ - { "proxy", required_argument, NULL, 'P' }, /* 5 */ - { "proxy-username", required_argument, NULL, 'L' }, /* 6 */ - { "proxy-password", required_argument, NULL, 'L' }, /* 7 */ - { "cache", no_argument, NULL, 'L' }, /* 8 */ - { "dl-seg-size", required_argument, NULL, 'L' }, /* 9 */ - { "max-seg-count", required_argument, NULL, 'L' }, /* 10 */ - { "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 */ - { "sonic-username", required_argument, NULL, 'L' }, /* 15 */ - { "sonic-password", required_argument, NULL, 'L' }, /* 16 */ - { "sonic-id3", no_argument, NULL, 'L' }, /* 17 */ - { "no-range-check", no_argument, NULL, 'L' }, /* 18 */ - { "sonic-insecure", no_argument, NULL, 'L' }, /* 19 */ - { "insecure-tls", no_argument, NULL, 'L' }, /* 20 */ - { "config", required_argument, NULL, 'L' }, /* 21 */ - { "single-file-mode", required_argument, NULL, 'L' }, /* 22 */ - { "cacert", required_argument, NULL, 'L' }, /* 23 */ - { "proxy-cacert", required_argument, NULL, 'L' }, /* 24 */ - { "refresh-timeout", required_argument, NULL, 'L' }, /* 25 */ - { 0, 0, 0, 0 } - }; - while ((c = - getopt_long(argc, argv, short_opts, long_opts, - &long_index)) != -1) { - switch (c) { - case 'o': - add_arg(fuse_argv, fuse_argc, "-o"); - add_arg(fuse_argv, fuse_argc, optarg); - break; - case 'h': - print_help(argv[0], 1); - add_arg(fuse_argv, fuse_argc, "-ho"); - /* - * skip everything else to print the help - */ - return 1; - case 'V': - print_version(); - add_arg(fuse_argv, fuse_argc, "-V"); - return 1; - case 'd': - add_arg(fuse_argv, fuse_argc, "-d"); - CONFIG.log_type |= debug; - break; - case 'f': - add_arg(fuse_argv, fuse_argc, "-f"); - break; - case 's': - add_arg(fuse_argv, fuse_argc, "-s"); - break; - case 'u': - CONFIG.http_username = strdup(optarg); - break; - case 'p': - CONFIG.http_password = strdup(optarg); - break; - case 'P': - CONFIG.proxy = strdup(optarg); - break; - case 'L': - /* - * Long options - */ - switch (long_index) { - case 6: - CONFIG.proxy_username = strdup(optarg); - break; - case 7: - CONFIG.proxy_password = strdup(optarg); - break; - case 8: - CONFIG.cache_enabled = 1; - break; - case 9: - CONFIG.data_blksz = atoi(optarg) * 1024 * 1024; - break; - case 10: - CONFIG.max_segbc = atoi(optarg); - break; - case 11: - CONFIG.max_conns = atoi(optarg); - break; - case 12: - CONFIG.user_agent = strdup(optarg); - break; - case 13: - CONFIG.http_wait_sec = atoi(optarg); - break; - case 14: - 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_id3 = 1; - break; - case 18: - CONFIG.no_range_check = 1; - break; - case 19: - CONFIG.sonic_insecure = 1; - break; - case 20: - CONFIG.insecure_tls = 1; - break; - case 21: - /* - * This is for --config, we don't need to do anything - */ - break; - case 22: - CONFIG.mode = SINGLE; - break; - case 23: - CONFIG.cafile = strdup(optarg); - break; - case 24: - CONFIG.proxy_cafile = strdup(optarg); - break; - case 25: - CONFIG.refresh_timeout = atoi(optarg); - break; - default: - fprintf(stderr, "see httpdirfs -h for usage\n"); - return 1; - } - break; - default: - fprintf(stderr, "see httpdirfs -h for usage\n"); - return 1; - } - }; - return 0; + break; + case 22: + CONFIG.mode = SINGLE; + break; + case 23: + CONFIG.cafile = strdup(optarg); + break; + case 24: + CONFIG.proxy_cafile = strdup(optarg); + break; + case 25: + CONFIG.refresh_timeout = atoi(optarg); + break; + default: + fprintf(stderr, "see httpdirfs -h for usage\n"); + return 1; + } + break; + default: + fprintf(stderr, "see httpdirfs -h for usage\n"); + return 1; + } + }; + return 0; } /** * \brief add an argument to an argv array * \details This is basically how you add a string to an array of string */ -void add_arg(char ***fuse_argv_ptr, int *fuse_argc, char *opt_string) -{ - (*fuse_argc)++; - *fuse_argv_ptr = realloc(*fuse_argv_ptr, *fuse_argc * sizeof(char *)); - char **fuse_argv = *fuse_argv_ptr; - fuse_argv[*fuse_argc - 1] = strdup(opt_string); +void add_arg(char ***fuse_argv_ptr, int *fuse_argc, char *opt_string) { + (*fuse_argc)++; + *fuse_argv_ptr = realloc(*fuse_argv_ptr, *fuse_argc * sizeof(char *)); + char **fuse_argv = *fuse_argv_ptr; + fuse_argv[*fuse_argc - 1] = strdup(opt_string); } -static void print_help(char *program_name, int long_help) -{ - /* FUSE prints its help to stderr */ - fprintf(stderr, "usage: %s [options] URL mountpoint\n", program_name); - if (long_help) { - print_long_help(); - } +static void print_help(char *program_name, int long_help) { + /* FUSE prints its help to stderr */ + fprintf(stderr, "usage: %s [options] URL mountpoint\n", program_name); + if (long_help) { + print_long_help(); + } } -static void print_long_help() -{ - /* FUSE prints its help to stderr */ - fprintf(stderr, "\n\ +static void print_long_help() { + /* FUSE prints its help to stderr */ + fprintf(stderr, "\n\ general options:\n\ --config Specify a configuration file \n\ -o opt,[opt...] Mount options\n\ diff --git a/src/memcache.c b/src/memcache.c index 386a2c4..df58b41 100644 --- a/src/memcache.c +++ b/src/memcache.c @@ -7,22 +7,21 @@ #include size_t write_memory_callback(void *recv_data, size_t size, size_t nmemb, - void *userp) -{ - size_t recv_size = size * nmemb; - TransferStruct *ts = (TransferStruct *) userp; + void *userp) { + size_t recv_size = size * nmemb; + TransferStruct *ts = (TransferStruct *)userp; - ts->data = realloc(ts->data, ts->curr_size + recv_size + 1); - if (!ts->data) { - /* - * out of memory! - */ - lprintf(fatal, "realloc failure!\n"); - } + ts->data = realloc(ts->data, ts->curr_size + recv_size + 1); + if (!ts->data) { + /* + * out of memory! + */ + lprintf(fatal, "realloc failure!\n"); + } - memmove(&ts->data[ts->curr_size], recv_data, recv_size); - ts->curr_size += recv_size; - ts->data[ts->curr_size] = '\0'; + memmove(&ts->data[ts->curr_size], recv_data, recv_size); + ts->curr_size += recv_size; + ts->data[ts->curr_size] = '\0'; - return recv_size; + return recv_size; } \ No newline at end of file diff --git a/src/memcache.h b/src/memcache.h index 77511fe..b266101 100644 --- a/src/memcache.h +++ b/src/memcache.h @@ -5,25 +5,22 @@ /** * \brief specify the type of data transfer */ -typedef enum { - FILESTAT = 's', - DATA = 'd' -} TransferType; +typedef enum { FILESTAT = 's', DATA = 'd' } TransferType; /** * \brief For storing transfer data and metadata */ struct TransferStruct { - /** \brief The array to store the data */ - char *data; - /** \brief The current size of the array */ - size_t curr_size; - /** \brief The type of transfer being done */ - TransferType type; - /** \brief Whether transfer is in progress */ - volatile int transferring; - /** \brief The link associated with the transfer */ - Link *link; + /** \brief The array to store the data */ + char *data; + /** \brief The current size of the array */ + size_t curr_size; + /** \brief The type of transfer being done */ + TransferType type; + /** \brief Whether transfer is in progress */ + volatile int transferring; + /** \brief The link associated with the transfer */ + Link *link; }; /** diff --git a/src/network.c b/src/network.c index 6d9d03b..5201f44 100644 --- a/src/network.c +++ b/src/network.c @@ -7,8 +7,8 @@ #include #include -#include #include +#include #include /* @@ -37,47 +37,44 @@ static pthread_mutex_t curl_lock; * \brief OpenSSL 1.02 cryptography callback function * \details Required for OpenSSL 1.02, but not OpenSSL 1.1 */ -static void crypto_lock_callback(int mode, int type, char *file, int line) -{ - (void) file; - (void) line; - if (mode & CRYPTO_LOCK) { - PTHREAD_MUTEX_LOCK(&(crypto_lockarray[type])); - } else { - PTHREAD_MUTEX_UNLOCK(&(crypto_lockarray[type])); - } +static void crypto_lock_callback(int mode, int type, char *file, int line) { + (void)file; + (void)line; + if (mode & CRYPTO_LOCK) { + PTHREAD_MUTEX_LOCK(&(crypto_lockarray[type])); + } else { + PTHREAD_MUTEX_UNLOCK(&(crypto_lockarray[type])); + } } /** * \brief OpenSSL 1.02 thread ID function * \details Required for OpenSSL 1.02, but not OpenSSL 1.1 */ -static unsigned long thread_id(void) -{ - unsigned long ret; +static unsigned long thread_id(void) { + unsigned long ret; - ret = (unsigned long) pthread_self(); - return ret; + ret = (unsigned long)pthread_self(); + return ret; } #pragma GCC diagnostic pop -static void crypto_lock_init(void) -{ - int i; +static void crypto_lock_init(void) { + int i; - crypto_lockarray = - (pthread_mutex_t *) OPENSSL_malloc(CRYPTO_num_locks() * - sizeof(pthread_mutex_t)); - for (i = 0; i < CRYPTO_num_locks(); i++) { - if (pthread_mutex_init(&(crypto_lockarray[i]), NULL)) { - lprintf(fatal, "crypto_lockarray[%d] initialisation \ -failed!\n", i); - }; - } + crypto_lockarray = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * + sizeof(pthread_mutex_t)); + for (i = 0; i < CRYPTO_num_locks(); i++) { + if (pthread_mutex_init(&(crypto_lockarray[i]), NULL)) { + lprintf(fatal, "crypto_lockarray[%d] initialisation \ +failed!\n", + i); + }; + } - CRYPTO_set_id_callback((unsigned long (*)()) thread_id); - CRYPTO_set_locking_callback((void (*)()) crypto_lock_callback); + CRYPTO_set_id_callback((unsigned long (*)())thread_id); + CRYPTO_set_locking_callback((void (*)())crypto_lock_callback); } /** @@ -85,24 +82,21 @@ failed!\n", i); * \details Adapted from: * https://curl.haxx.se/libcurl/c/threaded-shared-conn.html */ -static void -curl_callback_lock(CURL *handle, curl_lock_data data, - curl_lock_access access, void *userptr) -{ - (void) access; /* unused */ - (void) userptr; /* unused */ - (void) handle; /* unused */ - (void) data; /* unused */ - PTHREAD_MUTEX_LOCK(&curl_lock); +static void curl_callback_lock(CURL *handle, curl_lock_data data, + curl_lock_access access, void *userptr) { + (void)access; /* unused */ + (void)userptr; /* unused */ + (void)handle; /* unused */ + (void)data; /* unused */ + PTHREAD_MUTEX_LOCK(&curl_lock); } -static void -curl_callback_unlock(CURL *handle, curl_lock_data data, void *userptr) -{ - (void) userptr; /* unused */ - (void) handle; /* unused */ - (void) data; /* unused */ - PTHREAD_MUTEX_UNLOCK(&curl_lock); +static void curl_callback_unlock(CURL *handle, curl_lock_data data, + void *userptr) { + (void)userptr; /* unused */ + (void)handle; /* unused */ + (void)data; /* unused */ + PTHREAD_MUTEX_UNLOCK(&curl_lock); } /** @@ -110,218 +104,207 @@ curl_callback_unlock(CURL *handle, curl_lock_data data, void *userptr) * \details Adapted from: * https://curl.haxx.se/libcurl/c/10-at-a-time.html */ -static void -curl_process_msgs(CURLMsg *curl_msg, int n_running_curl, int n_mesgs) -{ - (void) n_running_curl; - (void) n_mesgs; - static volatile int slept = 0; - if (curl_msg->msg == CURLMSG_DONE) { - TransferStruct *ts; - CURL *curl = curl_msg->easy_handle; - CURLcode ret = - curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, - &ts); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - ts->transferring = 0; - char *url = NULL; - ret = curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - - /* - * Wait for 5 seconds if we get HTTP 429 - */ - long http_resp = 0; - ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } - if (HTTP_temp_failure(http_resp)) { - if (!slept) { - lprintf(warning, - "HTTP %ld, sleeping for %d sec\n", - http_resp, CONFIG.http_wait_sec); - sleep(CONFIG.http_wait_sec); - slept = 1; - } - } else { - slept = 0; - } - - if (!curl_msg->data.result) { - /* - * Transfer successful, set the file size - */ - if (ts->type == FILESTAT) { - Link_set_file_stat(ts->link, curl); - } - } else { - lprintf(error, "%d - %s <%s>\n", - curl_msg->data.result, - curl_easy_strerror(curl_msg->data.result), url); - } - curl_multi_remove_handle(curl_multi, curl); - /* - * clean up the handle, if we are querying the file size - */ - if (ts->type == FILESTAT) { - curl_easy_cleanup(curl); - FREE(ts); - } - } else { - lprintf(warning, "curl_msg->msg: %d\n", curl_msg->msg); +static void curl_process_msgs(CURLMsg *curl_msg, int n_running_curl, + int n_mesgs) { + (void)n_running_curl; + (void)n_mesgs; + static volatile int slept = 0; + if (curl_msg->msg == CURLMSG_DONE) { + TransferStruct *ts; + CURL *curl = curl_msg->easy_handle; + CURLcode ret = + curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, &ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); } + ts->transferring = 0; + char *url = NULL; + ret = curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + + /* + * Wait for 5 seconds if we get HTTP 429 + */ + long http_resp = 0; + ret = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } + if (HTTP_temp_failure(http_resp)) { + if (!slept) { + lprintf(warning, "HTTP %ld, sleeping for %d sec\n", http_resp, + CONFIG.http_wait_sec); + sleep(CONFIG.http_wait_sec); + slept = 1; + } + } else { + slept = 0; + } + + if (!curl_msg->data.result) { + /* + * Transfer successful, set the file size + */ + if (ts->type == FILESTAT) { + Link_set_file_stat(ts->link, curl); + } + } else { + lprintf(error, "%d - %s <%s>\n", curl_msg->data.result, + curl_easy_strerror(curl_msg->data.result), url); + } + curl_multi_remove_handle(curl_multi, curl); + /* + * clean up the handle, if we are querying the file size + */ + if (ts->type == FILESTAT) { + curl_easy_cleanup(curl); + FREE(ts); + } + } else { + lprintf(warning, "curl_msg->msg: %d\n", curl_msg->msg); + } } /** * \details effectively based on * https://curl.haxx.se/libcurl/c/multi-double.html */ -int curl_multi_perform_once(void) -{ - lprintf(network_lock_debug, - "thread %x: locking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&transfer_lock); +int curl_multi_perform_once(void) { + lprintf(network_lock_debug, "thread %x: locking transfer_lock;\n", + pthread_self()); + PTHREAD_MUTEX_LOCK(&transfer_lock); - /* - * Get curl multi interface to perform pending tasks - */ - int n_running_curl; - CURLMcode mc = curl_multi_perform(curl_multi, &n_running_curl); - if (mc) { - lprintf(error, "%s\n", curl_multi_strerror(mc)); - } + /* + * Get curl multi interface to perform pending tasks + */ + int n_running_curl; + CURLMcode mc = curl_multi_perform(curl_multi, &n_running_curl); + if (mc) { + lprintf(error, "%s\n", curl_multi_strerror(mc)); + } - mc = curl_multi_poll(curl_multi, NULL, 0, 100, NULL); - if (mc) { - lprintf(error, "%s\n", curl_multi_strerror(mc)); - } + mc = curl_multi_poll(curl_multi, NULL, 0, 100, NULL); + if (mc) { + lprintf(error, "%s\n", curl_multi_strerror(mc)); + } - /* - * Process the message queue - */ - int n_mesgs; - CURLMsg *curl_msg; - while ((curl_msg = curl_multi_info_read(curl_multi, &n_mesgs))) { - curl_process_msgs(curl_msg, n_running_curl, n_mesgs); - } + /* + * Process the message queue + */ + int n_mesgs; + CURLMsg *curl_msg; + while ((curl_msg = curl_multi_info_read(curl_multi, &n_mesgs))) { + curl_process_msgs(curl_msg, n_running_curl, n_mesgs); + } - lprintf(network_lock_debug, - "thread %x: unlocking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&transfer_lock); + lprintf(network_lock_debug, "thread %x: unlocking transfer_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&transfer_lock); - return n_running_curl; + return n_running_curl; } -void NetworkSystem_init(void) -{ - /* - * ------- Global related ---------- - */ - if (curl_global_init(CURL_GLOBAL_ALL)) { - lprintf(fatal, "curl_global_init() failed!\n"); - } +void NetworkSystem_init(void) { + /* + * ------- Global related ---------- + */ + if (curl_global_init(CURL_GLOBAL_ALL)) { + lprintf(fatal, "curl_global_init() failed!\n"); + } - /* - * -------- Share related ---------- - */ - CURL_SHARE = curl_share_init(); - if (!(CURL_SHARE)) { - lprintf(fatal, "curl_share_init() failed!\n"); - } + /* + * -------- Share related ---------- + */ + CURL_SHARE = curl_share_init(); + if (!(CURL_SHARE)) { + lprintf(fatal, "curl_share_init() failed!\n"); + } - curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); - curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); - curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, - CURL_LOCK_DATA_SSL_SESSION); + curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); + curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); + curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); - if (pthread_mutex_init(&curl_lock, NULL)) { - lprintf(fatal, "curl_lock initialisation failed!\n"); - } - curl_share_setopt(CURL_SHARE, CURLSHOPT_LOCKFUNC, curl_callback_lock); - curl_share_setopt(CURL_SHARE, CURLSHOPT_UNLOCKFUNC, - curl_callback_unlock); + if (pthread_mutex_init(&curl_lock, NULL)) { + lprintf(fatal, "curl_lock initialisation failed!\n"); + } + curl_share_setopt(CURL_SHARE, CURLSHOPT_LOCKFUNC, curl_callback_lock); + curl_share_setopt(CURL_SHARE, CURLSHOPT_UNLOCKFUNC, curl_callback_unlock); - /* - * ------------- Multi related ----------- - */ - curl_multi = curl_multi_init(); - if (!curl_multi) { - lprintf(fatal, "curl_multi_init() failed!\n"); - } - curl_multi_setopt(curl_multi, CURLMOPT_MAX_TOTAL_CONNECTIONS, - CONFIG.max_conns); - curl_multi_setopt(curl_multi, CURLMOPT_MAX_HOST_CONNECTIONS, - CONFIG.max_conns); + /* + * ------------- Multi related ----------- + */ + curl_multi = curl_multi_init(); + if (!curl_multi) { + lprintf(fatal, "curl_multi_init() failed!\n"); + } + curl_multi_setopt(curl_multi, CURLMOPT_MAX_TOTAL_CONNECTIONS, + CONFIG.max_conns); + curl_multi_setopt(curl_multi, CURLMOPT_MAX_HOST_CONNECTIONS, + CONFIG.max_conns); - /* - * ------------ Initialise locks --------- - */ - if (pthread_mutex_init(&transfer_lock, NULL)) { - lprintf(fatal, "transfer_lock initialisation failed!\n"); - } + /* + * ------------ Initialise locks --------- + */ + if (pthread_mutex_init(&transfer_lock, NULL)) { + lprintf(fatal, "transfer_lock initialisation failed!\n"); + } - /* - * cryptographic lock functions were shamelessly copied from - * https://curl.haxx.se/libcurl/c/threaded-ssl.html - */ - crypto_lock_init(); + /* + * cryptographic lock functions were shamelessly copied from + * https://curl.haxx.se/libcurl/c/threaded-ssl.html + */ + crypto_lock_init(); } -void transfer_blocking(CURL *curl) -{ - TransferStruct *ts; - CURLcode ret = curl_easy_getinfo(curl, CURLINFO_PRIVATE, &ts); - if (ret) { - lprintf(error, "%s", curl_easy_strerror(ret)); - } +void transfer_blocking(CURL *curl) { + TransferStruct *ts; + CURLcode ret = curl_easy_getinfo(curl, CURLINFO_PRIVATE, &ts); + if (ret) { + lprintf(error, "%s", curl_easy_strerror(ret)); + } - lprintf(network_lock_debug, - "thread %x: locking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&transfer_lock); + lprintf(network_lock_debug, "thread %x: locking transfer_lock;\n", + pthread_self()); + PTHREAD_MUTEX_LOCK(&transfer_lock); - CURLMcode res = curl_multi_add_handle(curl_multi, curl); - if (res > 0) { - lprintf(error, "%d, %s\n", res, curl_multi_strerror(res)); - } + CURLMcode res = curl_multi_add_handle(curl_multi, curl); + if (res > 0) { + lprintf(error, "%d, %s\n", res, curl_multi_strerror(res)); + } - lprintf(network_lock_debug, - "thread %x: unlocking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&transfer_lock); + lprintf(network_lock_debug, "thread %x: unlocking transfer_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&transfer_lock); - while (ts->transferring) { - curl_multi_perform_once(); - } + while (ts->transferring) { + curl_multi_perform_once(); + } } -void transfer_nonblocking(CURL *curl) -{ - lprintf(network_lock_debug, - "thread %x: locking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_LOCK(&transfer_lock); +void transfer_nonblocking(CURL *curl) { + lprintf(network_lock_debug, "thread %x: locking transfer_lock;\n", + pthread_self()); + PTHREAD_MUTEX_LOCK(&transfer_lock); - CURLMcode res = curl_multi_add_handle(curl_multi, curl); - if (res > 0) { - lprintf(error, "%s\n", curl_multi_strerror(res)); - } + CURLMcode res = curl_multi_add_handle(curl_multi, curl); + if (res > 0) { + lprintf(error, "%s\n", curl_multi_strerror(res)); + } - lprintf(network_lock_debug, - "thread %x: unlocking transfer_lock;\n", pthread_self()); - PTHREAD_MUTEX_UNLOCK(&transfer_lock); + lprintf(network_lock_debug, "thread %x: unlocking transfer_lock;\n", + pthread_self()); + PTHREAD_MUTEX_UNLOCK(&transfer_lock); } -int HTTP_temp_failure(HTTPResponseCode http_resp) -{ - switch (http_resp) { - case HTTP_TOO_MANY_REQUESTS: - case HTTP_CLOUDFLARE_UNKNOWN_ERROR: - case HTTP_CLOUDFLARE_TIMEOUT: - return 1; - default: - return 0; - } +int HTTP_temp_failure(HTTPResponseCode http_resp) { + switch (http_resp) { + case HTTP_TOO_MANY_REQUESTS: + case HTTP_CLOUDFLARE_UNKNOWN_ERROR: + case HTTP_CLOUDFLARE_TIMEOUT: + return 1; + default: + return 0; + } } diff --git a/src/network.h b/src/network.h index eead3c9..989235f 100644 --- a/src/network.h +++ b/src/network.h @@ -14,12 +14,12 @@ typedef struct TransferStruct TransferStruct; /** \brief HTTP response codes */ typedef enum { - HTTP_OK = 200, - HTTP_PARTIAL_CONTENT = 206, - HTTP_RANGE_NOT_SATISFIABLE = 416, - HTTP_TOO_MANY_REQUESTS = 429, - HTTP_CLOUDFLARE_UNKNOWN_ERROR = 520, - HTTP_CLOUDFLARE_TIMEOUT = 524 + HTTP_OK = 200, + HTTP_PARTIAL_CONTENT = 206, + HTTP_RANGE_NOT_SATISFIABLE = 416, + HTTP_TOO_MANY_REQUESTS = 429, + HTTP_CLOUDFLARE_UNKNOWN_ERROR = 520, + HTTP_CLOUDFLARE_TIMEOUT = 524 } HTTPResponseCode; /** \brief curl shared interface */ diff --git a/src/sonic.c b/src/sonic.c index 563af3e..8b05974 100644 --- a/src/sonic.c +++ b/src/sonic.c @@ -1,24 +1,24 @@ #include "sonic.h" #include "config.h" -#include "log.h" #include "link.h" +#include "log.h" #include "memcache.h" #include "util.h" #include #include -#include #include +#include #include typedef struct { - char *server; - char *username; - char *password; - char *client; - char *api_version; + char *server; + char *username; + char *password; + char *client; + char *api_version; } SonicConfigStruct; static SonicConfigStruct SONIC_CONFIG; @@ -26,128 +26,118 @@ static SonicConfigStruct SONIC_CONFIG; /** * \brief initialise Sonic configuration struct */ -void -sonic_config_init(const char *server, const char *username, - const char *password) -{ - 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; +void sonic_config_init(const char *server, const char *username, + const char *password) { + 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) { - /* - * API 1.13.0 is the minimum version that supports - * salt authentication scheme - */ - 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"; - } + if (!CONFIG.sonic_insecure) { + /* + * API 1.13.0 is the minimum version that supports + * salt authentication scheme + */ + 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"; + } } /** * \brief generate authentication string */ -static char *sonic_gen_auth_str(void) -{ - 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; - } +static char *sonic_gen_auth_str(void) { + 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; + } } /** * \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; +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; } /** * \brief generate a getMusicDirectory request URL */ -static char *sonic_getMusicDirectory_link(const char *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=%s", first_part, id); - FREE(first_part); - return url; +static char *sonic_getMusicDirectory_link(const char *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=%s", first_part, id); + FREE(first_part); + return url; } /** * \brief generate a getArtist request URL */ -static char *sonic_getArtist_link(const char *id) -{ - 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; +static char *sonic_getArtist_link(const char *id) { + 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; } /** * \brief generate a getAlbum request URL */ -static char *sonic_getAlbum_link(const char *id) -{ - 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; +static char *sonic_getAlbum_link(const char *id) { + 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; } /** * \brief generate a download request URL */ -static char *sonic_stream_link(const char *id) -{ - 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; +static char *sonic_stream_link(const char *id) { + 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; } /** @@ -164,366 +154,352 @@ static char *sonic_stream_link(const char *id) * parser terminates the strings properly, which is a fair assumption, * considering how mature expat is. */ -static void XMLCALL -XML_parser_general(void *data, const char *elem, const char **attr) -{ - /* - * 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") - && linktbl->links[0]->sonic.depth != 3) { - /* - * 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") - && linktbl->links[0]->sonic.depth == 3) { - link = CALLOC(1, sizeof(Link)); - link->type = LINK_DIR; - /* - * The new table should be a level 4 song table - */ - link->sonic.depth = 4; - } else if (!strcmp(elem, "song") - && linktbl->links[0]->sonic.depth == 4) { - link = CALLOC(1, sizeof(Link)); - link->type = LINK_FILE; - } else { - /* - * The element does not contain directory structural information - */ - return; - } - - int id_set = 0; - int linkname_set = 0; - - int track = 0; - char *title = ""; - char *suffix = ""; +static void XMLCALL XML_parser_general(void *data, const char *elem, + const char **attr) { + /* + * Error checking + */ + if (!strcmp(elem, "error")) { + lprintf(error, "error:\n"); for (int i = 0; attr[i]; i += 2) { - if (!strcmp("id", attr[i])) { - link->sonic.id = CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); - strncpy(link->sonic.id, attr[i + 1], MAX_FILENAME_LEN); - id_set = 1; - continue; - } + lprintf(error, "%s: %s\n", attr[i], attr[i + 1]); + } + } - 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; - } + LinkTable *linktbl = (LinkTable *)data; + Link *link; - /* - * "title" is used for directory name, - * "name" is for top level directories - * N.B. "path" attribute is given the preference - */ - if (!linkname_set) { - if (!strcmp("title", attr[i]) - || !strcmp("name", attr[i])) { - strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN); - linkname_set = 1; - continue; - } - } + /* + * 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") && linktbl->links[0]->sonic.depth != 3) { + /* + * 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") && linktbl->links[0]->sonic.depth == 3) { + link = CALLOC(1, sizeof(Link)); + link->type = LINK_DIR; + /* + * The new table should be a level 4 song table + */ + link->sonic.depth = 4; + } else if (!strcmp(elem, "song") && linktbl->links[0]->sonic.depth == 4) { + link = CALLOC(1, sizeof(Link)); + link->type = LINK_FILE; + } else { + /* + * The element does not contain directory structural information + */ + return; + } - if (!strcmp("isDir", attr[i])) { - if (!strcmp("false", attr[i + 1])) { - link->type = LINK_FILE; - } - continue; - } + int id_set = 0; + int linkname_set = 0; - 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; - } - - if (!strcmp("track", attr[i])) { - track = atoi(attr[i + 1]); - continue; - } - - if (!strcmp("title", attr[i])) { - title = (char *) attr[i + 1]; - continue; - } - - if (!strcmp("suffix", attr[i])) { - suffix = (char *) attr[i + 1]; - continue; - } + int track = 0; + char *title = ""; + char *suffix = ""; + for (int i = 0; attr[i]; i += 2) { + if (!strcmp("id", attr[i])) { + link->sonic.id = CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); + strncpy(link->sonic.id, attr[i + 1], MAX_FILENAME_LEN); + id_set = 1; + continue; } - if (!linkname_set && strnlen(title, MAX_PATH_LEN) > 0 && - strnlen(suffix, MAX_PATH_LEN) > 0) { - snprintf(link->linkname, MAX_FILENAME_LEN, "%02d - %s.%s", - track, title, suffix); - linkname_set = 1; + 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; } /* - * Clean up if linkname or id is not set + * "title" is used for directory name, + * "name" is for top level directories + * N.B. "path" attribute is given the preference */ - if (!linkname_set || !id_set) { - FREE(link); - return; + if (!linkname_set) { + if (!strcmp("title", attr[i]) || !strcmp("name", attr[i])) { + strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN); + linkname_set = 1; + continue; + } } - if (link->type == LINK_FILE) { - char *url = sonic_stream_link(link->sonic.id); - strncpy(link->f_url, url, MAX_PATH_LEN); - FREE(url); + if (!strcmp("isDir", attr[i])) { + if (!strcmp("false", attr[i + 1])) { + link->type = LINK_FILE; + } + continue; } - LinkTable_add(linktbl, link); + 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; + } + + if (!strcmp("track", attr[i])) { + track = atoi(attr[i + 1]); + continue; + } + + if (!strcmp("title", attr[i])) { + title = (char *)attr[i + 1]; + continue; + } + + if (!strcmp("suffix", attr[i])) { + suffix = (char *)attr[i + 1]; + continue; + } + } + + if (!linkname_set && strnlen(title, MAX_PATH_LEN) > 0 && + strnlen(suffix, MAX_PATH_LEN) > 0) { + 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) { + char *url = sonic_stream_link(link->sonic.id); + strncpy(link->f_url, url, MAX_PATH_LEN); + FREE(url); + } + + LinkTable_add(linktbl, link); } -static void sanitise_LinkTable(LinkTable *linktbl) -{ - for (int i = 0; i < linktbl->num; i++) { - if (!strcmp(linktbl->links[i]->linkname, ".")) { - /* Note the super long sanitised name to avoid collision */ - strcpy(linktbl->links[i]->linkname, "__DOT__"); - } - - if (!strcmp(linktbl->links[i]->linkname, "/")) { - /* Ditto */ - 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); - } +static void sanitise_LinkTable(LinkTable *linktbl) { + for (int i = 0; i < linktbl->num; i++) { + if (!strcmp(linktbl->links[i]->linkname, ".")) { + /* Note the super long sanitised name to avoid collision */ + strcpy(linktbl->links[i]->linkname, "__DOT__"); } + + if (!strcmp(linktbl->links[i]->linkname, "/")) { + /* Ditto */ + 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); + } + } } /** * \brief parse a XML string in order to fill in the LinkTable */ static LinkTable *sonic_url_to_LinkTable(const char *url, - XML_StartElementHandler handler, int depth) -{ - LinkTable *linktbl = LinkTable_alloc(url); - linktbl->links[0]->sonic.depth = depth; + XML_StartElementHandler handler, + int depth) { + LinkTable *linktbl = LinkTable_alloc(url); + linktbl->links[0]->sonic.depth = depth; - /* - * start downloading the base URL - */ - TransferStruct xml = Link_download_full(linktbl->links[0]); - if (xml.curr_size == 0) { - LinkTable_free(linktbl); - return NULL; - } + /* + * start downloading the base URL + */ + TransferStruct xml = Link_download_full(linktbl->links[0]); + if (xml.curr_size == 0) { + LinkTable_free(linktbl); + return NULL; + } - XML_Parser parser = XML_ParserCreate(NULL); - XML_SetUserData(parser, linktbl); + XML_Parser parser = XML_ParserCreate(NULL); + XML_SetUserData(parser, linktbl); - XML_SetStartElementHandler(parser, handler); + XML_SetStartElementHandler(parser, handler); - if (XML_Parse(parser, xml.data, xml.curr_size, 1) == XML_STATUS_ERROR) { - lprintf(error, - "Parse error at line %lu: %s\n", - XML_GetCurrentLineNumber(parser), - XML_ErrorString(XML_GetErrorCode(parser))); - } + if (XML_Parse(parser, xml.data, xml.curr_size, 1) == XML_STATUS_ERROR) { + lprintf(error, "Parse error at line %lu: %s\n", + XML_GetCurrentLineNumber(parser), + XML_ErrorString(XML_GetErrorCode(parser))); + } - XML_ParserFree(parser); + XML_ParserFree(parser); - FREE(xml.data); + FREE(xml.data); - LinkTable_print(linktbl); + LinkTable_print(linktbl); - sanitise_LinkTable(linktbl); - - return linktbl; + sanitise_LinkTable(linktbl); + return linktbl; } -LinkTable *sonic_LinkTable_new_index(const char *id) -{ - char *url; - if (strcmp(id, "0")) { - url = sonic_getMusicDirectory_link(id); +LinkTable *sonic_LinkTable_new_index(const char *id) { + 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; +} + +static void XMLCALL XML_parser_id3_root(void *data, const char *elem, + const char **attr) { + 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]); + } + } + + 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")) { + /* + * Add a subdirectory + */ + 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; + /* + * Allocate a new LinkTable + */ + link->next_table = LinkTable_alloc("/"); + } + } + /* + * Make sure we don't add an empty directory + */ + if (linkname_set) { + LinkTable_add(root_linktbl, link); } else { - url = sonic_gen_url_first_part("getIndexes"); + FREE(link); } - LinkTable *linktbl = - sonic_url_to_LinkTable(url, XML_parser_general, 0); + 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 + */ + link->sonic.depth = 3; + 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])) { + link->sonic.id = CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); + strncpy(link->sonic.id, attr[i + 1], MAX_FILENAME_LEN); + id_set = 1; + continue; + } + } + + /* + * Clean up if linkname is not set + */ + 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 + */ +} + +LinkTable *sonic_LinkTable_new_id3(int depth, const char *id) { + char *url; + LinkTable *linktbl = ROOT_LINK_TBL; + switch (depth) { + /* + * Root table + */ + case 0: + url = sonic_gen_url_first_part("getArtists"); + linktbl = sonic_url_to_LinkTable(url, XML_parser_id3_root, 0); FREE(url); - return linktbl; -} - -static void XMLCALL -XML_parser_id3_root(void *data, const char *elem, const char **attr) -{ - 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]); - } - } - - 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")) { - /* - * Add a subdirectory - */ - 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; - /* - * Allocate a new LinkTable - */ - 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 - */ - link->sonic.depth = 3; - 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])) { - link->sonic.id = - CALLOC(MAX_FILENAME_LEN + 1, sizeof(char)); - strncpy(link->sonic.id, attr[i + 1], MAX_FILENAME_LEN); - id_set = 1; - continue; - } - } - - /* - * Clean up if linkname is not set - */ - 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 - */ -} - -LinkTable *sonic_LinkTable_new_id3(int depth, const char *id) -{ - char *url; - LinkTable *linktbl = ROOT_LINK_TBL; - switch (depth) { - /* - * Root table - */ - case 0: - url = sonic_gen_url_first_part("getArtists"); - linktbl = sonic_url_to_LinkTable(url, XML_parser_id3_root, 0); - FREE(url); - break; - /* - * Album table - get all the albums of an artist - */ - case 3: - url = sonic_getArtist_link(id); - linktbl = sonic_url_to_LinkTable(url, XML_parser_general, depth); - FREE(url); - break; - /* - * Song table - get all the songs of an album - */ - 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; - } - - return linktbl; + break; + /* + * Album table - get all the albums of an artist + */ + case 3: + url = sonic_getArtist_link(id); + linktbl = sonic_url_to_LinkTable(url, XML_parser_general, depth); + FREE(url); + break; + /* + * Song table - get all the songs of an album + */ + 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; + } + + return linktbl; } diff --git a/src/sonic.h b/src/sonic.h index 85f58a0..6ff46ac 100644 --- a/src/sonic.h +++ b/src/sonic.h @@ -6,22 +6,22 @@ */ typedef struct { - /** - * \brief Sonic id field - * \details This is used to store the following: - * - Arist ID - * - Album ID - * - Song ID - * - Sub-directory ID (in the XML response, this is the ID on the "child" - * element) - */ - char *id; - /** - * \brief Sonic directory depth - * \details This is used exclusively in ID3 mode to store the depth of the - * current directory. - */ - int depth; + /** + * \brief Sonic id field + * \details This is used to store the following: + * - Arist ID + * - Album ID + * - Song ID + * - Sub-directory ID (in the XML response, this is the ID on the "child" + * element) + */ + char *id; + /** + * \brief Sonic directory depth + * \details This is used exclusively in ID3 mode to store the depth of the + * current directory. + */ + int depth; } Sonic; #include "link.h" diff --git a/src/util.c b/src/util.c index 1e868f6..9fc5e82 100644 --- a/src/util.c +++ b/src/util.c @@ -12,7 +12,6 @@ #include #include - /** * \brief Backtrace buffer size */ @@ -29,129 +28,112 @@ */ #define SALT_LEN 36 -char *path_append(const char *path, const char *filename) -{ - int needs_separator = 0; - if ((path[strnlen(path, MAX_PATH_LEN) - 1] != '/') - && (filename[0] != '/')) { - needs_separator = 1; - } +char *path_append(const char *path, const char *filename) { + int needs_separator = 0; + if ((path[strnlen(path, MAX_PATH_LEN) - 1] != '/') && (filename[0] != '/')) { + needs_separator = 1; + } - 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)); - strncpy(str, path, ul); - if (needs_separator) { - str[ul] = '/'; - } - strncat(str, filename, sl); - return str; + 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)); + strncpy(str, path, ul); + if (needs_separator) { + str[ul] = '/'; + } + strncat(str, filename, sl); + return str; } -int64_t round_div(int64_t a, int64_t b) -{ - return (a + (b / 2)) / b; +int64_t round_div(int64_t a, int64_t b) { return (a + (b / 2)) / b; } + +void PTHREAD_MUTEX_UNLOCK(pthread_mutex_t *x) { + int i; + i = pthread_mutex_unlock(x); + if (i) { + lprintf(fatal, "thread %x: %d, %s\n", pthread_self(), i, strerror(i)); + } } -void PTHREAD_MUTEX_UNLOCK(pthread_mutex_t *x) -{ - int i; - i = pthread_mutex_unlock(x); - if (i) { - lprintf(fatal, - "thread %x: %d, %s\n", pthread_self(), i, strerror(i)); - } +void PTHREAD_MUTEX_LOCK(pthread_mutex_t *x) { + int i; + i = pthread_mutex_lock(x); + if (i) { + lprintf(fatal, "thread %x: %d, %s\n", pthread_self(), i, strerror(i)); + } } -void PTHREAD_MUTEX_LOCK(pthread_mutex_t *x) -{ - int i; - i = pthread_mutex_lock(x); - if (i) { - lprintf(fatal, - "thread %x: %d, %s\n", pthread_self(), i, strerror(i)); - } +void exit_failure(void) { + int nptrs; + void *buffer[BT_BUF_SIZE]; + + nptrs = backtrace(buffer, BT_BUF_SIZE); + fprintf(stderr, "\nOops! HTTPDirFS crashed! :(\n"); + fprintf(stderr, "backtrace() returned the following %d addresses:\n", nptrs); + backtrace_symbols_fd(buffer, nptrs, STDERR_FILENO); + + exit(EXIT_FAILURE); } -void exit_failure(void) -{ - int nptrs; - void *buffer[BT_BUF_SIZE]; - - nptrs = backtrace(buffer, BT_BUF_SIZE); - fprintf(stderr, "\nOops! HTTPDirFS crashed! :(\n"); - fprintf(stderr, "backtrace() returned the following %d addresses:\n", - nptrs); - backtrace_symbols_fd(buffer, nptrs, STDERR_FILENO); - - exit(EXIT_FAILURE); +void erase_string(FILE *file, size_t max_len, char *s) { + size_t l = strnlen(s, max_len); + for (size_t k = 0; k < l; k++) { + fprintf(file, "\b"); + } + for (size_t k = 0; k < l; k++) { + fprintf(file, " "); + } + for (size_t k = 0; k < l; k++) { + fprintf(file, "\b"); + } } -void erase_string(FILE *file, size_t max_len, char *s) -{ - size_t l = strnlen(s, max_len); - for (size_t k = 0; k < l; k++) { - fprintf(file, "\b"); - } - for (size_t k = 0; k < l; k++) { - fprintf(file, " "); - } - for (size_t k = 0; k < l; k++) { - fprintf(file, "\b"); - } +char *generate_salt(void) { + char *out; + out = CALLOC(SALT_LEN + 1, sizeof(char)); + uuid_t uu; + uuid_generate(uu); + uuid_unparse(uu, out); + return out; } -char *generate_salt(void) -{ - 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; } -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) { + lprintf(fatal, "%s!\n", strerror(errno)); + } + return ptr; } -void *CALLOC(size_t nmemb, size_t size) -{ - void *ptr = calloc(nmemb, size); - if (!ptr) { - lprintf(fatal, "%s!\n", strerror(errno)); - } - return ptr; +void FREE(void *ptr) { + if (ptr) { + free(ptr); + } else { + lprintf(fatal, "attempted to free NULL ptr!\n"); + } } -void FREE(void *ptr) -{ - if (ptr) { - free(ptr); - } else { - lprintf(fatal, "attempted to free NULL ptr!\n"); - } -} - -char *str_to_hex(char *s) -{ - char *hex = CALLOC(strnlen(s, MAX_PATH_LEN) * 2 + 1, sizeof(char)); - for (char *c = s, *h = hex; *c; c++, h += 2) { - sprintf(h, "%x", *c); - } - return hex; +char *str_to_hex(char *s) { + char *hex = CALLOC(strnlen(s, MAX_PATH_LEN) * 2 + 1, sizeof(char)); + for (char *c = s, *h = hex; *c; c++, h += 2) { + sprintf(h, "%x", *c); + } + return hex; }