1
0
Fork 0
icecast-server/src/matchfile.c

189 lines
3.9 KiB
C

/* Icecast
*
* This program is distributed under the GNU General Public License, version 2.
* A copy of this license is included with this source.
*
* Copyright 2015-2018, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "common/avl/avl.h"
#include "matchfile.h"
#include "logging.h"
#include "util.h" /* for MAX_LINE_LEN and get_line() */
#define CATMODULE "matchfile"
struct matchfile_tag {
/* reference counter */
size_t refcount;
/* filename of input file */
char *filename;
time_t file_recheck;
time_t file_mtime;
avl_tree *contents;
};
static int __func_free(void *x) {
free(x);
return 1;
}
static int __func_compare (void *arg, void *a, void *b) {
(void)arg;
return strcmp(b, a);
}
static void __func_recheck(matchfile_t *file) {
time_t now = time(NULL);
struct stat file_stat;
FILE *input = NULL;
avl_tree *new_contents;
char line[MAX_LINE_LEN];
if (now < file->file_recheck)
return;
file->file_recheck = now + 10;
if (stat(file->filename, &file_stat) < 0) {
ICECAST_LOG_WARN("failed to check status of \"%s\": %s", file->filename, strerror(errno));
return;
}
if (file_stat.st_mtime == file->file_mtime)
return; /* common case, no update to file */
file->file_mtime = file_stat.st_mtime;
input = fopen(file->filename, "r");
if (!input) {
ICECAST_LOG_WARN("Failed to open file \"%s\": %s", file->filename, strerror(errno));
return;
}
new_contents = avl_tree_new(__func_compare, NULL);
while (get_line(input, line, MAX_LINE_LEN)) {
char *str;
if(!line[0] || line[0] == '#')
continue;
str = strdup(line);
if (str)
avl_insert(new_contents, str);
}
fclose(input);
if (file->contents) avl_tree_free(file->contents, __func_free);
file->contents = new_contents;
}
matchfile_t *matchfile_new(const char *filename) {
matchfile_t *ret;
if (!filename)
return NULL;
ret = calloc(1, sizeof(matchfile_t));
if (!ret)
return NULL;
ret->refcount = 1;
ret->filename = strdup(filename);
ret->file_mtime = 0;
ret->file_recheck = 0;
if (!ret->filename) {
matchfile_release(ret);
return NULL;
}
/* load initial database */
__func_recheck(ret);
return ret;
}
int matchfile_addref(matchfile_t *file) {
if (!file)
return -1;
file->refcount++;
return 0;
}
int matchfile_release(matchfile_t *file) {
if (!file)
return -1;
file->refcount--;
if (file->refcount)
return 0;
if (file->contents)
avl_tree_free(file->contents, __func_free);
free(file->filename);
free(file);
return 0;
}
/* we are not const char *key because of avl_get_by_key()... */
int matchfile_match(matchfile_t *file, const char *key) {
void *result;
if (!file)
return -1;
/* reload database if needed */
__func_recheck(file);
if (!file->contents)
return 0;
return avl_get_by_key(file->contents, (void*)key, &result) == 0 ? 1 : 0;
}
int matchfile_match_allow_deny(matchfile_t *allow, matchfile_t *deny, const char *key) {
if (!allow && !deny)
return 1;
if (!key)
return 0;
if (matchfile_match(deny, key) > 0) {
ICECAST_LOG_DEBUG("%s is banned", key);
return 0;
}
if (matchfile_match(allow, key) > 0) {
ICECAST_LOG_DEBUG("%s is allowed", key);
return 1;
} else if (allow) {
/* we are not on allow list but there is one, so reject */
ICECAST_LOG_DEBUG("%s is not allowed", key);
return 0;
}
/* default: allow */
return 1;
}