changed indentation style

This commit is contained in:
Fufu Fang 2021-08-31 11:18:39 +01:00
parent f791ceb308
commit 45d8cb8136
No known key found for this signature in database
GPG Key ID: 0F6BB5EF6F8BB729
16 changed files with 2546 additions and 2581 deletions

2
.gitignore vendored
View File

@ -12,3 +12,5 @@ doc
# Editor related # Editor related
*.kate-swp *.kate-swp
.vscode .vscode
*.c~
*.h~

View File

@ -3,4 +3,4 @@
- Static global variables: lower case letters - Static global variables: lower case letters
- Function names: TypeName_verb or verb_noun - Function names: TypeName_verb or verb_noun
- Type names: camel case with the first letter capitalised, e.g. CamelCase - Type names: camel case with the first letter capitalised, e.g. CamelCase
- Indentation style: ``indent -linux $SOURCE_FILES`` - Indentation style: ``indent -kr -nut *.c *.h``

File diff suppressed because it is too large Load Diff

View File

@ -28,44 +28,44 @@ typedef uint8_t Seg;
*/ */
struct Cache { struct Cache {
/** \brief the FILE pointer for the data file*/ /** \brief the FILE pointer for the data file*/
FILE *dfp; FILE *dfp;
/** \brief the FILE pointer for the metadata */ /** \brief the FILE pointer for the metadata */
FILE *mfp; FILE *mfp;
/** \brief the path to the local cache file */ /** \brief the path to the local cache file */
char *path; char *path;
/** \brief the Link associated with this cache data set */ /** \brief the Link associated with this cache data set */
Link *link; Link *link;
/** \brief the modified time of the file */ /** \brief the modified time of the file */
long time; long time;
/** \brief the size of the file */ /** \brief the size of the file */
off_t content_length; off_t content_length;
/** \brief the block size of the data file */ /** \brief the block size of the data file */
int blksz; int blksz;
/** \brief segment array byte count */ /** \brief segment array byte count */
long segbc; long segbc;
/** \brief the detail of each segment */ /** \brief the detail of each segment */
Seg *seg; Seg *seg;
/** \brief mutex lock for seek operation */ /** \brief mutex lock for seek operation */
pthread_mutex_t seek_lock; pthread_mutex_t seek_lock;
/** \brief mutex lock for write operation */ /** \brief mutex lock for write operation */
pthread_mutex_t w_lock; pthread_mutex_t w_lock;
/** \brief background download pthread */ /** \brief background download pthread */
pthread_t bgt; pthread_t bgt;
/** /**
* \brief mutex lock for the background download thread * \brief mutex lock for the background download thread
* \note This lock is locked by the foreground thread, but unlocked by the * \note This lock is locked by the foreground thread, but unlocked by the
* background thread! * background thread!
*/ */
pthread_mutex_t bgt_lock; pthread_mutex_t bgt_lock;
/** \brief mutex attributes for bgt_lock */ /** \brief mutex attributes for bgt_lock */
pthread_mutexattr_t bgt_lock_attr; pthread_mutexattr_t bgt_lock_attr;
/** \brief the offset of the next segment to be downloaded in background*/ /** \brief the offset of the next segment to be downloaded in background*/
off_t next_dl_offset; off_t next_dl_offset;
/** \brief the FUSE filesystem path to the remote file*/ /** \brief the FUSE filesystem path to the remote file*/
char *fs_path; char *fs_path;
}; };
/** /**

View File

@ -28,46 +28,46 @@ ConfigStruct CONFIG;
*/ */
void Config_init(void) void Config_init(void)
{ {
CONFIG.mode = NORMAL; CONFIG.mode = NORMAL;
CONFIG.log_type = log_level_init(); CONFIG.log_type = log_level_init();
/*---------------- Network related --------------*/ /*---------------- Network related --------------*/
CONFIG.http_username = NULL; 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;
/*--------------- Cache related ---------------*/ /*--------------- Cache related ---------------*/
CONFIG.cache_enabled = 0; 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 -------------*/ /*-------------- Sonic related -------------*/
CONFIG.sonic_username = NULL; 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;
} }

View File

@ -27,9 +27,9 @@
* \brief Operation modes * \brief Operation modes
*/ */
typedef enum { typedef enum {
NORMAL = 1, NORMAL = 1,
SONIC = 2, SONIC = 2,
SINGLE_FILE = 3, SINGLE_FILE = 3,
} OperationMode; } OperationMode;
/** /**
@ -39,48 +39,48 @@ typedef enum {
*/ */
typedef struct { typedef struct {
/** \brief Operation Mode */ /** \brief Operation Mode */
OperationMode mode; OperationMode mode;
/** \brief Current log level */ /** \brief Current log level */
int log_type; int log_type;
/*---------------- Network related --------------*/ /*---------------- Network related --------------*/
/** \brief HTTP username */ /** \brief HTTP username */
char *http_username; char *http_username;
/** \brief HTTP password */ /** \brief HTTP password */
char *http_password; char *http_password;
/** \brief HTTP proxy URL */ /** \brief HTTP proxy URL */
char *proxy; char *proxy;
/** \brief HTTP proxy username */ /** \brief HTTP proxy username */
char *proxy_username; char *proxy_username;
/** \brief HTTP proxy password */ /** \brief HTTP proxy password */
char *proxy_password; char *proxy_password;
/** \brief HTTP maximum connection count */ /** \brief HTTP maximum connection count */
long max_conns; long max_conns;
/** \brief HTTP user agent*/ /** \brief HTTP user agent*/
char *user_agent; char *user_agent;
/** \brief The waiting time after getting HTTP 429 (too many requests) */ /** \brief The waiting time after getting HTTP 429 (too many requests) */
int http_wait_sec; int http_wait_sec;
/** \brief Disable check for the server's support of HTTP range request */ /** \brief Disable check for the server's support of HTTP range request */
int no_range_check; int no_range_check;
/** \brief Disable TLS certificate verification */ /** \brief Disable TLS certificate verification */
int insecure_tls; int insecure_tls;
/*--------------- Cache related ---------------*/ /*--------------- Cache related ---------------*/
/** \brief Whether cache mode is enabled */ /** \brief Whether cache mode is enabled */
int cache_enabled; int cache_enabled;
/** \brief The cache location*/ /** \brief The cache location*/
char *cache_dir; char *cache_dir;
/** \brief The size of each download segment for cache mode */ /** \brief The size of each download segment for cache mode */
int data_blksz; int data_blksz;
/** \brief The maximum segment count for a single cache file */ /** \brief The maximum segment count for a single cache file */
int max_segbc; int max_segbc;
/*-------------- Sonic related -------------*/ /*-------------- Sonic related -------------*/
/** \brief The Sonic server username */ /** \brief The Sonic server username */
char *sonic_username; char *sonic_username;
/** \brief The Sonic server password */ /** \brief The Sonic server password */
char *sonic_password; char *sonic_password;
/** \brief Whether we are using sonic mode ID3 extension */ /** \brief Whether we are using sonic mode ID3 extension */
int sonic_id3; int sonic_id3;
/** \brief Whether we use the legacy sonic authentication mode */ /** \brief Whether we use the legacy sonic authentication mode */
int sonic_insecure; int sonic_insecure;
} ConfigStruct; } ConfigStruct;
/** /**

View File

@ -14,61 +14,61 @@
static void *fs_init(struct fuse_conn_info *conn) static void *fs_init(struct fuse_conn_info *conn)
{ {
(void)conn; (void) conn;
return NULL; return NULL;
} }
/** \brief release an opened file */ /** \brief release an opened file */
static int fs_release(const char *path, struct fuse_file_info *fi) static int fs_release(const char *path, struct fuse_file_info *fi)
{ {
(void)path; (void) path;
if (CACHE_SYSTEM_INIT) { if (CACHE_SYSTEM_INIT) {
Cache_close((Cache *) fi->fh); Cache_close((Cache *) fi->fh);
} }
return 0; return 0;
} }
/** \brief return the attributes for a single file indicated by path */ /** \brief return the attributes for a single file indicated by path */
static int fs_getattr(const char *path, struct stat *stbuf) static int fs_getattr(const char *path, struct stat *stbuf)
{ {
int res = 0; int res = 0;
memset(stbuf, 0, sizeof(struct stat)); memset(stbuf, 0, sizeof(struct stat));
if (!strcmp(path, "/")) { if (!strcmp(path, "/")) {
stbuf->st_mode = S_IFDIR | 0755; stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 1; stbuf->st_nlink = 1;
} else { } else {
Link *link = path_to_Link(path); Link *link = path_to_Link(path);
if (!link) { if (!link) {
return -ENOENT; return -ENOENT;
}
struct timespec spec;
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(); struct timespec spec;
stbuf->st_gid = getgid(); 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 */ /** \brief read a file */
@ -76,44 +76,44 @@ static int
fs_read(const char *path, char *buf, size_t size, off_t offset, fs_read(const char *path, char *buf, size_t size, off_t offset,
struct fuse_file_info *fi) struct fuse_file_info *fi)
{ {
long received; long received;
if (CACHE_SYSTEM_INIT) { if (CACHE_SYSTEM_INIT) {
received = Cache_read((Cache *) fi->fh, buf, size, offset); received = Cache_read((Cache *) fi->fh, buf, size, offset);
} else { } else {
received = path_download(path, buf, size, offset); received = path_download(path, buf, size, offset);
} }
return received; return received;
} }
/** \brief open a file indicated by the path */ /** \brief open a file indicated by the path */
static int fs_open(const char *path, struct fuse_file_info *fi) static int fs_open(const char *path, struct fuse_file_info *fi)
{ {
Link *link = path_to_Link(path); Link *link = path_to_Link(path);
if (!link) { if (!link) {
return -ENOENT;
}
if ((fi->flags & 3) != O_RDONLY) {
return -EACCES;
}
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 -ENOENT;
}
} }
if ((fi->flags & 3) != O_RDONLY) { }
return -EACCES; return 0;
}
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;
} }
/** /**
@ -131,45 +131,45 @@ static int
fs_readdir(const char *path, void *buf, fuse_fill_dir_t dir_add, fs_readdir(const char *path, void *buf, fuse_fill_dir_t dir_add,
off_t offset, struct fuse_file_info *fi) off_t offset, struct fuse_file_info *fi)
{ {
(void)offset; (void) offset;
(void)fi; (void) fi;
LinkTable *linktbl; LinkTable *linktbl;
if (!strcmp(path, "/")) { if (!strcmp(path, "/")) {
linktbl = ROOT_LINK_TBL; linktbl = ROOT_LINK_TBL;
} else { } else {
linktbl = path_to_Link_LinkTable_new(path); linktbl = path_to_Link_LinkTable_new(path);
if (!linktbl) { if (!linktbl) {
return -ENOENT; return -ENOENT;
}
} }
}
/* /*
* start adding the links * start adding the links
*/ */
dir_add(buf, ".", NULL, 0); dir_add(buf, ".", NULL, 0);
dir_add(buf, "..", NULL, 0); dir_add(buf, "..", NULL, 0);
for (int i = 1; i < linktbl->num; i++) { for (int i = 1; i < linktbl->num; i++) {
Link *link = linktbl->links[i]; Link *link = linktbl->links[i];
if (link->type != LINK_INVALID) { if (link->type != LINK_INVALID) {
dir_add(buf, link->linkname, NULL, 0); dir_add(buf, link->linkname, NULL, 0);
}
} }
}
return 0; return 0;
} }
static struct fuse_operations fs_oper = { static struct fuse_operations fs_oper = {
.getattr = fs_getattr, .getattr = fs_getattr,
.readdir = fs_readdir, .readdir = fs_readdir,
.open = fs_open, .open = fs_open,
.read = fs_read, .read = fs_read,
.init = fs_init, .init = fs_init,
.release = fs_release .release = fs_release
}; };
int fuse_local_init(int argc, char **argv) int fuse_local_init(int argc, char **argv)
{ {
return fuse_main(argc, argv, &fs_oper, NULL); return fuse_main(argc, argv, &fs_oper, NULL);
} }

1258
src/link.c

File diff suppressed because it is too large Load Diff

View File

@ -16,30 +16,30 @@ typedef struct Link Link;
/** \brief the link type */ /** \brief the link type */
typedef enum { typedef enum {
LINK_HEAD = 'H', LINK_HEAD = 'H',
LINK_DIR = 'D', LINK_DIR = 'D',
LINK_FILE = 'F', LINK_FILE = 'F',
LINK_INVALID = 'I', LINK_INVALID = 'I',
LINK_UNINITIALISED_FILE = 'U' LINK_UNINITIALISED_FILE = 'U'
} LinkType; } LinkType;
/** \brief for storing downloaded data in memory */ /** \brief for storing downloaded data in memory */
typedef struct { typedef struct {
char *data; char *data;
size_t size; size_t size;
} DataStruct; } DataStruct;
/** \brief specify the type of data transfer */ /** \brief specify the type of data transfer */
typedef enum { typedef enum {
FILESTAT = 's', FILESTAT = 's',
DATA = 'd' DATA = 'd'
} TransferType; } TransferType;
/** \brief for storing the link being transferred, and metadata */ /** \brief for storing the link being transferred, and metadata */
typedef struct { typedef struct {
TransferType type; TransferType type;
int transferring; int transferring;
Link *link; Link *link;
} TransferStruct; } TransferStruct;
/** /**
@ -53,21 +53,21 @@ typedef struct LinkTable LinkTable;
*/ */
struct Link { struct Link {
/** \brief The link name in the last level of the URL */ /** \brief The link name in the last level of the URL */
char linkname[MAX_FILENAME_LEN + 1]; char linkname[MAX_FILENAME_LEN + 1];
/** \brief The full URL of the file */ /** \brief The full URL of the file */
char f_url[MAX_PATH_LEN + 1]; char f_url[MAX_PATH_LEN + 1];
/** \brief The type of the link */ /** \brief The type of the link */
LinkType type; LinkType type;
/** \brief CURLINFO_CONTENT_LENGTH_DOWNLOAD of the file */ /** \brief CURLINFO_CONTENT_LENGTH_DOWNLOAD of the file */
size_t content_length; size_t content_length;
/** \brief The next LinkTable level, if it is a LINK_DIR */ /** \brief The next LinkTable level, if it is a LINK_DIR */
LinkTable *next_table; LinkTable *next_table;
/** \brief CURLINFO_FILETIME obtained from the server */ /** \brief CURLINFO_FILETIME obtained from the server */
long time; long time;
/** \brief How many times associated cache has been opened */ /** \brief How many times associated cache has been opened */
int cache_opened; int cache_opened;
/** \brief The pointer associated with the cache file */ /** \brief The pointer associated with the cache file */
Cache *cache_ptr; Cache *cache_ptr;
/** /**
* \brief Sonic id field * \brief Sonic id field
* \details This is used to store the followings: * \details This is used to store the followings:
@ -77,18 +77,18 @@ struct Link {
* - Sub-directory ID (in the XML response, this is the ID on the "child" * - Sub-directory ID (in the XML response, this is the ID on the "child"
* element) * element)
*/ */
char *sonic_id; char *sonic_id;
/** /**
* \brief Sonic directory depth * \brief Sonic directory depth
* \details This is used exclusively in ID3 mode to store the depth of the * \details This is used exclusively in ID3 mode to store the depth of the
* current directory. * current directory.
*/ */
int sonic_depth; int sonic_depth;
}; };
struct LinkTable { struct LinkTable {
int num; int num;
Link **links; Link **links;
}; };
/** /**

View File

@ -9,50 +9,50 @@
int log_level_init() int log_level_init()
{ {
char *env = getenv("HTTPDIRFS_LOG_LEVEL"); char *env = getenv("HTTPDIRFS_LOG_LEVEL");
if (env) { if (env) {
return atoi(env); return atoi(env);
} }
return DEFAULT_LOG_LEVEL; return DEFAULT_LOG_LEVEL;
} }
void void
log_printf(LogType type, const char *file, const char *func, int line, log_printf(LogType type, const char *file, const char *func, int line,
const char *format, ...) const char *format, ...)
{ {
FILE *out = stderr; FILE *out = stderr;
if (type & CONFIG.log_type) { if (type & CONFIG.log_type) {
switch (type) { switch (type) {
case fatal: case fatal:
fprintf(out, "Fatal: "); fprintf(out, "Fatal: ");
break; break;
case error: case error:
fprintf(out, "Error: "); fprintf(out, "Error: ");
break; break;
case warning: case warning:
fprintf(out, "Warning: "); fprintf(out, "Warning: ");
break; break;
case info: case info:
out = stderr; out = stderr;
goto print_actual_message; goto print_actual_message;
break; break;
default: default:
fprintf(out, "Debug (%x):", type); fprintf(out, "Debug (%x):", type);
break; break;
}
fprintf(out, "(%s:%s:%d): ", file, func, line);
print_actual_message:
{
}
va_list args;
va_start(args, format);
vfprintf(out, format, args);
va_end(args);
if (type == fatal) {
exit_failure();
}
} }
fprintf(out, "(%s:%s:%d): ", file, func, line);
print_actual_message:
{
}
va_list args;
va_start(args, format);
vfprintf(out, format, args);
va_end(args);
if (type == fatal) {
exit_failure();
}
}
} }

View File

@ -5,14 +5,14 @@
* \brief Log types * \brief Log types
*/ */
typedef enum { typedef enum {
fatal = 1 << 0, fatal = 1 << 0,
error = 1 << 1, error = 1 << 1,
warning = 1 << 2, warning = 1 << 2,
info = 1 << 3, info = 1 << 3,
debug = 1 << 4, debug = 1 << 4,
link_lock_debug = 1 << 5, link_lock_debug = 1 << 5,
network_lock_debug = 1 << 6, network_lock_debug = 1 << 6,
cache_lock_debug = 1 << 7, cache_lock_debug = 1 << 7,
} LogType; } LogType;
/** /**

View File

@ -20,301 +20,294 @@ static char *config_path = NULL;
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
/* /*
* Automatically print help if not enough arguments are supplied * Automatically print help if not enough arguments are supplied
*/ */
if (argc < 2) { if (argc < 2) {
print_help(argv[0], 0); print_help(argv[0], 0);
fprintf(stderr, "For more information, run \"%s --help.\"\n", fprintf(stderr, "For more information, run \"%s --help.\"\n",
argv[0]); argv[0]);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
/* /*
* These are passed into fuse initialiser * These are passed into fuse initialiser
*/ */
char **fuse_argv = NULL; char **fuse_argv = NULL;
int fuse_argc = 0; int fuse_argc = 0;
/* /*
* These are the combined argument with the config file * These are the combined argument with the config file
*/ */
char **all_argv = NULL; char **all_argv = NULL;
int all_argc = 0; int all_argc = 0;
/*--- Add the program's name to the combined argument list ---*/ /*--- Add the program's name to the combined argument list ---*/
add_arg(&all_argv, &all_argc, argv[0]); add_arg(&all_argv, &all_argc, argv[0]);
/*--- FUSE expects the first initialisation to be the program's name ---*/ /*--- FUSE expects the first initialisation to be the program's name ---*/
add_arg(&fuse_argv, &fuse_argc, argv[0]); add_arg(&fuse_argv, &fuse_argc, argv[0]);
/* /*
* initialise network configuration struct * initialise network configuration struct
*/ */
Config_init(); Config_init();
/* /*
* initialise network subsystem * initialise network subsystem
*/ */
NetworkSystem_init(); NetworkSystem_init();
/* /*
* Copy the command line argument list to the combined argument list * Copy the command line argument list to the combined argument list
*/ */
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
add_arg(&all_argv, &all_argc, argv[i]); add_arg(&all_argv, &all_argc, argv[i]);
if (!strcmp(argv[i], "--config")) { if (!strcmp(argv[i], "--config")) {
config_path = strdup(argv[i + 1]); config_path = strdup(argv[i + 1]);
}
} }
}
/* /*
* parse the config file, if it exists, store it in all_argv and * parse the config file, if it exists, store it in all_argv and
* all_argc * all_argc
*/ */
parse_config_file(&all_argv, &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)) {
/* /*
* parse the combined argument list * The user basically didn't supply enough arguments, if we reach here
* The point is to print some error messages
*/ */
if (parse_arg_list(all_argc, all_argv, &fuse_argv, &fuse_argc)) { goto fuse_start;
/* }
* 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 the last remaining argument, which is the mountpoint ---*/
add_arg(&fuse_argv, &fuse_argc, argv[argc - 1]); add_arg(&fuse_argv, &fuse_argc, argv[argc - 1]);
/* /*
* The second last remaining argument is the URL * The second last remaining argument is the URL
*/ */
char *base_url = argv[argc - 2]; char *base_url = argv[argc - 2];
if (strncmp(base_url, "http://", 7) && strncmp(base_url, "https://", 8)) { if (strncmp(base_url, "http://", 7)
fprintf(stderr, "Error: Please supply a valid URL.\n"); && strncmp(base_url, "https://", 8)) {
print_help(argv[0], 0); fprintf(stderr, "Error: Please supply a valid URL.\n");
exit(EXIT_FAILURE); print_help(argv[0], 0);
} else { exit(EXIT_FAILURE);
if (CONFIG.sonic_username && CONFIG.sonic_password) { } else {
CONFIG.mode = SONIC; if (CONFIG.sonic_username && CONFIG.sonic_password) {
} else if (CONFIG.sonic_username || CONFIG.sonic_password) { CONFIG.mode = SONIC;
fprintf(stderr, } else if (CONFIG.sonic_username || CONFIG.sonic_password) {
"Error: You have to supply both username and password to \ fprintf(stderr,
"Error: You have to supply both username and password to \
activate Sonic mode.\n"); activate Sonic mode.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
}
if (!LinkSystem_init(base_url)) {
fprintf(stderr, "Network initialisation failed.\n");
exit(EXIT_FAILURE);
}
} }
if (!LinkSystem_init(base_url)) {
fprintf(stderr, "Network initialisation failed.\n");
exit(EXIT_FAILURE);
}
}
fuse_start: 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) void parse_config_file(char ***argv, int *argc)
{ {
char *full_path; char *full_path;
if (!config_path) { if (!config_path) {
char *xdg_config_home = getenv("XDG_CONFIG_HOME"); char *xdg_config_home = getenv("XDG_CONFIG_HOME");
if (!xdg_config_home) { if (!xdg_config_home) {
char *home = getenv("HOME"); char *home = getenv("HOME");
char *xdg_config_home_default = "/.config"; char *xdg_config_home_default = "/.config";
xdg_config_home = xdg_config_home = path_append(home, xdg_config_home_default);
path_append(home, xdg_config_home_default);
}
full_path = path_append(xdg_config_home, "/httpdirfs/config");
} else {
full_path = config_path;
} }
full_path = path_append(xdg_config_home, "/httpdirfs/config");
} else {
full_path = config_path;
}
/* /*
* The buffer has to be able to fit a URL * The buffer has to be able to fit a URL
*/ */
int buf_len = MAX_PATH_LEN; int buf_len = MAX_PATH_LEN;
char buf[buf_len]; char buf[buf_len];
FILE *config = fopen(full_path, "r"); FILE *config = fopen(full_path, "r");
if (config) { if (config) {
while (fgets(buf, buf_len, config)) { while (fgets(buf, buf_len, config)) {
if (buf[0] == '-') { if (buf[0] == '-') {
(*argc)++; (*argc)++;
buf[strnlen(buf, buf_len) - 1] = '\0'; buf[strnlen(buf, buf_len) - 1] = '\0';
char *space; char *space;
space = strchr(buf, ' '); space = strchr(buf, ' ');
if (!space) { if (!space) {
*argv = *argv = realloc(*argv, *argc * sizeof(char **));
realloc(*argv, (*argv)[*argc - 1] = strndup(buf, buf_len);
*argc * sizeof(char **)); } else {
(*argv)[*argc - 1] = (*argc)++;
strndup(buf, buf_len); *argv = realloc(*argv, *argc * sizeof(char **));
} else { /*
(*argc)++; * Only copy up to the space character
*argv = */
realloc(*argv, (*argv)[*argc - 2] = strndup(buf, space - buf);
*argc * sizeof(char **)); /*
/* * Starts copying after the space
* Only copy up to the space character */
*/ (*argv)[*argc - 1] = strndup(space + 1,
(*argv)[*argc - 2] = buf_len -
strndup(buf, space - buf); (space + 1 - buf));
/*
* Starts copying after the space
*/
(*argv)[*argc - 1] = strndup(space + 1,
buf_len -
(space +
1 - buf));
}
}
} }
}
} }
FREE(full_path); }
FREE(full_path);
} }
static int static int
parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc) parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc)
{ {
char c; char c;
int long_index = 0; int long_index = 0;
const char *short_opts = "o:hVdfsp:u:P:"; const char *short_opts = "o:hVdfsp:u:P:";
const struct option long_opts[] = { 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 */
{ 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(argv[0], 1);
add_arg(fuse_argv, fuse_argc, "-V");
return 1;
case 'd':
add_arg(fuse_argv, fuse_argc, "-d");
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 */ break;
{"version", no_argument, NULL, 'V'}, /* 1 */ case 22:
{"debug", no_argument, NULL, 'd'}, /* 2 */ CONFIG.mode = SINGLE_FILE;
{"username", required_argument, NULL, 'u'}, /* 3 */ break;
{"password", required_argument, NULL, 'p'}, /* 4 */ default:
{"proxy", required_argument, NULL, 'P'}, /* 5 */ fprintf(stderr, "see httpdirfs -h for usage\n");
{"proxy-username", required_argument, NULL, 'L'}, /* 6 */ return 1;
{"proxy-password", required_argument, NULL, 'L'}, /* 7 */ }
{"cache", no_argument, NULL, 'L'}, /* 8 */ break;
{"dl-seg-size", required_argument, NULL, 'L'}, /* 9 */ default:
{"max-seg-count", required_argument, NULL, 'L'}, /* 10 */ fprintf(stderr, "see httpdirfs -h for usage\n");
{"max-conns", required_argument, NULL, 'L'}, /* 11 */ return 1;
{"user-agent", required_argument, NULL, 'L'}, /* 12 */ }
{"retry-wait", required_argument, NULL, 'L'}, /* 13 */ };
{"cache-location", required_argument, NULL, 'L'}, /* 14 */ return 0;
{"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 */
{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(argv[0], 1);
add_arg(fuse_argv, fuse_argc, "-V");
return 1;
case 'd':
add_arg(fuse_argv, fuse_argc, "-d");
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_FILE;
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;
} }
/** /**
@ -323,33 +316,33 @@ parse_arg_list(int argc, char **argv, char ***fuse_argv, int *fuse_argc)
*/ */
void add_arg(char ***fuse_argv_ptr, int *fuse_argc, char *opt_string) void add_arg(char ***fuse_argv_ptr, int *fuse_argc, char *opt_string)
{ {
(*fuse_argc)++; (*fuse_argc)++;
*fuse_argv_ptr = realloc(*fuse_argv_ptr, *fuse_argc * sizeof(char *)); *fuse_argv_ptr = realloc(*fuse_argv_ptr, *fuse_argc * sizeof(char *));
char **fuse_argv = *fuse_argv_ptr; char **fuse_argv = *fuse_argv_ptr;
fuse_argv[*fuse_argc - 1] = strdup(opt_string); fuse_argv[*fuse_argc - 1] = strdup(opt_string);
} }
static void print_help(char *program_name, int long_help) static void print_help(char *program_name, int long_help)
{ {
printf("usage: %s [options] URL mountpoint\n", program_name); printf("usage: %s [options] URL mountpoint\n", program_name);
if (long_help) { if (long_help) {
print_long_help(); print_long_help();
} }
} }
static void print_version() static void print_version()
{ {
printf("HTTPDirFS version " VERSION "\n"); printf("HTTPDirFS version " VERSION "\n");
/* /*
* --------- Print off SSL engine version --------- * --------- Print off SSL engine version ---------
*/ */
curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); curl_version_info_data *data = curl_version_info(CURLVERSION_NOW);
printf("libcurl SSL engine: %s\n", data->ssl_version); printf("libcurl SSL engine: %s\n", data->ssl_version);
} }
static void print_long_help() static void print_long_help()
{ {
printf("\n\ printf("\n\
general options:\n\ general options:\n\
--config Specify a configuration file \n\ --config Specify a configuration file \n\
-o opt,[opt...] Mount options\n\ -o opt,[opt...] Mount options\n\

View File

@ -39,13 +39,13 @@ static pthread_mutex_t curl_lock;
*/ */
static void crypto_lock_callback(int mode, int type, char *file, int line) static void crypto_lock_callback(int mode, int type, char *file, int line)
{ {
(void)file; (void) file;
(void)line; (void) line;
if (mode & CRYPTO_LOCK) { if (mode & CRYPTO_LOCK) {
PTHREAD_MUTEX_LOCK(&(crypto_lockarray[type])); PTHREAD_MUTEX_LOCK(&(crypto_lockarray[type]));
} else { } else {
PTHREAD_MUTEX_UNLOCK(&(crypto_lockarray[type])); PTHREAD_MUTEX_UNLOCK(&(crypto_lockarray[type]));
} }
} }
/** /**
@ -54,30 +54,30 @@ static void crypto_lock_callback(int mode, int type, char *file, int line)
*/ */
static unsigned long thread_id(void) static unsigned long thread_id(void)
{ {
unsigned long ret; unsigned long ret;
ret = (unsigned long)pthread_self(); ret = (unsigned long) pthread_self();
return ret; return ret;
} }
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
static void crypto_lock_init(void) static void crypto_lock_init(void)
{ {
int i; int i;
crypto_lockarray = crypto_lockarray =
(pthread_mutex_t *) OPENSSL_malloc(CRYPTO_num_locks() * (pthread_mutex_t *) OPENSSL_malloc(CRYPTO_num_locks() *
sizeof(pthread_mutex_t)); sizeof(pthread_mutex_t));
for (i = 0; i < CRYPTO_num_locks(); i++) { for (i = 0; i < CRYPTO_num_locks(); i++) {
if (pthread_mutex_init(&(crypto_lockarray[i]), NULL)) { if (pthread_mutex_init(&(crypto_lockarray[i]), NULL)) {
lprintf(fatal, "crypto_lockarray[%d] initialisation \ lprintf(fatal, "crypto_lockarray[%d] initialisation \
failed!\n", i); failed!\n", i);
}; };
} }
CRYPTO_set_id_callback((unsigned long (*)())thread_id); CRYPTO_set_id_callback((unsigned long (*)()) thread_id);
CRYPTO_set_locking_callback((void (*)())crypto_lock_callback); CRYPTO_set_locking_callback((void (*)()) crypto_lock_callback);
} }
/** /**
@ -89,20 +89,20 @@ static void
curl_callback_lock(CURL * handle, curl_lock_data data, curl_callback_lock(CURL * handle, curl_lock_data data,
curl_lock_access access, void *userptr) curl_lock_access access, void *userptr)
{ {
(void)access; /* unused */ (void) access; /* unused */
(void)userptr; /* unused */ (void) userptr; /* unused */
(void)handle; /* unused */ (void) handle; /* unused */
(void)data; /* unused */ (void) data; /* unused */
PTHREAD_MUTEX_LOCK(&curl_lock); PTHREAD_MUTEX_LOCK(&curl_lock);
} }
static void static void
curl_callback_unlock(CURL * handle, curl_lock_data data, void *userptr) curl_callback_unlock(CURL * handle, curl_lock_data data, void *userptr)
{ {
(void)userptr; /* unused */ (void) userptr; /* unused */
(void)handle; /* unused */ (void) handle; /* unused */
(void)data; /* unused */ (void) data; /* unused */
PTHREAD_MUTEX_UNLOCK(&curl_lock); PTHREAD_MUTEX_UNLOCK(&curl_lock);
} }
/** /**
@ -113,58 +113,58 @@ curl_callback_unlock(CURL * handle, curl_lock_data data, void *userptr)
static void static void
curl_process_msgs(CURLMsg * curl_msg, int n_running_curl, int n_mesgs) curl_process_msgs(CURLMsg * curl_msg, int n_running_curl, int n_mesgs)
{ {
(void)n_running_curl; (void) n_running_curl;
(void)n_mesgs; (void) n_mesgs;
static volatile int slept = 0; static volatile int slept = 0;
if (curl_msg->msg == CURLMSG_DONE) { if (curl_msg->msg == CURLMSG_DONE) {
TransferStruct *transfer; TransferStruct *transfer;
CURL *curl = curl_msg->easy_handle; CURL *curl = curl_msg->easy_handle;
curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE,
&transfer); &transfer);
transfer->transferring = 0; transfer->transferring = 0;
char *url = NULL; char *url = NULL;
curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url);
/* /*
* Wait for 5 seconds if we get HTTP 429 * Wait for 5 seconds if we get HTTP 429
*/ */
long http_resp = 0; long http_resp = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_resp);
if (HTTP_temp_failure(http_resp)) { if (HTTP_temp_failure(http_resp)) {
if (!slept) { if (!slept) {
lprintf(warning, lprintf(warning,
"HTTP %ld, sleeping for %d sec\n", "HTTP %ld, sleeping for %d sec\n",
http_resp, CONFIG.http_wait_sec); http_resp, CONFIG.http_wait_sec);
sleep(CONFIG.http_wait_sec); sleep(CONFIG.http_wait_sec);
slept = 1; slept = 1;
} }
} else {
slept = 0;
}
if (!curl_msg->data.result) {
/*
* Transfer successful, set the file size
*/
if (transfer->type == FILESTAT) {
Link_set_file_stat(transfer->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 (transfer->type == FILESTAT) {
curl_easy_cleanup(curl);
FREE(transfer);
}
} else { } else {
lprintf(warning, "curl_msg->msg: %d\n", curl_msg->msg); slept = 0;
} }
if (!curl_msg->data.result) {
/*
* Transfer successful, set the file size
*/
if (transfer->type == FILESTAT) {
Link_set_file_stat(transfer->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 (transfer->type == FILESTAT) {
curl_easy_cleanup(curl);
FREE(transfer);
}
} else {
lprintf(warning, "curl_msg->msg: %d\n", curl_msg->msg);
}
} }
/** /**
@ -173,215 +173,215 @@ curl_process_msgs(CURLMsg * curl_msg, int n_running_curl, int n_mesgs)
*/ */
int curl_multi_perform_once(void) int curl_multi_perform_once(void)
{ {
lprintf(network_lock_debug, lprintf(network_lock_debug,
"thread %x: locking transfer_lock;\n", pthread_self()); "thread %x: locking transfer_lock;\n", pthread_self());
PTHREAD_MUTEX_LOCK(&transfer_lock); PTHREAD_MUTEX_LOCK(&transfer_lock);
/* /*
* Get curl multi interface to perform pending tasks * Get curl multi interface to perform pending tasks
*/ */
int n_running_curl; int n_running_curl;
CURLMcode mc = curl_multi_perform(curl_multi, &n_running_curl); CURLMcode mc = curl_multi_perform(curl_multi, &n_running_curl);
if (mc > 0) { if (mc > 0) {
lprintf(error, "%s\n", curl_multi_strerror(mc)); lprintf(error, "%s\n", curl_multi_strerror(mc));
} }
fd_set fdread; fd_set fdread;
fd_set fdwrite; fd_set fdwrite;
fd_set fdexcep; fd_set fdexcep;
int maxfd = -1; int maxfd = -1;
long curl_timeo = -1; long curl_timeo = -1;
FD_ZERO(&fdread); FD_ZERO(&fdread);
FD_ZERO(&fdwrite); FD_ZERO(&fdwrite);
FD_ZERO(&fdexcep); FD_ZERO(&fdexcep);
/* /*
* set a default timeout for select() * set a default timeout for select()
*/ */
struct timeval timeout; struct timeval timeout;
timeout.tv_sec = 1; timeout.tv_sec = 1;
timeout.tv_usec = 0; timeout.tv_usec = 0;
curl_multi_timeout(curl_multi, &curl_timeo); curl_multi_timeout(curl_multi, &curl_timeo);
/* /*
* We effectively cap timeout to 1 sec * We effectively cap timeout to 1 sec
*/ */
if (curl_timeo >= 0) { if (curl_timeo >= 0) {
timeout.tv_sec = curl_timeo / 1000; timeout.tv_sec = curl_timeo / 1000;
if (timeout.tv_sec > 1) { if (timeout.tv_sec > 1) {
timeout.tv_sec = 1; timeout.tv_sec = 1;
} else {
timeout.tv_usec = (curl_timeo % 1000) * 1000;
}
}
/*
* get file descriptors from the transfers
*/
mc = curl_multi_fdset(curl_multi, &fdread, &fdwrite, &fdexcep, &maxfd);
if (mc > 0) {
lprintf(error, "%s.\n", curl_multi_strerror(mc));
}
if (maxfd == -1) {
usleep(100 * 1000);
} else { } else {
if (select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout) < timeout.tv_usec = (curl_timeo % 1000) * 1000;
0) {
lprintf(error, "select(): %s.\n", strerror(errno));
}
} }
}
/* /*
* Process the message queue * get file descriptors from the transfers
*/ */
int n_mesgs; mc = curl_multi_fdset(curl_multi, &fdread, &fdwrite, &fdexcep, &maxfd);
CURLMsg *curl_msg;
while ((curl_msg = curl_multi_info_read(curl_multi, &n_mesgs))) { if (mc > 0) {
curl_process_msgs(curl_msg, n_running_curl, n_mesgs); lprintf(error, "%s.\n", curl_multi_strerror(mc));
}
if (maxfd == -1) {
usleep(100 * 1000);
} else {
if (select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &timeout) < 0) {
lprintf(error, "select(): %s.\n", strerror(errno));
} }
}
lprintf(network_lock_debug, /*
"thread %x: unlocking transfer_lock;\n", pthread_self()); * Process the message queue
PTHREAD_MUTEX_UNLOCK(&transfer_lock); */
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);
}
return n_running_curl; lprintf(network_lock_debug,
"thread %x: unlocking transfer_lock;\n", pthread_self());
PTHREAD_MUTEX_UNLOCK(&transfer_lock);
return n_running_curl;
} }
void NetworkSystem_init(void) void NetworkSystem_init(void)
{ {
/* /*
* ------- Global related ---------- * ------- Global related ----------
*/ */
if (curl_global_init(CURL_GLOBAL_ALL)) { if (curl_global_init(CURL_GLOBAL_ALL)) {
lprintf(fatal, "curl_global_init() failed!\n"); lprintf(fatal, "curl_global_init() failed!\n");
} }
/* /*
* -------- Share related ---------- * -------- Share related ----------
*/ */
CURL_SHARE = curl_share_init(); CURL_SHARE = curl_share_init();
if (!(CURL_SHARE)) { if (!(CURL_SHARE)) {
lprintf(fatal, "curl_share_init() failed!\n"); 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_COOKIE);
curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE, curl_share_setopt(CURL_SHARE, CURLSHOPT_SHARE,
CURL_LOCK_DATA_SSL_SESSION); CURL_LOCK_DATA_SSL_SESSION);
if (pthread_mutex_init(&curl_lock, NULL)) { if (pthread_mutex_init(&curl_lock, NULL)) {
lprintf(fatal, "curl_lock initialisation failed!\n"); lprintf(fatal, "curl_lock initialisation failed!\n");
} }
curl_share_setopt(CURL_SHARE, CURLSHOPT_LOCKFUNC, curl_callback_lock); curl_share_setopt(CURL_SHARE, CURLSHOPT_LOCKFUNC, curl_callback_lock);
curl_share_setopt(CURL_SHARE, CURLSHOPT_UNLOCKFUNC, curl_share_setopt(CURL_SHARE, CURLSHOPT_UNLOCKFUNC,
curl_callback_unlock); curl_callback_unlock);
/* /*
* ------------- Multi related ----------- * ------------- Multi related -----------
*/ */
curl_multi = curl_multi_init(); curl_multi = curl_multi_init();
if (!curl_multi) { if (!curl_multi) {
lprintf(fatal, "curl_multi_init() failed!\n"); lprintf(fatal, "curl_multi_init() failed!\n");
} }
curl_multi_setopt(curl_multi, CURLMOPT_MAX_TOTAL_CONNECTIONS, curl_multi_setopt(curl_multi, CURLMOPT_MAX_TOTAL_CONNECTIONS,
CONFIG.max_conns); CONFIG.max_conns);
curl_multi_setopt(curl_multi, CURLMOPT_MAX_HOST_CONNECTIONS, curl_multi_setopt(curl_multi, CURLMOPT_MAX_HOST_CONNECTIONS,
CONFIG.max_conns); CONFIG.max_conns);
/* /*
* ------------ Initialise locks --------- * ------------ Initialise locks ---------
*/ */
if (pthread_mutex_init(&transfer_lock, NULL)) { if (pthread_mutex_init(&transfer_lock, NULL)) {
lprintf(fatal, "transfer_lock initialisation failed!\n"); lprintf(fatal, "transfer_lock initialisation failed!\n");
} }
/* /*
* cryptographic lock functions were shamelessly copied from * cryptographic lock functions were shamelessly copied from
* https://curl.haxx.se/libcurl/c/threaded-ssl.html * https://curl.haxx.se/libcurl/c/threaded-ssl.html
*/ */
crypto_lock_init(); crypto_lock_init();
} }
void transfer_blocking(CURL * curl) void transfer_blocking(CURL * curl)
{ {
/* /*
* We don't need to malloc here, as the transfer is finished before * We don't need to malloc here, as the transfer is finished before
* the variable gets popped from the stack * the variable gets popped from the stack
*/ */
volatile TransferStruct transfer; volatile TransferStruct transfer;
transfer.type = DATA; transfer.type = DATA;
transfer.transferring = 1; transfer.transferring = 1;
curl_easy_setopt(curl, CURLOPT_PRIVATE, &transfer); curl_easy_setopt(curl, CURLOPT_PRIVATE, &transfer);
lprintf(network_lock_debug, lprintf(network_lock_debug,
"thread %x: locking transfer_lock;\n", pthread_self()); "thread %x: locking transfer_lock;\n", pthread_self());
PTHREAD_MUTEX_LOCK(&transfer_lock); PTHREAD_MUTEX_LOCK(&transfer_lock);
CURLMcode res = curl_multi_add_handle(curl_multi, curl); CURLMcode res = curl_multi_add_handle(curl_multi, curl);
lprintf(network_lock_debug, lprintf(network_lock_debug,
"thread %x: unlocking transfer_lock;\n", pthread_self()); "thread %x: unlocking transfer_lock;\n", pthread_self());
PTHREAD_MUTEX_UNLOCK(&transfer_lock); PTHREAD_MUTEX_UNLOCK(&transfer_lock);
if (res > 0) { if (res > 0) {
lprintf(error, "%d, %s\n", res, curl_multi_strerror(res)); lprintf(error, "%d, %s\n", res, curl_multi_strerror(res));
} }
while (transfer.transferring) { while (transfer.transferring) {
curl_multi_perform_once(); curl_multi_perform_once();
} }
} }
void transfer_nonblocking(CURL * curl) void transfer_nonblocking(CURL * curl)
{ {
lprintf(network_lock_debug, lprintf(network_lock_debug,
"thread %x: locking transfer_lock;\n", pthread_self()); "thread %x: locking transfer_lock;\n", pthread_self());
PTHREAD_MUTEX_LOCK(&transfer_lock); PTHREAD_MUTEX_LOCK(&transfer_lock);
CURLMcode res = curl_multi_add_handle(curl_multi, curl); CURLMcode res = curl_multi_add_handle(curl_multi, curl);
lprintf(network_lock_debug, lprintf(network_lock_debug,
"thread %x: unlocking transfer_lock;\n", pthread_self()); "thread %x: unlocking transfer_lock;\n", pthread_self());
PTHREAD_MUTEX_UNLOCK(&transfer_lock); PTHREAD_MUTEX_UNLOCK(&transfer_lock);
if (res > 0) { if (res > 0) {
lprintf(error, "%s\n", curl_multi_strerror(res)); lprintf(error, "%s\n", curl_multi_strerror(res));
} }
} }
size_t size_t
write_memory_callback(void *contents, size_t size, size_t nmemb, void *userp) write_memory_callback(void *contents, size_t size, size_t nmemb,
void *userp)
{ {
size_t realsize = size * nmemb; size_t realsize = size * nmemb;
DataStruct *mem = (DataStruct *) userp; DataStruct *mem = (DataStruct *) userp;
mem->data = realloc(mem->data, mem->size + realsize + 1); mem->data = realloc(mem->data, mem->size + realsize + 1);
if (!mem->data) { if (!mem->data) {
/* /*
* out of memory! * out of memory!
*/ */
lprintf(fatal, "realloc failure!\n"); lprintf(fatal, "realloc failure!\n");
} }
memmove(&mem->data[mem->size], contents, realsize); memmove(&mem->data[mem->size], contents, realsize);
mem->size += realsize; mem->size += realsize;
mem->data[mem->size] = 0; mem->data[mem->size] = 0;
return realsize; return realsize;
} }
int HTTP_temp_failure(HTTPResponseCode http_resp) int HTTP_temp_failure(HTTPResponseCode http_resp)
{ {
switch (http_resp) { switch (http_resp) {
case HTTP_TOO_MANY_REQUESTS: case HTTP_TOO_MANY_REQUESTS:
case HTTP_CLOUDFLARE_UNKNOWN_ERROR: case HTTP_CLOUDFLARE_UNKNOWN_ERROR:
case HTTP_CLOUDFLARE_TIMEOUT: case HTTP_CLOUDFLARE_TIMEOUT:
return 1; return 1;
default: default:
return 0; return 0;
} }
} }

View File

@ -10,12 +10,12 @@
/** \brief HTTP response codes */ /** \brief HTTP response codes */
typedef enum { typedef enum {
HTTP_OK = 200, HTTP_OK = 200,
HTTP_PARTIAL_CONTENT = 206, HTTP_PARTIAL_CONTENT = 206,
HTTP_RANGE_NOT_SATISFIABLE = 416, HTTP_RANGE_NOT_SATISFIABLE = 416,
HTTP_TOO_MANY_REQUESTS = 429, HTTP_TOO_MANY_REQUESTS = 429,
HTTP_CLOUDFLARE_UNKNOWN_ERROR = 520, HTTP_CLOUDFLARE_UNKNOWN_ERROR = 520,
HTTP_CLOUDFLARE_TIMEOUT = 524 HTTP_CLOUDFLARE_TIMEOUT = 524
} HTTPResponseCode; } HTTPResponseCode;
/** \brief curl shared interface */ /** \brief curl shared interface */
@ -35,7 +35,8 @@ void transfer_nonblocking(CURL * curl);
/** \brief callback function for file transfer */ /** \brief callback function for file transfer */
size_t size_t
write_memory_callback(void *contents, size_t size, size_t nmemb, void *userp); write_memory_callback(void *contents, size_t size, size_t nmemb,
void *userp);
/** /**
* \brief check if a HTTP response code corresponds to a temporary failure * \brief check if a HTTP response code corresponds to a temporary failure

View File

@ -14,11 +14,11 @@
#include <unistd.h> #include <unistd.h>
typedef struct { typedef struct {
char *server; char *server;
char *username; char *username;
char *password; char *password;
char *client; char *client;
char *api_version; char *api_version;
} SonicConfigStruct; } SonicConfigStruct;
static SonicConfigStruct SONIC_CONFIG; static SonicConfigStruct SONIC_CONFIG;
@ -30,30 +30,30 @@ void
sonic_config_init(const char *server, const char *username, sonic_config_init(const char *server, const char *username,
const char *password) const char *password)
{ {
SONIC_CONFIG.server = strndup(server, MAX_PATH_LEN); SONIC_CONFIG.server = strndup(server, MAX_PATH_LEN);
/* /*
* Correct for the extra '/' * Correct for the extra '/'
*/ */
size_t server_url_len = strnlen(SONIC_CONFIG.server, MAX_PATH_LEN) - 1; size_t server_url_len = strnlen(SONIC_CONFIG.server, MAX_PATH_LEN) - 1;
if (SONIC_CONFIG.server[server_url_len] == '/') { if (SONIC_CONFIG.server[server_url_len] == '/') {
SONIC_CONFIG.server[server_url_len] = '\0'; SONIC_CONFIG.server[server_url_len] = '\0';
} }
SONIC_CONFIG.username = strndup(username, MAX_FILENAME_LEN); SONIC_CONFIG.username = strndup(username, MAX_FILENAME_LEN);
SONIC_CONFIG.password = strndup(password, MAX_FILENAME_LEN); SONIC_CONFIG.password = strndup(password, MAX_FILENAME_LEN);
SONIC_CONFIG.client = DEFAULT_USER_AGENT; SONIC_CONFIG.client = DEFAULT_USER_AGENT;
if (!CONFIG.sonic_insecure) { if (!CONFIG.sonic_insecure) {
/* /*
* API 1.13.0 is the minimum version that supports * API 1.13.0 is the minimum version that supports
* salt authentication scheme * salt authentication scheme
*/ */
SONIC_CONFIG.api_version = "1.13.0"; SONIC_CONFIG.api_version = "1.13.0";
} else { } else {
/* /*
* API 1.8.0 is the minimum version that supports ID3 mode * API 1.8.0 is the minimum version that supports ID3 mode
*/ */
SONIC_CONFIG.api_version = "1.8.0"; SONIC_CONFIG.api_version = "1.8.0";
} }
} }
/** /**
@ -61,33 +61,32 @@ sonic_config_init(const char *server, const char *username,
*/ */
static char *sonic_gen_auth_str(void) static char *sonic_gen_auth_str(void)
{ {
if (!CONFIG.sonic_insecure) { if (!CONFIG.sonic_insecure) {
char *salt = generate_salt(); char *salt = generate_salt();
size_t pwd_len = size_t pwd_len = strnlen(SONIC_CONFIG.password, MAX_FILENAME_LEN);
strnlen(SONIC_CONFIG.password, MAX_FILENAME_LEN); size_t pwd_salt_len = pwd_len + strnlen(salt, 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));
char *pwd_salt = CALLOC(pwd_salt_len + 1, sizeof(char)); strncat(pwd_salt, SONIC_CONFIG.password, MAX_FILENAME_LEN);
strncat(pwd_salt, SONIC_CONFIG.password, MAX_FILENAME_LEN); strncat(pwd_salt + pwd_len, salt, MAX_FILENAME_LEN);
strncat(pwd_salt + pwd_len, salt, MAX_FILENAME_LEN); char *token = generate_md5sum(pwd_salt);
char *token = generate_md5sum(pwd_salt); char *auth_str = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
char *auth_str = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); snprintf(auth_str, MAX_PATH_LEN,
snprintf(auth_str, MAX_PATH_LEN, ".view?u=%s&t=%s&s=%s&v=%s&c=%s",
".view?u=%s&t=%s&s=%s&v=%s&c=%s", SONIC_CONFIG.username, token, salt,
SONIC_CONFIG.username, token, salt, SONIC_CONFIG.api_version, SONIC_CONFIG.client);
SONIC_CONFIG.api_version, SONIC_CONFIG.client); FREE(salt);
FREE(salt); FREE(token);
FREE(token); return auth_str;
return auth_str; } else {
} else { char *pwd_hex = str_to_hex(SONIC_CONFIG.password);
char *pwd_hex = str_to_hex(SONIC_CONFIG.password); char *auth_str = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
char *auth_str = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); snprintf(auth_str, MAX_PATH_LEN,
snprintf(auth_str, MAX_PATH_LEN, ".view?u=%s&p=enc:%s&v=%s&c=%s",
".view?u=%s&p=enc:%s&v=%s&c=%s", SONIC_CONFIG.username, pwd_hex,
SONIC_CONFIG.username, pwd_hex, SONIC_CONFIG.api_version, SONIC_CONFIG.client);
SONIC_CONFIG.api_version, SONIC_CONFIG.client); FREE(pwd_hex);
FREE(pwd_hex); return auth_str;
return auth_str; }
}
} }
/** /**
@ -95,12 +94,12 @@ static char *sonic_gen_auth_str(void)
*/ */
static char *sonic_gen_url_first_part(char *method) static char *sonic_gen_url_first_part(char *method)
{ {
char *auth_str = sonic_gen_auth_str(); char *auth_str = sonic_gen_auth_str();
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
snprintf(url, MAX_PATH_LEN, "%s/rest/%s%s", SONIC_CONFIG.server, snprintf(url, MAX_PATH_LEN, "%s/rest/%s%s", SONIC_CONFIG.server,
method, auth_str); method, auth_str);
FREE(auth_str); FREE(auth_str);
return url; return url;
} }
/** /**
@ -108,11 +107,11 @@ static char *sonic_gen_url_first_part(char *method)
*/ */
static char *sonic_getMusicDirectory_link(const char *id) static char *sonic_getMusicDirectory_link(const char *id)
{ {
char *first_part = sonic_gen_url_first_part("getMusicDirectory"); char *first_part = sonic_gen_url_first_part("getMusicDirectory");
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id); snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id);
FREE(first_part); FREE(first_part);
return url; return url;
} }
/** /**
@ -120,11 +119,11 @@ static char *sonic_getMusicDirectory_link(const char *id)
*/ */
static char *sonic_getArtist_link(const char *id) static char *sonic_getArtist_link(const char *id)
{ {
char *first_part = sonic_gen_url_first_part("getArtist"); char *first_part = sonic_gen_url_first_part("getArtist");
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id); snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id);
FREE(first_part); FREE(first_part);
return url; return url;
} }
/** /**
@ -132,11 +131,11 @@ static char *sonic_getArtist_link(const char *id)
*/ */
static char *sonic_getAlbum_link(const char *id) static char *sonic_getAlbum_link(const char *id)
{ {
char *first_part = sonic_gen_url_first_part("getAlbum"); char *first_part = sonic_gen_url_first_part("getAlbum");
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id); snprintf(url, MAX_PATH_LEN, "%s&id=%s", first_part, id);
FREE(first_part); FREE(first_part);
return url; return url;
} }
/** /**
@ -144,11 +143,11 @@ static char *sonic_getAlbum_link(const char *id)
*/ */
static char *sonic_stream_link(const char *id) static char *sonic_stream_link(const char *id)
{ {
char *first_part = sonic_gen_url_first_part("stream"); char *first_part = sonic_gen_url_first_part("stream");
char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char)); char *url = CALLOC(MAX_PATH_LEN + 1, sizeof(char));
snprintf(url, MAX_PATH_LEN, "%s&format=raw&id=%s", first_part, id); snprintf(url, MAX_PATH_LEN, "%s&format=raw&id=%s", first_part, id);
FREE(first_part); FREE(first_part);
return url; return url;
} }
/** /**
@ -168,158 +167,155 @@ static char *sonic_stream_link(const char *id)
static void XMLCALL static void XMLCALL
XML_parser_general(void *data, const char *elem, const char **attr) XML_parser_general(void *data, const char *elem, const char **attr)
{ {
/* /*
* Error checking * Error checking
*/ */
if (!strcmp(elem, "error")) { if (!strcmp(elem, "error")) {
lprintf(error, "error:\n"); 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 = "";
for (int i = 0; attr[i]; i += 2) { for (int i = 0; attr[i]; i += 2) {
if (!strcmp("id", attr[i])) { lprintf(error, "%s: %s\n", attr[i], attr[i + 1]);
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 (!strcmp("path", attr[i])) { LinkTable *linktbl = (LinkTable *) data;
memset(link->linkname, 0, MAX_FILENAME_LEN); Link *link;
/*
* 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;
}
/* /*
* "title" is used for directory name, * Please refer to the documentation at the function prototype of
* "name" is for top level directories * sonic_LinkTable_new_id3()
* N.B. "path" attribute is given the preference */
*/ if (!strcmp(elem, "child")) {
if (!linkname_set) { link = CALLOC(1, sizeof(Link));
if (!strcmp("title", attr[i]) /*
|| !strcmp("name", attr[i])) { * Initialise to LINK_DIR, as the LINK_FILE is set later.
strncpy(link->linkname, attr[i + 1], */
MAX_FILENAME_LEN); link->type = LINK_DIR;
linkname_set = 1; } else if (!strcmp(elem, "artist")
continue; && 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])) { int id_set = 0;
if (!strcmp("false", attr[i + 1])) { int linkname_set = 0;
link->type = LINK_FILE;
}
continue;
}
if (!strcmp("created", attr[i])) { int track = 0;
struct tm *tm = CALLOC(1, sizeof(struct tm)); char *title = "";
strptime(attr[i + 1], "%Y-%m-%dT%H:%M:%S.000Z", tm); char *suffix = "";
link->time = mktime(tm); for (int i = 0; attr[i]; i += 2) {
FREE(tm); if (!strcmp("id", attr[i])) {
continue; link->sonic_id = CALLOC(MAX_FILENAME_LEN + 1, sizeof(char));
} strncpy(link->sonic_id, attr[i + 1], MAX_FILENAME_LEN);
id_set = 1;
if (!strcmp("size", attr[i])) { continue;
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 && strlen(title) > 0 && strlen(suffix) > 0) { if (!strcmp("path", attr[i])) {
snprintf(link->linkname, MAX_FILENAME_LEN, "%02d - %s.%s", memset(link->linkname, 0, MAX_FILENAME_LEN);
track, title, suffix); /*
linkname_set = 1; * 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) { if (!linkname_set) {
FREE(link); if (!strcmp("title", attr[i])
return; || !strcmp("name", attr[i])) {
strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN);
linkname_set = 1;
continue;
}
} }
if (link->type == LINK_FILE) { if (!strcmp("isDir", attr[i])) {
char *url = sonic_stream_link(link->sonic_id); if (!strcmp("false", attr[i + 1])) {
strncpy(link->f_url, url, MAX_PATH_LEN); link->type = LINK_FILE;
FREE(url); }
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 && strlen(title) > 0 && strlen(suffix) > 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);
} }
/** /**
@ -329,181 +325,177 @@ static LinkTable *sonic_url_to_LinkTable(const char *url,
XML_StartElementHandler handler, XML_StartElementHandler handler,
int depth) int depth)
{ {
LinkTable *linktbl = LinkTable_alloc(url); LinkTable *linktbl = LinkTable_alloc(url);
linktbl->links[0]->sonic_depth = depth; linktbl->links[0]->sonic_depth = depth;
/* /*
* start downloading the base URL * start downloading the base URL
*/ */
DataStruct xml = Link_to_DataStruct(linktbl->links[0]); DataStruct xml = Link_to_DataStruct(linktbl->links[0]);
if (xml.size == 0) { if (xml.size == 0) {
LinkTable_free(linktbl); LinkTable_free(linktbl);
return NULL; return NULL;
} }
XML_Parser parser = XML_ParserCreate(NULL); XML_Parser parser = XML_ParserCreate(NULL);
XML_SetUserData(parser, linktbl); XML_SetUserData(parser, linktbl);
XML_SetStartElementHandler(parser, handler); XML_SetStartElementHandler(parser, handler);
if (XML_Parse(parser, xml.data, xml.size, 1) == XML_STATUS_ERROR) { if (XML_Parse(parser, xml.data, xml.size, 1) == XML_STATUS_ERROR) {
lprintf(error, lprintf(error,
"Parse error at line %lu: %s\n", "Parse error at line %lu: %s\n",
XML_GetCurrentLineNumber(parser), XML_GetCurrentLineNumber(parser),
XML_ErrorString(XML_GetErrorCode(parser))); XML_ErrorString(XML_GetErrorCode(parser)));
} }
XML_ParserFree(parser); XML_ParserFree(parser);
FREE(xml.data); FREE(xml.data);
LinkTable_print(linktbl); LinkTable_print(linktbl);
return linktbl; return linktbl;
} }
LinkTable *sonic_LinkTable_new_index(const char *id) LinkTable *sonic_LinkTable_new_index(const char *id)
{ {
char *url; char *url;
if (strcmp(id, "0")) { if (strcmp(id, "0")) {
url = sonic_getMusicDirectory_link(id); url = sonic_getMusicDirectory_link(id);
} else { } else {
url = sonic_gen_url_first_part("getIndexes"); url = sonic_gen_url_first_part("getIndexes");
} }
LinkTable *linktbl = sonic_url_to_LinkTable(url, XML_parser_general, 0); LinkTable *linktbl =
FREE(url); sonic_url_to_LinkTable(url, XML_parser_general, 0);
return linktbl; FREE(url);
return linktbl;
} }
static void XMLCALL static void XMLCALL
XML_parser_id3_root(void *data, const char *elem, const char **attr) XML_parser_id3_root(void *data, const char *elem, const char **attr)
{ {
if (!strcmp(elem, "error")) { if (!strcmp(elem, "error")) {
lprintf(error, "\n"); lprintf(error, "\n");
for (int i = 0; attr[i]; i += 2) { for (int i = 0; attr[i]; i += 2) {
lprintf(error, "%s: %s\n", attr[i], attr[i + 1]); lprintf(error, "%s: %s\n", attr[i], attr[i + 1]);
}
} }
}
LinkTable *root_linktbl = (LinkTable *) data; LinkTable *root_linktbl = (LinkTable *) data;
LinkTable *this_linktbl = NULL; 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")) {
/* /*
* Set the current linktbl, if we have more than head link. * Add a subdirectory
*/ */
if (root_linktbl->num > 1) { link = CALLOC(1, sizeof(Link));
this_linktbl = link->type = LINK_DIR;
root_linktbl->links[root_linktbl->num - 1]->next_table; for (int i = 0; attr[i]; i += 2) {
} if (!strcmp("name", attr[i])) {
strncpy(link->linkname, attr[i + 1], MAX_FILENAME_LEN);
int id_set = 0; linkname_set = 1;
int linkname_set = 0;
Link *link;
if (!strcmp(elem, "index")) {
/* /*
* Add a subdirectory * Allocate a new LinkTable
*/ */
link = CALLOC(1, sizeof(Link)); link->next_table = LinkTable_alloc("/");
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 * Make sure we don't add an empty directory
* information
*/ */
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) LinkTable *sonic_LinkTable_new_id3(int depth, const char *id)
{ {
char *url; char *url;
LinkTable *linktbl = ROOT_LINK_TBL; LinkTable *linktbl = ROOT_LINK_TBL;
switch (depth) { switch (depth) {
/* /*
* Root table * Root table
*/ */
case 0: case 0:
url = sonic_gen_url_first_part("getArtists"); url = sonic_gen_url_first_part("getArtists");
linktbl = sonic_url_to_LinkTable(url, XML_parser_id3_root, 0); linktbl = sonic_url_to_LinkTable(url, XML_parser_id3_root, 0);
FREE(url); FREE(url);
break; break;
/* /*
* Album table - get all the albums of an artist * Album table - get all the albums of an artist
*/ */
case 3: case 3:
url = sonic_getArtist_link(id); url = sonic_getArtist_link(id);
linktbl = linktbl = sonic_url_to_LinkTable(url, XML_parser_general, depth);
sonic_url_to_LinkTable(url, XML_parser_general, depth); FREE(url);
FREE(url); break;
break; /*
/* * Song table - get all the songs of an album
* Song table - get all the songs of an album */
*/ case 4:
case 4: url = sonic_getAlbum_link(id);
url = sonic_getAlbum_link(id); linktbl = sonic_url_to_LinkTable(url, XML_parser_general, depth);
linktbl = FREE(url);
sonic_url_to_LinkTable(url, XML_parser_general, depth); break;
FREE(url); default:
break; /*
default: * We shouldn't reach here.
/* */
* We shouldn't reach here. lprintf(fatal, "case %d.\n", depth);
*/ break;
lprintf(fatal, "case %d.\n", depth); }
break; return linktbl;
}
return linktbl;
} }

View File

@ -29,126 +29,126 @@
char *path_append(const char *path, const char *filename) char *path_append(const char *path, const char *filename)
{ {
int needs_separator = 0; int needs_separator = 0;
if ((path[strnlen(path, MAX_PATH_LEN) - 1] != '/') if ((path[strnlen(path, MAX_PATH_LEN) - 1] != '/')
&& (filename[0] != '/')) { && (filename[0] != '/')) {
needs_separator = 1; needs_separator = 1;
} }
char *str; char *str;
size_t ul = strnlen(path, MAX_PATH_LEN); size_t ul = strnlen(path, MAX_PATH_LEN);
size_t sl = strnlen(filename, MAX_FILENAME_LEN); size_t sl = strnlen(filename, MAX_FILENAME_LEN);
str = CALLOC(ul + sl + needs_separator + 1, sizeof(char)); str = CALLOC(ul + sl + needs_separator + 1, sizeof(char));
strncpy(str, path, ul); strncpy(str, path, ul);
if (needs_separator) { if (needs_separator) {
str[ul] = '/'; str[ul] = '/';
} }
strncat(str, filename, sl); strncat(str, filename, sl);
return str; return str;
} }
int64_t round_div(int64_t a, int64_t b) int64_t round_div(int64_t a, int64_t b)
{ {
return (a + (b / 2)) / b; return (a + (b / 2)) / b;
} }
void PTHREAD_MUTEX_UNLOCK(pthread_mutex_t * x) void PTHREAD_MUTEX_UNLOCK(pthread_mutex_t * x)
{ {
int i; int i;
i = pthread_mutex_unlock(x); i = pthread_mutex_unlock(x);
if (i) { if (i) {
lprintf(fatal, lprintf(fatal,
"thread %x: pthread_mutex_unlock() failed, %d, %s\n", "thread %x: pthread_mutex_unlock() failed, %d, %s\n",
pthread_self(), i, strerror(i)); pthread_self(), i, strerror(i));
} }
} }
void PTHREAD_MUTEX_LOCK(pthread_mutex_t * x) void PTHREAD_MUTEX_LOCK(pthread_mutex_t * x)
{ {
int i; int i;
i = pthread_mutex_lock(x); i = pthread_mutex_lock(x);
if (i) { if (i) {
lprintf(fatal, lprintf(fatal,
"thread %x: pthread_mutex_lock() failed, %d, %s\n", "thread %x: pthread_mutex_lock() failed, %d, %s\n",
pthread_self(), i, strerror(i)); pthread_self(), i, strerror(i));
} }
} }
void exit_failure(void) void exit_failure(void)
{ {
int nptrs; int nptrs;
void *buffer[BT_BUF_SIZE]; void *buffer[BT_BUF_SIZE];
nptrs = backtrace(buffer, BT_BUF_SIZE); nptrs = backtrace(buffer, BT_BUF_SIZE);
fprintf(stderr, "\nOops! HTTPDirFS crashed! :(\n"); fprintf(stderr, "\nOops! HTTPDirFS crashed! :(\n");
fprintf(stderr, "backtrace() returned the following %d addresses:\n", fprintf(stderr, "backtrace() returned the following %d addresses:\n",
nptrs); nptrs);
backtrace_symbols_fd(buffer, nptrs, STDERR_FILENO); backtrace_symbols_fd(buffer, nptrs, STDERR_FILENO);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
void erase_string(FILE * file, size_t max_len, char *s) void erase_string(FILE * file, size_t max_len, char *s)
{ {
size_t l = strnlen(s, max_len); size_t l = strnlen(s, max_len);
for (size_t k = 0; k < l; k++) { for (size_t k = 0; k < l; k++) {
fprintf(file, "\b"); fprintf(file, "\b");
} }
for (size_t k = 0; k < l; k++) { for (size_t k = 0; k < l; k++) {
fprintf(file, " "); fprintf(file, " ");
} }
for (size_t k = 0; k < l; k++) { for (size_t k = 0; k < l; k++) {
fprintf(file, "\b"); fprintf(file, "\b");
} }
} }
char *generate_salt(void) char *generate_salt(void)
{ {
char *out; char *out;
out = CALLOC(SALT_LEN + 1, sizeof(char)); out = CALLOC(SALT_LEN + 1, sizeof(char));
uuid_t uu; uuid_t uu;
uuid_generate(uu); uuid_generate(uu);
uuid_unparse(uu, out); uuid_unparse(uu, out);
return out; return out;
} }
char *generate_md5sum(const char *str) char *generate_md5sum(const char *str)
{ {
MD5_CTX c; MD5_CTX c;
unsigned char md5[MD5_DIGEST_LENGTH]; unsigned char md5[MD5_DIGEST_LENGTH];
size_t len = strnlen(str, MAX_PATH_LEN); size_t len = strnlen(str, MAX_PATH_LEN);
char *out = CALLOC(MD5_HASH_LEN + 1, sizeof(char)); char *out = CALLOC(MD5_HASH_LEN + 1, sizeof(char));
MD5_Init(&c); MD5_Init(&c);
MD5_Update(&c, str, len); MD5_Update(&c, str, len);
MD5_Final(md5, &c); MD5_Final(md5, &c);
for (int i = 0; i < MD5_DIGEST_LENGTH; i++) { for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
sprintf(out + 2 * i, "%02x", md5[i]); sprintf(out + 2 * i, "%02x", md5[i]);
} }
return out; return out;
} }
void *CALLOC(size_t nmemb, size_t size) void *CALLOC(size_t nmemb, size_t size)
{ {
void *ptr = calloc(nmemb, size); void *ptr = calloc(nmemb, size);
if (!ptr) { if (!ptr) {
lprintf(fatal, "calloc() failed, %s!\n", strerror(errno)); lprintf(fatal, "calloc() failed, %s!\n", strerror(errno));
} }
return ptr; return ptr;
} }
void FREE(void *ptr) void FREE(void *ptr)
{ {
free(ptr); free(ptr);
ptr = NULL; ptr = NULL;
} }
char *str_to_hex(char *s) char *str_to_hex(char *s)
{ {
char *hex = CALLOC(strlen(s) * 2 + 1, sizeof(char)); char *hex = CALLOC(strlen(s) * 2 + 1, sizeof(char));
for (char *c = s, *h = hex; *c; c++, h += 2) { for (char *c = s, *h = hex; *c; c++, h += 2) {
sprintf(h, "%x", *c); sprintf(h, "%x", *c);
} }
return hex; return hex;
} }