src/api/api_esfilter.c \
src/api/api_intlconv.c \
src/api/api_access.c \
- src/api/api_dvr.c
+ src/api/api_dvr.c \
+ src/api/api_caclient.c
SRCS += \
src/parsers/parsers.c \
# MPEGTS core
SRCS-$(CONFIG_MPEGTS) += \
src/descrambler/descrambler.c \
+ src/descrambler/caclient.c \
src/input/mpegts.c \
src/input/mpegts/mpegts_input.c \
src/input/mpegts/mpegts_network.c \
api_intlconv_init();
api_access_init();
api_dvr_init();
+ api_caclient_init();
}
void api_done ( void )
void api_done ( void );
void api_idnode_init ( void );
void api_input_init ( void );
-void api_input_satip_init ( void );
void api_service_init ( void );
void api_channel_init ( void );
void api_mpegts_init ( void );
void api_intlconv_init ( void );
void api_access_init ( void );
void api_dvr_init ( void );
+void api_caclient_init ( void );
/*
* IDnode
--- /dev/null
+/*
+ * tvheadend - API access to Conditional Access Clients
+ *
+ * Copyright (C) 2014 Jaroslav Kysela
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tvheadend.h"
+#include "access.h"
+#include "htsmsg.h"
+#include "api.h"
+#include "descrambler/caclient.h"
+
+/*
+ *
+ */
+static int
+api_caclient_list
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ caclient_t *cac;
+ htsmsg_t *l, *e;
+
+ l = htsmsg_create_list();
+ TAILQ_FOREACH(cac, &caclients, cac_link) {
+ e = htsmsg_create_map();
+ htsmsg_add_str(e, "key", idnode_uuid_as_str(&cac->cac_id));
+ htsmsg_add_str(e, "val", idnode_get_title(&cac->cac_id));
+ htsmsg_add_msg(l, NULL, e);
+ }
+ *resp = htsmsg_create_map();
+ htsmsg_add_msg(*resp, "entries", l);
+ return 0;
+}
+
+static int
+api_caclient_builders
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ const idclass_t **r;
+ htsmsg_t *l, *e;
+
+ /* List of available builder classes */
+ l = htsmsg_create_list();
+ for (r = caclient_classes; *r; r++)
+ if ((e = idclass_serialize(*r)))
+ htsmsg_add_msg(l, NULL, e);
+
+ /* Output */
+ *resp = htsmsg_create_map();
+ htsmsg_add_msg(*resp, "entries", l);
+
+ return 0;
+}
+
+static int
+api_caclient_create
+ ( access_t *perm, void *opaque, const char *op, htsmsg_t *args, htsmsg_t **resp )
+{
+ int err = 0;
+ const char *clazz;
+ htsmsg_t *conf;
+
+ if (!(clazz = htsmsg_get_str(args, "class")))
+ return EINVAL;
+ if (!(conf = htsmsg_get_map(args, "conf")))
+ return EINVAL;
+ htsmsg_set_str(conf, "class", clazz);
+
+ pthread_mutex_lock(&global_lock);
+ if (caclient_create(NULL, conf, 1) == NULL)
+ err = -EINVAL;
+ pthread_mutex_unlock(&global_lock);
+
+ return err;
+}
+
+/*
+ * Init
+ */
+void
+api_caclient_init ( void )
+{
+ static api_hook_t ah[] = {
+ { "caclient/list", ACCESS_ADMIN, api_caclient_list, NULL },
+ { "caclient/class", ACCESS_ADMIN, api_idnode_class, (void*)&caclient_class },
+ { "caclient/builders", ACCESS_ADMIN, api_caclient_builders, NULL },
+ { "caclient/create", ACCESS_ADMIN, api_caclient_create, NULL },
+ { NULL },
+ };
+
+ api_register_all(ah);
+}
--- /dev/null
+/*
+ * Tvheadend - conditional access key client superclass
+ * Copyright (C) 2014 Jaroslav Kysela <perex@perex.cz>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "tvheadend.h"
+#include "settings.h"
+#include "caclient.h"
+
+const idclass_t *caclient_classes[] = {
+#if ENABLE_CWC
+ &caclient_cwc_class,
+#endif
+#if ENABLE_CAPMT
+ &caclient_capmt_class,
+#endif
+ NULL
+};
+
+struct caclient_entry_queue caclients;
+static pthread_mutex_t caclients_mutex;
+
+static void caclient_class_save ( idnode_t *in );
+
+static const idclass_t *
+caclient_class_find(const char *name)
+{
+ const idclass_t **r;
+ for (r = caclient_classes; *r; r++) {
+ if (strcmp((*r)->ic_class, name) == 0)
+ return *r;
+ }
+ return NULL;
+}
+
+static void
+caclient_reindex(void)
+{
+ caclient_t *cac;
+ int i = 1;
+
+ TAILQ_FOREACH(cac, &caclients, cac_link)
+ cac->cac_save = 0;
+ TAILQ_FOREACH(cac, &caclients, cac_link) {
+ if (cac->cac_index != i) {
+ cac->cac_index = i;
+ cac->cac_save = 1;
+ }
+ i++;
+ }
+ TAILQ_FOREACH(cac, &caclients, cac_link)
+ if (cac->cac_save) {
+ cac->cac_save = 0;
+ caclient_class_save((idnode_t *)cac);
+ }
+}
+
+static int
+cac_cmp(caclient_t *a, caclient_t *b)
+{
+ return a->cac_index - b->cac_index;
+}
+
+caclient_t *
+caclient_create
+ (const char *uuid, htsmsg_t *conf, int save)
+{
+ caclient_t *cac = NULL;
+ const idclass_t *c = NULL;
+ const char *s;
+
+ lock_assert(&global_lock);
+
+ if ((s = htsmsg_get_str(conf, "class")) != NULL)
+ c = caclient_class_find(s);
+ if (c == NULL) {
+ tvherror("caclient", "wrong class %s!", s);
+ abort();
+ }
+#if ENABLE_CWC
+ if (c == &caclient_cwc_class)
+ cac = cwc_create();
+#endif
+#if ENABLE_CAPMT
+ if (c == &caclient_capmt_class)
+ cac = capmt_create();
+#endif
+ if (cac == NULL)
+ abort();
+ if (cac == NULL) {
+ tvherror("caclient", "CA Client class %s is not available!", s);
+ return NULL;
+ }
+ if (idnode_insert(&cac->cac_id, uuid, c, 0)) {
+ if (uuid)
+ tvherror("caclient", "invalid uuid '%s'", uuid);
+ free(cac);
+ return NULL;
+ }
+ if (conf)
+ idnode_load(&cac->cac_id, conf);
+ pthread_mutex_lock(&caclients_mutex);
+ if (cac->cac_index) {
+ TAILQ_INSERT_SORTED(&caclients, cac, cac_link, cac_cmp);
+ } else {
+ TAILQ_INSERT_TAIL(&caclients, cac, cac_link);
+ caclient_reindex();
+ }
+ pthread_mutex_unlock(&caclients_mutex);
+ if (save)
+ caclient_class_save((idnode_t *)cac);
+ cac->cac_conf_changed(cac);
+ return cac;
+}
+
+static void
+caclient_delete(caclient_t *cac, int delconf)
+{
+ cac->cac_enabled = 0;
+ cac->cac_conf_changed(cac);
+ if (delconf)
+ hts_settings_remove("caclient/%s", idnode_uuid_as_str(&cac->cac_id));
+ pthread_mutex_lock(&caclients_mutex);
+ TAILQ_REMOVE(&caclients, cac, cac_link);
+ pthread_mutex_unlock(&caclients_mutex);
+ idnode_unlink(&cac->cac_id);
+ if (cac->cac_free)
+ cac->cac_free(cac);
+ free(cac->cac_name);
+ free(cac->cac_comment);
+ free(cac);
+}
+
+static void
+caclient_class_save ( idnode_t *in )
+{
+ htsmsg_t *c = htsmsg_create_map();
+ idnode_save(in, c);
+ hts_settings_save(c, "caclient/%s", idnode_uuid_as_str(in));
+ htsmsg_destroy(c);
+}
+
+static const char *
+caclient_class_get_title ( idnode_t *in )
+{
+ caclient_t *cac = (caclient_t *)in;
+ static char buf[32];
+ if (cac->cac_name && cac->cac_name[0])
+ return cac->cac_name;
+ snprintf(buf, sizeof(buf), "CA Client %i", cac->cac_index);
+ return buf;
+}
+
+static void
+caclient_class_delete(idnode_t *self)
+{
+ caclient_t *cac = (caclient_t *)self;
+ caclient_delete(cac, 1);
+}
+
+static void
+caclient_class_moveup(idnode_t *self)
+{
+ caclient_t *cac = (caclient_t *)self;
+ caclient_t *prev = TAILQ_PREV(cac, caclient_entry_queue, cac_link);
+ if (prev) {
+ TAILQ_REMOVE(&caclients, cac, cac_link);
+ TAILQ_INSERT_BEFORE(prev, cac, cac_link);
+ caclient_reindex();
+ }
+}
+
+static void
+caclient_class_movedown(idnode_t *self)
+{
+ caclient_t *cac = (caclient_t *)self;
+ caclient_t *next = TAILQ_NEXT(cac, cac_link);
+ if (next) {
+ TAILQ_REMOVE(&caclients, cac, cac_link);
+ TAILQ_INSERT_AFTER(&caclients, next, cac, cac_link);
+ caclient_reindex();
+ }
+}
+
+static const void *
+caclient_class_class_get(void *o)
+{
+ caclient_t *cac = o;
+ static const char *ret;
+ ret = cac->cac_id.in_class->ic_class;
+ return &ret;
+}
+
+static int
+caclient_class_class_set(void *o, const void *v)
+{
+ /* just ignore, create fcn does the right job */
+ return 0;
+}
+
+const idclass_t caclient_class =
+{
+ .ic_class = "caclient",
+ .ic_caption = "Conditional Access Client",
+ .ic_save = caclient_class_save,
+ .ic_event = "caclient",
+ .ic_get_title = caclient_class_get_title,
+ .ic_delete = caclient_class_delete,
+ .ic_moveup = caclient_class_moveup,
+ .ic_movedown = caclient_class_movedown,
+ .ic_properties = (const property_t[]){
+ {
+ .type = PT_STR,
+ .id = "class",
+ .name = "Class",
+ .opts = PO_RDONLY | PO_HIDDEN,
+ .get = caclient_class_class_get,
+ .set = caclient_class_class_set,
+ },
+ {
+ .type = PT_INT,
+ .id = "index",
+ .name = "Index",
+ .opts = PO_RDONLY | PO_HIDDEN,
+ .off = offsetof(caclient_t, cac_index),
+ },
+ {
+ .type = PT_BOOL,
+ .id = "enabled",
+ .name = "Enabled",
+ .off = offsetof(caclient_t, cac_enabled),
+ },
+ {
+ .type = PT_STR,
+ .id = "name",
+ .name = "Client Name",
+ .off = offsetof(caclient_t, cac_name),
+ .notify = idnode_notify_title_changed,
+ },
+ {
+ .type = PT_STR,
+ .id = "comment",
+ .name = "Comment",
+ .off = offsetof(caclient_t, cac_comment),
+ },
+ { }
+ }
+};
+
+void
+caclient_start ( struct service *t )
+{
+ caclient_t *cac;
+
+ pthread_mutex_lock(&caclients_mutex);
+ TAILQ_FOREACH(cac, &caclients, cac_link)
+ if (cac->cac_enabled)
+ cac->cac_start(cac, t);
+ pthread_mutex_unlock(&caclients_mutex);
+}
+
+void
+caclient_caid_update(struct mpegts_mux *mux, uint16_t caid, uint16_t pid, int valid)
+{
+ caclient_t *cac;
+
+ lock_assert(&global_lock);
+
+ pthread_mutex_lock(&caclients_mutex);
+ TAILQ_FOREACH(cac, &caclients, cac_link)
+ if (cac->cac_caid_update && cac->cac_enabled)
+ cac->cac_caid_update(cac, mux, caid, pid, valid);
+ pthread_mutex_unlock(&caclients_mutex);
+}
+
+/*
+ * Initialize
+ */
+void
+caclient_init(void)
+{
+ htsmsg_t *c, *e;
+ htsmsg_field_t *f;
+
+ pthread_mutex_init(&caclients_mutex, NULL);
+ TAILQ_INIT(&caclients);
+
+ if (!(c = hts_settings_load("caclient")))
+ return;
+ HTSMSG_FOREACH(f, c) {
+ if (!(e = htsmsg_field_get_map(f)))
+ continue;
+ caclient_create(f->hmf_name, e, 0);
+ }
+ htsmsg_destroy(c);
+}
+
+void
+caclient_done(void)
+{
+ caclient_t *cac;
+
+ pthread_mutex_lock(&global_lock);
+ while ((cac = TAILQ_FIRST(&caclients)) != NULL)
+ caclient_delete(cac, 0);
+ pthread_mutex_unlock(&global_lock);
+}
--- /dev/null
+/*
+ * tvheadend, Conditional Access Client
+ * Copyright (C) 2014 Jaroslav Kysela
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TVH_CACLIENT_H__
+#define __TVH_CACLIENT_H__
+
+#include "tvheadend.h"
+#include "idnode.h"
+
+struct mpegts_mux;
+
+extern const idclass_t caclient_class;
+extern const idclass_t caclient_cwc_class;
+extern const idclass_t caclient_capmt_class;
+
+TAILQ_HEAD(caclient_entry_queue, caclient);
+
+extern struct caclient_entry_queue caclients;
+
+extern const idclass_t *caclient_classes[];
+
+typedef struct caclient {
+ idnode_t cac_id;
+ TAILQ_ENTRY(caclient) cac_link;
+
+ int cac_save;
+ int cac_index;
+ int cac_enabled;
+ char *cac_name;
+ char *cac_comment;
+
+ void (*cac_free)(struct caclient *cac);
+ void (*cac_start)(struct caclient *cac, struct service *t);
+ void (*cac_conf_changed)(struct caclient *cac);
+ void (*cac_caid_update)(struct caclient *cac,
+ struct mpegts_mux *mux,
+ uint16_t caid, uint16_t pid, int valid);
+} caclient_t;
+
+caclient_t *caclient_create
+ (const char *uuid, htsmsg_t *conf, int save);
+
+void caclient_start( struct service *t );
+void caclient_caid_update(struct mpegts_mux *mux,
+ uint16_t caid, uint16_t pid, int valid);
+
+void caclient_init(void);
+void caclient_done(void);
+
+caclient_t *cwc_create(void);
+caclient_t *capmt_create(void);
+
+#endif /* __TVH_CACLIENT_H__ */
#include "tvheadend.h"
-#if ENABLE_CAPMT
-
#include "input.h"
+#include "caclient.h"
#include "service.h"
#include "tcp.h"
#include "tvhpoll.h"
-#include "capmt.h"
#include "notify.h"
#include "subscriptions.h"
-#include "dtable.h"
#include "tvhcsa.h"
#if ENABLE_LINUXDVB
#include "input/mpegts/linuxdvb/linuxdvb_private.h"
#define MAX_SOCKETS 16 // max sockets (simultaneous channels) per demux
#define MAX_PIDS 64 // max opened pids
+typedef enum {
+ CAPMT_OSCAM_SO_WRAPPER,
+ CAPMT_OSCAM_OLD,
+ CAPMT_OSCAM_MULTILIST,
+ CAPMT_OSCAM_TCP,
+ CAPMT_OSCAM_UNIX_SOCKET
+} capmt_oscam_mode_t;
+
/**
*
*/
-TAILQ_HEAD(capmt_queue, capmt);
LIST_HEAD(capmt_service_list, capmt_service);
LIST_HEAD(capmt_caid_ecm_list, capmt_caid_ecm);
-static struct capmt_queue capmts;
-static pthread_cond_t capmt_config_changed;
/*
* ECM descriptor
*
*/
typedef struct capmt {
- pthread_cond_t capmt_cond;
+ caclient_t;
- TAILQ_ENTRY(capmt) capmt_link; /* Linkage protected via global_lock */
+ pthread_cond_t capmt_cond;
struct capmt_service_list capmt_services;
/* from capmt configuration */
char *capmt_sockfile;
int capmt_port;
- char *capmt_comment;
- char *capmt_id;
- enum {
- CAPMT_OSCAM_SO_WRAPPER,
- CAPMT_OSCAM_OLD,
- CAPMT_OSCAM_MULTILIST,
- CAPMT_OSCAM_TCP,
- CAPMT_OSCAM_UNIX_SOCKET
- } capmt_oscam;
+ int capmt_oscam;
/* capmt sockets */
int sids[MAX_SOCKETS];
static void capmt_table_input(void *opaque, int pid,
const uint8_t *data, int len);
-/**
- *
- */
-static htsmsg_t *
-capmt_record_build(capmt_t *capmt)
-{
- htsmsg_t *e = htsmsg_create_map();
-
- htsmsg_add_str(e, "id", capmt->capmt_id);
- htsmsg_add_u32(e, "enabled", !!capmt->capmt_enabled);
- htsmsg_add_u32(e, "connected", capmt->capmt_connected);
-
- htsmsg_add_str(e, "camdfilename", capmt->capmt_sockfile ?: "");
- htsmsg_add_u32(e, "port", capmt->capmt_port);
- htsmsg_add_u32(e, "oscam", capmt->capmt_oscam);
- htsmsg_add_str(e, "comment", capmt->capmt_comment ?: "");
-
- return e;
-}
-
/**
*
*/
static void
capmt_set_connected(capmt_t *capmt, int c)
{
+ htsmsg_t *m;
if (c == capmt->capmt_connected)
return;
+ m = htsmsg_create_map();
capmt->capmt_connected = c;
- notify_by_msg("capmt", capmt_record_build(capmt));
+ htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&capmt->cac_id));
+ htsmsg_add_u32(m, "connected", capmt->capmt_connected);
+ notify_by_msg("caclient_status", m);
}
/*
else
capmt_set_connected(capmt, 0);
- pthread_mutex_lock(&global_lock);
+ pthread_mutex_lock(&capmt->capmt_mutex);
while(capmt->capmt_running && capmt->capmt_enabled == 0)
- pthread_cond_wait(&capmt->capmt_cond, &global_lock);
+ pthread_cond_wait(&capmt->capmt_cond, &capmt->capmt_mutex);
- pthread_mutex_unlock(&global_lock);
+ pthread_mutex_unlock(&capmt->capmt_mutex);
if (!capmt->capmt_running) continue;
tvhlog(LOG_INFO, "capmt", "Automatic reconnection attempt in in %d seconds", d);
pthread_mutex_lock(&global_lock);
- pthread_cond_timedwait(&capmt_config_changed, &global_lock, &ts);
+ pthread_cond_timedwait(&capmt->capmt_cond, &global_lock, &ts);
pthread_mutex_unlock(&global_lock);
}
*
* global_lock is held
*/
-void
-capmt_service_start(service_t *s)
+static void
+capmt_service_start(caclient_t *cac, service_t *s)
{
- capmt_t *capmt;
+ capmt_t *capmt = (capmt_t *)cac;
capmt_service_t *ct;
capmt_caid_ecm_t *cce;
th_descrambler_t *td;
tuner = lfe->lfe_adapter->la_dvb_number;
#endif
- TAILQ_FOREACH(capmt, &capmts, capmt_link) {
- LIST_FOREACH(ct, &capmt->capmt_services, ct_link) {
- /* skip, if we already have this service */
- if (ct->td_service == (service_t *)t)
- return;
+ pthread_mutex_lock(&capmt->capmt_mutex);
+
+ LIST_FOREACH(ct, &capmt->capmt_services, ct_link) {
+ /* skip, if we already have this service */
+ if (ct->td_service == (service_t *)t) {
+ pthread_mutex_unlock(&capmt->capmt_mutex);
+ return;
}
}
- TAILQ_FOREACH(capmt, &capmts, capmt_link) {
- /* skip, if we're not active */
- if (!capmt->capmt_enabled)
- continue;
-
- if (tuner < 0 && capmt->capmt_oscam != CAPMT_OSCAM_TCP &&
- capmt->capmt_oscam != CAPMT_OSCAM_UNIX_SOCKET) {
- tvhlog(LOG_WARNING, "capmt",
- "Virtual adapters are supported only in modes 3 and 4 (service \"%s\")",
- t->s_dvb_svcname);
- continue;
- }
+ if (tuner < 0 && capmt->capmt_oscam != CAPMT_OSCAM_TCP &&
+ capmt->capmt_oscam != CAPMT_OSCAM_UNIX_SOCKET) {
+ tvhlog(LOG_WARNING, "capmt",
+ "Virtual adapters are supported only in modes 3 and 4 (service \"%s\")",
+ t->s_dvb_svcname);
+ pthread_mutex_unlock(&capmt->capmt_mutex);
+ return;
+ }
- if (tuner < 0) {
- for (i = 0; i < MAX_CA; i++) {
- if (capmt->capmt_adapters[i].ca_tuner == NULL && tuner < 0)
- tuner = i;
- if (capmt->capmt_adapters[i].ca_tuner == t->s_dvb_active_input) {
- tuner = i;
- break;
- }
- }
- if (tuner < 0 || tuner >= MAX_CA) {
- tvhlog(LOG_ERR, "capmt",
- "No free adapter slot available for service \"%s\"",
- t->s_dvb_svcname);
- continue;
+ if (tuner < 0) {
+ for (i = 0; i < MAX_CA; i++) {
+ if (capmt->capmt_adapters[i].ca_tuner == NULL && tuner < 0)
+ tuner = i;
+ if (capmt->capmt_adapters[i].ca_tuner == t->s_dvb_active_input) {
+ tuner = i;
+ break;
}
}
+ if (tuner < 0 || tuner >= MAX_CA) {
+ tvhlog(LOG_ERR, "capmt",
+ "No free adapter slot available for service \"%s\"",
+ t->s_dvb_svcname);
+ pthread_mutex_unlock(&capmt->capmt_mutex);
+ return;
+ }
+ }
- tvhlog(LOG_INFO, "capmt",
- "Starting CAPMT server for service \"%s\" on adapter %d seq 0x%04x",
- t->s_dvb_svcname, tuner, capmt->capmt_seq);
+ tvhlog(LOG_INFO, "capmt",
+ "Starting CAPMT server for service \"%s\" on adapter %d seq 0x%04x",
+ t->s_dvb_svcname, tuner, capmt->capmt_seq);
- capmt->capmt_adapters[tuner].ca_tuner = t->s_dvb_active_input;
+ capmt->capmt_adapters[tuner].ca_tuner = t->s_dvb_active_input;
- /* create new capmt service */
- ct = calloc(1, sizeof(capmt_service_t));
- ct->ct_capmt = capmt;
- ct->ct_seq = capmt->capmt_seq;
- ct->ct_adapter = tuner;
+ /* create new capmt service */
+ ct = calloc(1, sizeof(capmt_service_t));
+ ct->ct_capmt = capmt;
+ ct->ct_seq = capmt->capmt_seq;
+ ct->ct_adapter = tuner;
- /* update the sequence (oscam calls it pid?) to a safe value */
- capmt->capmt_seq++;
- capmt->capmt_seq &= 0x1fff;
- if (capmt->capmt_seq == 8191 || capmt->capmt_seq == 0)
- capmt->capmt_seq = 1;
+ /* update the sequence (oscam calls it pid?) to a safe value */
+ capmt->capmt_seq++;
+ capmt->capmt_seq &= 0x1fff;
+ if (capmt->capmt_seq == 8191 || capmt->capmt_seq == 0)
+ capmt->capmt_seq = 1;
- change = 0;
- pthread_mutex_lock(&t->s_stream_mutex);
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
- caid_t *c;
- if (t->s_dvb_prefcapid_lock == 2 &&
- t->s_dvb_prefcapid != st->es_pid)
+ change = 0;
+ pthread_mutex_lock(&t->s_stream_mutex);
+ TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
+ caid_t *c;
+ if (t->s_dvb_prefcapid_lock == 2 &&
+ t->s_dvb_prefcapid != st->es_pid)
+ continue;
+ LIST_FOREACH(c, &st->es_caids, link) {
+ if(c == NULL || c->use == 0)
continue;
- LIST_FOREACH(c, &st->es_caids, link) {
- if(c == NULL || c->use == 0)
- continue;
- tvhlog(LOG_DEBUG, "capmt",
- "New caid 0x%04X for service \"%s\"", c->caid, t->s_dvb_svcname);
-
- /* add it to list */
- cce = calloc(1, sizeof(capmt_caid_ecm_t));
- cce->cce_caid = c->caid;
- cce->cce_ecmpid = st->es_pid;
- cce->cce_providerid = c->providerid;
- LIST_INSERT_HEAD(&ct->ct_caid_ecm, cce, cce_link);
- ct->ct_constcw |= c->caid == 0x2600 ? 1 : 0;
- change = 1;
- }
+ tvhlog(LOG_DEBUG, "capmt",
+ "New caid 0x%04X for service \"%s\"", c->caid, t->s_dvb_svcname);
+
+ /* add it to list */
+ cce = calloc(1, sizeof(capmt_caid_ecm_t));
+ cce->cce_caid = c->caid;
+ cce->cce_ecmpid = st->es_pid;
+ cce->cce_providerid = c->providerid;
+ LIST_INSERT_HEAD(&ct->ct_caid_ecm, cce, cce_link);
+ ct->ct_constcw |= c->caid == 0x2600 ? 1 : 0;
+ change = 1;
}
- pthread_mutex_unlock(&t->s_stream_mutex);
+ }
+ pthread_mutex_unlock(&t->s_stream_mutex);
+
+ td = (th_descrambler_t *)ct;
+ tvhcsa_init(td->td_csa = &ct->ct_csa);
+ snprintf(buf, sizeof(buf), "capmt-%s-%i",
+ capmt->capmt_sockfile,
+ capmt->capmt_port);
+ td->td_nicename = strdup(buf);
+ td->td_service = s;
+ td->td_stop = capmt_service_destroy;
+ td->td_caid_change = capmt_caid_change;
+ td->td_ecm_reset = capmt_ecm_reset;
+
+ LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
+ LIST_INSERT_HEAD(&capmt->capmt_services, ct, ct_link);
+
+ /* wake-up idle thread */
+ pthread_cond_signal(&capmt->capmt_cond);
- td = (th_descrambler_t *)ct;
- tvhcsa_init(td->td_csa = &ct->ct_csa);
- snprintf(buf, sizeof(buf), "capmt-%s-%i",
- capmt->capmt_sockfile,
- capmt->capmt_port);
- td->td_nicename = strdup(buf);
- td->td_service = s;
- td->td_stop = capmt_service_destroy;
- td->td_caid_change = capmt_caid_change;
- td->td_ecm_reset = capmt_ecm_reset;
- LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
-
- LIST_INSERT_HEAD(&capmt->capmt_services, ct, ct_link);
-
- /* wake-up idle thread */
- pthread_cond_signal(&capmt->capmt_cond);
- pthread_cond_signal(&capmt_config_changed);
+ pthread_mutex_unlock(&capmt->capmt_mutex);
- if (change)
- capmt_notify_server(capmt, NULL, 0);
- }
+ if (change)
+ capmt_notify_server(capmt, NULL, 0);
}
*
*/
static void
-capmt_destroy(capmt_t *capmt)
+capmt_free(caclient_t *cac)
{
- lock_assert(&global_lock);
- TAILQ_REMOVE(&capmts, capmt, capmt_link);
- capmt->capmt_running = 0;
- pthread_cond_signal(&capmt->capmt_cond);
- pthread_mutex_unlock(&global_lock);
- tvh_write(capmt->capmt_pipe.wr, "", 1);
- pthread_join(capmt->capmt_tid, NULL);
- pthread_mutex_lock(&global_lock);
+ capmt_t *capmt = (capmt_t *)cac;
tvhlog(LOG_INFO, "capmt", "mode %i %s %s port %i destroyed",
capmt->capmt_oscam,
capmt->capmt_oscam == CAPMT_OSCAM_TCP ? "IP address" : "sockfile",
capmt->capmt_sockfile, capmt->capmt_port);
capmt_flush_queue(capmt, 1);
- free(capmt->capmt_id);
free(capmt->capmt_sockfile);
- free(capmt->capmt_comment);
- free(capmt);
}
/**
*
*/
-static capmt_t *
-capmt_entry_find(const char *id, int create)
+static void
+capmt_conf_changed(caclient_t *cac)
{
- char buf[20];
- capmt_t *capmt;
- static int tally;
-
- if(id != NULL) {
- TAILQ_FOREACH(capmt, &capmts, capmt_link)
- if(!strcmp(capmt->capmt_id, id))
- return capmt;
- }
- if(create == 0)
- return NULL;
+ capmt_t *capmt = (capmt_t *)cac;
+ pthread_t tid;
- if(id == NULL) {
- tally++;
- snprintf(buf, sizeof(buf), "%d", tally);
- id = buf;
+ if (capmt->cac_enabled) {
+ if (capmt->capmt_sockfile == NULL || capmt->capmt_sockfile[0] == '\0')
+ return;
+ if (!capmt->capmt_running) {
+ capmt->capmt_running = 1;
+ tvhthread_create(&capmt->capmt_tid, NULL, capmt_thread, capmt);
+ return;
+ }
+ pthread_mutex_lock(&capmt->capmt_mutex);
+ capmt->capmt_reconfigure = 1;
+ pthread_cond_signal(&capmt->capmt_cond);
+ pthread_mutex_unlock(&capmt->capmt_mutex);
} else {
- tally = MAX(atoi(id), tally);
- }
-
- capmt = calloc(1, sizeof(capmt_t));
- pthread_mutex_init(&capmt->capmt_mutex, NULL);
- pthread_cond_init(&capmt->capmt_cond, NULL);
- capmt->capmt_id = strdup(id);
- capmt->capmt_running = 1;
- capmt->capmt_seq = 1;
- TAILQ_INIT(&capmt->capmt_writeq);
-
- TAILQ_INSERT_TAIL(&capmts, capmt, capmt_link);
-
- tvh_pipe(O_NONBLOCK, &capmt->capmt_pipe);
-
- tvhthread_create(&capmt->capmt_tid, NULL, capmt_thread, capmt);
-
- return capmt;
-}
-
-
-/**
- *
- */
-static htsmsg_t *
-capmt_entry_update(void *opaque, const char *id, htsmsg_t *values, int maycreate)
-{
- capmt_t *capmt;
- const char *s;
- uint32_t u32;
-
- if((capmt = capmt_entry_find(id, maycreate)) == NULL)
- return NULL;
-
- lock_assert(&global_lock);
-
- if((s = htsmsg_get_str(values, "camdfilename")) != NULL) {
- free(capmt->capmt_sockfile);
- capmt->capmt_sockfile = strdup(s);
- }
-
- if(!htsmsg_get_u32(values, "port", &u32))
- capmt->capmt_port = u32;
-
- if(!htsmsg_get_u32(values, "oscam", &u32))
- capmt->capmt_oscam = u32;
-
- if((s = htsmsg_get_str(values, "comment")) != NULL) {
- free(capmt->capmt_comment);
- capmt->capmt_comment = strdup(s);
+ if (!capmt->capmt_running)
+ return;
+ pthread_mutex_lock(&capmt->capmt_mutex);
+ capmt->capmt_running = 0;
+ pthread_cond_signal(&capmt->capmt_cond);
+ tid = capmt->capmt_tid;
+ pthread_mutex_unlock(&capmt->capmt_mutex);
+ tvh_write(capmt->capmt_pipe.wr, "", 1);
+ pthread_join(tid, NULL);
}
- if(!htsmsg_get_u32(values, "enabled", &u32))
- capmt->capmt_enabled = u32;
-
-
- capmt->capmt_reconfigure = 1;
-
- pthread_cond_signal(&capmt->capmt_cond);
-
- pthread_cond_broadcast(&capmt_config_changed);
-
- return capmt_record_build(capmt);
-}
-
-
-
-/**
- *
- */
-static int
-capmt_entry_delete(void *opaque, const char *id)
-{
- capmt_t *capmt;
-
- pthread_cond_broadcast(&capmt_config_changed);
-
- if((capmt = capmt_entry_find(id, 0)) == NULL)
- return -1;
- capmt_destroy(capmt);
- return 0;
}
-
-/**
- *
- */
static htsmsg_t *
-capmt_entry_get_all(void *opaque)
+caclient_capmt_class_oscam_mode_list ( void *o )
{
- htsmsg_t *r = htsmsg_create_list();
- capmt_t *capmt;
-
- TAILQ_FOREACH(capmt, &capmts, capmt_link)
- htsmsg_add_msg(r, NULL, capmt_record_build(capmt));
-
- return r;
+ static const struct strtab tab[] = {
+ { "OSCam pc-nodmx (rev >= 9756)", CAPMT_OSCAM_UNIX_SOCKET},
+ { "OSCam TCP (rev >= 9574)", CAPMT_OSCAM_TCP },
+ { "OSCam (rev >= 9095)", CAPMT_OSCAM_MULTILIST },
+ { "Older OSCam", CAPMT_OSCAM_OLD },
+ { "Wrapper (capmt_ca.so)", CAPMT_OSCAM_SO_WRAPPER },
+ };
+ return strtab2htsmsg(tab);
}
-/**
- *
- */
-static htsmsg_t *
-capmt_entry_get(void *opaque, const char *id)
+const idclass_t caclient_capmt_class =
{
- capmt_t *capmt;
-
-
- if((capmt = capmt_entry_find(id, 0)) == NULL)
- return NULL;
- return capmt_record_build(capmt);
-}
-
-/**
- *
- */
-static htsmsg_t *
-capmt_entry_create(void *opaque)
-{
- pthread_cond_broadcast(&capmt_config_changed);
- return capmt_record_build(capmt_entry_find(NULL, 1));
-}
-
-/**
- *
- */
-static const dtable_class_t capmt_dtc = {
- .dtc_record_get = capmt_entry_get,
- .dtc_record_get_all = capmt_entry_get_all,
- .dtc_record_create = capmt_entry_create,
- .dtc_record_update = capmt_entry_update,
- .dtc_record_delete = capmt_entry_delete,
- .dtc_read_access = ACCESS_ADMIN,
- .dtc_write_access = ACCESS_ADMIN,
- .dtc_mutex = &global_lock,
+ .ic_super = &caclient_class,
+ .ic_class = "caclient_capmt",
+ .ic_caption = "CAPMT (Linux DVBAPI)",
+ .ic_properties = (const property_t[]){
+ {
+ .type = PT_INT,
+ .id = "mode",
+ .name = "Mode",
+ .off = offsetof(capmt_t, capmt_oscam),
+ .list = caclient_capmt_class_oscam_mode_list,
+ .def.i = CAPMT_OSCAM_MULTILIST,
+ },
+ {
+ .type = PT_STR,
+ .id = "camdfilename",
+ .name = "Camd.socket Filename / IP Address (mode 3)",
+ .off = offsetof(capmt_t, capmt_sockfile),
+ .def.s = "/tmp/camd.socket",
+ },
+ {
+ .type = PT_U16,
+ .id = "port",
+ .name = "Listen/Connect Port",
+ .off = offsetof(capmt_t, capmt_port),
+ .def.i = 9000
+ },
+ { }
+ }
};
-/**
+/*
*
*/
-void
-capmt_init(void)
+caclient_t *capmt_create(void)
{
- dtable_t *dt;
-
- TAILQ_INIT(&capmts);
+ capmt_t *capmt = calloc(1, sizeof(*capmt));
- pthread_cond_init(&capmt_config_changed, NULL);
+ pthread_mutex_init(&capmt->capmt_mutex, NULL);
+ pthread_cond_init(&capmt->capmt_cond, NULL);
+ capmt->capmt_seq = 1;
+ TAILQ_INIT(&capmt->capmt_writeq);
+ tvh_pipe(O_NONBLOCK, &capmt->capmt_pipe);
- dt = dtable_create(&capmt_dtc, "capmt", NULL);
- dtable_load(dt);
-}
+ capmt->cac_free = capmt_free;
+ capmt->cac_start = capmt_service_start;
+ capmt->cac_conf_changed = capmt_conf_changed;
-void
-capmt_done(void)
-{
- capmt_t *capmt;
-
- dtable_delete("capmt");
- pthread_mutex_lock(&global_lock);
- while ((capmt = TAILQ_FIRST(&capmts)) != NULL)
- capmt_destroy(capmt);
- pthread_mutex_unlock(&global_lock);
+ return (caclient_t *)capmt;
}
-
-#else /* ENABLE_CAPMT */
-
-void capmt_init ( void ) {}
-void capmt_done ( void ) {}
-void capmt_service_start(service_t *s) {}
-
-#endif
+++ /dev/null
-/*
- * tvheadend, CAPMT interface
- * Copyright (C) 2009
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef CAPMT_H_
-#define CAPMT_H_
-
-void capmt_init(void);
-
-void capmt_done(void);
-
-void capmt_service_start(struct service *t);
-
-#endif /* CAPMT_H_ */
#include "tvheadend.h"
#include "tcp.h"
-#include "cwc.h"
+#include "caclient.h"
#include "notify.h"
#include "atomic.h"
-#include "dtable.h"
#include "subscriptions.h"
#include "service.h"
#include "input.h"
LIST_HEAD(cwc_service_list, cwc_service);
TAILQ_HEAD(cwc_message_queue, cwc_message);
LIST_HEAD(ecm_section_list, ecm_section);
-static struct cwc_queue cwcs;
-static pthread_cond_t cwc_config_changed;
-static pthread_mutex_t cwc_mutex;
static char *crypt_md5(const char *pw, const char *salt);
/**
*
*/
typedef struct cwc {
+ caclient_t;
+
int cwc_fd;
int cwc_connected;
pthread_cond_t cwc_cond;
+ pthread_mutex_t cwc_mutex;
pthread_mutex_t cwc_writer_mutex;
pthread_cond_t cwc_writer_cond;
int cwc_writer_running;
struct cwc_message_queue cwc_writeq;
- TAILQ_ENTRY(cwc) cwc_link; /* Linkage protected via cwc_mutex */
-
struct cwc_service_list cwc_services;
char *cwc_username;
char *cwc_password;
char *cwc_password_salted; /* salted version */
- char *cwc_comment;
char *cwc_hostname;
int cwc_port;
- char *cwc_id;
int cwc_emm;
int cwc_emmex;
{
htsmsg_t *m = htsmsg_create_map();
- htsmsg_add_str(m, "id", cwc->cwc_id);
+ htsmsg_add_str(m, "uuid", idnode_uuid_as_str(&cwc->cac_id));
htsmsg_add_u32(m, "connected", !!cwc->cwc_connected);
- notify_by_msg("cwcStatus", m);
+ notify_by_msg("caclient_status", m);
}
/**
{
int r;
- pthread_mutex_unlock(&cwc_mutex);
+ pthread_mutex_unlock(&cwc->cwc_mutex);
r = tcp_read_timeout(cwc->cwc_fd, buf, len, timeout);
- pthread_mutex_lock(&cwc_mutex);
+ pthread_mutex_lock(&cwc->cwc_mutex);
if (r && tvheadend_running)
tvhwarn("cwc", "read error %d (%s)", r, strerror(r));
static void *
cwc_thread(void *aux)
{
- cwc_service_t *ct;
- cs_card_data_t *cd;
cwc_t *cwc = aux;
int fd, d;
char errbuf[100];
- mpegts_service_t *t;
char hostname[256];
int port;
struct timespec ts;
int attempts = 0;
- pthread_mutex_lock(&cwc_mutex);
+ pthread_mutex_lock(&cwc->cwc_mutex);
while(cwc->cwc_running) {
- while(cwc->cwc_running && cwc->cwc_enabled == 0)
- pthread_cond_wait(&cwc->cwc_cond, &cwc_mutex);
- if (cwc->cwc_running == 0) continue;
- if (cwc->cwc_hostname == NULL) continue;
-
snprintf(hostname, sizeof(hostname), "%s", cwc->cwc_hostname);
port = cwc->cwc_port;
tvhlog(LOG_INFO, "cwc", "Attemping to connect to %s:%d", hostname, port);
- pthread_mutex_unlock(&cwc_mutex);
+ pthread_mutex_unlock(&cwc->cwc_mutex);
fd = tcp_connect(hostname, port, NULL, errbuf, sizeof(errbuf), 10);
- pthread_mutex_lock(&cwc_mutex);
+ pthread_mutex_lock(&cwc->cwc_mutex);
if(fd == -1) {
attempts++;
"%s:%i: Automatic connection attempt in %d seconds",
cwc->cwc_hostname, cwc->cwc_port, d-1);
- pthread_cond_timedwait(&cwc_config_changed, &cwc_mutex, &ts);
+ pthread_cond_timedwait(&cwc->cwc_cond, &cwc->cwc_mutex, &ts);
}
- tvhlog(LOG_INFO, "cwc", "%s:%i destroyed",
+ tvhlog(LOG_INFO, "cwc", "%s:%i inactive",
cwc->cwc_hostname, cwc->cwc_port);
-
- while((ct = LIST_FIRST(&cwc->cwc_services)) != NULL) {
- t = (mpegts_service_t *)ct->td_service;
- pthread_mutex_lock(&t->s_stream_mutex);
- cwc_service_destroy((th_descrambler_t *)&ct);
- pthread_mutex_unlock(&t->s_stream_mutex);
- }
-
- while((cd = LIST_FIRST(&cwc->cwc_cards)) != NULL) {
- LIST_REMOVE(cd, cs_card);
- descrambler_close_emm(cd->cwc_mux, cd, cd->cwc_caid);
- free(cd->cwc_providers);
- free(cd);
- }
- free((void *)cwc->cwc_password);
- free((void *)cwc->cwc_password_salted);
- free((void *)cwc->cwc_username);
- free((void *)cwc->cwc_comment);
- free((void *)cwc->cwc_hostname);
- free((void *)cwc->cwc_id);
- free((void *)cwc->cwc_viaccess_emm.shared_emm);
- free(cwc);
-
- pthread_mutex_unlock(&cwc_mutex);
+ pthread_mutex_unlock(&cwc->cwc_mutex);
return NULL;
}
}
if (pcard->cwc_mux == NULL)
return;
- pthread_mutex_lock(&cwc_mutex);
cwc = pcard->cwc;
+ pthread_mutex_lock(&cwc->cwc_mutex);
ca_update_id = pcard->cwc_mux;
if (cwc->cwc_forward_emm && cwc->cwc_writer_running) {
if (cwc->cwc_emmex) {
}
}
end_of_job:
- pthread_mutex_unlock(&cwc_mutex);
+ pthread_mutex_unlock(&cwc->cwc_mutex);
}
*
* global_lock is held. Not that we care about that, but either way, it is.
*/
-void
-cwc_service_start(service_t *t)
+static void
+cwc_service_start(caclient_t *cac, service_t *t)
{
- cwc_t *cwc;
+ cwc_t *cwc = (cwc_t *)cac;
cwc_service_t *ct;
th_descrambler_t *td;
elementary_stream_t *st;
if (!idnode_is_instance(&t->s_id, &mpegts_service_class))
return;
- pthread_mutex_lock(&cwc_mutex);
- TAILQ_FOREACH(cwc, &cwcs, cwc_link) {
- LIST_FOREACH(ct, &cwc->cwc_services, cs_link) {
- if (ct->td_service == t && ct->cs_cwc == cwc)
- break;
- }
- pthread_mutex_lock(&t->s_stream_mutex);
- LIST_FOREACH(pcard, &cwc->cwc_cards, cs_card) {
- if (pcard->cwc_caid == 0) continue;
- TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
- if (((mpegts_service_t *)t)->s_dvb_prefcapid_lock == 2 &&
- ((mpegts_service_t *)t)->s_dvb_prefcapid != st->es_pid)
- continue;
- LIST_FOREACH(c, &st->es_caids, link) {
- if (c->use && c->caid == pcard->cwc_caid)
- break;
- }
- if (c) break;
- }
- if (st) break;
- }
- if (!pcard) {
- if (ct) cwc_service_destroy((th_descrambler_t*)ct);
- pthread_mutex_unlock(&t->s_stream_mutex);
- continue;
- }
-
- pthread_mutex_unlock(&t->s_stream_mutex);
- if (ct) continue;
-
- ct = calloc(1, sizeof(cwc_service_t));
- ct->cs_cwc = cwc;
- ct->cs_channel = -1;
- ct->cs_mux = ((mpegts_service_t *)t)->s_dvb_mux;
- ct->ecm_state = ECM_INIT;
- ct->cs_constcw = pcard->cwc_caid == 0x2600;
-
- td = (th_descrambler_t *)ct;
- tvhcsa_init(td->td_csa = &ct->cs_csa);
- snprintf(buf, sizeof(buf), "cwc-%s-%i", cwc->cwc_hostname, cwc->cwc_port);
- td->td_nicename = strdup(buf);
- td->td_service = t;
- td->td_stop = cwc_service_destroy;
- td->td_ecm_reset = cwc_ecm_reset;
- td->td_ecm_idle = cwc_ecm_idle;
- LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
-
- LIST_INSERT_HEAD(&cwc->cwc_services, ct, cs_link);
-
- pthread_mutex_lock(&t->s_stream_mutex);
- i = 0;
+ pthread_mutex_lock(&cwc->cwc_mutex);
+ LIST_FOREACH(ct, &cwc->cwc_services, cs_link) {
+ if (ct->td_service == t && ct->cs_cwc == cwc)
+ break;
+ }
+ pthread_mutex_lock(&t->s_stream_mutex);
+ LIST_FOREACH(pcard, &cwc->cwc_cards, cs_card) {
+ if (pcard->cwc_caid == 0) continue;
TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
- LIST_FOREACH(c, &st->es_caids, link)
- if (c->use && c->caid == pcard->cwc_caid) {
- ct->cs_epids[i++] = st->es_pid;
+ if (((mpegts_service_t *)t)->s_dvb_prefcapid_lock == 2 &&
+ ((mpegts_service_t *)t)->s_dvb_prefcapid != st->es_pid)
+ continue;
+ LIST_FOREACH(c, &st->es_caids, link) {
+ if (c->use && c->caid == pcard->cwc_caid)
break;
- }
- if (i == CWC_ES_PIDS) break;
+ }
+ if (c) break;
}
+ if (st) break;
+ }
+ if (!pcard) {
+ if (ct) cwc_service_destroy((th_descrambler_t*)ct);
pthread_mutex_unlock(&t->s_stream_mutex);
+ pthread_mutex_unlock(&cwc->cwc_mutex);
+ return;
+ }
- for (i = 0; i < CWC_ES_PIDS; i++)
- if (ct->cs_epids[i])
- descrambler_open_pid(ct->cs_mux, ct,
- DESCRAMBLER_ECM_PID(ct->cs_epids[i]),
- cwc_table_input, t);
+ pthread_mutex_unlock(&t->s_stream_mutex);
+ if (ct) {
+ pthread_mutex_unlock(&cwc->cwc_mutex);
+ return;
+ }
- tvhlog(LOG_DEBUG, "cwc", "%s using CWC %s:%d",
- service_nicename(t), cwc->cwc_hostname, cwc->cwc_port);
+ ct = calloc(1, sizeof(cwc_service_t));
+ ct->cs_cwc = cwc;
+ ct->cs_channel = -1;
+ ct->cs_mux = ((mpegts_service_t *)t)->s_dvb_mux;
+ ct->ecm_state = ECM_INIT;
+ ct->cs_constcw = pcard->cwc_caid == 0x2600;
+
+ td = (th_descrambler_t *)ct;
+ tvhcsa_init(td->td_csa = &ct->cs_csa);
+ snprintf(buf, sizeof(buf), "cwc-%s-%i", cwc->cwc_hostname, cwc->cwc_port);
+ td->td_nicename = strdup(buf);
+ td->td_service = t;
+ td->td_stop = cwc_service_destroy;
+ td->td_ecm_reset = cwc_ecm_reset;
+ td->td_ecm_idle = cwc_ecm_idle;
+ LIST_INSERT_HEAD(&t->s_descramblers, td, td_service_link);
+
+ LIST_INSERT_HEAD(&cwc->cwc_services, ct, cs_link);
+ pthread_mutex_lock(&t->s_stream_mutex);
+ i = 0;
+ TAILQ_FOREACH(st, &t->s_filt_components, es_filt_link) {
+ LIST_FOREACH(c, &st->es_caids, link)
+ if (c->use && c->caid == pcard->cwc_caid) {
+ ct->cs_epids[i++] = st->es_pid;
+ break;
+ }
+ if (i == CWC_ES_PIDS) break;
}
- pthread_mutex_unlock(&cwc_mutex);
+ pthread_mutex_unlock(&t->s_stream_mutex);
+
+ for (i = 0; i < CWC_ES_PIDS; i++)
+ if (ct->cs_epids[i])
+ descrambler_open_pid(ct->cs_mux, ct,
+ DESCRAMBLER_ECM_PID(ct->cs_epids[i]),
+ cwc_table_input, t);
+
+ tvhlog(LOG_DEBUG, "cwc", "%s using CWC %s:%d",
+ service_nicename(t), cwc->cwc_hostname, cwc->cwc_port);
+
+ pthread_mutex_unlock(&cwc->cwc_mutex);
}
*
*/
static void
-cwc_destroy(cwc_t *cwc)
+cwc_free(caclient_t *cac)
{
- pthread_t tid;
+ cwc_t *cwc = (cwc_t *)cac;
+ cwc_service_t *ct;
+ cs_card_data_t *cd;
+ mpegts_service_t *t;
+
+ while((ct = LIST_FIRST(&cwc->cwc_services)) != NULL) {
+ t = (mpegts_service_t *)ct->td_service;
+ pthread_mutex_lock(&t->s_stream_mutex);
+ cwc_service_destroy((th_descrambler_t *)&ct);
+ pthread_mutex_unlock(&t->s_stream_mutex);
+ }
- lock_assert(&cwc_mutex);
- TAILQ_REMOVE(&cwcs, cwc, cwc_link);
- cwc->cwc_running = 0;
- pthread_cond_signal(&cwc->cwc_cond);
- tid = cwc->cwc_tid;
- pthread_mutex_unlock(&cwc_mutex);
- pthread_kill(tid, SIGTERM);
- pthread_join(tid, NULL);
- pthread_mutex_lock(&cwc_mutex);
+ while((cd = LIST_FIRST(&cwc->cwc_cards)) != NULL) {
+ LIST_REMOVE(cd, cs_card);
+ descrambler_close_emm(cd->cwc_mux, cd, cd->cwc_caid);
+ free(cd->cwc_providers);
+ free(cd);
+ }
+ free((void *)cwc->cwc_password);
+ free((void *)cwc->cwc_password_salted);
+ free((void *)cwc->cwc_username);
+ free((void *)cwc->cwc_hostname);
+ free((void *)cwc->cwc_viaccess_emm.shared_emm);
}
/**
*
*/
-void
-cwc_caid_update(mpegts_mux_t *mux, uint16_t caid, uint16_t pid, int valid)
+static void
+cwc_caid_update(caclient_t *cac, mpegts_mux_t *mux, uint16_t caid, uint16_t pid, int valid)
{
- cwc_t *cwc;
+ cwc_t *cwc = (cwc_t *)cac;;
struct cs_card_data *pcard;
tvhtrace("cwc",
- "caid update event - mux %p caid %04x (%i) pid %04x (%i) valid %i",
- mux, caid, caid, pid, pid, valid);
- pthread_mutex_lock(&cwc_mutex);
- TAILQ_FOREACH(cwc, &cwcs, cwc_link) {
- if (valid < 0 || cwc->cwc_running) {
- LIST_FOREACH(pcard, &cwc->cwc_cards, cs_card) {
- if (valid < 0 || pcard->cwc_caid == caid) {
- if (pcard->cwc_mux && pcard->cwc_mux != mux) continue;
- if (valid > 0) {
- pcard->cwc = cwc;
- pcard->cwc_mux = mux;
- descrambler_open_emm(mux, pcard, caid, cwc_emm);
- } else {
- pcard->cwc_mux = NULL;
- descrambler_close_emm(mux, pcard, caid);
- }
+ "caid update event - client %s mux %p caid %04x (%i) pid %04x (%i) valid %i",
+ cac->cac_name, mux, caid, caid, pid, pid, valid);
+ pthread_mutex_lock(&cwc->cwc_mutex);
+ if (valid < 0 || cwc->cwc_running) {
+ LIST_FOREACH(pcard, &cwc->cwc_cards, cs_card) {
+ if (valid < 0 || pcard->cwc_caid == caid) {
+ if (pcard->cwc_mux && pcard->cwc_mux != mux) continue;
+ if (valid > 0) {
+ pcard->cwc = cwc;
+ pcard->cwc_mux = mux;
+ descrambler_open_emm(mux, pcard, caid, cwc_emm);
+ } else {
+ pcard->cwc_mux = NULL;
+ descrambler_close_emm(mux, pcard, caid);
}
}
}
}
- pthread_mutex_unlock(&cwc_mutex);
-}
-
-
-/**
- *
- */
-static cwc_t *
-cwc_entry_find(const char *id, int create)
-{
- char buf[20];
- cwc_t *cwc;
- static int tally;
-
- if(id != NULL) {
- TAILQ_FOREACH(cwc, &cwcs, cwc_link) {
- if(!strcmp(cwc->cwc_id, id))
- return cwc;
- }
- }
- if(create == 0)
- return NULL;
-
- if(id == NULL) {
- tally++;
- snprintf(buf, sizeof(buf), "%d", tally);
- id = buf;
- } else {
- tally = MAX(atoi(id), tally);
- }
-
- cwc = calloc(1, sizeof(cwc_t));
- pthread_cond_init(&cwc->cwc_cond, NULL);
- cwc->cwc_id = strdup(id);
- cwc->cwc_running = 1;
- TAILQ_INSERT_TAIL(&cwcs, cwc, cwc_link);
-
- tvhthread_create(&cwc->cwc_tid, NULL, cwc_thread, cwc);
-
- return cwc;
+ pthread_mutex_unlock(&cwc->cwc_mutex);
}
-
-/**
- *
- */
-static htsmsg_t *
-cwc_record_build(cwc_t *cwc)
-{
- htsmsg_t *e = htsmsg_create_map();
- char buf[100];
-
- htsmsg_add_str(e, "id", cwc->cwc_id);
- htsmsg_add_u32(e, "enabled", !!cwc->cwc_enabled);
- htsmsg_add_u32(e, "connected", !!cwc->cwc_connected);
-
- htsmsg_add_str(e, "hostname", cwc->cwc_hostname ?: "");
- htsmsg_add_u32(e, "port", cwc->cwc_port);
-
- htsmsg_add_str(e, "username", cwc->cwc_username ?: "");
- htsmsg_add_str(e, "password", cwc->cwc_password ?: "");
- snprintf(buf, sizeof(buf),
- "%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
- "%02x:%02x:%02x:%02x:%02x:%02x:%02x",
- cwc->cwc_confedkey[0x0],
- cwc->cwc_confedkey[0x1],
- cwc->cwc_confedkey[0x2],
- cwc->cwc_confedkey[0x3],
- cwc->cwc_confedkey[0x4],
- cwc->cwc_confedkey[0x5],
- cwc->cwc_confedkey[0x6],
- cwc->cwc_confedkey[0x7],
- cwc->cwc_confedkey[0x8],
- cwc->cwc_confedkey[0x9],
- cwc->cwc_confedkey[0xa],
- cwc->cwc_confedkey[0xb],
- cwc->cwc_confedkey[0xc],
- cwc->cwc_confedkey[0xd]);
-
- htsmsg_add_str(e, "deskey", buf);
- htsmsg_add_u32(e, "emm", cwc->cwc_emm);
- htsmsg_add_u32(e, "emmex", cwc->cwc_emmex);
- htsmsg_add_str(e, "comment", cwc->cwc_comment ?: "");
-
- return e;
-}
-
-
/**
*
*/
}
}
-
/**
*
*/
-static htsmsg_t *
-cwc_entry_update(void *opaque, const char *id, htsmsg_t *values, int maycreate)
+static void
+cwc_conf_changed(caclient_t *cac)
{
- cwc_t *cwc;
- const char *s;
- uint32_t u32;
- uint8_t key[14];
- int u, l, i;
-
- if((cwc = cwc_entry_find(id, maycreate)) == NULL)
- return NULL;
-
- if((s = htsmsg_get_str(values, "username")) != NULL) {
- free(cwc->cwc_username);
- cwc->cwc_username = strdup(s);
- }
-
- if((s = htsmsg_get_str(values, "password")) != NULL) {
- free(cwc->cwc_password);
- free(cwc->cwc_password_salted);
- cwc->cwc_password = strdup(s);
- cwc->cwc_password_salted = crypt_md5(s, "$1$abcdefgh$");
- }
-
- if((s = htsmsg_get_str(values, "comment")) != NULL) {
- free(cwc->cwc_comment);
- cwc->cwc_comment = strdup(s);
- }
-
- if((s = htsmsg_get_str(values, "hostname")) != NULL) {
- free(cwc->cwc_hostname);
- cwc->cwc_hostname = strdup(s);
- }
-
- if(!htsmsg_get_u32(values, "enabled", &u32))
- cwc->cwc_enabled = u32;
+ cwc_t *cwc = (cwc_t *)cac;
+ pthread_t tid;
- if(!htsmsg_get_u32(values, "port", &u32))
- cwc->cwc_port = u32;
+ free(cwc->cwc_password_salted);
+ cwc->cwc_password_salted =
+ cwc->cwc_password ? crypt_md5(cwc->cwc_password, "$1$abcdefgh$") : NULL;
- if((s = htsmsg_get_str(values, "deskey")) != NULL) {
- for(i = 0; i < 14; i++) {
- while(*s != 0 && !isxdigit(*s)) s++;
- if(*s == 0)
- break;
- u = nibble(*s++);
- while(*s != 0 && !isxdigit(*s)) s++;
- if(*s == 0)
- break;
- l = nibble(*s++);
- key[i] = (u << 4) | l;
+ if (cac->cac_enabled) {
+ if (cwc->cwc_hostname == NULL || cwc->cwc_hostname[0] == '\0')
+ return;
+ if (!cwc->cwc_running) {
+ cwc->cwc_running = 1;
+ tvhthread_create(&cwc->cwc_tid, NULL, cwc_thread, cwc);
+ return;
}
- memcpy(cwc->cwc_confedkey, key, 14);
+ pthread_mutex_lock(&cwc->cwc_mutex);
+ cwc->cwc_reconfigure = 1;
+ if(cwc->cwc_fd != -1)
+ shutdown(cwc->cwc_fd, SHUT_RDWR);
+ pthread_cond_signal(&cwc->cwc_cond);
+ pthread_mutex_unlock(&cwc->cwc_mutex);
+ } else {
+ if (!cwc->cwc_running)
+ return;
+ pthread_mutex_lock(&cwc->cwc_mutex);
+ cwc->cwc_running = 0;
+ pthread_cond_signal(&cwc->cwc_cond);
+ tid = cwc->cwc_tid;
+ pthread_mutex_unlock(&cwc->cwc_mutex);
+ pthread_kill(tid, SIGTERM);
+ pthread_join(tid, NULL);
}
- if(!htsmsg_get_u32(values, "emm", &u32))
- cwc->cwc_emm = u32;
-
- if(!htsmsg_get_u32(values, "emmex", &u32))
- cwc->cwc_emmex = u32;
-
- cwc->cwc_reconfigure = 1;
-
- if(cwc->cwc_fd != -1)
- shutdown(cwc->cwc_fd, SHUT_RDWR);
-
- pthread_cond_signal(&cwc->cwc_cond);
-
- pthread_cond_broadcast(&cwc_config_changed);
-
- return cwc_record_build(cwc);
}
-
-
/**
*
*/
static int
-cwc_entry_delete(void *opaque, const char *id)
+caclient_cwc_class_deskey_set(void *o, const void *v)
{
- cwc_t *cwc;
-
- pthread_cond_broadcast(&cwc_config_changed);
-
- if((cwc = cwc_entry_find(id, 0)) == NULL)
- return -1;
- cwc_destroy(cwc);
- return 0;
-}
-
-
-/**
- *
- */
-static htsmsg_t *
-cwc_entry_get_all(void *opaque)
-{
- htsmsg_t *r = htsmsg_create_list();
- cwc_t *cwc;
-
- TAILQ_FOREACH(cwc, &cwcs, cwc_link)
- htsmsg_add_msg(r, NULL, cwc_record_build(cwc));
-
- return r;
+ cwc_t *cwc = o;
+ const char *s = v ?: "";
+ uint8_t key[14];
+ int i, u, l;
+
+ for(i = 0; i < ARRAY_SIZE(key); i++) {
+ while(*s != 0 && !isxdigit(*s)) s++;
+ u = *s ? nibble(*s++) : 0;
+ while(*s != 0 && !isxdigit(*s)) s++;
+ l = *s ? nibble(*s++) : 0;
+ key[i] = (u << 4) | l;
+ }
+ i = memcmp(cwc->cwc_confedkey, key, ARRAY_SIZE(key)) != 0;
+ memcpy(cwc->cwc_confedkey, key, ARRAY_SIZE(key));
+ return i;
}
-/**
- *
- */
-static htsmsg_t *
-cwc_entry_get(void *opaque, const char *id)
+static const void *
+caclient_cwc_class_deskey_get(void *o)
{
- cwc_t *cwc;
-
-
- if((cwc = cwc_entry_find(id, 0)) == NULL)
- return NULL;
- return cwc_record_build(cwc);
+ cwc_t *cwc = o;
+ static char buf[64];
+ static const char *ret = buf;
+ snprintf(buf, sizeof(buf),
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ cwc->cwc_confedkey[0x0],
+ cwc->cwc_confedkey[0x1],
+ cwc->cwc_confedkey[0x2],
+ cwc->cwc_confedkey[0x3],
+ cwc->cwc_confedkey[0x4],
+ cwc->cwc_confedkey[0x5],
+ cwc->cwc_confedkey[0x6],
+ cwc->cwc_confedkey[0x7],
+ cwc->cwc_confedkey[0x8],
+ cwc->cwc_confedkey[0x9],
+ cwc->cwc_confedkey[0xa],
+ cwc->cwc_confedkey[0xb],
+ cwc->cwc_confedkey[0xc],
+ cwc->cwc_confedkey[0xd]);
+ return &ret;
}
-/**
- *
- */
-/**
- *
- */
-static htsmsg_t *
-cwc_entry_create(void *opaque)
+const idclass_t caclient_cwc_class =
{
- pthread_cond_broadcast(&cwc_config_changed);
- return cwc_record_build(cwc_entry_find(NULL, 1));
-}
-
-
-
-
-/**
- *
- */
-static const dtable_class_t cwc_dtc = {
- .dtc_record_get = cwc_entry_get,
- .dtc_record_get_all = cwc_entry_get_all,
- .dtc_record_create = cwc_entry_create,
- .dtc_record_update = cwc_entry_update,
- .dtc_record_delete = cwc_entry_delete,
- .dtc_read_access = ACCESS_ADMIN,
- .dtc_write_access = ACCESS_ADMIN,
- .dtc_mutex = &cwc_mutex,
+ .ic_super = &caclient_class,
+ .ic_class = "caclient_cwc",
+ .ic_caption = "Code Word Client (newcamd)",
+ .ic_properties = (const property_t[]){
+ {
+ .type = PT_STR,
+ .id = "username",
+ .name = "Username",
+ .off = offsetof(cwc_t, cwc_username),
+ },
+ {
+ .type = PT_STR,
+ .id = "password",
+ .name = "Password",
+ .off = offsetof(cwc_t, cwc_password),
+ .opts = PO_PASSWORD
+ },
+ {
+ .type = PT_STR,
+ .id = "hostname",
+ .name = "Hostname / IP",
+ .opts = PO_PASSWORD,
+ .off = offsetof(cwc_t, cwc_hostname),
+ .def.s = "localhost",
+ },
+ {
+ .type = PT_INT,
+ .id = "port",
+ .name = "Port",
+ .off = offsetof(cwc_t, cwc_port),
+ },
+ {
+ .type = PT_STR,
+ .id = "deskey",
+ .name = "DES Key",
+ .opts = PO_PASSWORD,
+ .set = caclient_cwc_class_deskey_set,
+ .get = caclient_cwc_class_deskey_get,
+ .def.s = "00:01:02:03:04:05:06:07:08:09:0a:0b:0c:0d",
+ },
+ {
+ .type = PT_BOOL,
+ .id = "emm",
+ .name = "Update Card (EMM)",
+ .off = offsetof(cwc_t, cwc_emm),
+ .def.i = 1
+ },
+ {
+ .type = PT_BOOL,
+ .id = "emmex",
+ .name = "One Mux (EMM)",
+ .off = offsetof(cwc_t, cwc_emmex),
+ .def.i = 1
+ },
+ { }
+ }
};
-
-
-/**
+/*
*
*/
-void
-cwc_init(void)
+caclient_t *cwc_create(void)
{
- dtable_t *dt;
+ cwc_t *cwc = calloc(1, sizeof(*cwc));
- TAILQ_INIT(&cwcs);
- pthread_mutex_init(&cwc_mutex, NULL);
- pthread_cond_init(&cwc_config_changed, NULL);
-
- dt = dtable_create(&cwc_dtc, "cwc", NULL);
- dtable_load(dt);
+ pthread_mutex_init(&cwc->cwc_mutex, NULL);
+ pthread_cond_init(&cwc->cwc_cond, NULL);
+ cwc->cac_free = cwc_free;
+ cwc->cac_start = cwc_service_start;
+ cwc->cac_conf_changed = cwc_conf_changed;
+ cwc->cac_caid_update = cwc_caid_update;
+ return (caclient_t *)cwc;
}
-
-/**
+/*
*
*/
-void
-cwc_done(void)
-{
- cwc_t *cwc;
-
- dtable_delete("cwc");
- pthread_mutex_lock(&global_lock);
- pthread_mutex_lock(&cwc_mutex);
- while ((cwc = TAILQ_FIRST(&cwcs)) != NULL)
- cwc_destroy(cwc);
- pthread_mutex_unlock(&cwc_mutex);
- pthread_mutex_unlock(&global_lock);
-}
-
#include <openssl/md5.h>
+++ /dev/null
-/*
- * tvheadend, CWC interface
- * Copyright (C) 2007 Andreas Ă–man
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef CWC_H_
-#define CWC_H_
-
-struct mpegts_mux;
-
-void cwc_init(void);
-
-void cwc_done(void);
-
-void cwc_service_start(struct service *t);
-
-void cwc_caid_update(struct mpegts_mux *mux,
- uint16_t caid, uint16_t pid, int valid);
-
-#endif /* CWC_H_ */
#include "tvheadend.h"
#include "descrambler.h"
-#include "cwc.h"
-#include "capmt.h"
+#include "caclient.h"
#include "ffdecsa/FFdecsa.h"
#include "input.h"
#include "tvhcsa.h"
void
descrambler_init ( void )
{
-#if ENABLE_CWC
- cwc_init();
-#endif
-#if ENABLE_CAPMT
- capmt_init();
-#endif
#if (ENABLE_CWC || ENABLE_CAPMT) && !ENABLE_DVBCSA
ffdecsa_init();
#endif
+ caclient_init();
}
void
descrambler_done ( void )
{
-#if ENABLE_CAPMT
- capmt_done();
-#endif
-#if ENABLE_CWC
- cwc_done();
-#endif
+ caclient_done();
}
/*
return;
((mpegts_service_t *)t)->s_dvb_mux->mm_descrambler_flush = 0;
-#if ENABLE_CWC
- cwc_service_start(t);
-#endif
-#if ENABLE_CAPMT
- capmt_service_start(t);
-#endif
+ caclient_start(t);
if (t->s_descramble == NULL) {
t->s_descramble = dr = calloc(1, sizeof(th_descrambler_runtime_t));
sbuf_init(&dr->dr_buf);
if (mux == NULL)
return;
tvhtrace("descrambler", "mux %p - flush tables", mux);
-#if ENABLE_CWC
- cwc_caid_update(mux, 0, 0, -1);
-#endif
+ caclient_caid_update(mux, 0, 0, -1);
pthread_mutex_lock(&mux->mm_descrambler_lock);
mux->mm_descrambler_flush = 1;
while ((dt = TAILQ_FIRST(&mux->mm_descrambler_tables)) != NULL) {
pid = ((data[2] << 8) | data[3]) & 0x1fff;
if (pid == 0)
goto next;
-#if ENABLE_CWC
- cwc_caid_update(mux, caid, pid, 1);
-#endif
+ caclient_caid_update(mux, caid, pid, 1);
pthread_mutex_lock(&mux->mm_descrambler_lock);
TAILQ_FOREACH(emm, &mux->mm_descrambler_emms, link)
if (emm->caid == caid) {
}
pthread_mutex_unlock(&mux->mm_descrambler_lock);
while ((emm = TAILQ_FIRST(&removing)) != NULL) {
- if (emm->pid != EMM_PID_UNKNOWN) {
-#if ENABLE_CWC
- cwc_caid_update(mux, emm->caid, emm->pid, 0);
-#endif
- }
+ if (emm->pid != EMM_PID_UNKNOWN)
+ caclient_caid_update(mux, emm->caid, emm->pid, 0);
TAILQ_REMOVE(&removing, emm, link);
free(emm);
}
const char *tvheadend_cwd;
const char *tvheadend_webroot;
const tvh_caps_t tvheadend_capabilities[] = {
-#if ENABLE_CWC
- { "cwc", NULL },
-#endif
-#if ENABLE_CAPMT
- { "capmt", NULL },
+#if ENABLE_CWC || ENABLE_CAPMT
+ { "caclient", NULL },
#endif
#if ENABLE_V4L
{ "v4l", NULL },
if (!(new = htsmsg_field_get_str(f)))
continue;
if (!p->set && strcmp((*str) ?: "", new)) {
- free(*str);
+ /* make sure that the string is valid all time */
+ void *old = *str;
*str = strdup(new);
+ free(old);
save = 1;
}
break;
extjs_load(hq, "static/app/tableeditor.js");
extjs_load(hq, "static/app/cteditor.js");
extjs_load(hq, "static/app/acleditor.js");
-#if ENABLE_CWC
- extjs_load(hq, "static/app/cwceditor.js");
-#endif
-#if ENABLE_CAPMT
- extjs_load(hq, "static/app/capmteditor.js");
+#if ENABLE_CWC || ENABLE_CAPMT
+ extjs_load(hq, "static/app/caclient.js");
#endif
extjs_load(hq, "static/app/tvadapters.js");
extjs_load(hq, "static/app/idnode.js");
--- /dev/null
+/*
+ * Conditional Access Client (cwc,capmt)
+ */
+
+tvheadend.caclient_builders = new Ext.data.JsonStore({
+ url: 'api/caclient/builders',
+ root: 'entries',
+ fields: ['class', 'caption', 'props'],
+ id: 'class',
+ autoLoad: true
+});
+
+tvheadend.caclient = function(panel, index) {
+ var list = 'enabled,name,username,password,hostname,mode,camdfilename,' +
+ 'port,deskey,emm,emmex,comment';
+
+ tvheadend.idnode_form_grid(panel, {
+ url: 'api/caclient',
+ clazz: 'caclient',
+ comet: 'caclient',
+ titleS: 'CA',
+ titleP: 'CAs',
+ titleC: 'Client Name',
+ iconCls: 'key',
+ tabIndex: index,
+ list: { url: 'api/caclient/list', params: { } },
+ edit: { params: { list: list } },
+ add: {
+ url: 'api/caclient',
+ titleS: 'Conditional Access Client',
+ select: {
+ label: 'Type',
+ store: tvheadend.caclient_builders,
+ displayField: 'caption',
+ valueField: 'class',
+ propField: 'props',
+ list: list
+ },
+ create: { },
+ },
+ del: true,
+ move: true,
+ help: function() {
+ new tvheadend.help('Conditional Access Client', 'config_caclient.html');
+ },
+ });
+
+ return panel;
+
+}
return store;
};
+tvheadend.idnode_filter_fields = function(d, list)
+{
+ if (!list)
+ return d;
+ var o = list.split(',');
+ var r = [];
+ for (var i = 0; i < o.length; i++)
+ for (var j = 0; j < d.length; j++)
+ if (d[j].id === o[i]) {
+ r.push(d[j]);
+ break;
+ }
+ return r;
+}
+
Ext.ux.grid.filter.IntsplitFilter = Ext.extend(Ext.ux.grid.filter.NumericFilter, {
fieldCls : Ext.form.TextField,
if (r) {
var d = r.get(conf.select.propField);
if (d) {
+ d = tvheadend.idnode_filter_fields(d, conf.select.list || null);
pclass = r.get(conf.select.valueField);
win.setTitle('Add ' + s.lastSelectionText);
panel.remove(s);
});
/* Model */
- var sortable = true;
- if (conf.move)
- sortable = false;
var model = new Ext.grid.ColumnModel({
- defaultSortable: sortable,
+ defaultSortable: conf.move ? false : true,
columns: columns
});
var selectuuid = null;
/* Store */
+ var listurl = conf.list ? conf.list.url : null;
+ var params = conf.list ? conf.list.params : null;
store = new Ext.data.JsonStore({
root: 'entries',
- url: 'api/idnode/load',
- baseParams: {
+ url: listurl || 'api/idnode/load',
+ baseParams: params || {
enum: 1,
'class': conf.clazz
},
fields: ['key','val'],
remoteSort: false,
pruneModifiedRecords: true,
- sortInfo: {
- field: 'val',
- direction: 'ASC'
- }
+ sortInfo: conf.move ? null : { field: 'val', direction: 'ASC' }
});
store.on('load', function(records) {
/* Model */
var model = new Ext.grid.ColumnModel({
- defaultSortable: true,
+ defaultSortable: conf.move ? false : true,
columns: [{
width: 300,
id: 'val',
header: conf.titleC,
- sortable: true,
+ sortable: conf.move ? false : true,
dataIndex: 'val'
}]
});
});
buttons.push(abuttons.del);
}
- if (conf.add || conf.del)
+ if (conf.move) {
+ abuttons.up = new Ext.Toolbar.Button({
+ tooltip: 'Move selected entry up',
+ iconCls: 'moveup',
+ text: 'Move Up',
+ disabled: true,
+ handler: function() {
+ var r = select.getSelections();
+ if (r && r.length > 0) {
+ var uuid = r[0].id;
+ tvheadend.Ajax({
+ url: 'api/idnode/moveup',
+ params: {
+ uuid: uuid
+ },
+ success: function(d)
+ {
+ store.reload();
+ }
+ });
+ }
+ }
+ });
+ buttons.push(abuttons.up);
+ abuttons.down = new Ext.Toolbar.Button({
+ tooltip: 'Move selected entry down',
+ iconCls: 'movedown',
+ text: 'Move Down',
+ disabled: true,
+ handler: function() {
+ var r = select.getSelections();
+ if (r && r.length > 0) {
+ var uuid = r[0].id;
+ tvheadend.Ajax({
+ url: 'api/idnode/movedown',
+ params: {
+ uuid: uuid
+ },
+ success: function(d)
+ {
+ store.reload();
+ }
+ });
+ }
+ }
+ });
+ buttons.push(abuttons.down);
+ }
+ if (conf.add || conf.del || conf.move)
buttons.push('-');
/* Extra buttons */
return;
if (current && current.uuid == r.id)
return;
+ var params = conf.edit ? (conf.edit.params || {}) : {};
+ params.uuid = r.id;
+ params.meta = 1;
tvheadend.Ajax({
url: 'api/idnode/load',
- params: {
- uuid: r.id,
- meta: 1
- },
+ params: params,
success: function(d) {
d = json_decode(d);
roweditor_destroy();
}
abuttons.save.setDisabled(false);
abuttons.undo.setDisabled(false);
- abuttons.del.setDisabled(false);
+ if (abuttons.del)
+ abuttons.del.setDisabled(false);
+ if (abuttons.up) {
+ abuttons.up.setDisabled(false);
+ abuttons.down.setDisabled(false);
+ }
mpanel.add(editor);
mpanel.doLayout();
}
cp.add(tsdvr);
/* CSA */
- if (tvheadend.capabilities.indexOf('cwc') !== -1 ||
- tvheadend.capabilities.indexOf('capmt') !== -1) {
-
- var csa = new Ext.TabPanel({
- activeTab: 0,
- autoScroll: true,
- title: 'CSA',
- iconCls: 'key',
- items: []
- });
-
- if (tvheadend.capabilities.indexOf('cwc') !== -1)
- tvheadend.cwceditor(csa);
- if (tvheadend.capabilities.indexOf('capmt') !== -1)
- tvheadend.capmteditor(csa);
-
- cp.add(csa);
- }
+ if (tvheadend.capabilities.indexOf('caclient') !== -1)
+ tvheadend.caclient(cp, null);
/* Stream Config */
var stream = new Ext.TabPanel({