From 27b9a918514e35bc9d1661395ba40984388f0c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=96man?= Date: Sat, 17 May 2008 07:31:24 +0000 Subject: [PATCH] Add support for configuring code word clients --- Makefile | 2 +- ajaxui/ajaxui.css | 7 + ajaxui/ajaxui.h | 4 + ajaxui/ajaxui_config.c | 9 +- ajaxui/ajaxui_config_cwc.c | 266 ++++++++++++++++++++++++++ ajaxui/ajaxui_mailbox.c | 24 +++ ajaxui/ajaxui_mailbox.h | 5 + cwc.c | 380 ++++++++++++++++++++++++++----------- cwc.h | 64 +++++++ notify.c | 13 ++ notify.h | 5 + 11 files changed, 662 insertions(+), 117 deletions(-) create mode 100644 ajaxui/ajaxui_config_cwc.c diff --git a/Makefile b/Makefile index 5d784f2d..f37371e8 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ VPATH += ajaxui SRCS += ajaxui.c ajaxui_mailbox.c ajaxui_channels.c \ ajaxui_config.c ajaxui_config_channels.c ajaxui_config_dvb.c \ ajaxui_config_transport.c ajaxui_config_xmltv.c \ - ajaxui_config_access.c + ajaxui_config_access.c ajaxui_config_cwc.c JSSRCS += tvheadend.js diff --git a/ajaxui/ajaxui.css b/ajaxui/ajaxui.css index ec43a3c1..84dd96b6 100644 --- a/ajaxui/ajaxui.css +++ b/ajaxui/ajaxui.css @@ -87,6 +87,13 @@ img { border: 0; } width: 50%; } +.cell_20 { + margin-top: 2px; + margin-bottom: 2px; + float: left; + width: 20%; +} + .normaltable { height: 300px; overflow: auto; diff --git a/ajaxui/ajaxui.h b/ajaxui/ajaxui.h index 5435b422..b84f5edf 100644 --- a/ajaxui/ajaxui.h +++ b/ajaxui/ajaxui.h @@ -101,6 +101,9 @@ void ajax_config_xmltv_init(void); int ajax_config_access_tab(http_connection_t *hc, http_reply_t *hr); void ajax_config_access_init(void); +int ajax_config_cwc_tab(http_connection_t *hc, http_reply_t *hr); +void ajax_config_cwc_init(void); + void ajax_config_transport_init(void); int ajax_transport_build_list(http_connection_t *hc, tcp_queue_t *tq, @@ -119,4 +122,5 @@ void ajax_button(tcp_queue_t *tq, const char *caption, const char *code, ...); void ajax_transport_build_mapper_state(char *buf, size_t siz, th_transport_t *t, int mapped); + #endif /* AJAXUI_H_ */ diff --git a/ajaxui/ajaxui_config.c b/ajaxui/ajaxui_config.c index 5c27465b..10d06c56 100644 --- a/ajaxui/ajaxui_config.c +++ b/ajaxui/ajaxui_config.c @@ -32,13 +32,15 @@ #define AJAX_CONFIG_TAB_CHANNELS 0 #define AJAX_CONFIG_TAB_DVB 1 #define AJAX_CONFIG_TAB_XMLTV 2 -#define AJAX_CONFIG_TAB_ACCESS 3 -#define AJAX_CONFIG_TABS 4 +#define AJAX_CONFIG_TAB_CWC 3 +#define AJAX_CONFIG_TAB_ACCESS 4 +#define AJAX_CONFIG_TABS 5 const char *ajax_config_tabnames[] = { [AJAX_CONFIG_TAB_CHANNELS] = "Channels & Groups", [AJAX_CONFIG_TAB_DVB] = "DVB adapters", [AJAX_CONFIG_TAB_XMLTV] = "XML-TV", + [AJAX_CONFIG_TAB_CWC] = "Code-word Client", [AJAX_CONFIG_TAB_ACCESS] = "Access control", }; @@ -85,6 +87,8 @@ ajax_config_dispatch(http_connection_t *hc, http_reply_t *hr, return ajax_config_dvb_tab(hc, hr); case AJAX_CONFIG_TAB_XMLTV: return ajax_config_xmltv_tab(hc, hr); + case AJAX_CONFIG_TAB_CWC: + return ajax_config_cwc_tab(hc, hr); case AJAX_CONFIG_TAB_ACCESS: return ajax_config_access_tab(hc, hr); @@ -138,4 +142,5 @@ ajax_config_init(void) ajax_config_dvb_init(); ajax_config_xmltv_init(); ajax_config_access_init(); + ajax_config_cwc_init(); } diff --git a/ajaxui/ajaxui_config_cwc.c b/ajaxui/ajaxui_config_cwc.c new file mode 100644 index 00000000..569f9575 --- /dev/null +++ b/ajaxui/ajaxui_config_cwc.c @@ -0,0 +1,266 @@ +/* + * tvheadend, AJAX / HTML user interface + * Copyright (C) 2008 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 . + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include "tvhead.h" +#include "http.h" +#include "ajaxui.h" +#include "cwc.h" +/** + * CWC configuration + */ +int +ajax_config_cwc_tab(http_connection_t *hc, http_reply_t *hr) +{ + tcp_queue_t *tq = &hr->hr_tq; + + ajax_box_begin(tq, AJAX_BOX_SIDEBOX, NULL, NULL, "Code-word Client"); + + tcp_qprintf(tq, "
"); + + ajax_js(tq, + "new Ajax.Updater('cwclist', '/ajax/cwclist', " + "{method: 'get', evalScripts: true});"); + + tcp_qprintf(tq, "
"); + + tcp_qprintf(tq, + "
" + "
Hostname:
" + "
" + "" + "
"); + + tcp_qprintf(tq, + "
" + "
Port:
" + "
" + "" + "
"); + + + tcp_qprintf(tq, + "
" + "
Username:
" + "
" + "" + "
"); + + tcp_qprintf(tq, + "
" + "
Password:
" + "
" + "" + "
"); + + tcp_qprintf(tq, + "
" + "
DES-key:
" + "
" + "" + "
"); + + tcp_qprintf(tq, + "
" + ""); + + tcp_qprintf(tq, "
\r\n"); + + ajax_box_end(tq, AJAX_BOX_SIDEBOX); + + tcp_qprintf(tq, ""); + http_output_html(hc, hr); + return 0; +} + + +/** + * + */ +static int +ajax_cwclist(http_connection_t *hc, http_reply_t *hr, + const char *remain, void *opaque) +{ + tcp_queue_t *tq = &hr->hr_tq; + ajax_table_t ta; + cwc_t *cwc; + char id[20]; + + ajax_table_top(&ta, hc, tq, + (const char *[]){"Code-word Server", + "Username", + "Enabled", + "Status", + "Crypto", + "", + NULL}, + (int[]){3, 2, 1, 6, 6, 1}); + + TAILQ_FOREACH(cwc, &cwcs, cwc_link) { + snprintf(id, sizeof(id), "cwc_%d", cwc->cwc_id); + ajax_table_row_start(&ta, id); + + ajax_table_cell(&ta, NULL, "%s:%d", + cwc->cwc_tcp_session.tcp_hostname, + cwc->cwc_tcp_session.tcp_port); + + ajax_table_cell(&ta, NULL, "%s", cwc->cwc_username); + + ajax_table_cell(&ta, NULL, + "", + 1 ? "checked " : "", cwc->cwc_id); + + ajax_table_cell(&ta, "status", cwc_status_to_text(cwc)); + + ajax_table_cell(&ta, "crypto", cwc_crypto_to_text(cwc)); + + ajax_table_cell(&ta, NULL, + "" + "Delete...", + cwc->cwc_id, cwc->cwc_tcp_session.tcp_hostname); + } + + ajax_table_bottom(&ta); + http_output_html(hc, hr); + return 0; +} + + +/** + * + */ +static int +ajax_cwcadd(http_connection_t *hc, http_reply_t *hr, + const char *remain, void *opaque) +{ + tcp_queue_t *tq = &hr->hr_tq; + const char *errtxt; + + errtxt = cwc_add(http_arg_get(&hc->hc_req_args, "hostname"), + http_arg_get(&hc->hc_req_args, "port"), + http_arg_get(&hc->hc_req_args, "username"), + http_arg_get(&hc->hc_req_args, "password"), + http_arg_get(&hc->hc_req_args, "deskey"), + "1", 1, 1); + + if(errtxt != NULL) { + tcp_qprintf(tq, "alert('%s');", errtxt); + } else { + + tcp_qprintf(tq, "$('hostname').clear();\r\n"); + tcp_qprintf(tq, "$('port').clear();\r\n"); + tcp_qprintf(tq, "$('username').clear();\r\n"); + tcp_qprintf(tq, "$('password').clear();\r\n"); + tcp_qprintf(tq, "$('deskey').clear();\r\n"); + tcp_qprintf(tq, + "new Ajax.Updater('cwclist', '/ajax/cwclist', " + "{method: 'get', evalScripts: true});"); + } + http_output(hc, hr, "text/javascript; charset=UTF8", NULL, 0); + + return 0; +} + + +/** + * + */ +static int +ajax_cwcdel(http_connection_t *hc, http_reply_t *hr, + const char *remain, void *opaque) +{ + tcp_queue_t *tq = &hr->hr_tq; + const char *txt; + cwc_t *cwc; + + if((txt = http_arg_get(&hc->hc_req_args, "id")) == NULL) + return HTTP_STATUS_BAD_REQUEST; + + if((cwc = cwc_find(atoi(txt))) == NULL) + return HTTP_STATUS_BAD_REQUEST; + + cwc_delete(cwc); + tcp_qprintf(tq, + "new Ajax.Updater('cwclist', '/ajax/cwclist', " + "{method: 'get', evalScripts: true});"); + + http_output(hc, hr, "text/javascript; charset=UTF8", NULL, 0); + return 0; +} + +/** + * + */ +static int +ajax_cwcchange(http_connection_t *hc, http_reply_t *hr, + const char *remain, void *opaque) +{ + // tcp_queue_t *tq = &hr->hr_tq; + const char *txt; + cwc_t *cwc; + + if((txt = http_arg_get(&hc->hc_req_args, "id")) == NULL) + return HTTP_STATUS_BAD_REQUEST; + + if((cwc = cwc_find(atoi(txt))) == NULL) + return HTTP_STATUS_BAD_REQUEST; + + if((txt = http_arg_get(&hc->hc_req_args, "checked")) == NULL) + return HTTP_STATUS_BAD_REQUEST; + + // cwc_set_state(cwc, atoi(txt)); + http_output(hc, hr, "text/javascript; charset=UTF8", NULL, 0); + return 0; +} + + +/** + * + */ +void +ajax_config_cwc_init(void) +{ + http_path_add("/ajax/cwclist" , NULL, ajax_cwclist, + AJAX_ACCESS_CONFIG); + http_path_add("/ajax/cwcadd" , NULL, ajax_cwcadd, + AJAX_ACCESS_CONFIG); + http_path_add("/ajax/cwcdel" , NULL, ajax_cwcdel, + AJAX_ACCESS_CONFIG); + http_path_add("/ajax/cwcchange" , NULL, ajax_cwcchange, + AJAX_ACCESS_CONFIG); +} diff --git a/ajaxui/ajaxui_mailbox.c b/ajaxui/ajaxui_mailbox.c index fa90dbe1..d41ada92 100644 --- a/ajaxui/ajaxui_mailbox.c +++ b/ajaxui/ajaxui_mailbox.c @@ -33,6 +33,7 @@ #include "epg_xmltv.h" #include "dvb_support.h" #include "ajaxui_mailbox.h" +#include "cwc.h" #define MAILBOX_UNUSED_TIMEOUT 15 #define MAILBOX_EMPTY_REPLY_TIMEOUT 10 @@ -485,3 +486,26 @@ ajax_mailbox_transport_status_change(struct th_transport *t) "status", t->tht_identifier, transport_status_to_text(t->tht_last_status)); } + + + + +void +ajax_mailbox_cwc_status_change(struct cwc *cwc) +{ + char id[20]; + snprintf(id, sizeof(id), "cwc_%d", cwc->cwc_id); + + ajax_mailbox_update_div("cwc", "status", id, cwc_status_to_text(cwc)); +} + + + +void +ajax_mailbox_cwc_crypto_change(struct cwc *cwc) +{ + char id[20]; + snprintf(id, sizeof(id), "cwc_%d", cwc->cwc_id); + + ajax_mailbox_update_div("cwc", "crypto", id, cwc_crypto_to_text(cwc)); +} diff --git a/ajaxui/ajaxui_mailbox.h b/ajaxui/ajaxui_mailbox.h index 719c4ad3..ee5983c4 100644 --- a/ajaxui/ajaxui_mailbox.h +++ b/ajaxui/ajaxui_mailbox.h @@ -42,4 +42,9 @@ void ajax_mailbox_xmltv_grabber_status_change(struct xmltv_grabber *xg); struct th_transport; void ajax_mailbox_transport_status_change(struct th_transport *t); +struct cwc; +void ajax_mailbox_cwc_status_change(struct cwc *cwc); + +void ajax_mailbox_cwc_crypto_change(struct cwc *cwc); + #endif /* AJAXUI_MAILBOX_H_ */ diff --git a/cwc.c b/cwc.c index 88edae2d..8018f8fe 100644 --- a/cwc.c +++ b/cwc.c @@ -37,6 +37,9 @@ #include "dispatch.h" #include "transports.h" #include "cwc.h" +#include "notify.h" + +static int cwc_tally; #define CWC_KEEPALIVE_INTERVAL 600 @@ -66,6 +69,8 @@ typedef enum { MSG_KEEPALIVE = CWS_FIRSTCMDNO + 0x1d } net_msg_type_t; +struct cwc_queue cwcs; + extern char *cwc_krypt(const char *key, const char *salt); static LIST_HEAD(, cwc_transport) cwc_pending_requests; @@ -104,45 +109,19 @@ typedef struct cwc_transport { } cwc_transport_t; +/** + * + */ +static void +cwc_set_state(cwc_t *cwc, int newstate, const char *errtxt) +{ + cwc->cwc_state = newstate; + if(newstate == CWC_STATE_IDLE) + cwc->cwc_errtxt = errtxt; - -typedef struct cwc { - tcp_session_t cwc_tcp_session; /* Must be first */ - - LIST_ENTRY(cwc) cwc_link; - - LIST_HEAD(, cwc_transport) cwc_transports; - - enum { - CWC_STATE_IDLE, - CWC_STATE_WAIT_LOGIN_KEY, - CWC_STATE_WAIT_LOGIN_ACK, - CWC_STATE_WAIT_CARD_DATA, - CWC_STATE_RUNNING, - } cwc_state; - - uint16_t cwc_caid; - - uint16_t cwc_seq; - - uint8_t cwc_key[16]; - - uint8_t cwc_buf[256]; - int cwc_bufptr; - - /* From configuration */ - - uint8_t cwc_confedkey[14]; - const char *cwc_username; - const char *cwc_password; /* salted version */ - - dtimer_t cwc_idle_timer; - - dtimer_t cwc_send_ka_timer; -} cwc_t; - -static LIST_HEAD(, cwc) cwcs; + notify_cwc_status_change(cwc); +} @@ -355,7 +334,7 @@ cwc_send_data_req(cwc_t *cwc) { uint8_t buf[CWS_NETMSGSIZE]; - cwc->cwc_state = CWC_STATE_WAIT_CARD_DATA; + cwc_set_state(cwc, CWC_STATE_WAIT_CARD_DATA, NULL); buf[0] = MSG_CARD_DATA_REQ; buf[1] = 0; @@ -407,6 +386,8 @@ cwc_dispatch_card_data_reply(cwc_t *cwc, uint8_t msgtype, cwc->cwc_caid = (msg[4] << 8) | msg[5]; n = psi_caid2name(cwc->cwc_caid) ?: "Unknown"; + notify_cwc_crypto_change(cwc); + syslog(LOG_INFO, "cwc: %s: Connected as user 0x%02x " "to a %s-card [0x%04x : %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x] " "with %d providers", @@ -415,7 +396,7 @@ cwc_dispatch_card_data_reply(cwc_t *cwc, uint8_t msgtype, msg[6], msg[7], msg[8], msg[9], msg[10], msg[11], msg[12], msg[13], msg[14]); - cwc->cwc_state = CWC_STATE_RUNNING; + cwc_set_state(cwc, CWC_STATE_RUNNING, NULL); /* Send KA every CWC_KEEPALIVE_INTERVAL seconds */ @@ -436,7 +417,7 @@ cwc_send_login(cwc_t *cwc) int ul = strlen(cwc->cwc_username) + 1; int pl = strlen(cwc->cwc_password) + 1; - cwc->cwc_state = CWC_STATE_WAIT_LOGIN_ACK; + cwc_set_state(cwc, CWC_STATE_WAIT_LOGIN_ACK, NULL); buf[0] = MSG_CLIENT_2_SERVER_LOGIN; buf[1] = 0; @@ -621,13 +602,13 @@ cwc_tcp_callback(tcpevent_t event, void *tcpsession) case TCP_CONNECT: cwc->cwc_seq = 0; cwc->cwc_bufptr = 0; - cwc->cwc_state = CWC_STATE_WAIT_LOGIN_KEY; + cwc_set_state(cwc, CWC_STATE_WAIT_LOGIN_KEY, NULL); /* We want somthing within 20 seconds */ dtimer_arm(&cwc->cwc_idle_timer, cwc_timeout, cwc, 20); break; case TCP_DISCONNECT: - cwc->cwc_state = CWC_STATE_IDLE; + cwc_set_state(cwc, CWC_STATE_IDLE, "Disconnected"); dtimer_disarm(&cwc->cwc_send_ka_timer); break; @@ -734,21 +715,29 @@ cwc_descramble(th_descrambler_t *td, th_transport_t *t, struct th_stream *st, /** * */ -static void -cwc_transport_stop(th_descrambler_t *td) +static void +cwc_transport_destroy(cwc_transport_t *ct) { - cwc_transport_t *ct = (cwc_transport_t *)td; - if(ct->ct_pending) LIST_REMOVE(ct, ct_link); LIST_REMOVE(ct, ct_cwc_link); - LIST_REMOVE(td, td_transport_link); free_key_struct(ct->ct_keys); free(ct); } +/** + * + */ +static void +cwc_transport_stop(th_descrambler_t *td) +{ + LIST_REMOVE(td, td_transport_link); + + cwc_transport_destroy((cwc_transport_t *)td); +} + /** * Check if our CAID's matches, and if so, link @@ -761,7 +750,7 @@ cwc_transport_start(th_transport_t *t) th_descrambler_t *td; th_stream_t *st; - LIST_FOREACH(cwc, &cwcs, cwc_link) { + TAILQ_FOREACH(cwc, &cwcs, cwc_link) { LIST_FOREACH(st, &t->tht_streams, st_link) if(st->st_caid == cwc->cwc_caid) @@ -785,6 +774,54 @@ cwc_transport_start(th_transport_t *t) } +/** + * + */ +static void +cwc_save(void) +{ + cwc_t *cwc; + + FILE *fp; + char buf[400]; + + snprintf(buf, sizeof(buf), "%s/cwc.cfg", settings_dir); + if((fp = settings_open_for_write(buf)) == NULL) + return; + + TAILQ_FOREACH(cwc, &cwcs, cwc_link) { + + fprintf(fp, "cwc {\n"); + fprintf(fp, "\thostname = %s\n", cwc->cwc_tcp_session.tcp_hostname); + fprintf(fp, "\tport = %d\n", cwc->cwc_tcp_session.tcp_port); + + fprintf(fp, "\tusername = %s\n", cwc->cwc_username); + fprintf(fp, "\tpassword = %s\n", cwc->cwc_password); + fprintf(fp, "\tdeskey = " + "%02x:%02x:%02x:%02x:%02x:%02x:%02x:" + "%02x:%02x:%02x:%02x:%02x:%02x:%02x\n", + 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]); + fprintf(fp, "\tenabled = %d\n", 1); + + + fprintf(fp, "}\n"); + } + fclose(fp); + +} /** * */ @@ -804,80 +841,195 @@ nibble(char c) } + +/** + * + */ +const char * +cwc_add(const char *hostname, const char *porttxt, const char *username, + const char *password, const char *deskey, const char *enabled, + int save, int salt) +{ + const char *k; + cwc_t *cwc; + uint8_t key[14]; + + int i, u, l, port; + + if(hostname == NULL || strlen(hostname) < 1) + return "Invalid hostname"; + + if(porttxt == NULL) + return "Invalid port"; + + port = atoi(porttxt); + if(port < 1 || port > 65535) + return "Invalid port"; + + if(username == NULL || strlen(username) < 1) + return "Invalid username"; + if(password == NULL || strlen(password) < 1) + return "Invalid password"; + + if(deskey == NULL) + return "Invalid DES-key"; + + k = deskey; + for(i = 0; i < 14; i++) { + while(*k != 0 && !isxdigit(*k)) k++; + if(*k == 0) + break; + u = nibble(*k++); + while(*k != 0 && !isxdigit(*k)) k++; + if(*k == 0) + break; + l = nibble(*k++); + key[i] = (u << 4) | l; + } + + if(i != 14) + return "DES-key does not contain 14 valid hexadecimal bytes"; + + if(enabled == NULL) + return "Enabled not set"; + + cwc = tcp_create_client(hostname, port, sizeof(cwc_t), + "cwc", cwc_tcp_callback); + + // cwc->cwc_enabled = atoi(enabled); + cwc->cwc_username = strdup(username); + + if(salt) { + cwc->cwc_password = strdup(cwc_krypt(password, "$1$abcdefgh$")); + } else { + cwc->cwc_password = strdup(password); + } + + cwc->cwc_id = ++cwc_tally; + + memcpy(cwc->cwc_confedkey, key, 14); + TAILQ_INSERT_TAIL(&cwcs, cwc, cwc_link); + + if(save) + cwc_save(); + return NULL; +} + +/** + * + */ +cwc_t * +cwc_find(int id) +{ + cwc_t *cwc; + TAILQ_FOREACH(cwc, &cwcs, cwc_link) + if(cwc->cwc_id == id) + break; + return cwc; +} + + +/** + * + */ +void +cwc_delete(cwc_t *cwc) +{ + cwc_transport_t *ct; + + while((ct = LIST_FIRST(&cwc->cwc_transports)) != NULL) + cwc_transport_destroy(ct); + + TAILQ_REMOVE(&cwcs, cwc, cwc_link); + free((void *)cwc->cwc_password); + free((void *)cwc->cwc_username); + + tcp_destroy_client(&cwc->cwc_tcp_session); + cwc_save(); +} + +#if 0 +/** + * + */ +void +cwc_set_enable(cwc_t *cwc, int enabled) +{ + if(cwc->enabled == enabled) + return; + + cwc->enabled = enabled; + + tcp_client_set_state(&cwc->cwc_tcp_session, enabled); + cwc_save(); +} +#endif + + + /** * */ void cwc_init(void) { - cwc_t *cwc; - void *iterator = NULL, *head; - const char *username; - const char *password; - const char *deskey, *k; - const char *hostname; - int port; - uint8_t key[14]; - int i, u, l; + struct config_head cl; + config_entry_t *ce; + char buf[400]; - while((head = config_iterate_sections(&iterator, "cwc")) != NULL) { + TAILQ_INIT(&cwcs); + + snprintf(buf, sizeof(buf), "%s/cwc.cfg", settings_dir); + + TAILQ_INIT(&cl); + config_read_file0(buf, &cl); - if((hostname = config_get_str_sub(head, "hostname", NULL)) == NULL) { - syslog(LOG_INFO, "cwc: config section missing 'hostname'"); - } - - if((port = atoi(config_get_str_sub(head, "port", "0"))) == 0) { - syslog(LOG_INFO, "cwc: config section missing 'port'"); - } - - if((username = config_get_str_sub(head, "username", NULL)) == NULL) { - syslog(LOG_INFO, "cwc: config section missing 'username'"); - } - - if((password = config_get_str_sub(head, "password", NULL)) == NULL) { - syslog(LOG_INFO, "cwc: config section missing 'password'"); - } - - if((deskey = config_get_str_sub(head, "deskey", NULL)) == NULL) { - syslog(LOG_INFO, "cwc: config section missing 'deskey'"); - } - - - k = deskey; - for(i = 0; i < 14; i++) { - while(*k != 0 && !isxdigit(*k)) k++; - if(*k == 0) - break; - u = nibble(*k++); - while(*k != 0 && !isxdigit(*k)) k++; - if(*k == 0) - break; - l = nibble(*k++); - key[i] = (u << 4) | l; - } - - if(i != 14) { - syslog(LOG_INFO, "cwc: 'deskey' is not 14 valid hexadecimal bytes"); + TAILQ_FOREACH(ce, &cl, ce_link) { + if(ce->ce_type != CFG_SUB || strcasecmp("cwc", ce->ce_key)) continue; - } - - if(hostname == NULL || username == NULL || password == NULL || - deskey == NULL || port == 0) - continue; - - cwc = tcp_create_client(hostname, port, sizeof(cwc_t), - "cwc", cwc_tcp_callback); - - cwc->cwc_username = strdup(username); - - if(!strncmp(password, "$1$", 3)) { - cwc->cwc_password = strdup(password); - } else { - cwc->cwc_password = strdup(cwc_krypt(password, "$1$abcdefgh$")); - } - - memcpy(cwc->cwc_confedkey, key, 14); - LIST_INSERT_HEAD(&cwcs, cwc, cwc_link); - + + cwc_add(config_get_str_sub(&ce->ce_sub, "hostname", NULL), + config_get_str_sub(&ce->ce_sub, "port", NULL), + config_get_str_sub(&ce->ce_sub, "username", NULL), + config_get_str_sub(&ce->ce_sub, "password", NULL), + config_get_str_sub(&ce->ce_sub, "deskey", NULL), + config_get_str_sub(&ce->ce_sub, "enabled", NULL), + 0, 0); } } + +/** + * + */ +static struct strtab cwcstatustab[] = { + { "Waiting for login key", CWC_STATE_WAIT_LOGIN_KEY }, + { "Waiting for login ACK", CWC_STATE_WAIT_LOGIN_ACK }, + { "Waiting for card data", CWC_STATE_WAIT_CARD_DATA }, + { "Running", CWC_STATE_RUNNING }, +}; + +const char * +cwc_status_to_text(struct cwc *cwc) +{ + if(cwc->cwc_state == CWC_STATE_IDLE) + return cwc->cwc_errtxt ?: "Not connected"; + + return val2str(cwc->cwc_state, cwcstatustab) ?: "Invalid status"; +} + +const char * +cwc_crypto_to_text(struct cwc *cwc) +{ + const char *n; + static char buf[40]; + + if(cwc->cwc_state != CWC_STATE_RUNNING) + return "Unknown"; + + n = psi_caid2name(cwc->cwc_caid); + if(n != NULL) + return n; + + snprintf(buf, sizeof(buf), "0x%x", cwc->cwc_caid); + return buf; +} diff --git a/cwc.h b/cwc.h index afcf5ab3..202ed34c 100644 --- a/cwc.h +++ b/cwc.h @@ -19,8 +19,72 @@ #ifndef CWC_H_ #define CWC_H_ +#include "tcp.h" + +TAILQ_HEAD(cwc_queue, cwc); + +extern struct cwc_queue cwcs; + +typedef struct cwc { + tcp_session_t cwc_tcp_session; /* Must be first */ + + TAILQ_ENTRY(cwc) cwc_link; + + LIST_HEAD(, cwc_transport) cwc_transports; + + enum { + CWC_STATE_IDLE, + CWC_STATE_WAIT_LOGIN_KEY, + CWC_STATE_WAIT_LOGIN_ACK, + CWC_STATE_WAIT_CARD_DATA, + CWC_STATE_RUNNING, + CWC_STATE_HOST_ERROR, + CWC_STATE_ACCESS_ERROR, + } cwc_state; + + uint16_t cwc_caid; + + uint16_t cwc_seq; + + uint8_t cwc_key[16]; + + uint8_t cwc_buf[256]; + int cwc_bufptr; + + /* From configuration */ + + uint8_t cwc_confedkey[14]; + const char *cwc_username; + const char *cwc_password; /* salted version */ + + dtimer_t cwc_idle_timer; + + dtimer_t cwc_send_ka_timer; + + int cwc_id; + + const char *cwc_errtxt; + +} cwc_t; + + void cwc_init(void); void cwc_transport_start(th_transport_t *t); +const char *cwc_add(const char *hostname, const char *porttxt, + const char *username, const char *password, + const char *deskey, const char *enabled, + int save, int salt); + +const char *cwc_status_to_text(struct cwc *cwc); + +cwc_t *cwc_find(int id); + +void cwc_delete(cwc_t *cwc); + +void cwc_set_enable(cwc_t *cwc, int enabled); + +const char *cwc_crypto_to_text(struct cwc *cwc); + #endif /* CWC_H_ */ diff --git a/notify.c b/notify.c index 5801c97e..484567d6 100644 --- a/notify.c +++ b/notify.c @@ -85,3 +85,16 @@ notify_transprot_status_change(struct th_transport *t) ajax_mailbox_transport_status_change(t); } + + +void +notify_cwc_status_change(struct cwc *cwc) +{ + ajax_mailbox_cwc_status_change(cwc); +} + +void +notify_cwc_crypto_change(struct cwc *cwc) +{ + ajax_mailbox_cwc_status_change(cwc); +} diff --git a/notify.h b/notify.h index 827d4ac4..82c63f2f 100644 --- a/notify.h +++ b/notify.h @@ -40,4 +40,9 @@ void notify_xmltv_grabber_status_change(struct xmltv_grabber *xg); struct th_transport; void notify_transprot_status_change(struct th_transport *t); +struct cwc; +void notify_cwc_status_change(struct cwc *cwc); + +void notify_cwc_crypto_change(struct cwc *cwc); + #endif /* NOTIFY_H_ */