ACL: Many improvements

- added min-max range for channel numbers for the ACL entry
- added channel tag match for the ACL entry
- introduced channel_access() routine
- moved the username channel tag to ae_rights
- add advanced streaming checkbox to control service and mux streaming separately
This commit is contained in:
Jaroslav Kysela 2014-08-11 17:37:13 +02:00
parent da10a35ced
commit 3be76456d8
10 changed files with 392 additions and 95 deletions

View file

@ -59,11 +59,16 @@ The columns have the following functions:
Enables access to streaming function. The 'streaming' access is enough to
make Showtime (over HTSP) work.
<dt>Advanced Streaming
<dd>
Enables access to advanced streaming function for HTTP - like direct
service or whole MPEG-TS stream (mux)..
<dt>Video Recorder
<dd>
Enables access to all video recording functions. This also include administration of the auto recordings.
<dt>User Configs (VR)
<dt>Username Configs (VR)
<dd>
Use a DVR configuration profile matched to the authorized user by name
(the configuration profile must have same name as the user). If the DVR
@ -78,13 +83,28 @@ The columns have the following functions:
<dd>
Enables access to the Configuration tab.
<dt>Channel Tag Only
<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
a channel number equal or greater to this value.
<dt>Max Channel Num
<dd>
If nonzero, the user will only be able to access channels with
a channel number equal or lower to this value.
<dt>Channel Tag
<dd>
If set, the user will only be able to access channels with
a channel tag equal to this value.
<dt>Comment
<dd>
Allows the administrator to set a comment only visible in this editor.

View file

@ -37,6 +37,7 @@
#include "access.h"
#include "dtable.h"
#include "settings.h"
#include "htsmsg.h"
struct access_entry_queue access_entries;
struct access_ticket_queue access_tickets;
@ -152,6 +153,16 @@ access_ticket_verify(const char *id, const char *resource)
return 0;
}
/**
*
*/
void
access_destroy(access_t *a)
{
htsmsg_destroy(a->aa_chtags);
free(a);
}
/**
*
*/
@ -264,24 +275,96 @@ access_verify(const char *username, const char *password,
return (mask & bits) == mask ? 0 : -1;
}
/*
*
*/
static void
access_update(access_t *a, access_entry_t *ae)
{
if(ae->ae_chmin || ae->ae_chmax) {
if(a->aa_chmin || a->aa_chmax) {
if (a->aa_chmin < ae->ae_chmin)
a->aa_chmin = ae->ae_chmin;
if (a->aa_chmax > ae->ae_chmax)
a->aa_chmax = ae->ae_chmax;
} else {
a->aa_chmin = ae->ae_chmin;
a->aa_chmax = ae->ae_chmax;
}
}
if(ae->ae_chtag) {
if (a->aa_chtags == NULL)
a->aa_chtags = htsmsg_create_list();
htsmsg_add_str(a->aa_chtags, NULL, ae->ae_chtag);
}
a->aa_rights |= ae->ae_rights;
}
/**
*
*/
uint32_t
access_get_hashed(const char *username, const uint8_t digest[20],
const uint8_t *challenge, struct sockaddr *src,
int *entrymatch)
access_t *
access_get(const char *username, const char *password, struct sockaddr *src)
{
access_t *a = calloc(1, sizeof(*a));
access_entry_t *ae;
if (access_noacl) {
a->aa_rights = ACCESS_FULL;
return a;
}
if(username != NULL && superuser_username != NULL &&
password != NULL && superuser_password != NULL &&
!strcmp(username, superuser_username) &&
!strcmp(password, superuser_password)) {
a->aa_rights = ACCESS_FULL;
return a;
}
TAILQ_FOREACH(ae, &access_entries, ae_link) {
if(!ae->ae_enabled)
continue;
if(ae->ae_username[0] != '*') {
/* acl entry requires username to match */
if(username == NULL || password == NULL)
continue; /* Didn't get one */
if(strcmp(ae->ae_username, username) ||
strcmp(ae->ae_password, password))
continue; /* username/password mismatch */
}
if(!netmask_verify(ae, src))
continue; /* IP based access mismatches */
a->aa_match = 1;
access_update(a, ae);
}
return a;
}
/**
*
*/
access_t *
access_get_hashed(const char *username, const uint8_t digest[20],
const uint8_t *challenge, struct sockaddr *src)
{
access_t *a = calloc(1, sizeof(*a));
access_entry_t *ae;
SHA_CTX shactx;
uint8_t d[20];
uint32_t r = 0;
int match = 0;
if(access_noacl)
return 0xffffffff;
if(access_noacl) {
a->aa_rights = ACCESS_FULL;
return a;
}
if(superuser_username != NULL && superuser_password != NULL) {
@ -291,8 +374,10 @@ access_get_hashed(const char *username, const uint8_t digest[20],
SHA1_Update(&shactx, challenge, 32);
SHA1_Final(d, &shactx);
if(!strcmp(superuser_username, username) && !memcmp(d, digest, 20))
return 0xffffffff;
if(!strcmp(superuser_username, username) && !memcmp(d, digest, 20)) {
a->aa_rights = ACCESS_FULL;
return a;
}
}
@ -312,12 +397,12 @@ access_get_hashed(const char *username, const uint8_t digest[20],
if(strcmp(ae->ae_username, username) || memcmp(d, digest, 20))
continue;
match = 1;
r |= ae->ae_rights;
a->aa_match = 1;
access_update(a, ae);
}
if(entrymatch != NULL)
*entrymatch = match;
return r;
return a;
}
@ -325,11 +410,11 @@ access_get_hashed(const char *username, const uint8_t digest[20],
/**
*
*/
uint32_t
access_t *
access_get_by_addr(struct sockaddr *src)
{
access_t *a = calloc(1, sizeof(*a));
access_entry_t *ae;
uint32_t r = 0;
TAILQ_FOREACH(ae, &access_entries, ae_link) {
@ -342,29 +427,12 @@ access_get_by_addr(struct sockaddr *src)
if(!netmask_verify(ae, src))
continue; /* IP based access mismatches */
r |= ae->ae_rights;
access_update(a, ae);
}
return r;
return a;
}
/**
*
*/
uint32_t
access_tag_only(const char *username)
{
access_entry_t *ae;
uint32_t r = 0;
TAILQ_FOREACH(ae, &access_entries, ae_link) {
if(!strcmp(ae->ae_username, username))
return ae->ae_tagonly;
}
return r;
}
/**
*
*/
@ -536,6 +604,7 @@ access_entry_destroy(access_entry_t *ae)
free(ae->ae_username);
free(ae->ae_password);
free(ae->ae_comment);
free(ae->ae_chtag);
TAILQ_REMOVE(&access_entries, ae, ae_link);
free(ae);
}
@ -579,11 +648,15 @@ access_record_build(access_entry_t *ae)
htsmsg_add_str(e, "prefix", buf + 1);
htsmsg_add_u32(e, "streaming", ae->ae_rights & ACCESS_STREAMING ? 1 : 0);
htsmsg_add_u32(e, "adv_streaming", ae->ae_rights & ACCESS_ADVANCED_STREAMING ? 1 : 0);
htsmsg_add_u32(e, "dvr" , ae->ae_rights & ACCESS_RECORDER ? 1 : 0);
htsmsg_add_u32(e, "dvrallcfg", ae->ae_rights & ACCESS_RECORDER_ALL ? 1 : 0);
htsmsg_add_u32(e, "webui" , ae->ae_rights & ACCESS_WEB_INTERFACE ? 1 : 0);
htsmsg_add_u32(e, "admin" , ae->ae_rights & ACCESS_ADMIN ? 1 : 0);
htsmsg_add_u32(e, "tag_only" , ae->ae_tagonly);
htsmsg_add_u32(e, "tag_only" , ae->ae_rights & ACCESS_TAG_ONLY ? 1 : 0);
htsmsg_add_u32(e, "channel_min" , ae->ae_chmin);
htsmsg_add_u32(e, "channel_max" , ae->ae_chmax);
htsmsg_add_str(e, "channel_tag", ae->ae_chtag ?: "");
htsmsg_add_str(e, "id", ae->ae_id);
@ -668,6 +741,12 @@ access_record_update(void *opaque, const char *id, htsmsg_t *values,
if(!htsmsg_get_u32(values, "streaming", &u32))
access_update_flag(ae, ACCESS_STREAMING, u32);
if(!htsmsg_get_u32(values, "adv_streaming", &u32))
access_update_flag(ae, ACCESS_ADVANCED_STREAMING, u32);
else
access_update_flag(ae, ACCESS_ADVANCED_STREAMING,
(ae->ae_rights & ACCESS_STREAMING));
if(!htsmsg_get_u32(values, "dvr", &u32))
access_update_flag(ae, ACCESS_RECORDER, u32);
@ -686,7 +765,21 @@ access_record_update(void *opaque, const char *id, htsmsg_t *values,
access_update_flag(ae, ACCESS_WEB_INTERFACE, u32);
if(!htsmsg_get_u32(values, "tag_only", &u32))
ae->ae_tagonly = u32;
access_update_flag(ae, ACCESS_TAG_ONLY, u32);
if(!htsmsg_get_u32(values, "channel_min", &u32))
ae->ae_chmin = u32;
if(!htsmsg_get_u32(values, "channel_max", &u32))
ae->ae_chmax = u32;
if((s = htsmsg_get_str(values, "channel_tag")) != NULL) {
free(ae->ae_chtag);
if (s[0] == 0)
ae->ae_chtag = NULL;
else
ae->ae_chtag = strdup(s);
}
return access_record_build(ae);
}
@ -738,7 +831,6 @@ access_init(int createdefault, int noacl)
struct timeval tv;
} randseed;
access_noacl = noacl;
if (noacl)
tvhlog(LOG_WARNING, "access", "Access control checking disabled");
@ -759,8 +851,7 @@ access_init(int createdefault, int noacl)
ae->ae_comment = strdup("Default access entry");
ae->ae_enabled = 1;
ae->ae_tagonly = 0;
ae->ae_rights = 0xffffffff;
ae->ae_rights = ACCESS_FULL;
TAILQ_INIT(&ae->ae_ipmasks);

View file

@ -19,6 +19,7 @@
#ifndef ACCESS_H_
#define ACCESS_H_
#include "htsmsg.h"
typedef struct access_ipmask {
TAILQ_ENTRY(access_ipmask) ai_link;
@ -47,7 +48,9 @@ typedef struct access_entry {
char *ae_comment;
int ae_enabled;
int ae_tagonly;
uint32_t ae_chmin;
uint32_t ae_chmax;
char *ae_chtag;
uint32_t ae_rights;
@ -67,13 +70,25 @@ typedef struct access_ticket {
char *at_resource;
} access_ticket_t;
#define ACCESS_ANONYMOUS 0x0
#define ACCESS_STREAMING 0x1
#define ACCESS_WEB_INTERFACE 0x2
#define ACCESS_RECORDER 0x4
#define ACCESS_RECORDER_ALL 0x8
#define ACCESS_ADMIN 0x10
#define ACCESS_FULL 0x3f
typedef struct access {
uint32_t aa_rights;
uint32_t aa_chmin;
uint32_t aa_chmax;
htsmsg_t *aa_chtags;
int aa_match;
} access_t;
#define ACCESS_ANONYMOUS 0
#define ACCESS_STREAMING (1<<0)
#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_FULL \
(ACCESS_STREAMING | ACCESS_WEB_INTERFACE | ACCESS_RECORDER | ACCESS_ADMIN)
/**
* Create a new ticket for the requested resource and generate a id for it
@ -86,6 +101,12 @@ const char* access_ticket_create(const char *resource);
int access_ticket_verify(const char *id, const char *resource);
int access_ticket_delete(const char *ticket_id);
/**
* Free the access structure
*/
void access_destroy(access_t *a);
/**
* Verifies that the given user in combination with the source ip
* complies with the requested mask
@ -96,17 +117,23 @@ int access_verify(const char *username, const char *password,
struct sockaddr *src, uint32_t mask);
/**
*
* Get the access structure
*/
uint32_t access_get_hashed(const char *username, const uint8_t digest[20],
const uint8_t *challenge, struct sockaddr *src,
int *entrymatch);
access_t *access_get(const char *username, const char *password,
struct sockaddr *src);
/**
*
*/
uint32_t access_get_by_addr(struct sockaddr *src);
access_t *
access_get_hashed(const char *username, const uint8_t digest[20],
const uint8_t *challenge, struct sockaddr *src);
/**
*
*/
access_t *
access_get_by_addr(struct sockaddr *src);
/**
*
@ -114,9 +141,4 @@ uint32_t access_get_by_addr(struct sockaddr *src);
void access_init(int createdefault, int noacl);
void access_done(void);
/**
*
*/
uint32_t access_tag_only(const char *username);
#endif /* ACCESS_H_ */

View file

@ -413,6 +413,47 @@ channel_find_by_number ( int no )
return ch;
}
/**
* Check if user can access the channel
*/
int
channel_access(channel_t *ch, access_t *a, const char *username)
{
/* Channel number check */
if (ch && (a->aa_chmin || a->aa_chmax)) {
int chnum = channel_get_number(ch);
if (chnum < a->aa_chmin || chnum > a->aa_chmax)
return 0;
}
/* Channel tag check */
if (ch && a->aa_chtags) {
channel_tag_mapping_t *ctm;
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))
goto chtags_ok;
}
}
return 0;
}
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;
}
/* **************************************************************************
* Property updating
* *************************************************************************/

View file

@ -22,6 +22,8 @@
#include "epg.h"
#include "idnode.h"
struct access;
RB_HEAD(channel_tree, channel);
LIST_HEAD(channel_tag_mapping_list, channel_tag_mapping);
@ -147,6 +149,8 @@ channel_tag_t *channel_tag_find_by_name(const char *name, int create);
channel_tag_t *channel_tag_find_by_identifier(uint32_t id);
int channel_access(channel_t *ch, struct access *a, const char *username);
int channel_tag_map(channel_t *ch, channel_tag_t *ct);
void channel_save(channel_t *ch);

View file

@ -159,7 +159,7 @@ typedef struct htsp_connection {
struct htsp_file_list htsp_files;
int htsp_file_id;
uint32_t htsp_granted_access;
access_t *htsp_granted_access;
uint8_t htsp_challenge[32];
@ -431,19 +431,10 @@ htsp_generate_challenge(htsp_connection_t *htsp)
/**
* Cehck if user can access the channel
*/
static int
htsp_user_access_channel(htsp_connection_t *htsp, channel_t *ch) {
if (!access_tag_only(htsp->htsp_username ?: ""))
return 1;
else {
if (!ch) return 0;
channel_tag_mapping_t *ctm;
LIST_FOREACH(ctm, &ch->ch_ctms, ctm_channel_link) {
if (!strcmp(htsp->htsp_username ?: "", ctm->ctm_tag->ct_name))
return 1;
}
}
return 0;
static inline int
htsp_user_access_channel(htsp_connection_t *htsp, channel_t *ch)
{
return channel_access(ch, htsp->htsp_granted_access, htsp->htsp_username);
}
#define HTSP_CHECK_CHANNEL_ACCESS(htsp, ch)\
@ -838,7 +829,7 @@ htsp_method_authenticate(htsp_connection_t *htsp, htsmsg_t *in)
{
htsmsg_t *r = htsmsg_create_map();
if(!(htsp->htsp_granted_access & HTSP_PRIV_MASK))
if(!(htsp->htsp_granted_access->aa_rights & HTSP_PRIV_MASK))
htsmsg_add_u32(r, "noaccess", 1);
return r;
@ -1972,9 +1963,8 @@ htsp_authenticate(htsp_connection_t *htsp, htsmsg_t *m)
const char *username;
const void *digest;
size_t digestlen;
uint32_t access;
access_t *rights;
int privgain;
int match;
if((username = htsmsg_get_str(m, "username")) == NULL)
return;
@ -1990,15 +1980,18 @@ htsp_authenticate(htsp_connection_t *htsp, htsmsg_t *m)
if(htsmsg_get_bin(m, "digest", &digest, &digestlen))
return;
access = access_get_hashed(username, digest, htsp->htsp_challenge,
(struct sockaddr *)htsp->htsp_peer, &match);
rights = access_get_hashed(username, digest, htsp->htsp_challenge,
(struct sockaddr *)htsp->htsp_peer);
privgain = (access | htsp->htsp_granted_access) != htsp->htsp_granted_access;
privgain = (rights->aa_rights |
htsp->htsp_granted_access->aa_rights) !=
htsp->htsp_granted_access->aa_rights;
if(privgain)
tvhlog(LOG_INFO, "htsp", "%s: Privileges raised", htsp->htsp_logname);
htsp->htsp_granted_access |= access;
access_destroy(htsp->htsp_granted_access);
htsp->htsp_granted_access = rights;
}
/**
@ -2081,7 +2074,7 @@ readmsg:
for(i = 0; i < NUM_METHODS; i++) {
if(!strcmp(method, htsp_methods[i].name)) {
if((htsp->htsp_granted_access & htsp_methods[i].privmask) !=
if((htsp->htsp_granted_access->aa_rights & htsp_methods[i].privmask) !=
htsp_methods[i].privmask) {
pthread_mutex_unlock(&global_lock);
@ -2281,6 +2274,7 @@ htsp_serve(int fd, void **opaque, struct sockaddr_storage *source,
free(htsp.htsp_peername);
free(htsp.htsp_username);
free(htsp.htsp_clientname);
access_destroy(htsp.htsp_granted_access);
*opaque = NULL;
}

View file

@ -35,6 +35,7 @@
#include "http.h"
#include "access.h"
#include "notify.h"
#include "channels.h"
static void *http_server;
@ -395,26 +396,58 @@ http_redirect(http_connection_t *hc, const char *location,
}
/**
* Return non-zero if no access
*
*/
int
http_access_verify(http_connection_t *hc, int mask)
static int http_access_verify_ticket(http_connection_t *hc)
{
const char *ticket_id = http_arg_get(&hc->hc_req_args, "ticket");
if(!access_ticket_verify(ticket_id, hc->hc_url))
{
if(!access_ticket_verify(ticket_id, hc->hc_url)) {
char addrstr[50];
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrstr, 50);
tvhlog(LOG_INFO, "HTTP", "%s: using ticket %s for %s",
addrstr, ticket_id, hc->hc_url);
return 0;
}
return -1;
}
/**
* Return non-zero if no access
*/
int
http_access_verify(http_connection_t *hc, int mask)
{
if (!http_access_verify_ticket(hc))
return 0;
return access_verify(hc->hc_username, hc->hc_password,
(struct sockaddr *)hc->hc_peer, mask);
}
/**
* Return non-zero if no access
*/
int
http_access_verify_channel(http_connection_t *hc, int mask,
struct channel *ch)
{
access_t *a;
int res = -1;
assert(ch);
if (!http_access_verify_ticket(hc))
return 0;
a = access_get(hc->hc_username, hc->hc_password,
(struct sockaddr *)hc->hc_peer);
if (channel_access(ch, a, hc->hc_username))
res = 0;
access_destroy(a);
return res;
}
/**
* Execute url callback
*

View file

@ -23,6 +23,8 @@
#include "url.h"
#include "tvhpoll.h"
struct channel;
typedef TAILQ_HEAD(http_arg_list, http_arg) http_arg_list_t;
typedef RB_HEAD(,http_arg) http_arg_tree_t;
@ -203,6 +205,7 @@ void http_server_init(const char *bindaddr);
void http_server_done(void);
int http_access_verify(http_connection_t *hc, int mask);
int http_access_verify_channel(http_connection_t *hc, int mask, struct channel *ch);
void http_deescape(char *s);

View file

@ -34,6 +34,11 @@ tvheadend.acleditor = function() {
header: "Streaming",
dataIndex: 'streaming',
width: 100
}, {
xtype: 'checkcolumn',
header: "Advanced Streaming",
dataIndex: 'adv_streaming',
width: 100
}, {
xtype: 'checkcolumn',
header: "Video Recorder",
@ -41,9 +46,9 @@ tvheadend.acleditor = function() {
width: 100
}, {
xtype: 'checkcolumn',
header: "User Configs (VR)",
header: "Username Configs (VR)",
dataIndex: 'dvrallcfg',
width: 100
width: 150
}, {
xtype: 'checkcolumn',
header: "Web Interface",
@ -56,9 +61,51 @@ tvheadend.acleditor = function() {
width: 100
}, {
xtype: 'checkcolumn',
header: "Channel Tag Only",
header: "Username Channel Tag Match",
dataIndex: 'tag_only',
width: 200
width: 150
}, {
xtype: 'numbercolumn',
header: "Min Channel Num",
dataIndex: 'channel_min',
width: 120,
format: '0',
editor: {
xtype: 'numberfield',
allowBlank: 'false',
minValue: 0,
maxValue: 2147483647,
allowNegative: false,
allowDecimals: false
}
}, {
xtype: 'numbercolumn',
header: "Max Channel Num",
dataIndex: 'channel_max',
width: 120,
format: '0',
editor: {
xtype: 'numberfield',
allowBlank: 'false',
minValue: 0,
maxValue: 2147483647,
allowNegative: false,
allowDecimals: false
}
}, {
header: "Channel Tag",
dataIndex: 'channel_tag',
width: 200,
editor: new Ext.form.ComboBox({
displayField: 'name',
store: tvheadend.channelTags,
mode: 'local',
editable: true,
forceSelection: true,
typeAhead: true,
triggerAction: 'all',
emptyText: 'Match this channel tag...'
})
}, {
header: "Comment",
dataIndex: 'comment',
@ -68,10 +115,30 @@ tvheadend.acleditor = function() {
var UserRecord = Ext.data.Record.create(
['enabled', 'streaming', 'dvr', 'dvrallcfg', 'admin', 'webui', 'username', 'tag_only',
'prefix', 'password', 'comment'
'prefix', 'password', 'comment', 'channel_min', 'channel_max', 'channel_tag',
'adv_streaming'
]);
tvheadend.accesscontrolStore = new Ext.data.JsonStore({
root: 'entries',
fields: UserRecord,
url: "tablemgr",
autoLoad: true,
id: 'id',
baseParams: {
table: "accesscontrol",
op: "get"
}
});
tvheadend.accesscontrolStore.on('update', function (store, record, operation) {
if (operation == 'edit') {
if (record.isModified('channel_tag') && record.data.channel_tag == '(Clear filter)')
record.set('channel_tag',"");
}
});
return new tvheadend.tableEditor('Access control', 'accesscontrol', cm,
UserRecord, [], null, 'config_access.html',
UserRecord, [], tvheadend.accesscontrolStore, 'config_access.html',
'group');
};

View file

@ -368,6 +368,9 @@ http_channel_playlist(http_connection_t *hc, channel_t *channel)
const char *host;
muxer_container_type_t mc;
if (http_access_verify_channel(hc, ACCESS_STREAMING, channel))
return HTTP_STATUS_UNAUTHORIZED;
mc = muxer_container_txt2type(http_arg_get(&hc->hc_req_args, "mux"));
if(mc == MC_UNKNOWN)
mc = dvr_config_find_by_name_default("")->dvr_mc;
@ -431,6 +434,8 @@ http_tag_playlist(http_connection_t *hc, channel_tag_t *tag)
htsbuf_qprintf(hq, "#EXTM3U\n");
LIST_FOREACH(ctm, &tag->ct_ctms, ctm_tag_link) {
if (http_access_verify_channel(hc, ACCESS_STREAMING, ctm->ctm_channel))
continue;
snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel_get_id(ctm->ctm_channel));
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", channel_get_name(ctm->ctm_channel));
htsbuf_qprintf(hq, "http://%s%s?ticket=%s", host, buf,
@ -527,6 +532,10 @@ http_channel_list_playlist(http_connection_t *hc)
htsbuf_qprintf(hq, "#EXTM3U\n");
for (idx = 0; idx < count; idx++) {
ch = chlist[idx];
if (http_access_verify_channel(hc, ACCESS_STREAMING, ch))
continue;
snprintf(buf, sizeof(buf), "/stream/channelid/%d", channel_get_id(ch));
htsbuf_qprintf(hq, "#EXTINF:-1,%s\n", channel_get_name(ch));
@ -568,6 +577,10 @@ http_dvr_list_playlist(http_connection_t *hc)
if(!fsize)
continue;
if (de->de_channel &&
http_access_verify_channel(hc, ACCESS_RECORDER, de->de_channel))
continue;
durration = de->de_stop - de->de_start;
durration += (de->de_stop_extra + de->de_start_extra)*60;
bandwidth = ((8*fsize) / (durration*1024.0));
@ -721,6 +734,9 @@ http_stream_service(http_connection_t *hc, service_t *service, int weight)
const char *name;
char addrbuf[50];
if(http_access_verify(hc, ACCESS_ADVANCED_STREAMING))
return HTTP_STATUS_UNAUTHORIZED;
cfg = dvr_config_find_by_name_default("");
/* Build muxer config - this takes the defaults from the default dvr config, which is a hack */
@ -788,6 +804,9 @@ http_stream_mux(http_connection_t *hc, mpegts_mux_t *mm, int weight)
char addrbuf[50];
muxer_config_t muxcfg = { 0 };
if(http_access_verify(hc, ACCESS_ADVANCED_STREAMING))
return HTTP_STATUS_UNAUTHORIZED;
streaming_queue_init(&sq, SMT_PACKET);
tcp_get_ip_str((struct sockaddr*)hc->hc_peer, addrbuf, 50);
@ -833,6 +852,9 @@ http_stream_channel(http_connection_t *hc, channel_t *ch, int weight)
const char *name;
char addrbuf[50];
if (http_access_verify_channel(hc, ACCESS_STREAMING, ch))
return HTTP_STATUS_UNAUTHORIZED;
cfg = dvr_config_find_by_name_default("");
/* Build muxer config - this takes the defaults from the default dvr config, which is a hack */