From: Jaroslav Kysela Date: Tue, 30 Sep 2014 10:35:51 +0000 (+0200) Subject: caclient: initial code to move cwc,capmt to idnode X-Git-Tag: v4.1~1249 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d307001c52597098d87e278be1322036fb4156ca;p=thirdparty%2Ftvheadend.git caclient: initial code to move cwc,capmt to idnode --- diff --git a/Makefile b/Makefile index e430ce46b..7cb65df14 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,8 @@ SRCS += \ 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 \ @@ -203,6 +204,7 @@ SRCS += src/muxer.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 \ diff --git a/src/api.c b/src/api.c index 4eadf9409..f0ac5be3b 100644 --- a/src/api.c +++ b/src/api.c @@ -133,6 +133,7 @@ void api_init ( void ) api_intlconv_init(); api_access_init(); api_dvr_init(); + api_caclient_init(); } void api_done ( void ) diff --git a/src/api.h b/src/api.h index 08e0869d4..c6fe371cc 100644 --- a/src/api.h +++ b/src/api.h @@ -62,7 +62,6 @@ void api_init ( 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 ); @@ -74,6 +73,7 @@ void api_esfilter_init ( void ); void api_intlconv_init ( void ); void api_access_init ( void ); void api_dvr_init ( void ); +void api_caclient_init ( void ); /* * IDnode diff --git a/src/api/api_caclient.c b/src/api/api_caclient.c new file mode 100644 index 000000000..e186ec2a1 --- /dev/null +++ b/src/api/api_caclient.c @@ -0,0 +1,105 @@ +/* + * 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 . + */ + +#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); +} diff --git a/src/descrambler/caclient.c b/src/descrambler/caclient.c new file mode 100644 index 000000000..759e4543a --- /dev/null +++ b/src/descrambler/caclient.c @@ -0,0 +1,320 @@ +/* + * Tvheadend - conditional access key client superclass + * 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 . + */ + +#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); +} diff --git a/src/descrambler/caclient.h b/src/descrambler/caclient.h new file mode 100644 index 000000000..0e26b78f3 --- /dev/null +++ b/src/descrambler/caclient.h @@ -0,0 +1,68 @@ +/* + * 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 . + */ + +#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__ */ diff --git a/src/descrambler/capmt.c b/src/descrambler/capmt.c index cd48b8da8..289b97b9e 100644 --- a/src/descrambler/capmt.c +++ b/src/descrambler/capmt.c @@ -38,17 +38,14 @@ #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" @@ -113,14 +110,19 @@ typedef struct dmx_filter { #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 @@ -239,9 +241,9 @@ typedef struct capmt_adapter { * */ 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; @@ -250,15 +252,7 @@ typedef struct capmt { /* 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]; @@ -290,26 +284,6 @@ static void capmt_send_request(capmt_service_t *ct, int lm); 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; -} - /** * */ @@ -456,10 +430,14 @@ capmt_pid_flush(capmt_t *capmt) 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); } /* @@ -1428,12 +1406,12 @@ capmt_thread(void *aux) 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; @@ -1511,7 +1489,7 @@ capmt_thread(void *aux) 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); } @@ -1782,10 +1760,10 @@ capmt_enumerate_services(capmt_t *capmt, int force) * * 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; @@ -1809,109 +1787,108 @@ capmt_service_start(service_t *s) 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); } @@ -1919,219 +1896,113 @@ capmt_service_start(service_t *s) * */ 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 diff --git a/src/descrambler/capmt.h b/src/descrambler/capmt.h deleted file mode 100644 index 711c95d9b..000000000 --- a/src/descrambler/capmt.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * 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 . - */ - -#ifndef CAPMT_H_ -#define CAPMT_H_ - -void capmt_init(void); - -void capmt_done(void); - -void capmt_service_start(struct service *t); - -#endif /* CAPMT_H_ */ diff --git a/src/descrambler/cwc.c b/src/descrambler/cwc.c index 0f70fc88e..bdfd02623 100755 --- a/src/descrambler/cwc.c +++ b/src/descrambler/cwc.c @@ -34,10 +34,9 @@ #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" @@ -87,9 +86,6 @@ LIST_HEAD(cwc_cards_list, cs_card_data); 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); /** @@ -207,6 +203,8 @@ typedef struct cs_card_data { * */ typedef struct cwc { + caclient_t; + int cwc_fd; int cwc_connected; @@ -216,13 +214,12 @@ typedef struct cwc { 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; @@ -265,10 +262,8 @@ typedef struct cwc { 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; @@ -558,9 +553,9 @@ cwc_comet_status_update(cwc_t *cwc) { 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); } /** @@ -990,9 +985,9 @@ cwc_read(cwc_t *cwc, void *buf, size_t len, int timeout) { 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)); @@ -1186,36 +1181,28 @@ cwc_session(cwc_t *cwc) 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++; @@ -1256,35 +1243,12 @@ cwc_thread(void *aux) "%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; } @@ -1346,8 +1310,8 @@ cwc_emm(void *opaque, int pid, const uint8_t *data, int len) } 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) { @@ -1395,7 +1359,7 @@ cwc_emm(void *opaque, int pid, const uint8_t *data, int len) } } end_of_job: - pthread_mutex_unlock(&cwc_mutex); + pthread_mutex_unlock(&cwc->cwc_mutex); } @@ -2048,10 +2012,10 @@ cwc_service_destroy(th_descrambler_t *td) * * 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; @@ -2064,78 +2028,80 @@ cwc_service_start(service_t *t) 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); } @@ -2143,139 +2109,64 @@ cwc_service_start(service_t *t) * */ 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; -} - - /** * */ @@ -2294,197 +2185,175 @@ nibble(char c) } } - /** * */ -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 diff --git a/src/descrambler/cwc.h b/src/descrambler/cwc.h deleted file mode 100644 index a256c1a29..000000000 --- a/src/descrambler/cwc.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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 . - */ - -#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_ */ diff --git a/src/descrambler/descrambler.c b/src/descrambler/descrambler.c index 17e72dc81..3ab4a73eb 100755 --- a/src/descrambler/descrambler.c +++ b/src/descrambler/descrambler.c @@ -18,8 +18,7 @@ #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" @@ -81,26 +80,16 @@ static struct caid_tab caidnametab[] = { 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(); } /* @@ -123,12 +112,7 @@ descrambler_service_start ( service_t *t ) 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); @@ -638,9 +622,7 @@ descrambler_flush_tables( mpegts_mux_t *mux ) 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) { @@ -690,9 +672,7 @@ descrambler_cat_data( mpegts_mux_t *mux, const uint8_t *data, int len ) 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) { @@ -724,11 +704,8 @@ next: } 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); } diff --git a/src/main.c b/src/main.c index d2979bed1..9cafab08c 100644 --- a/src/main.c +++ b/src/main.c @@ -134,11 +134,8 @@ int tvheadend_htsp_port_extra; 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 }, diff --git a/src/prop.c b/src/prop.c index 8af6d9d23..ea709468b 100644 --- a/src/prop.c +++ b/src/prop.c @@ -173,8 +173,10 @@ prop_write_values 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; diff --git a/src/webui/extjs.c b/src/webui/extjs.c index 521102faf..8917e4608 100755 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -142,11 +142,8 @@ extjs_root(http_connection_t *hc, const char *remain, void *opaque) 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"); diff --git a/src/webui/static/app/caclient.js b/src/webui/static/app/caclient.js new file mode 100644 index 000000000..15224d3a2 --- /dev/null +++ b/src/webui/static/app/caclient.js @@ -0,0 +1,50 @@ +/* + * 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; + +} diff --git a/src/webui/static/app/idnode.js b/src/webui/static/app/idnode.js index a40ce66bf..90fe41326 100644 --- a/src/webui/static/app/idnode.js +++ b/src/webui/static/app/idnode.js @@ -107,6 +107,21 @@ tvheadend.idnode_enum_store = function(f) 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, @@ -838,6 +853,7 @@ tvheadend.idnode_create = function(conf, onlyDefault) 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); @@ -980,11 +996,8 @@ tvheadend.idnode_grid = function(panel, conf) }); /* Model */ - var sortable = true; - if (conf.move) - sortable = false; var model = new Ext.grid.ColumnModel({ - defaultSortable: sortable, + defaultSortable: conf.move ? false : true, columns: columns }); @@ -1441,10 +1454,12 @@ tvheadend.idnode_form_grid = function(panel, conf) 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 }, @@ -1454,10 +1469,7 @@ tvheadend.idnode_form_grid = function(panel, conf) 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) { @@ -1476,12 +1488,12 @@ tvheadend.idnode_form_grid = function(panel, conf) /* 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' }] }); @@ -1568,7 +1580,55 @@ tvheadend.idnode_form_grid = function(panel, conf) }); 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 */ @@ -1611,12 +1671,12 @@ tvheadend.idnode_form_grid = function(panel, conf) 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(); @@ -1636,7 +1696,12 @@ tvheadend.idnode_form_grid = function(panel, conf) } 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(); } diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index e8b50519e..c96024b70 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -393,24 +393,8 @@ function accessUpdate(o) { 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({