diff --git a/docs/html/config_access.html b/docs/html/config_access.html
index 0e6d73bc..48cc42d9 100644
--- a/docs/html/config_access.html
+++ b/docs/html/config_access.html
@@ -68,13 +68,11 @@ The columns have the following functions:
Enables access to all video recording functions. This also include administration of the auto recordings.
- All Configs (VR)
+ DVR Config Profile
- Allow to use all DVR configuration profiles. If not set, a DVR
- configuration profile matched to the authorized user by name is always used
- (the configuration profile must have same name as the user). If the DVR
- configuration profile does not exists, the default profile is used.
- The user is also not allowed to select another profile.
+ If set, the user will only be able to use the DVR config profile
+ equal to this value.
+ Note that this field is unset when the DVR Config Profile is removed.
Web interface
@@ -84,13 +82,6 @@ The columns have the following functions:
Enables access to the Configuration tab.
- Username Channel Tag Match
-
- If enabled, the user will only be able to access channels with a tag the
- same name as the username.
-
- This provides a very rudimentary way of limiting access to certain channels.
-
Min Channel Num
If nonzero, the user will only be able to access channels with
@@ -103,11 +94,9 @@ The columns have the following functions:
Channel Tag
- If set, the user will only be able to access channels with
- a channel tag equal to this value. Note that this field stores
- the tag name (not identified). It means that this field would not be
- updated when the tag name is changed. A manual re-set of this field
- is required (for security reasons).
+ If set, the user will only be able to access channels containing
+ this channel tag.
+ Note that this field is unset when the channel tag is removed.
Comment
diff --git a/src/access.c b/src/access.c
index 9b970820..fa8ec0a8 100644
--- a/src/access.c
+++ b/src/access.c
@@ -37,6 +37,7 @@
#include "access.h"
#include "settings.h"
#include "channels.h"
+#include "dvr/dvr.h"
#include "tcp.h"
struct access_entry_queue access_entries;
@@ -161,6 +162,7 @@ access_destroy(access_t *a)
{
free(a->aa_username);
free(a->aa_representative);
+ htsmsg_destroy(a->aa_dvrcfgs);
htsmsg_destroy(a->aa_chtags);
free(a);
}
@@ -295,10 +297,16 @@ access_update(access_t *a, access_entry_t *ae)
}
}
- if(ae->ae_chtag && ae->ae_chtag[0] != '\0') {
+ if(ae->ae_dvr_config && ae->ae_dvr_config->dvr_config_name[0] != '\0') {
+ if (a->aa_dvrcfgs == NULL)
+ a->aa_dvrcfgs = htsmsg_create_list();
+ htsmsg_add_str(a->aa_dvrcfgs, NULL, idnode_uuid_as_str(&ae->ae_dvr_config->dvr_id));
+ }
+
+ if(ae->ae_chtag && ae->ae_chtag->ct_name[0] != '\0') {
if (a->aa_chtags == NULL)
a->aa_chtags = htsmsg_create_list();
- htsmsg_add_str(a->aa_chtags, NULL, ae->ae_chtag);
+ htsmsg_add_str(a->aa_chtags, NULL, idnode_uuid_as_str(&ae->ae_chtag->ct_id));
}
a->aa_rights |= ae->ae_rights;
@@ -557,12 +565,8 @@ access_entry_update_rights(access_entry_t *ae)
r |= ACCESS_ADVANCED_STREAMING;
if (ae->ae_dvr)
r |= ACCESS_RECORDER;
- if (ae->ae_dvrallcfg)
- r |= ACCESS_RECORDER_ALL;
if (ae->ae_webui)
r |= ACCESS_WEB_INTERFACE;
- if (ae->ae_tag_only)
- r |= ACCESS_TAG_ONLY;
if (ae->ae_admin)
r |= ACCESS_ADMIN;
ae->ae_rights = r;
@@ -639,6 +643,11 @@ access_entry_destroy(access_entry_t *ae)
TAILQ_REMOVE(&access_entries, ae, ae_link);
idnode_unlink(&ae->ae_id);
+ if (ae->ae_dvr_config)
+ LIST_REMOVE(ae, ae_dvr_config_link);
+ if (ae->ae_chtag)
+ LIST_REMOVE(ae, ae_channel_tag_link);
+
while((ai = TAILQ_FIRST(&ae->ae_ipmasks)) != NULL)
{
TAILQ_REMOVE(&ae->ae_ipmasks, ai, ai_link);
@@ -653,6 +662,38 @@ access_entry_destroy(access_entry_t *ae)
free(ae);
}
+/*
+ *
+ */
+void
+access_destroy_by_dvr_config(dvr_config_t *cfg, int delconf)
+{
+ access_entry_t *ae;
+
+ while ((ae = LIST_FIRST(&cfg->dvr_accesses)) != NULL) {
+ LIST_REMOVE(ae, ae_dvr_config_link);
+ ae->ae_dvr_config = NULL;
+ if (delconf)
+ access_entry_save(ae);
+ }
+}
+
+/*
+ *
+ */
+void
+access_destroy_by_channel_tag(channel_tag_t *ct, int delconf)
+{
+ access_entry_t *ae;
+
+ while ((ae = LIST_FIRST(&ct->ct_accesses)) != NULL) {
+ LIST_REMOVE(ae, ae_channel_tag_link);
+ ae->ae_chtag = NULL;
+ if (delconf)
+ access_entry_save(ae);
+ }
+}
+
/**
*
*/
@@ -806,14 +847,66 @@ access_entry_class_password2_set(void *o, const void *v)
return 0;
}
-static htsmsg_t *
-access_entry_chtag_list ( void *o )
+static int
+access_entry_chtag_set(void *o, const void *v)
{
- channel_tag_t *ct;
- htsmsg_t *m = htsmsg_create_list();
- TAILQ_FOREACH(ct, &channel_tags, ct_link)
- htsmsg_add_str(m, NULL, ct->ct_name);
- return m;
+ access_entry_t *ae = (access_entry_t *)o;
+ channel_tag_t *tag = v ? channel_tag_find_by_uuid(v) : NULL;
+ if (tag == NULL && ae->ae_chtag) {
+ LIST_REMOVE(ae, ae_channel_tag_link);
+ ae->ae_chtag = NULL;
+ return 1;
+ } else if (ae->ae_chtag != tag) {
+ if (ae->ae_chtag)
+ LIST_REMOVE(ae, ae_channel_tag_link);
+ ae->ae_chtag = tag;
+ LIST_INSERT_HEAD(&tag->ct_accesses, ae, ae_channel_tag_link);
+ return 1;
+ }
+ return 0;
+}
+
+static const void *
+access_entry_chtag_get(void *o)
+{
+ static const char *ret;
+ access_entry_t *ae = (access_entry_t *)o;
+ if (ae->ae_chtag)
+ ret = idnode_uuid_as_str(&ae->ae_chtag->ct_id);
+ else
+ ret = "";
+ return &ret;
+}
+
+static int
+access_entry_dvr_config_set(void *o, const void *v)
+{
+ access_entry_t *ae = (access_entry_t *)o;
+ dvr_config_t *cfg = v ? dvr_config_find_by_uuid(v) : NULL;
+ if (cfg == NULL && ae->ae_dvr_config) {
+ LIST_REMOVE(ae, ae_dvr_config_link);
+ ae->ae_dvr_config = NULL;
+ return 1;
+ } else if (ae->ae_dvr_config != cfg) {
+ if (ae->ae_dvr_config)
+ LIST_REMOVE(ae, ae_dvr_config_link);
+ ae->ae_dvr_config = cfg;
+ LIST_INSERT_HEAD(&cfg->dvr_accesses, ae, ae_dvr_config_link);
+ return 1;
+ }
+ return 0;
+}
+
+static const void *
+access_entry_dvr_config_get(void *o)
+{
+ static const char *ret;
+ access_entry_t *ae = (access_entry_t *)o;
+ if (ae->ae_dvr_config)
+ ret = idnode_uuid_as_str(&ae->ae_dvr_config->dvr_id);
+ else
+ ret = "";
+ return &ret;
}
const idclass_t access_entry_class = {
@@ -886,10 +979,13 @@ const idclass_t access_entry_class = {
.off = offsetof(access_entry_t, ae_dvr),
},
{
- .type = PT_BOOL,
- .id = "dvrallcfg",
- .name = "All Configs (VR)",
- .off = offsetof(access_entry_t, ae_dvrallcfg),
+ .type = PT_STR,
+ .id = "dvr_config",
+ .name = "DVR Config Profile",
+ .set = access_entry_dvr_config_set,
+ .get = access_entry_dvr_config_get,
+ .list = dvr_entry_class_config_name_list,
+ .off = offsetof(access_entry_t, ae_dvr_config),
},
{
.type = PT_BOOL,
@@ -903,12 +999,6 @@ const idclass_t access_entry_class = {
.name = "Admin",
.off = offsetof(access_entry_t, ae_admin),
},
- {
- .type = PT_BOOL,
- .id = "tag_only",
- .name = "Username Channel Tag Match",
- .off = offsetof(access_entry_t, ae_tag_only),
- },
{
.type = PT_U32,
.id = "channel_min",
@@ -926,7 +1016,9 @@ const idclass_t access_entry_class = {
.id = "channel_tag",
.name = "Channel Tag",
.off = offsetof(access_entry_t, ae_chtag),
- .list = access_entry_chtag_list,
+ .set = access_entry_chtag_set,
+ .get = access_entry_chtag_get,
+ .list = channel_tag_class_get_list,
},
{
.type = PT_STR,
diff --git a/src/access.h b/src/access.h
index 9315c50e..b6adad0d 100644
--- a/src/access.h
+++ b/src/access.h
@@ -22,6 +22,9 @@
#include "idnode.h"
#include "htsmsg.h"
+struct dvr_config;
+struct channel_tag;
+
typedef struct access_ipmask {
TAILQ_ENTRY(access_ipmask) ai_link;
@@ -48,18 +51,25 @@ typedef struct access_entry {
char *ae_password;
char *ae_password2;
char *ae_comment;
+
int ae_index;
int ae_enabled;
+
int ae_streaming;
int ae_adv_streaming;
+
int ae_dvr;
- int ae_dvrallcfg;
+ struct dvr_config *ae_dvr_config;
+ LIST_ENTRY(access_entry) ae_dvr_config_link;
+
int ae_webui;
int ae_admin;
- int ae_tag_only;
+
uint32_t ae_chmin;
uint32_t ae_chmax;
- char *ae_chtag;
+
+ struct channel_tag *ae_chtag;
+ LIST_ENTRY(access_entry) ae_channel_tag_link;
uint32_t ae_rights;
@@ -85,6 +95,7 @@ typedef struct access {
char *aa_username;
char *aa_representative;
uint32_t aa_rights;
+ htsmsg_t *aa_dvrcfgs;
uint32_t aa_chmin;
uint32_t aa_chmax;
htsmsg_t *aa_chtags;
@@ -96,14 +107,11 @@ typedef struct access {
#define ACCESS_ADVANCED_STREAMING (1<<1)
#define ACCESS_WEB_INTERFACE (1<<2)
#define ACCESS_RECORDER (1<<3)
-#define ACCESS_RECORDER_ALL (1<<4)
-#define ACCESS_TAG_ONLY (1<<5)
-#define ACCESS_ADMIN (1<<6)
+#define ACCESS_ADMIN (1<<4)
#define ACCESS_FULL \
(ACCESS_STREAMING | ACCESS_ADVANCED_STREAMING | \
- ACCESS_WEB_INTERFACE | ACCESS_RECORDER | \
- ACCESS_RECORDER_ALL | ACCESS_ADMIN)
+ ACCESS_WEB_INTERFACE | ACCESS_RECORDER | ACCESS_ADMIN)
/**
* Create a new ticket for the requested resource and generate a id for it
@@ -165,6 +173,14 @@ access_entry_create(const char *uuid, htsmsg_t *conf);
void
access_entry_save(access_entry_t *ae);
+/**
+ *
+ */
+void
+access_destroy_by_dvr_config(struct dvr_config *cfg, int delconf);
+void
+access_destroy_by_channel_tag(struct channel_tag *ct, int delconf);
+
/**
*
*/
diff --git a/src/api/api_dvr.c b/src/api/api_dvr.c
index 69369fa2..92a5b5e2 100644
--- a/src/api/api_dvr.c
+++ b/src/api/api_dvr.c
@@ -25,21 +25,28 @@
static const char *
api_dvr_config_name( access_t *perm, const char *config_uuid )
{
- dvr_config_t *cfg;
+ dvr_config_t *cfg = NULL;
+ htsmsg_field_t *f;
+ const char *uuid;
lock_assert(&global_lock);
- if (access_verify2(perm, ACCESS_RECORDER_ALL))
+ if (perm->aa_dvrcfgs == NULL)
return config_uuid; /* no change */
- cfg = dvr_config_find_by_name(perm->aa_username);
- if (cfg)
- return cfg->dvr_config_name;
+ config_uuid = config_uuid ?: "";
+ HTSMSG_FOREACH(f, perm->aa_dvrcfgs) {
+ uuid = htsmsg_field_get_str(f) ?: "";
+ if (strcmp(uuid, config_uuid) == 0)
+ return config_uuid;
+ if (!cfg)
+ cfg = dvr_config_find_by_uuid(uuid);
+ }
- if (perm->aa_username)
- tvhlog(LOG_INFO, "dvr", "User '%s' has no dvr config with identical name, using default...", perm->aa_username);
+ if (!cfg && perm->aa_username)
+ tvhlog(LOG_INFO, "dvr", "User '%s' has no valid dvr config in ACL, using default...", perm->aa_username);
- return NULL; /* default */
+ return cfg ? idnode_uuid_as_str(&cfg->dvr_id) : NULL;
}
/*
@@ -71,9 +78,6 @@ api_dvr_config_create
return EINVAL;
if (s[0] == '\0')
return EINVAL;
- if (access_verify2(perm, ACCESS_ADMIN) &&
- access_verify2(perm, ACCESS_RECORDER_ALL | ACCESS_RECORDER))
- return EACCES;
pthread_mutex_lock(&global_lock);
if ((cfg = dvr_config_create(NULL, NULL, conf)))
@@ -153,16 +157,20 @@ api_dvr_entry_create
{
dvr_entry_t *de;
htsmsg_t *conf;
+ const char *s1, *s2;
if (!(conf = htsmsg_get_map(args, "conf")))
return EINVAL;
- if (access_verify2(perm, ACCESS_RECORDER_ALL)) {
+ pthread_mutex_lock(&global_lock);
+ s1 = htsmsg_get_str(conf, "config_name");
+ s2 = api_dvr_config_name(perm, s1);
+ if (strcmp(s1 ?: "", s2 ?: "")) {
htsmsg_delete_field(conf, "config_name");
- htsmsg_add_str(conf, "config_name", perm->aa_username ?: "");
+ if (s2)
+ htsmsg_add_str(conf, "config_name", s2);
}
- pthread_mutex_lock(&global_lock);
if ((de = dvr_entry_create(NULL, conf)))
dvr_entry_save(de);
pthread_mutex_unlock(&global_lock);
diff --git a/src/channels.c b/src/channels.c
index 6e5926c2..3dc23f4b 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -163,19 +163,6 @@ channel_class_tags_set ( void *obj, const void *p )
return channel_set_tags_by_list(obj, (htsmsg_t*)p);
}
-static htsmsg_t *
-channel_class_tags_enum ( void *obj )
-{
- htsmsg_t *e, *m = htsmsg_create_map();
- htsmsg_add_str(m, "type", "api");
- htsmsg_add_str(m, "uri", "channeltag/list");
- htsmsg_add_str(m, "event", "channeltag");
- e = htsmsg_create_map();
- htsmsg_add_bool(e, "enum", 1);
- htsmsg_add_msg(m, "params", e);
- return m;
-}
-
static void
channel_class_icon_notify ( void *obj )
{
@@ -369,7 +356,7 @@ const idclass_t channel_class = {
.name = "Tags",
.get = channel_class_tags_get,
.set = channel_class_tags_set,
- .list = channel_class_tags_enum,
+ .list = channel_tag_class_get_list,
.rend = channel_class_tags_rend
},
{}
@@ -430,7 +417,8 @@ channel_access(channel_t *ch, access_t *a, const char *username)
htsmsg_field_t *f;
HTSMSG_FOREACH(f, a->aa_chtags) {
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
- if (!strcmp(htsmsg_field_get_str(f) ?: "", ctm->ctm_tag->ct_name))
+ if (!strcmp(htsmsg_field_get_str(f) ?: "",
+ idnode_uuid_as_str(&ctm->ctm_tag->ct_id)))
goto chtags_ok;
}
}
@@ -438,17 +426,6 @@ channel_access(channel_t *ch, access_t *a, const char *username)
}
chtags_ok:
- /* Channel tag <-> user name match */
- if (ch && (a->aa_rights & ACCESS_TAG_ONLY) != 0) {
- channel_tag_mapping_t *ctm;
- LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
- if (!strcmp(username ?: "", ctm->ctm_tag->ct_name))
- goto tagonly_ok;
- }
- return 0;
- }
-tagonly_ok:
-
return 1;
}
@@ -761,6 +738,9 @@ channel_tag_create(const char *uuid, htsmsg_t *conf)
channel_tag_t *ct;
ct = calloc(1, sizeof(channel_tag_t));
+ LIST_INIT(&ct->ct_ctms);
+ LIST_INIT(&ct->ct_autorecs);
+ LIST_INIT(&ct->ct_accesses);
if (idnode_insert(&ct->ct_id, uuid, &channel_tag_class, IDNODE_SHORT_UUID)) {
if (uuid)
@@ -807,6 +787,9 @@ channel_tag_destroy(channel_tag_t *ct, int delconf)
TAILQ_REMOVE(&channel_tags, ct, ct_link);
idnode_unlink(&ct->ct_id);
+ autorec_destroy_by_channel_tag(ct, delconf);
+ access_destroy_by_channel_tag(ct, delconf);
+
free(ct->ct_name);
free(ct->ct_comment);
free(ct->ct_icon);
@@ -849,6 +832,17 @@ channel_tag_class_get_title (idnode_t *self)
return ct->ct_name ?: "";
}
+/* exported for others */
+htsmsg_t *
+channel_tag_class_get_list(void *o)
+{
+ htsmsg_t *m = htsmsg_create_map();
+ htsmsg_add_str(m, "type", "api");
+ htsmsg_add_str(m, "uri", "channeltag/list");
+ htsmsg_add_str(m, "event", "channeltag");
+ return m;
+}
+
const idclass_t channel_tag_class = {
.ic_class = "channeltag",
.ic_caption = "Channel Tag",
diff --git a/src/channels.h b/src/channels.h
index 227caffb..fe0fc9cc 100644
--- a/src/channels.h
+++ b/src/channels.h
@@ -95,6 +95,8 @@ typedef struct channel_tag {
struct dvr_autorec_entry_list ct_autorecs;
+ struct access_entry_list ct_accesses;
+
int ct_htsp_id;
} channel_tag_t;
@@ -164,6 +166,8 @@ static inline channel_tag_t *channel_tag_find_by_uuid(const char *uuid)
void channel_tag_save(channel_tag_t *ct);
+htsmsg_t * channel_tag_class_get_list(void *o);
+
int channel_access(channel_t *ch, struct access *a, const char *username);
int channel_tag_map(channel_t *ch, channel_tag_t *ct);
diff --git a/src/config.c b/src/config.c
index 07465925..3afdfbf6 100644
--- a/src/config.c
+++ b/src/config.c
@@ -853,6 +853,109 @@ config_migrate_v10 ( void )
config_migrate_move("channeltags", "channel/tag");
}
+static const char *
+config_find_uuid( htsmsg_t *map, const char *name, const char *value )
+{
+ htsmsg_t *e;
+ htsmsg_field_t *f;
+ const char *s;
+
+ HTSMSG_FOREACH(f, map) {
+ if (!(e = htsmsg_field_get_map(f))) continue;
+ if ((s = htsmsg_get_str(e, name)) != NULL) {
+ if (!strcmp(s, value))
+ return f->hmf_name;
+ }
+ }
+ return NULL;
+}
+
+static void
+config_modify_acl_dvallcfg( htsmsg_t *c, htsmsg_t *dvr_config )
+{
+ uint32_t a;
+ const char *username, *uuid;
+
+ username = htsmsg_get_str(c, "username");
+ if (!htsmsg_get_u32(c, "dvallcfg", &a))
+ if (a == 0) {
+ uuid = username ? config_find_uuid(dvr_config, "name", username) : NULL;
+ if (uuid)
+ htsmsg_add_str(c, "dvr_config", uuid);
+ }
+ htsmsg_delete_field(c, "dvallcfg");
+}
+
+static void
+config_modify_acl_tag_only( htsmsg_t *c, htsmsg_t *channel_tag )
+{
+ uint32_t a;
+ const char *username, *tag, *uuid;
+
+ username = htsmsg_get_str(c, "username");
+ tag = htsmsg_get_str(c, "channel_tag");
+ if (!tag || tag[0] == '\0')
+ tag = NULL;
+ if (tag == NULL && !htsmsg_get_u32(c, "tag_only", &a)) {
+ if (a) {
+ uuid = username ? config_find_uuid(channel_tag, "name", username) : NULL;
+ if (uuid)
+ htsmsg_add_str(c, "channel_tag", uuid);
+ }
+ } else if (tag) {
+ uuid = config_find_uuid(channel_tag, "name", tag);
+ if (uuid) {
+ htsmsg_delete_field(c, "channel_tag");
+ htsmsg_add_str(c, "channel_tag", uuid);
+ }
+ }
+ htsmsg_delete_field(c, "tag_only");
+}
+
+static void
+config_modify_dvr_config_name( htsmsg_t *c, htsmsg_t *dvr_config )
+{
+ const char *config_name, *uuid;
+
+ config_name = htsmsg_get_str(c, "config_name");
+ uuid = config_name ? config_find_uuid(dvr_config, "name", config_name) : NULL;
+ htsmsg_delete_field(c, "config_name");
+ htsmsg_add_str(c, "config_name", uuid ?: "");
+}
+
+
+static void
+config_migrate_v11 ( void )
+{
+ htsmsg_t *dvr_config;
+ htsmsg_t *channel_tag;
+ htsmsg_t *c, *e;
+ htsmsg_field_t *f;
+
+ dvr_config = hts_settings_load("dvr/config");
+ channel_tag = hts_settings_load("channel/tag");
+
+ if ((c = hts_settings_load("accesscontrol")) != NULL) {
+ HTSMSG_FOREACH(f, c) {
+ if (!(e = htsmsg_field_get_map(f))) continue;
+ config_modify_acl_dvallcfg(e, dvr_config);
+ config_modify_acl_tag_only(e, channel_tag);
+ }
+ htsmsg_destroy(c);
+ }
+
+ if ((c = hts_settings_load("dvr/log")) != NULL) {
+ HTSMSG_FOREACH(f, c) {
+ if (!(e = htsmsg_field_get_map(f))) continue;
+ config_modify_dvr_config_name(e, dvr_config);
+ }
+ htsmsg_destroy(c);
+ }
+
+ htsmsg_destroy(channel_tag);
+ htsmsg_destroy(dvr_config);
+}
+
/*
* Migration table
*/
@@ -867,6 +970,7 @@ static const config_migrate_t config_migrate_table[] = {
config_migrate_v8,
config_migrate_v9,
config_migrate_v10,
+ config_migrate_v11
};
/*
diff --git a/src/dvr/dvr.h b/src/dvr/dvr.h
index 015b5b8a..355db379 100644
--- a/src/dvr/dvr.h
+++ b/src/dvr/dvr.h
@@ -28,6 +28,7 @@
typedef struct dvr_config {
idnode_t dvr_id;
+
LIST_ENTRY(dvr_config) config_link;
int dvr_enabled;
@@ -72,6 +73,10 @@ typedef struct dvr_config {
/* Duplicate detect */
int dvr_dup_detect_episode;
+ struct dvr_entry_list dvr_entries;
+
+ struct access_entry_list dvr_accesses;
+
} dvr_config_t;
extern struct dvr_config_list dvrconfigs;
@@ -150,7 +155,7 @@ typedef struct dvr_entry {
*/
dvr_config_t *de_config;
- char *de_config_name;
+ LIST_ENTRY(dvr_entry) de_config_link;
time_t de_start;
time_t de_stop;
@@ -185,6 +190,7 @@ typedef struct dvr_entry {
* Recording state (onyl valid if de_sched_state == DVR_RECORDING)
*/
dvr_rs_state_t de_rec_state;
+ int de_locked;
/**
* Number of errors (only to be modified by the recording thread)
@@ -346,6 +352,7 @@ dvr_entry_update( dvr_entry_t *de,
time_t de_start_extra, time_t de_stop_extra );
void dvr_init(void);
+void dvr_config_init(void);
void dvr_done(void);
@@ -438,6 +445,8 @@ void dvr_autorec_check_serieslink(epg_serieslink_t *s);
void autorec_destroy_by_channel(channel_t *ch, int delconf);
+void autorec_destroy_by_channel_tag(channel_tag_t *ct, int delconf);
+
/**
*
*/
diff --git a/src/dvr/dvr_autorec.c b/src/dvr/dvr_autorec.c
index 8b2d3429..c07ee9b9 100644
--- a/src/dvr/dvr_autorec.c
+++ b/src/dvr/dvr_autorec.c
@@ -392,18 +392,6 @@ dvr_autorec_entry_class_tag_get(void *o)
return &ret;
}
-static htsmsg_t *
-dvr_autorec_entry_class_tag_list(void *o)
-{
- htsmsg_t *e, *m = htsmsg_create_map();
- htsmsg_add_str(m, "type", "api");
- htsmsg_add_str(m, "uri", "channeltag/list");
- htsmsg_add_str(m, "event", "channeltag");
- e = htsmsg_create_map();
- htsmsg_add_msg(m, "params", e);
- return m;
-}
-
static int
dvr_autorec_entry_class_time_set(void *o, const void *v, int *tm)
{
@@ -726,7 +714,7 @@ const idclass_t dvr_autorec_entry_class = {
.name = "Channel Tag",
.set = dvr_autorec_entry_class_tag_set,
.get = dvr_autorec_entry_class_tag_get,
- .list = dvr_autorec_entry_class_tag_list,
+ .list = channel_tag_class_get_list,
},
{
.type = PT_STR,
@@ -913,7 +901,7 @@ dvr_autorec_changed(dvr_autorec_entry_t *dae, int purge)
CHANNEL_FOREACH(ch) {
RB_FOREACH(e, &ch->ch_epg_schedule, sched_link) {
if(autorec_cmp(dae, e))
- dvr_entry_create_by_autorec(e, dae);
+ dvr_entry_create_by_autorec(e, dae);
}
}
}
@@ -936,3 +924,25 @@ autorec_destroy_by_channel(channel_t *ch, int delconf)
htsmsg_add_u32(m, "reload", 1);
notify_by_msg("autorec", m);
}
+
+/*
+ *
+ */
+void
+autorec_destroy_by_channel_tag(channel_tag_t *ct, int delconf)
+{
+ dvr_autorec_entry_t *dae;
+ htsmsg_t *m;
+
+ while((dae = LIST_FIRST(&ct->ct_autorecs)) != NULL) {
+ LIST_REMOVE(dae, dae_channel_tag_link);
+ dae->dae_channel_tag = NULL;
+ if (delconf)
+ dvr_autorec_save(dae);
+ }
+
+ /* Notify web clients that we have messed with the tables */
+ m = htsmsg_create_map();
+ htsmsg_add_u32(m, "reload", 1);
+ notify_by_msg("autorec", m);
+}
diff --git a/src/dvr/dvr_db.c b/src/dvr/dvr_db.c
index 8aa1f05e..981abec7 100644
--- a/src/dvr/dvr_db.c
+++ b/src/dvr/dvr_db.c
@@ -43,7 +43,7 @@ struct dvr_entry_list dvrentries;
static gtimer_t dvr_dbus_timer;
#endif
-static void dvr_entry_remove(dvr_entry_t *de, int delconf);
+static void dvr_entry_destroy(dvr_entry_t *de, int delconf);
static void dvr_timer_expire(void *aux);
static void dvr_timer_start_recording(void *aux);
static void dvr_timer_stop_recording(void *aux);
@@ -421,7 +421,7 @@ dvr_entry_create(const char *uuid, htsmsg_t *conf)
if(de2 != de &&
de2->de_start == de->de_start &&
de2->de_sched_state != DVR_COMPLETED) {
- dvr_entry_remove(de, 0);
+ dvr_entry_destroy(de, 0);
return NULL;
}
}
@@ -524,7 +524,11 @@ dvr_entry_create_htsp(const char *config_uuid,
const char *creator, dvr_autorec_entry_t *dae,
dvr_prio_t pri)
{
- return _dvr_entry_create(config_uuid, NULL,
+ dvr_config_t *cfg = dvr_config_find_by_uuid(config_uuid);
+ if (!cfg)
+ cfg = dvr_config_find_by_name(config_uuid);
+ return _dvr_entry_create(cfg ? idnode_uuid_as_str(&cfg->dvr_id) : NULL,
+ NULL,
ch, start, stop, start_extra, stop_extra,
title, description, lang, content_type,
creator, dae, pri);
@@ -625,8 +629,10 @@ dvr_entry_dec_ref(dvr_entry_t *de)
if(de->de_autorec != NULL)
LIST_REMOVE(de, de_autorec_link);
+ if(de->de_config != NULL)
+ LIST_REMOVE(de, de_config_link);
+
free(de->de_filename);
- free(de->de_config_name);
free(de->de_creator);
if (de->de_title) lang_str_destroy(de->de_title);
if (de->de_desc) lang_str_destroy(de->de_desc);
@@ -639,7 +645,7 @@ dvr_entry_dec_ref(dvr_entry_t *de)
*
*/
static void
-dvr_entry_remove(dvr_entry_t *de, int delconf)
+dvr_entry_destroy(dvr_entry_t *de, int delconf)
{
if (delconf)
hts_settings_remove("dvr/log/%s", idnode_uuid_as_str(&de->de_id));
@@ -665,6 +671,27 @@ dvr_entry_remove(dvr_entry_t *de, int delconf)
dvr_entry_dec_ref(de);
}
+/**
+ *
+ */
+static void
+dvr_entry_destroy_by_config(dvr_config_t *cfg, int delconf)
+{
+ dvr_entry_t *de;
+ dvr_config_t *def = NULL;
+
+ while ((de = LIST_FIRST(&cfg->dvr_entries)) != NULL) {
+ LIST_REMOVE(de, de_config_link);
+ if (!def)
+ def = dvr_config_find_by_name_default("");
+ de->de_config = def;
+ if (def)
+ LIST_INSERT_HEAD(&def->dvr_entries, de, de_config_link);
+ if (delconf)
+ dvr_entry_save(de);
+ }
+}
+
/**
*
*/
@@ -707,7 +734,7 @@ static void
dvr_timer_expire(void *aux)
{
dvr_entry_t *de = aux;
- dvr_entry_remove(de, 1);
+ dvr_entry_destroy(de, 1);
}
@@ -718,6 +745,9 @@ static dvr_entry_t *_dvr_entry_update
{
int save = 0;
+ if (de->de_locked)
+ return de;
+
/* Start/Stop */
if (e) {
start = e->start;
@@ -836,7 +866,7 @@ dvr_event_replaced(epg_broadcast_t *e, epg_broadcast_t *new_e)
/* If this was craeted by autorec - just remove it, it'll get recreated */
if (de->de_autorec) {
- dvr_entry_remove(de, 1);
+ dvr_entry_destroy(de, 1);
/* Find match */
} else {
@@ -1006,7 +1036,7 @@ dvr_entry_cancel(dvr_entry_t *de)
{
switch(de->de_sched_state) {
case DVR_SCHEDULED:
- dvr_entry_remove(de, 1);
+ dvr_entry_destroy(de, 1);
return NULL;
case DVR_RECORDING:
@@ -1015,11 +1045,11 @@ dvr_entry_cancel(dvr_entry_t *de)
return de;
case DVR_COMPLETED:
- dvr_entry_remove(de, 1);
+ dvr_entry_destroy(de, 1);
return NULL;
case DVR_MISSED_TIME:
- dvr_entry_remove(de, 1);
+ dvr_entry_destroy(de, 1);
return NULL;
default:
@@ -1052,7 +1082,10 @@ dvr_entry_class_save(idnode_t *self)
static void
dvr_entry_class_delete(idnode_t *self)
{
- dvr_entry_remove((dvr_entry_t *)self, 1);
+ dvr_entry_t *de = (dvr_entry_t *)self;
+ if (de->de_locked)
+ return;
+ dvr_entry_destroy(de, 1);
}
static const char *
@@ -1070,25 +1103,52 @@ static int
dvr_entry_class_config_name_set(void *o, const void *v)
{
dvr_entry_t *de = (dvr_entry_t *)o;
- const char *s = v ?: "";
- if (strcmp(s, de->de_config_name ?: "")) {
- free(de->de_config_name);
- de->de_config_name = strdup(v);
- de->de_config = dvr_config_find_by_name_default(de->de_config_name);
+ dvr_config_t *cfg;
+
+ if (de->de_locked)
+ return 0;
+ cfg = v ? dvr_config_find_by_uuid(v) : NULL;
+ if (!cfg)
+ cfg = dvr_config_find_by_name_default("");
+ if (cfg == NULL) {
+ if (de->de_config) {
+ LIST_REMOVE(de, de_config_link);
+ de->de_config = NULL;
+ return 1;
+ }
+ } else if (de->de_config != cfg) {
+ if (de->de_config)
+ LIST_REMOVE(de, de_config_link);
+ de->de_config = cfg;
+ LIST_INSERT_HEAD(&cfg->dvr_entries, de, de_config_link);
return 1;
}
- /* for sure */
- de->de_config = dvr_config_find_by_name_default(de->de_config_name);
return 0;
}
+static const void *
+dvr_entry_class_config_name_get(void *o)
+{
+ static const char *ret;
+ dvr_entry_t *de = (dvr_entry_t *)o;
+ if (de->de_config)
+ ret = idnode_uuid_as_str(&de->de_config->dvr_id);
+ else
+ ret = "";
+ return &ret;
+}
+
static int
dvr_entry_class_channel_set(void *o, const void *v)
{
dvr_entry_t *de = (dvr_entry_t *)o;
- channel_t *ch = v ? channel_find_by_uuid(v) : NULL;
- if (!de->de_config_name)
- dvr_entry_class_config_name_set(o, "");
+ channel_t *ch;
+
+ if (de->de_locked)
+ return 0;
+ ch = v ? channel_find_by_uuid(v) : NULL;
+ if (!de->de_config)
+ de->de_config = dvr_config_find_by_name_default("");
if (ch == NULL) {
if (de->de_channel) {
LIST_REMOVE(de, de_channel_link);
@@ -1134,8 +1194,10 @@ dvr_entry_class_channel_name_set(void *o, const void *v)
{
dvr_entry_t *de = (dvr_entry_t *)o;
channel_t *ch;
- if (!de->de_config_name)
- dvr_entry_class_config_name_set(o, "");
+ if (de->de_locked)
+ return 0;
+ if (!de->de_config)
+ de->de_config = dvr_config_find_by_name_default("");
if (!strcmp(de->de_channel_name ?: "", v ?: ""))
return 0;
ch = v ? channel_find_by_name(v) : NULL;
@@ -1629,8 +1691,8 @@ const idclass_t dvr_entry_class = {
.id = "config_name",
.name = "DVR Configuration",
.set = dvr_entry_class_config_name_set,
+ .get = dvr_entry_class_config_name_get,
.list = dvr_entry_class_config_name_list,
- .off = offsetof(dvr_entry_t, de_config_name),
},
{
.type = PT_STR,
@@ -1813,6 +1875,8 @@ dvr_config_create(const char *name, const char *uuid, htsmsg_t *conf)
name = "";
cfg = calloc(1, sizeof(dvr_config_t));
+ LIST_INIT(&cfg->dvr_entries);
+ LIST_INIT(&cfg->dvr_accesses);
if (idnode_insert(&cfg->dvr_id, uuid, &dvr_config_class, 0)) {
if (uuid)
@@ -1872,6 +1936,10 @@ dvr_config_destroy(dvr_config_t *cfg, int delconf)
}
LIST_REMOVE(cfg, config_link);
idnode_unlink(&cfg->dvr_id);
+
+ dvr_entry_destroy_by_config(cfg, delconf);
+ access_destroy_by_dvr_config(cfg, delconf);
+
free(cfg->dvr_charset_id);
free(cfg->dvr_charset);
free(cfg->dvr_storage);
@@ -1962,12 +2030,23 @@ static int
dvr_config_class_perm(idnode_t *self, access_t *a, htsmsg_t *msg_to_write)
{
dvr_config_t *cfg = (dvr_config_t *)self;
+ htsmsg_field_t *f;
+ const char *uuid, *my_uuid;
+
if (access_verify2(a, ACCESS_RECORDER))
return -1;
if (!access_verify2(a, ACCESS_ADMIN))
return 0;
- if (access_verify2(a, ACCESS_RECORDER_ALL))
- return 0;
+ if (a->aa_dvrcfgs) {
+ my_uuid = idnode_uuid_as_str(&cfg->dvr_id);
+ HTSMSG_FOREACH(f, a->aa_dvrcfgs) {
+ uuid = htsmsg_field_get_str(f) ?: "";
+ if (!strcmp(uuid, my_uuid))
+ goto fine;
+ }
+ return -1;
+ }
+fine:
if (strcmp(cfg->dvr_config_name ?: "", a->aa_username ?: ""))
return -1;
return 0;
@@ -2433,7 +2512,7 @@ dvr_entry_delete(dvr_entry_t *de)
}
}
- dvr_entry_remove(de, 1);
+ dvr_entry_destroy(de, 1);
}
/**
@@ -2444,7 +2523,7 @@ dvr_entry_cancel_delete(dvr_entry_t *de)
{
switch(de->de_sched_state) {
case DVR_SCHEDULED:
- dvr_entry_remove(de, 1);
+ dvr_entry_destroy(de, 1);
break;
case DVR_RECORDING:
@@ -2455,7 +2534,7 @@ dvr_entry_cancel_delete(dvr_entry_t *de)
break;
case DVR_MISSED_TIME:
- dvr_entry_remove(de, 1);
+ dvr_entry_destroy(de, 1);
break;
default:
@@ -2467,7 +2546,7 @@ dvr_entry_cancel_delete(dvr_entry_t *de)
*
*/
void
-dvr_init(void)
+dvr_config_init(void)
{
htsmsg_t *m, *l;
htsmsg_field_t *f;
@@ -2520,7 +2599,11 @@ dvr_init(void)
cfg->dvr_config_name, cfg->dvr_storage);
}
}
+}
+void
+dvr_init(void)
+{
#if ENABLE_INOTIFY
dvr_inotify_init();
#endif
@@ -2542,10 +2625,10 @@ dvr_done(void)
dvr_inotify_done();
#endif
pthread_mutex_lock(&global_lock);
+ while ((de = LIST_FIRST(&dvrentries)) != NULL)
+ dvr_entry_destroy(de, 0);
while ((cfg = LIST_FIRST(&dvrconfigs)) != NULL)
dvr_config_destroy(cfg, 0);
- while ((de = LIST_FIRST(&dvrentries)) != NULL)
- dvr_entry_remove(de, 0);
pthread_mutex_unlock(&global_lock);
dvr_autorec_done();
}
diff --git a/src/dvr/dvr_rec.c b/src/dvr/dvr_rec.c
index 472177b5..45146f10 100644
--- a/src/dvr/dvr_rec.c
+++ b/src/dvr/dvr_rec.c
@@ -66,6 +66,9 @@ dvr_rec_subscribe(dvr_entry_t *de)
int flags;
assert(de->de_s == NULL);
+ assert(!de->de_locked);
+
+ de->de_locked = 1;
if(de->de_pri < 5)
weight = prio2weight[de->de_pri];
@@ -117,6 +120,8 @@ dvr_rec_unsubscribe(dvr_entry_t *de, int stopcode)
globalheaders_destroy(de->de_gh);
de->de_last_error = stopcode;
+
+ de->de_locked = 0;
}
@@ -177,7 +182,10 @@ pvr_generate_filename(dvr_entry_t *de, const streaming_start_t *ss)
struct stat st;
char *filename, *s;
struct tm tm;
- dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
+ dvr_config_t *cfg = de->de_config;
+
+ if (de == NULL)
+ return -1;
strncpy(path, cfg->dvr_storage, sizeof(path));
path[sizeof(path)-1] = '\0';
@@ -304,9 +312,14 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
const source_info_t *si = &ss->ss_si;
const streaming_start_component_t *ssc;
int i;
- dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
+ dvr_config_t *cfg = de->de_config;
muxer_container_type_t mc;
+ if (!cfg) {
+ dvr_rec_fatal_error(de, "Unable to determine config profile");
+ return -1;
+ }
+
mc = dvr_entry_get_mc(de);
de->de_mux = muxer_create(mc, &cfg->dvr_muxcnf);
@@ -427,7 +440,7 @@ static void *
dvr_thread(void *aux)
{
dvr_entry_t *de = aux;
- dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
+ dvr_config_t *cfg = de->de_config;
streaming_queue_t *sq = &de->de_sq;
streaming_message_t *sm;
th_pkt_t *pkt;
@@ -666,7 +679,7 @@ dvr_thread_epilog(dvr_entry_t *de)
muxer_destroy(de->de_mux);
de->de_mux = NULL;
- dvr_config_t *cfg = dvr_config_find_by_name_default(de->de_config_name);
- if(cfg->dvr_postproc && de->de_filename)
+ dvr_config_t *cfg = de->de_config;
+ if(cfg && cfg->dvr_postproc && de->de_filename)
dvr_spawn_postproc(de,cfg->dvr_postproc);
}
diff --git a/src/htsp_server.c b/src/htsp_server.c
index eced3aba..ff46aac0 100644
--- a/src/htsp_server.c
+++ b/src/htsp_server.c
@@ -652,7 +652,6 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
htsmsg_t *out = htsmsg_create_map();
const char *s = NULL, *error = NULL;
const char *p;
- dvr_config_t *cfg;
htsmsg_add_u32(out, "id", idnode_get_short_uuid(&de->de_id));
if (de->de_channel)
@@ -666,11 +665,9 @@ htsp_build_dvrentry(dvr_entry_t *de, const char *method)
if( de->de_desc && (s = lang_str_get(de->de_desc, NULL)))
htsmsg_add_str(out, "description", s);
- if( de->de_filename && de->de_config_name ) {
- if ((cfg = dvr_config_find_by_name_default(de->de_config_name))) {
- if ((p = tvh_strbegins(de->de_filename, cfg->dvr_storage)))
- htsmsg_add_str(out, "path", p);
- }
+ if( de->de_filename && de->de_config ) {
+ if ((p = tvh_strbegins(de->de_filename, de->de_config->dvr_storage)))
+ htsmsg_add_str(out, "path", p);
}
switch(de->de_sched_state) {
diff --git a/src/idnode.c b/src/idnode.c
index 1d8a0294..0ab8e0b0 100644
--- a/src/idnode.c
+++ b/src/idnode.c
@@ -227,7 +227,7 @@ idnode_get_short_uuid (const idnode_t *in)
const char *
idnode_uuid_as_str(const idnode_t *in)
{
- static tvh_uuid_t ret[16];
+ static tvh_uuid_t __thread ret[16];
static uint8_t p = 0;
bin2hex(ret[p].hex, sizeof(ret[p].hex), in->in_uuid, sizeof(in->in_uuid));
const char *s = ret[p].hex;
diff --git a/src/main.c b/src/main.c
index 719ba4aa..0a2ae7a5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -821,6 +821,8 @@ main(int argc, char **argv)
subscription_init();
+ dvr_config_init();
+
access_init(opt_firstrun, opt_noacl);
#if ENABLE_TIMESHIFT
diff --git a/src/tvheadend.h b/src/tvheadend.h
index f32731dc..cf928a4c 100644
--- a/src/tvheadend.h
+++ b/src/tvheadend.h
@@ -177,6 +177,7 @@ void gtimer_disarm(gtimer_t *gti);
/*
* List / Queue header declarations
*/
+LIST_HEAD(access_entry_list, access_entry);
LIST_HEAD(th_subscription_list, th_subscription);
LIST_HEAD(dvr_config_list, dvr_config);
LIST_HEAD(dvr_entry_list, dvr_entry);
diff --git a/src/webui/static/app/acleditor.js b/src/webui/static/app/acleditor.js
index eb71cf1e..76013cd6 100644
--- a/src/webui/static/app/acleditor.js
+++ b/src/webui/static/app/acleditor.js
@@ -12,6 +12,10 @@ tvheadend.acleditor = function(panel)
items: []
});
+ var list = 'enabled,username,password,prefix,streaming,adv_streaming,' +
+ 'dvr,dvr_config,webui,admin,channel_min,channel_max,channel_tag,' +
+ 'comment';
+
tvheadend.idnode_grid(panel, {
url: 'api/access/entry',
comet: 'acl_entries',
@@ -20,10 +24,14 @@ tvheadend.acleditor = function(panel)
tabIndex: 0,
add: {
url: 'api/access/entry',
+ params: {
+ list: list,
+ },
create: { }
},
del: true,
move: true,
+ list: list,
help: function() {
new tvheadend.help('Access Control Entries', 'config_access.html');
},