]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
ACL/DVR: Rewrite channel tag and add DVR config selection
authorJaroslav Kysela <perex@perex.cz>
Tue, 2 Sep 2014 07:44:52 +0000 (09:44 +0200)
committerJaroslav Kysela <perex@perex.cz>
Mon, 8 Sep 2014 14:48:59 +0000 (16:48 +0200)
- remove also obsolete only_tag
- accept only uuid for DVR entry config name
- try to lock DVR entry when used in dvr_thread

16 files changed:
docs/html/config_access.html
src/access.c
src/access.h
src/api/api_dvr.c
src/channels.c
src/channels.h
src/config.c
src/dvr/dvr.h
src/dvr/dvr_autorec.c
src/dvr/dvr_db.c
src/dvr/dvr_rec.c
src/htsp_server.c
src/idnode.c
src/main.c
src/tvheadend.h
src/webui/static/app/acleditor.js

index 0e6d73bce5df4eae089c815ad42de17b9bd189fd..48cc42d9474cc925782f32cdc60041e95b76da1e 100644 (file)
@@ -68,13 +68,11 @@ The columns have the following functions:
   <dd>
   Enables access to all video recording functions. This also include administration of the auto recordings.
 
-  <dt>All Configs (VR)
+  <dt>DVR Config Profile
   <dd>
-  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.
 
   <dt>Web interface
   <dd>
@@ -84,13 +82,6 @@ The columns have the following functions:
   <dd>
   Enables access to the Configuration tab.
   
-  <dt>Username Channel Tag Match
-  <dd>
-  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.
-
   <dt>Min Channel Num
   <dd>
   If nonzero, the user will only be able to access channels with
@@ -103,11 +94,9 @@ The columns have the following functions:
 
   <dt>Channel Tag
   <dd>
-  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.
 
   <dt>Comment
   <dd>
index 9b970820fc0d78a572f0f2fc9c7a474a5c8485d4..fa8ec0a8522c7770d1e3b8f590ca797d42ceea55 100644 (file)
@@ -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,
index 9315c50eef3342716cdf01e4b78f99cd1e7a3042..b6adad0dc99bde5c6bb371b6889cfe86cf8371a2 100644 (file)
@@ -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);
+
 /**
  *
  */
index 69369fa23e669de9a9d8d7f604e0ee08b7e41e1d..92a5b5e2c619edb4daf2469d4ac7811c4ee2b20c 100644 (file)
 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);
index 6e5926c26353f52088abe680d32f0832d3435070..3dc23f4becbdc98f7cfdd24a785e5c41b61609f2 100644 (file)
@@ -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",
index 227caffbae26c322025efd679ebc12fb23873e77..fe0fc9cc5105cc3bcc6780cfcab570ea7bd0a558 100644 (file)
@@ -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);
index 07465925017d969137fba355bf8cd4d56dd69fe5..3afdfbf66ea4e492f8a61737401d6cd6735223ef 100644 (file)
@@ -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
 };
 
 /*
index 015b5b8a949a51f4f545a63f19e9f5e2534ece78..355db379c363448ee4b2a34f4ef5e104d3566954 100644 (file)
@@ -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);
+
 /**
  *
  */
index 8b2d3429df32d9f7e025959e6f4c136fb81c2bc0..c07ee9b9204f6453e190a9f96d49bd5ad118f91c 100644 (file)
@@ -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);
+}
index 8aa1f05e7524fdaab8ef195b23037031d51a082e..981abec7945649bf38653eacecca2952a862f473 100644 (file)
@@ -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();
 }
index 472177b55e73bc80887f430deb80519499a5f9e1..45146f1077f7344bd41bddd7025b1a6c5ba4ccfb 100644 (file)
@@ -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);
 }
index eced3aba11bbd9cda61758ae1d68f1adb7dbec80..ff46aac04b7750365e22c5e03d980ceb9564a8c9 100644 (file)
@@ -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) {
index 1d8a0294addc3e8a120a8f113966c2670e81343c..0ab8e0b0650265955a93b9023f02bb1fb0d65672 100644 (file)
@@ -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;
index 719ba4aa5088ee7c2a3e973f1820d3eef93960d8..0a2ae7a5bb3b007199963d774e53dbfdaa867032 100644 (file)
@@ -821,6 +821,8 @@ main(int argc, char **argv)
 
   subscription_init();
 
+  dvr_config_init();
+
   access_init(opt_firstrun, opt_noacl);
 
 #if ENABLE_TIMESHIFT
index f32731dc97b368f8904570dbe50758799a499b70..cf928a4c0ed21a22270b8500b418406754c9ab07 100644 (file)
@@ -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);
index eb71cf1e2984f82773caecc8bd7674c5023c26fd..76013cd6d537233b9e173c31471b73d3a21ad240 100644 (file)
@@ -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');
         },