oscam-2.26.01-11942-802-wit.../oscam-config-reader.c

1695 lines
56 KiB
C

#define MODULE_LOG_PREFIX "config"
#include "globals.h"
#include "module-stat.h"
#include "oscam-aes.h"
#include "oscam-array.h"
#include "oscam-conf.h"
#include "oscam-conf-chk.h"
#include "oscam-conf-mk.h"
#include "oscam-config.h"
#include "oscam-garbage.h"
#include "oscam-lock.h"
#include "oscam-reader.h"
#include "oscam-string.h"
#ifdef MODULE_GBOX
#include "module-gbox.h"
#endif
#ifdef CS_CACHEEX_AIO
#include "module-cacheex.h"
#endif
#define cs_srvr "oscam.server"
extern const struct s_cardreader *cardreaders[];
extern char *RDR_CD_TXT[];
static void reader_label_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
int i, found = 0;
if(!cs_strlen(value))
{ return; }
for(i = 0; i < (int)cs_strlen(value); i++)
{
if(value[i] == ' ')
{
value[i] = '_';
found++;
}
}
if(found)
{ fprintf(stderr, "Configuration reader: corrected label to %s\n", value); }
cs_strncpy(rdr->label, value, sizeof(rdr->label));
return;
}
fprintf_conf(f, token, "%s\n", rdr->label);
}
static void ecmwhitelist_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
if(cs_strlen(value))
chk_ecm_whitelist(value, &rdr->ecm_whitelist);
else
ecm_whitelist_clear(&rdr->ecm_whitelist);
return;
}
value = mk_t_ecm_whitelist(&rdr->ecm_whitelist);
if(cs_strlen(value) > 0 || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%s\n", value); }
free_mk_t(value);
}
static void ecmheaderwhitelist_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
if(cs_strlen(value))
chk_ecm_hdr_whitelist(value, &rdr->ecm_hdr_whitelist);
else
ecm_hdr_whitelist_clear(&rdr->ecm_hdr_whitelist);
return;
}
value = mk_t_ecm_hdr_whitelist(&rdr->ecm_hdr_whitelist);
if(cs_strlen(value) > 0 || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%s\n", value); }
free_mk_t(value);
}
static void protocol_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
if(cs_strlen(value) == 0)
{ return; }
struct protocol_map
{
char *name;
int typ;
} protocols[] =
{
{ "serial", R_SERIAL },
{ "camd35", R_CAMD35 },
{ "cs378x", R_CS378X },
{ "cs357x", R_CAMD35 },
{ "camd33", R_CAMD33 },
{ "gbox", R_GBOX },
{ "cccam", R_CCCAM },
{ "cccam_ext", R_CCCAM },
{ "cccam_mcs", R_CCCAM },
{ "constcw", R_CONSTCW },
{ "radegast", R_RADEGAST },
{ "scam", R_SCAM },
{ "ghttp", R_GHTTP },
{ "newcamd", R_NEWCAMD },
{ "newcamd525", R_NEWCAMD },
{ "newcamd524", R_NEWCAMD },
{ "drecas", R_DRECAS },
{ "emu", R_EMU },
{ NULL, 0 }
}, *p;
int i;
// Parse card readers
for(i = 0; cardreaders[i]; i++)
{
if(streq(value, cardreaders[i]->desc))
{
rdr->crdr = cardreaders[i];
rdr->typ = cardreaders[i]->typ;
return;
}
}
// Parse protocols
for(i = 0, p = &protocols[0]; p->name; p = &protocols[++i])
{
if(streq(p->name, value))
{
rdr->typ = p->typ;
break;
}
}
if(rdr->typ == R_NEWCAMD)
{ rdr->ncd_proto = streq(value, "newcamd524") ? NCD_524 : NCD_525; }
if(!rdr->typ)
{
fprintf(stderr, "ERROR: '%s' is unsupported reader protocol!\n", value);
rdr->enable = 0;
}
return;
}
fprintf_conf(f, token, "%s\n", reader_get_type_desc(rdr, 0));
}
static void device_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
int32_t isphysical = !is_network_reader(rdr);
if(value)
{
int32_t i;
char *ptr, *saveptr1 = NULL;
for(i = 0, ptr = strtok_r(value, ",", &saveptr1); (i < 3) && (ptr); ptr = strtok_r(NULL, ",", &saveptr1), i++)
{
trim(ptr);
switch(i)
{
case 0:
cs_strncpy(rdr->device, ptr, sizeof(rdr->device));
break;
case 1:
rdr->r_port = atoi(ptr);
break;
case 2:
rdr->l_port = atoi(ptr);
break;
}
}
return;
}
fprintf_conf(f, token, "%s", rdr->device); // it should not have \n at the end
if((rdr->r_port || cfg.http_full_cfg) && !isphysical)
{ fprintf(f, ",%d", rdr->r_port); }
if((rdr->l_port || cfg.http_full_cfg) && !isphysical && strncmp(reader_get_type_desc(rdr, 0), "cccam", 5))
{ fprintf(f, ",%d", rdr->l_port); }
fprintf(f, "\n");
}
static void reader_services_fn(const char *token, char *value, void *setting, FILE *f)
{
services_fn(token, value, setting, f);
if(value)
{
struct s_reader *rdr = container_of(setting, struct s_reader, sidtabs);
if(rdr)
{ rdr->changes_since_shareupdate = 1; }
}
}
static void reader_lb_services_fn(const char *token, char *value, void *setting, FILE *f)
{
services_fn(token, value, setting, f);
if(value)
{
struct s_reader *rdr = container_of(setting, struct s_reader, lb_sidtabs);
if(rdr)
{ rdr->changes_since_shareupdate = 1; }
}
}
static void reader_caid_fn(const char *token, char *value, void *setting, FILE *f)
{
check_caidtab_fn(token, value, setting, f);
if(value)
{
struct s_reader *rdr = container_of(setting, struct s_reader, ctab);
if(rdr)
{ rdr->changes_since_shareupdate = 1; }
}
}
#ifdef WITH_CARDREADER
static void boxid_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
rdr->boxid = cs_strlen(value) ? a2i(value, 4) : 0;
return;
}
if(rdr->boxid)
{ fprintf_conf(f, token, "%08X\n", rdr->boxid); }
else if(cfg.http_full_cfg)
{ fprintf_conf(f, token, "\n"); }
}
#endif
#ifdef READER_TONGFANG
static void tongfang3_calibsn_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
rdr->tongfang3_calibsn = strlen(value) ? a2i(value, 4) : 0;
return;
}
if(rdr->tongfang3_calibsn)
fprintf_conf(f, token, "%08X\n", rdr->tongfang3_calibsn);
else if(cfg.http_full_cfg)
{ fprintf_conf(f, token, "\n"); }
}
static void tongfang_boxid_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
rdr->tongfang_boxid = cs_strlen(value) ? a2i(value, 4) : 0;
return;
}
if(rdr->tongfang_boxid)
{ fprintf_conf(f, token, "%08X\n", rdr->tongfang_boxid); }
else if(cfg.http_full_cfg)
{ fprintf_conf(f, token, "\n"); }
}
static void stbid_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
int32_t len = cs_strlen(value);
if(len != 16)
{
rdr->stbid_length = 0;
memset(rdr->stbid, 0, 8);
}
else
{
if(key_atob_l(value, rdr->stbid, len))
{
fprintf(stderr, "reader stbid parse error, %s=%s\n", token, value);
rdr->stbid_length = 0;
memset(rdr->stbid, 0, sizeof(rdr->stbid));
}
else
{
rdr->stbid_length = len/2;
}
}
return;
}
int32_t len = rdr->stbid_length;
if(len > 0)
{
char tmp[len * 2 + 1];
fprintf_conf(f, "stbid", "%s\n", cs_hexdump(0, rdr->stbid, len, tmp, sizeof(tmp)));
}
else if(cfg.http_full_cfg)
{
fprintf_conf(f, "stbid", "\n");
}
}
static void tongfang3_deskey_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
int32_t len = cs_strlen(value);
if(len != 16)
{
rdr->tongfang3_deskey_length = 0;
memset(rdr->tongfang3_deskey, 0, 8);
}
else
{
if(key_atob_l(value, rdr->tongfang3_deskey, len))
{
fprintf(stderr, "reader tongfang3_deskey parse error, %s=%s\n", token, value);
rdr->tongfang3_deskey_length = 0;
memset(rdr->tongfang3_deskey, 0, sizeof(rdr->tongfang3_deskey));
}
else
{
rdr->tongfang3_deskey_length = len/2;
}
}
return;
}
int32_t len = rdr->tongfang3_deskey_length;
if(len > 0)
{
char tmp[len * 2 + 1];
fprintf_conf(f, "tongfang3_deskey", "%s\n", cs_hexdump(0, rdr->tongfang3_deskey, len, tmp, sizeof(tmp)));
}
else if(cfg.http_full_cfg)
{
fprintf_conf(f, "tongfang3_deskey", "\n");
}
}
#endif
#ifdef WITH_CARDREADER
static void cwpkkey_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
int32_t len = strlen(value);
// rdr_log(rdr, "CWPK config key length: %16X", len);
if(len == 0 || len > 32)
{
rdr->cwpk_mod_length = 0;
memset(rdr->cwpk_mod, 0, sizeof(rdr->cwpk_mod));
}
else
{
if(key_atob_l(value, rdr->cwpk_mod, len))
{
fprintf(stderr, "reader cwpkkey parse error, %s=%s\n", token, value);
rdr->cwpk_mod_length = 0;
memset(rdr->cwpk_mod, 0, sizeof(rdr->cwpk_mod));
}
else
{
rdr->cwpk_mod_length = len/2;
}
}
return;
}
int32_t len = rdr->cwpk_mod_length;
if(len > 0)
{
char tmp[len * 2 + 1];
fprintf_conf(f, "cwpkkey", "%s\n", cs_hexdump(0, rdr->cwpk_mod, len, tmp, sizeof(tmp)));
}
else if(cfg.http_full_cfg)
{ fprintf_conf(f, "cwpkkey", "\n"); }
}
static void rsakey_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
int32_t len = cs_strlen(value);
if(len != 128 && len != 240)
{
rdr->rsa_mod_length = 0;
memset(rdr->rsa_mod, 0, 120);
}
else
{
if(key_atob_l(value, rdr->rsa_mod, len))
{
fprintf(stderr, "reader rsakey parse error, %s=%s\n", token, value);
rdr->rsa_mod_length = 0;
memset(rdr->rsa_mod, 0, sizeof(rdr->rsa_mod));
}
else
{
rdr->rsa_mod_length = len/2;
}
}
return;
}
int32_t len = rdr->rsa_mod_length;
if(len > 0)
{
char tmp[len * 2 + 1];
fprintf_conf(f, "rsakey", "%s\n", cs_hexdump(0, rdr->rsa_mod, len, tmp, sizeof(tmp)));
}
else if(cfg.http_full_cfg)
{ fprintf_conf(f, "rsakey", "\n"); }
}
static void deskey_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
int32_t len = cs_strlen(value);
if(((len % 16) != 0) || len == 0 || len > 128*2)
{
rdr->des_key_length = 0;
memset(rdr->des_key, 0, sizeof(rdr->des_key));
}
else
{
if(key_atob_l(value, rdr->des_key, len))
{
fprintf(stderr, "reader 3DES key parse error, %s=%s\n", token, value);
rdr->des_key_length = 0;
memset(rdr->des_key, 0, sizeof(rdr->des_key));
}
else
{
rdr->des_key_length = len/2;
}
}
return;
}
int32_t len = rdr->des_key_length;
if(len > 0)
{
char tmp[len * 2 + 1];
fprintf_conf(f, "deskey", "%s\n", cs_hexdump(0, rdr->des_key, len, tmp, sizeof(tmp)));
}
else if(cfg.http_full_cfg)
{ fprintf_conf(f, "deskey", "\n"); }
}
static void boxkey_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
int32_t len = cs_strlen(value);
if(((len % 8) != 0) || len == 0 || len > 32)
{
rdr->boxkey_length = 0;
memset(rdr->boxkey, 0, sizeof(rdr->boxkey));
}
else
{
if(key_atob_l(value, rdr->boxkey, len))
{
fprintf(stderr, "reader boxkey parse error, %s=%s\n", token, value);
rdr->boxkey_length = 0;
memset(rdr->boxkey, 0, sizeof(rdr->boxkey));
}
else
{
rdr->boxkey_length = len/2;
}
}
return;
}
int32_t len = rdr->boxkey_length;
if(len > 0)
{
char tmp[len * 2 + 1];
fprintf_conf(f, "boxkey", "%s\n", cs_hexdump(0, rdr->boxkey, len, tmp, sizeof(tmp)));
}
else if(cfg.http_full_cfg)
{ fprintf_conf(f, "boxkey", "\n"); }
}
#endif
#if defined(READER_NAGRA) || defined(READER_NAGRA_MERLIN)
static void param_fn(const char *token, char *value, void *setting, long data, FILE *f)
{
uint8_t *var = setting, valid_len = data & 0xFF;
uint8_t *var_len = (var + (data >> 8));
if(value)
{
int32_t len = cs_strlen(value);
if(len != valid_len * 2 || key_atob_l(value, var, len))
{
if(len > 0)
{ fprintf(stderr, "reader %s parse error, %s=%s\n", token, token, value); }
*var_len = 0;
memset(var, 0, valid_len);
}
else
{
*var_len = valid_len; // found and correct
}
return;
}
if(*var_len)
{
char tmp[*var_len * 2 + 1];
fprintf_conf(f, token, "%s\n", cs_hexdump(0, var, *var_len, tmp, sizeof(tmp)));
}
else if(cfg.http_full_cfg)
{ fprintf_conf(f, token, "\n"); }
}
#endif
static void flags_fn(const char *token, char *value, void *setting, long flag, FILE *f)
{
uint32_t *var = setting;
if(value)
{
int i = atoi(value);
if(!i && (*var & flag))
{ *var -= flag; }
if(i)
{ *var |= flag; }
return;
}
if((*var & flag) || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%d\n", (*var & flag) ? 1 : 0); }
}
#ifdef READER_VIDEOGUARD
static void ins7E_fn(const char *token, char *value, void *setting, long var_size, FILE *f)
{
uint8_t *var = setting;
var_size -= 1; // var_size contains sizeof(var) which is [X + 1]
if(value)
{
int32_t len = cs_strlen(value);
if(len != var_size * 2 || key_atob_l(value, var, len))
{
if(len > 0)
{ fprintf(stderr, "reader %s parse error, %s=%s\n", token, token, value); }
memset(var, 0, var_size + 1);
}
else
{
var[var_size] = 1; // found and correct
}
return;
}
if(var[var_size])
{
char tmp[var_size * 2 + 1];
fprintf_conf(f, token, "%s\n", cs_hexdump(0, var, var_size, tmp, sizeof(tmp)));
}
else if(cfg.http_full_cfg)
{ fprintf_conf(f, token, "\n"); }
}
static void ins42_fn(const char *token, char *value, void *setting, long var_size, FILE *f)
{
uint8_t *var = setting;
var_size -= 1; // var_size contains sizeof(var) which is [X + 1]
if(value)
{
int32_t len = cs_strlen(value);
if(len != var_size * 2 || key_atob_l(value, var, len))
{
if(len > 0)
{ fprintf(stderr, "reader %s parse error, %s=%s\n", token, token, value); }
memset(var, 0, var_size + 1);
}
else
{
var[var_size] = 1; // found and correct
}
return;
}
if(var[var_size])
{
char tmp[var_size * 2 + 1];
fprintf_conf(f, token, "%s\n", cs_hexdump(0, var, var_size, tmp, sizeof(tmp)));
}
else if(cfg.http_full_cfg)
{ fprintf_conf(f, token, "\n"); }
}
static void des_and_3des_key_fn(const char *token, char *value, void *setting, FILE *f)
{
uint8_t *var = setting;
if(value)
{
int32_t len = cs_strlen(value);
if(((len != 16) && (len != 32)) || (key_atob_l(value, var, len)))
{
if(len > 0)
{ fprintf(stderr, "reader %s parse error, %s=%s\n", token, token, value); }
memset(var, 0, 17);
}
else
{
var[16] = len/2;
}
return;
}
if(var[16])
{
char tmp[var[16] * 2 + 1];
fprintf_conf(f, token, "%s\n", cs_hexdump(0, var, var[16], tmp, sizeof(tmp)));
}
else if(cfg.http_full_cfg)
{ fprintf_conf(f, token, "\n"); }
}
#endif
#ifdef WITH_CARDREADER
static void atr_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
memset(rdr->atr, 0, sizeof(rdr->atr));
rdr->atrlen = cs_strlen(value);
if(rdr->atrlen)
{
if(rdr->atrlen > (int32_t)sizeof(rdr->atr) * 2)
{ rdr->atrlen = (int32_t)sizeof(rdr->atr) * 2; }
key_atob_l(value, rdr->atr, rdr->atrlen);
}
return;
}
if(rdr->atr[0] || cfg.http_full_cfg)
{
int j;
fprintf_conf(f, token, "%s", ""); // it should not have \n at the end
if(rdr->atr[0])
{
for(j = 0; j < rdr->atrlen / 2; j++)
{
fprintf(f, "%02X", rdr->atr[j]);
}
}
fprintf(f, "\n");
}
}
static void detect_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
int i;
for(i = 0; RDR_CD_TXT[i]; i++)
{
if(!strcmp(value, RDR_CD_TXT[i]))
{
rdr->detect = i;
}
else
{
if(value[0] == '!' && streq(value + 1, RDR_CD_TXT[i]))
{ rdr->detect = i | 0x80; }
}
}
return;
}
fprintf_conf(f, token, "%s%s\n", rdr->detect & 0x80 ? "!" : "", RDR_CD_TXT[rdr->detect & 0x7f]);
}
#endif
void ftab_fn(const char *token, char *value, void *setting, long ftab_type, FILE *f)
{
FTAB *ftab = setting;
if(value)
{
if(cs_strlen(value))
chk_ftab(value, ftab);
else
ftab_clear(ftab);
return;
}
if(ftab_type & FTAB_READER)
{
struct s_reader *rdr = NULL;
if(ftab_type & FTAB_PROVID) { rdr = container_of(setting, struct s_reader, ftab); }
if(ftab_type & FTAB_CHID) { rdr = container_of(setting, struct s_reader, fchid); }
if(ftab_type & FTAB_FBPCAID) { rdr = container_of(setting, struct s_reader, fallback_percaid); }
if(ftab_type & FTAB_LOCALCARDS) { rdr = container_of(setting, struct s_reader, localcards); }
if(ftab_type & FTAB_IGNCHKSMCAID){ rdr = container_of(setting, struct s_reader, disablecrccws_only_for); }
#ifdef WITH_EMU
if(ftab_type & FTAB_EMUAU) { rdr = container_of(setting, struct s_reader, emu_auproviders); }
#endif
#ifdef MODULE_GBOX
if(ftab_type & FTAB_CCCGBXRESHARE){ rdr = container_of(setting, struct s_reader, ccc_gbx_reshare_ident); }
#endif
if(rdr)
{ rdr->changes_since_shareupdate = 1; }
}
value = mk_t_ftab(ftab);
if(cs_strlen(value) > 0 || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%s\n", value); }
free_mk_t(value);
}
#ifdef READER_VIACCESS
static void aeskeys_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
parse_aes_keys(rdr, value);
return;
}
value = mk_t_aeskeys(rdr);
if(cs_strlen(value) > 0 || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%s\n", value); }
free_mk_t(value);
}
#endif
static void emmcache_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
rdr->cachemm = 0;
rdr->rewritemm = 0;
rdr->logemm = 0;
rdr->deviceemm = 0;
if(cs_strlen(value))
{
int i;
char *ptr, *saveptr1 = NULL;
for(i = 0, ptr = strtok_r(value, ",", &saveptr1); (i < 4) && (ptr); ptr = strtok_r(NULL, ",", &saveptr1), i++)
{
switch(i)
{
case 0:
rdr->cachemm = atoi(ptr);
break;
case 1:
rdr->rewritemm = atoi(ptr);
break;
case 2:
rdr->logemm = atoi(ptr);
break;
case 3:
rdr->deviceemm = atoi(ptr);
}
}
if(rdr->rewritemm <= 0)
{
fprintf(stderr, "Setting reader \"emmcache\" to %i,%d,%i,%i instead of %i,%i,%i,%i.",
rdr->cachemm, 1, rdr->logemm, rdr->deviceemm,
rdr->cachemm, rdr->rewritemm, rdr->logemm, rdr->deviceemm);
fprintf(stderr, "Zero or negative number of rewrites is silly\n");
rdr->rewritemm = 1;
}
}
return;
}
if(rdr->cachemm || rdr->logemm || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%d,%d,%d,%d\n", rdr->cachemm, rdr->rewritemm, rdr->logemm,rdr->deviceemm); }
}
static void blockemm_bylen_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
char *ptr, *saveptr1 = NULL, dash;
struct s_emmlen_range *blocklen;
uint32_t num;
if(!cs_strlen(value))
{
ll_destroy_data(&rdr->blockemmbylen);
return;
}
if(!rdr->blockemmbylen)
{ rdr->blockemmbylen = ll_create("blockemmbylen"); }
else
{ ll_clear_data(rdr->blockemmbylen); }
for(ptr = strtok_r(value, ",", &saveptr1); ptr;
ptr = strtok_r(NULL, ",", &saveptr1))
{
if(!cs_malloc(&blocklen, sizeof(*blocklen)))
{ return; }
num = sscanf(ptr, "%hd%c%hd", &blocklen->min, &dash, &blocklen->max);
if(num <= 0)
{
NULLFREE(blocklen);
fprintf(stderr, "blockemm-bylen parse error: %s\n", value);
continue;
}
if(num == 1) // single values: x1, x2, x3, ...
{ blocklen->max = blocklen->min; }
else if(num == 2) // range values with open end: x1-
{ blocklen->max = 0; }
ll_append(rdr->blockemmbylen, blocklen);
}
return;
}
value = mk_t_emmbylen(rdr);
if(cs_strlen(value) > 0 || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%s\n", value); }
free_mk_t(value);
}
static void nano_fn(const char *token, char *value, void *setting, FILE *f)
{
uint16_t *nano = setting;
if(value)
{
*nano = 0;
if(cs_strlen(value) > 0)
{
if(streq(value, "all"))
{
*nano = 0xFFFF;
}
else
{
int32_t i;
char *ptr, *saveptr1 = NULL;
for(ptr = strtok_r(value, ",", &saveptr1); ptr; ptr = strtok_r(NULL, ",", &saveptr1))
{
i = (byte_atob(ptr) % 0x80);
if(i >= 0 && i <= 16)
{ *nano |= (1 << i); }
}
}
}
return;
}
value = mk_t_nano(*nano);
if(cs_strlen(value) > 0 || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%s\n", value); }
free_mk_t(value);
}
static void auprovid_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
rdr->auprovid = 0;
if(cs_strlen(value))
{ rdr->auprovid = a2i(value, 3); }
return;
}
if(rdr->auprovid)
{ fprintf_conf(f, token, "%06X\n", rdr->auprovid); }
else if(cfg.http_full_cfg)
{ fprintf_conf(f, token, "\n"); }
}
static void ratelimitecm_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
rdr->ratelimitecm = 0;
if(cs_strlen(value))
{
int i;
rdr->ratelimitecm = atoi(value);
for(i = 0; i < MAXECMRATELIMIT; i++) // reset all slots
{
rdr->rlecmh[i].srvid = -1;
rdr->rlecmh[i].last.time = -1;
}
}
return;
}
if(rdr->ratelimitecm || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%d\n", rdr->ratelimitecm); }
}
static void ecmunique_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
if(cs_strlen(value) == 0)
{
rdr->ecmunique = 0; // default
}
else
{
rdr->ecmunique = atoi(value);
if(rdr->ecmunique >= 1)
{ rdr->ecmunique = 1; }
else
{ rdr->ecmunique = 0; }
}
return;
}
if((rdr->ratelimitecm && rdr->ecmunique != 0) || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%d\n", rdr->ecmunique); }
}
static void ratelimittime_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
if(cs_strlen(value) == 0)
{
if(rdr->ratelimitecm > 0)
{
rdr->ratelimittime = 9000; // default 9 seconds
rdr->srvidholdtime = 2000; // default 2 seconds hold
}
else
{
rdr->ratelimitecm = 0; // in case someone set a negative value
rdr->ratelimittime = 0;
rdr->srvidholdtime = 0;
}
}
else
{
rdr->ratelimittime = atoi(value);
if (rdr->ratelimittime < 60) rdr->ratelimittime *= 1000;
}
return;
}
if(rdr->ratelimitecm || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%d\n", rdr->ratelimittime); }
}
static void srvidholdtime_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
if(cs_strlen(value) == 0)
{
if(rdr->ratelimitecm > 0)
{
rdr->srvidholdtime = 2000; // default 2 seconds hold
}
else
{
rdr->ratelimitecm = 0; // in case someone set a negative value
rdr->srvidholdtime = 0;
}
}
else
{
rdr->srvidholdtime = atoi(value);
if (rdr->srvidholdtime < 60) rdr->srvidholdtime *=1000;
}
return;
}
if(rdr->ratelimitecm || cfg.http_full_cfg)
{ fprintf_conf(f, token, "%d\n", rdr->srvidholdtime); }
}
static void cooldown_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
if(cs_strlen(value) == 0)
{
rdr->cooldown[0] = 0;
rdr->cooldown[1] = 0;
}
else
{
int32_t i;
char *ptr, *saveptr1 = NULL;
for(i = 0, ptr = strtok_r(value, ",", &saveptr1); (i < 2) && (ptr); ptr = strtok_r(NULL, ",", &saveptr1), i++)
{
rdr->cooldown[i] = atoi(ptr);
}
if(rdr->cooldown[0] <= 0 || rdr->cooldown[1] <= 0)
{
fprintf(stderr, "cooldown must have 2 positive values (x,y) set values %d,%d ! cooldown deactivated\n",
rdr->cooldown[0], rdr->cooldown[1]);
rdr->cooldown[0] = 0;
rdr->cooldown[1] = 0;
}
}
return;
}
if(rdr->cooldown[0] || cfg.http_full_cfg)
{
fprintf_conf(f, token, "%d,%d\n", rdr->cooldown[0], rdr->cooldown[1]);
}
}
static void parallelfactor_fn(const char *token, char *value, void *setting, FILE *f)
{
struct s_reader *rdr = setting;
if(value)
{
if(cs_strlen(value) == 0)
{
rdr->parallelfactor = 1.5; // default
}
else
{
// Parse float manually to avoid locale issues (strtof uses locale decimal separator)
// Accept both '.' and ',' as decimal separator
char tmp[32];
cs_strncpy(tmp, value, sizeof(tmp));
// Find decimal separator (either . or ,)
char *sep = strchr(tmp, '.');
if(!sep) sep = strchr(tmp, ',');
if(sep)
{
*sep = '\0'; // Split at separator
int32_t integer_part = atoi(tmp);
int32_t decimal_part = atoi(sep + 1);
// Calculate decimal value (assuming 1 decimal place)
rdr->parallelfactor = (float)integer_part + (float)decimal_part / 10.0f;
}
else
{
rdr->parallelfactor = (float)atoi(tmp);
}
if(rdr->parallelfactor < 0)
rdr->parallelfactor = 1.5; // default on negative
}
return;
}
// Write config: use comma as decimal separator
if(rdr->parallelfactor != 1.5 || cfg.http_full_cfg)
{
char buf[16];
snprintf(buf, sizeof(buf), "%.1f", rdr->parallelfactor);
char *dot = strchr(buf, '.');
if(dot) *dot = ',';
fprintf_conf(f, token, "%s\n", buf);
}
}
static void cooldowndelay_fn(const char *UNUSED(token), char *value, void *setting, FILE *UNUSED(f))
{
struct s_reader *rdr = setting;
if(value)
{
rdr->cooldown[0] = cs_strlen(value) ? atoi(value) : 0;
}
// This option is *not* written in the config file.
// It is only set by WebIf as convenience
}
static void cooldowntime_fn(const char *UNUSED(token), char *value, void *setting, FILE *UNUSED(f))
{
struct s_reader *rdr = setting;
if(value)
{
if(cs_strlen(value) == 0)
{
rdr->cooldown[0] = 0; // no cooling down time means no cooling set
rdr->cooldown[1] = 0;
}
else
{
rdr->cooldown[1] = atoi(value);
}
return;
}
// This option is *not* written in the config file.
// It is only set by WebIf as convenience
}
void reader_fixups_fn(void *var)
{
struct s_reader *rdr = var;
#ifdef WITH_LB
if(rdr->lb_weight > 1000)
{ rdr->lb_weight = 1000; }
else if(rdr->lb_weight <= 0)
{ rdr->lb_weight = 100; }
#endif
#ifdef CS_CACHEEX_AIO
caidtab2ftab_add(&rdr->cacheex.localgenerated_only_in_caidtab, &rdr->cacheex.lg_only_in_tab);
caidtab_clear(&rdr->cacheex.localgenerated_only_in_caidtab);
caidtab2ftab_add(&rdr->cacheex.localgenerated_only_caidtab, &rdr->cacheex.lg_only_tab);
caidtab_clear(&rdr->cacheex.localgenerated_only_caidtab);
#endif
if(is_cascading_reader(rdr) && (rdr->typ == R_CAMD35 || rdr->typ == R_CS378X))
{
#ifdef CS_CACHEEX
if(rdr && rdr->cacheex.mode>1)
{ rdr->keepalive = 1; } // with cacheex, it is required!
else
#endif
if(rdr->typ == R_CAMD35)
{ rdr->keepalive = 0; } // with NO-cacheex, and UDP, keepalive is not required!
}
// Allocate/reallocate parallel_slots if maxparallel changed
// Two separate arrays: active slots (maxparallel) and pending slots (round(maxparallel * parallelfactor))
if(rdr->maxparallel > 0)
{
// Set default parallelfactor if negative (not configured)
// parallelfactor = 0 is valid (disables pending slots)
if(rdr->parallelfactor < 0)
rdr->parallelfactor = 1.5;
// Calculate pending slots size: round(maxparallel * parallelfactor) without math.h
// parallelfactor = 0 means no pending slots (zapping support disabled)
int32_t pending_size = (int32_t)(rdr->maxparallel * rdr->parallelfactor + 0.5);
// Only allocate if not yet allocated (first time setup)
// Size changes require OSCam restart to take effect
if(!rdr->parallel_slots)
{
if(!cs_malloc(&rdr->parallel_slots, rdr->maxparallel * sizeof(struct s_parallel_slot)))
{ rdr->maxparallel = 0; } // disable on allocation failure
else if(pending_size > 0 && !cs_malloc(&rdr->parallel_slots_prov, pending_size * sizeof(struct s_parallel_slot)))
{
NULLFREE(rdr->parallel_slots); // free first array on second failure
rdr->maxparallel = 0;
}
else
{
cs_lock_create(__func__, &rdr->parallel_lock, "parallel_lock", 5000);
rdr->blocked_services = ll_create("blocked_services");
}
rdr->parallel_full = 0; // reset full flag
}
}
else if(rdr->parallel_slots || rdr->parallel_slots_prov)
{
// maxparallel was disabled, free the slots
NULLFREE(rdr->parallel_slots);
NULLFREE(rdr->parallel_slots_prov);
ll_destroy_data(&rdr->blocked_services);
rdr->parallel_full = 0;
}
}
#define OFS(X) offsetof(struct s_reader, X)
#define SIZEOF(X) sizeof(((struct s_reader *)0)->X)
static const struct config_list reader_opts[] =
{
DEF_OPT_FIXUP_FUNC(reader_fixups_fn),
DEF_OPT_FUNC("label" , 0, reader_label_fn),
#ifdef WEBIF
DEF_OPT_STR("description" , OFS(description), NULL),
#endif
DEF_OPT_INT8("enable" , OFS(enable), 1),
DEF_OPT_FUNC("protocol" , 0, protocol_fn),
DEF_OPT_FUNC("device" , 0, device_fn),
DEF_OPT_UINT8("ipv4force" , OFS(ipv4force), 0),
DEF_OPT_HEX("key" , OFS(ncd_key), SIZEOF(ncd_key)),
DEF_OPT_SSTR("user" , OFS(r_usr), "", SIZEOF(r_usr)),
DEF_OPT_SSTR("password" , OFS(r_pwd), "", SIZEOF(r_pwd)),
DEF_OPT_SSTR("pincode" , OFS(pincode), "none", SIZEOF(pincode)),
#ifdef MODULE_GBOX
DEF_OPT_UINT8("gbox_max_distance" , OFS(gbox_maxdist), DEFAULT_GBOX_MAX_DIST),
DEF_OPT_UINT8("gbox_max_ecm_send" , OFS(gbox_maxecmsend), DEFAULT_GBOX_MAX_ECM_SEND),
DEF_OPT_UINT8("gbox_reshare" , OFS(gbox_reshare), DEFAULT_GBOX_RESHARE),
DEF_OPT_INT8("cccam_reshare" , OFS(gbox_cccam_reshare), -1),
DEF_OPT_UINT8("force_remm" , OFS(gbox_force_remm), 0),
DEF_OPT_FUNC_X("ccc_gbx_reshare_ident" , OFS(ccc_gbx_reshare_ident), ftab_fn, FTAB_READER | FTAB_CCCGBXRESHARE),
DEF_OPT_UINT8("send_offline_cmd" , OFS(send_offline_cmd), 0),
#endif
DEF_OPT_STR("readnano" , OFS(emmfile), NULL),
DEF_OPT_FUNC("services" , OFS(sidtabs), reader_services_fn),
DEF_OPT_FUNC("lb_whitelist_services" , OFS(lb_sidtabs), reader_lb_services_fn),
DEF_OPT_INT32("inactivitytimeout" , OFS(tcp_ito), DEFAULT_INACTIVITYTIMEOUT),
DEF_OPT_INT32("reconnecttimeout" , OFS(tcp_rto), DEFAULT_TCP_RECONNECT_TIMEOUT),
DEF_OPT_INT32("reconnectdelay" , OFS(tcp_reconnect_delay), 60000),
DEF_OPT_INT32("resetcycle" , OFS(resetcycle), 0),
DEF_OPT_INT8("disableserverfilter" , OFS(ncd_disable_server_filt), 0),
DEF_OPT_INT8("connectoninit" , OFS(ncd_connect_on_init), 0),
DEF_OPT_UINT8("keepalive" , OFS(keepalive), 0),
#ifdef WITH_CARDREADER
DEF_OPT_INT8("smargopatch" , OFS(smargopatch), 0),
DEF_OPT_INT8("autospeed" , OFS(autospeed), 1),
DEF_OPT_UINT8("sc8in1_dtrrts_patch" , OFS(sc8in1_dtrrts_patch), 0),
#endif
DEF_OPT_INT8("fallback" , OFS(fallback), 0),
DEF_OPT_FUNC_X("fallback_percaid" , OFS(fallback_percaid), ftab_fn, FTAB_READER | FTAB_FBPCAID),
DEF_OPT_FUNC_X("localcards" , OFS(localcards), ftab_fn, FTAB_READER | FTAB_LOCALCARDS),
DEF_OPT_FUNC_X("disablecrccws_only_for" , OFS(disablecrccws_only_for), ftab_fn, FTAB_READER | FTAB_IGNCHKSMCAID),
#ifdef CS_CACHEEX
DEF_OPT_INT8("cacheex" , OFS(cacheex.mode), 0),
DEF_OPT_INT8("cacheex_maxhop" , OFS(cacheex.maxhop), 0),
#ifdef CS_CACHEEX_AIO
DEF_OPT_INT8("cacheex_maxhop_lg" , OFS(cacheex.maxhop_lg), 0),
#endif
DEF_OPT_FUNC("cacheex_ecm_filter" , OFS(cacheex.filter_caidtab), cacheex_hitvaluetab_fn),
DEF_OPT_UINT8("cacheex_allow_request" , OFS(cacheex.allow_request), 0),
DEF_OPT_UINT8("cacheex_drop_csp" , OFS(cacheex.drop_csp), 0),
DEF_OPT_UINT8("cacheex_allow_filter" , OFS(cacheex.allow_filter), 1),
#ifdef CS_CACHEEX_AIO
DEF_OPT_UINT8("cacheex_allow_maxhop" , OFS(cacheex.allow_maxhop), 0),
#endif
DEF_OPT_UINT8("cacheex_block_fakecws" , OFS(cacheex.block_fakecws), 0),
#ifdef CS_CACHEEX_AIO
DEF_OPT_UINT8("cacheex_cw_check_for_push" , OFS(cacheex.cw_check_for_push), 0),
DEF_OPT_UINT8("cacheex_lg_only_remote_settings", OFS(cacheex.lg_only_remote_settings), 1),
DEF_OPT_UINT8("cacheex_localgenerated_only" , OFS(cacheex.localgenerated_only), 0),
DEF_OPT_FUNC("cacheex_localgenerated_only_caid", OFS(cacheex.localgenerated_only_caidtab), check_caidtab_fn),
DEF_OPT_FUNC_X("cacheex_lg_only_tab" , OFS(cacheex.lg_only_tab), ftab_fn, FTAB_ACCOUNT),
DEF_OPT_UINT8("cacheex_lg_only_in_aio_only" , OFS(cacheex.lg_only_in_aio_only), 0),
DEF_OPT_UINT8("cacheex_localgenerated_only_in", OFS(cacheex.localgenerated_only_in), 0),
DEF_OPT_FUNC("cacheex_localgenerated_only_in_caid", OFS(cacheex.localgenerated_only_in_caidtab), check_caidtab_fn),
DEF_OPT_FUNC_X("cacheex_lg_only_in_tab" , OFS(cacheex.lg_only_in_tab), ftab_fn, FTAB_ACCOUNT),
DEF_OPT_FUNC("cacheex_nopushafter" , OFS(cacheex.cacheex_nopushafter_tab), caidvaluetab_fn),
#endif
#endif
DEF_OPT_FUNC("caid" , OFS(ctab), reader_caid_fn),
#ifdef WITH_CARDREADER
DEF_OPT_FUNC("atr" , 0, atr_fn),
DEF_OPT_FUNC("boxid" , 0, boxid_fn),
#endif
#ifdef READER_TONGFANG
DEF_OPT_FUNC("tongfang3_calibsn" , 0, tongfang3_calibsn_fn),
DEF_OPT_FUNC("tongfang_boxid" , 0, tongfang_boxid_fn),
DEF_OPT_FUNC("stbid" , 0, stbid_fn),
DEF_OPT_FUNC("tongfang3_deskey" , 0, tongfang3_deskey_fn),
#endif
#ifdef WITH_CARDREADER
DEF_OPT_FUNC("boxkey" , 0, boxkey_fn),
DEF_OPT_FUNC("rsakey" , 0, rsakey_fn),
DEF_OPT_FUNC("cwpkkey" , 0, cwpkkey_fn),
DEF_OPT_FUNC("deskey" , 0, deskey_fn),
#endif
#ifdef READER_NAGRA_MERLIN
DEF_OPT_FUNC_X("mod1" , OFS(mod1), param_fn, SIZEOF(mod1) ^ (OFS(mod1_length) - OFS(mod1)) << 8),
DEF_OPT_FUNC_X("idird" , OFS(idird), param_fn, SIZEOF(idird) ^ (OFS(idird_length) - OFS(idird)) << 8),
DEF_OPT_FUNC_X("cmd0eprov" , OFS(cmd0eprov), param_fn, SIZEOF(cmd0eprov) ^ (OFS(cmd0eprov_length) - OFS(cmd0eprov)) << 8),
DEF_OPT_FUNC_X("mod2" , OFS(mod2), param_fn, SIZEOF(mod2) ^ (OFS(mod2_length) - OFS(mod2)) << 8),
DEF_OPT_FUNC_X("key3588" , OFS(key3588), param_fn, SIZEOF(key3588) ^ (OFS(key3588_length) - OFS(key3588)) << 8),
DEF_OPT_FUNC_X("key3460" , OFS(key3460), param_fn, SIZEOF(key3460) ^ (OFS(key3460_length) - OFS(key3460)) << 8),
DEF_OPT_FUNC_X("key3310" , OFS(key3310), param_fn, SIZEOF(key3310) ^ (OFS(key3310_length) - OFS(key3310)) << 8),
DEF_OPT_FUNC_X("data50" , OFS(data50), param_fn, SIZEOF(data50) ^ (OFS(data50_length) - OFS(data50)) << 8),
DEF_OPT_FUNC_X("mod50" , OFS(mod50), param_fn, SIZEOF(mod50) ^ (OFS(mod50_length) - OFS(mod50)) << 8),
DEF_OPT_FUNC_X("nuid" , OFS(nuid), param_fn, SIZEOF(nuid) ^ (OFS(nuid_length) - OFS(nuid)) << 8),
DEF_OPT_FUNC_X("forcepair" , OFS(forcepair), param_fn, SIZEOF(forcepair) ^ (OFS(forcepair_length) - OFS(forcepair)) << 8),
DEF_OPT_FUNC_X("otpcsc" , OFS(otpcsc), param_fn, SIZEOF(otpcsc) ^ (OFS(otpcsc_length) - OFS(otpcsc)) << 8),
DEF_OPT_FUNC_X("otacsc" , OFS(otacsc), param_fn, SIZEOF(otacsc) ^ (OFS(otacsc_length) - OFS(otacsc)) << 8),
DEF_OPT_FUNC_X("cwpkcaid" , OFS(cwpkcaid), param_fn, SIZEOF(cwpkcaid) ^ (OFS(cwpkcaid_length) - OFS(cwpkcaid)) << 8),
DEF_OPT_FUNC_X("cwekey0" , OFS(cwekey[0]), param_fn, SIZEOF(cwekey[0]) ^ (OFS(cwekey_length[0]) - OFS(cwekey[0])) << 8),
DEF_OPT_FUNC_X("cwekey1" , OFS(cwekey[1]), param_fn, SIZEOF(cwekey[1]) ^ (OFS(cwekey_length[1]) - OFS(cwekey[1])) << 8),
DEF_OPT_FUNC_X("cwekey2" , OFS(cwekey[2]), param_fn, SIZEOF(cwekey[2]) ^ (OFS(cwekey_length[2]) - OFS(cwekey[2])) << 8),
DEF_OPT_FUNC_X("cwekey3" , OFS(cwekey[3]), param_fn, SIZEOF(cwekey[3]) ^ (OFS(cwekey_length[3]) - OFS(cwekey[3])) << 8),
DEF_OPT_FUNC_X("cwekey4" , OFS(cwekey[4]), param_fn, SIZEOF(cwekey[4]) ^ (OFS(cwekey_length[4]) - OFS(cwekey[4])) << 8),
DEF_OPT_FUNC_X("cwekey5" , OFS(cwekey[5]), param_fn, SIZEOF(cwekey[5]) ^ (OFS(cwekey_length[5]) - OFS(cwekey[5])) << 8),
DEF_OPT_FUNC_X("cwekey6" , OFS(cwekey[6]), param_fn, SIZEOF(cwekey[6]) ^ (OFS(cwekey_length[6]) - OFS(cwekey[6])) << 8),
DEF_OPT_FUNC_X("cwekey7" , OFS(cwekey[7]), param_fn, SIZEOF(cwekey[7]) ^ (OFS(cwekey_length[7]) - OFS(cwekey[7])) << 8),
DEF_OPT_FUNC_X("cwekey8" , OFS(cwekey[8]), param_fn, SIZEOF(cwekey[8]) ^ (OFS(cwekey_length[8]) - OFS(cwekey[8])) << 8),
DEF_OPT_FUNC_X("cwekey9" , OFS(cwekey[9]), param_fn, SIZEOF(cwekey[9]) ^ (OFS(cwekey_length[9]) - OFS(cwekey[9])) << 8),
DEF_OPT_FUNC_X("cwekey10" , OFS(cwekey[10]), param_fn, SIZEOF(cwekey[10]) ^ (OFS(cwekey_length[10]) - OFS(cwekey[10])) << 8),
DEF_OPT_FUNC_X("cwekey11" , OFS(cwekey[11]), param_fn, SIZEOF(cwekey[11]) ^ (OFS(cwekey_length[11]) - OFS(cwekey[11])) << 8),
DEF_OPT_FUNC_X("cwekey12" , OFS(cwekey[12]), param_fn, SIZEOF(cwekey[12]) ^ (OFS(cwekey_length[12]) - OFS(cwekey[12])) << 8),
DEF_OPT_FUNC_X("cwekey13" , OFS(cwekey[13]), param_fn, SIZEOF(cwekey[13]) ^ (OFS(cwekey_length[13]) - OFS(cwekey[13])) << 8),
DEF_OPT_FUNC_X("cwekey14" , OFS(cwekey[14]), param_fn, SIZEOF(cwekey[14]) ^ (OFS(cwekey_length[14]) - OFS(cwekey[14])) << 8),
DEF_OPT_FUNC_X("cwekey15" , OFS(cwekey[15]), param_fn, SIZEOF(cwekey[15]) ^ (OFS(cwekey_length[15]) - OFS(cwekey[15])) << 8),
DEF_OPT_FUNC_X("cwekey16" , OFS(cwekey[16]), param_fn, SIZEOF(cwekey[16]) ^ (OFS(cwekey_length[16]) - OFS(cwekey[16])) << 8),
DEF_OPT_INT8("forcecwswap" , OFS(forcecwswap), 0),
DEF_OPT_INT8("evensa" , OFS(evensa), 0),
DEF_OPT_INT8("forceemmg" , OFS(forceemmg), 0),
DEF_OPT_INT8("cwpkota" , OFS(cwpkota), 0),
DEF_OPT_INT8("headermode" , OFS(headermode), 1),
DEF_OPT_INT8("cak7_mode" , OFS(cak7_mode), 0),
#endif
#if defined(READER_NAGRA)
DEF_OPT_FUNC_X("cak63nuid" , OFS(cak63nuid), param_fn, SIZEOF(cak63nuid) ^ (OFS(cak63nuid_length) - OFS(cak63nuid)) << 8),
DEF_OPT_FUNC_X("cak63cwekey" , OFS(cak63cwekey), param_fn, SIZEOF(cak63cwekey) ^ (OFS(cak63cwekey_length) - OFS(cak63cwekey)) << 8),
#endif
#if defined(READER_VIDEOGUARD)
DEF_OPT_INT8("ndsversion" , OFS(ndsversion), 0),
DEF_OPT_INT32("card_startdate_basemonth" , OFS(card_startdate_basemonth), 1),
DEF_OPT_INT32("card_startdate_baseyear" , OFS(card_startdate_baseyear), 1997),
DEF_OPT_INT32("card_expiredate_basemonth" , OFS(card_expiredate_basemonth), 1),
DEF_OPT_INT32("card_expiredate_baseyear" , OFS(card_expiredate_baseyear), 1997),
DEF_OPT_FUNC_X("ins7e" , OFS(ins7E), ins7E_fn, SIZEOF(ins7E)),
DEF_OPT_FUNC_X("ins42" , OFS(ins42), ins42_fn, SIZEOF(ins42)),
DEF_OPT_FUNC_X("ins7e11" , OFS(ins7E11), ins7E_fn, SIZEOF(ins7E11)),
DEF_OPT_FUNC_X("ins2e06" , OFS(ins2e06), ins7E_fn, SIZEOF(ins2e06)),
DEF_OPT_FUNC("k1_generic" , OFS(k1_generic), des_and_3des_key_fn),
DEF_OPT_FUNC("k1_unique" , OFS(k1_unique), des_and_3des_key_fn),
DEF_OPT_INT8("fix07" , OFS(fix_07), 1),
DEF_OPT_INT8("fix9993" , OFS(fix_9993), 0),
DEF_OPT_INT8("readtiers" , OFS(readtiers), 1),
#endif
#ifdef READER_IRDETO
DEF_OPT_INT8("force_irdeto" , OFS(force_irdeto), 0),
#endif
#ifdef READER_CRYPTOWORKS
DEF_OPT_INT8("needsglobalfirst" , OFS(needsglobalfirst), 0),
#endif
DEF_OPT_UINT32("ecmnotfoundlimit" , OFS(ecmnotfoundlimit), 0),
DEF_OPT_FUNC("ecmwhitelist" , 0, ecmwhitelist_fn),
DEF_OPT_FUNC("ecmheaderwhitelist" , 0, ecmheaderwhitelist_fn),
#ifdef WITH_CARDREADER
DEF_OPT_FUNC("detect" , 0, detect_fn),
#ifdef READER_NAGRA
DEF_OPT_INT8("nagra_read" , OFS(nagra_read), 0),
DEF_OPT_INT8("detect_seca_nagra_tunneled_card", OFS(detect_seca_nagra_tunneled_card), 1),
#endif
DEF_OPT_INT32("mhz" , OFS(mhz), 357),
DEF_OPT_INT32("cardmhz" , OFS(cardmhz), 357),
#endif
#ifdef WITH_AZBOX
DEF_OPT_INT32("mode" , OFS(azbox_mode), -1),
#endif
DEF_OPT_FUNC_X("ident" , OFS(ftab), ftab_fn, FTAB_READER | FTAB_PROVID),
DEF_OPT_FUNC_X("chid" , OFS(fchid), ftab_fn, FTAB_READER | FTAB_CHID),
DEF_OPT_FUNC("class" , OFS(cltab), class_fn),
#ifdef READER_VIACCESS
DEF_OPT_FUNC("aeskeys" , 0, aeskeys_fn),
#endif
DEF_OPT_FUNC("group" , OFS(grp), group_fn),
DEF_OPT_FUNC("emmcache" , 0, emmcache_fn),
DEF_OPT_FUNC_X("blockemm-unknown" , OFS(blockemm), flags_fn, EMM_UNKNOWN),
DEF_OPT_FUNC_X("blockemm-u" , OFS(blockemm), flags_fn, EMM_UNIQUE),
DEF_OPT_FUNC_X("blockemm-s" , OFS(blockemm), flags_fn, EMM_SHARED),
DEF_OPT_FUNC_X("blockemm-g" , OFS(blockemm), flags_fn, EMM_GLOBAL),
DEF_OPT_FUNC_X("saveemm-unknown" , OFS(saveemm), flags_fn, EMM_UNKNOWN),
DEF_OPT_FUNC_X("saveemm-u" , OFS(saveemm), flags_fn, EMM_UNIQUE),
DEF_OPT_FUNC_X("saveemm-s" , OFS(saveemm), flags_fn, EMM_SHARED),
DEF_OPT_FUNC_X("saveemm-g" , OFS(saveemm), flags_fn, EMM_GLOBAL),
DEF_OPT_FUNC("blockemm-bylen" , 0, blockemm_bylen_fn),
#ifdef WITH_LB
DEF_OPT_INT32("lb_weight" , OFS(lb_weight), 100),
DEF_OPT_INT8("lb_force_fallback" , OFS(lb_force_fallback), 0),
#endif
DEF_OPT_FUNC("savenano" , OFS(s_nano), nano_fn),
DEF_OPT_FUNC("blocknano" , OFS(b_nano), nano_fn),
DEF_OPT_INT8("dropbadcws" , OFS(dropbadcws), 0),
DEF_OPT_INT8("disablecrccws" , OFS(disablecrccws), 0),
#ifdef WITH_CARDREADER
DEF_OPT_INT32("use_gpio" , OFS(use_gpio), 0),
#endif
#ifdef MODULE_PANDORA
DEF_OPT_UINT8("pand_send_ecm" , OFS(pand_send_ecm), 0),
#endif
#ifdef MODULE_CCCAM
DEF_OPT_SSTR("cccversion" , OFS(cc_version), "", SIZEOF(cc_version)),
DEF_OPT_INT8("cccmaxhops" , OFS(cc_maxhops), DEFAULT_CC_MAXHOPS),
DEF_OPT_INT8("cccmindown" , OFS(cc_mindown), 0),
DEF_OPT_INT8("cccwantemu" , OFS(cc_want_emu), 0),
DEF_OPT_INT8("ccckeepalive" , OFS(cc_keepalive), DEFAULT_CC_KEEPALIVE),
DEF_OPT_INT8("cccreshare" , OFS(cc_reshare), DEFAULT_CC_RESHARE),
DEF_OPT_INT32("cccreconnect" , OFS(cc_reconnect), DEFAULT_CC_RECONNECT),
DEF_OPT_INT8("ccchop" , OFS(cc_hop), 0),
#endif
#ifdef MODULE_GHTTP
DEF_OPT_UINT8("use_ssl" , OFS(ghttp_use_ssl), 0),
#endif
#if defined(READER_DRE) || defined(READER_DRECAS)
DEF_OPT_HEX("force_ua" , OFS(force_ua), 4),
DEF_OPT_STR("exec_cmd_file" , OFS(userscript), NULL),
#endif
#ifdef READER_DRECAS
DEF_OPT_STR("stmkeys" , OFS(stmkeys), NULL),
#endif
#ifdef WITH_EMU
DEF_OPT_FUNC_X("emu_auproviders" , OFS(emu_auproviders), ftab_fn, FTAB_READER | FTAB_EMUAU),
DEF_OPT_INT8("emu_datecodedenabled" , OFS(emu_datecodedenabled), 0),
#endif
DEF_OPT_INT8("resetalways" , OFS(resetalways), 0),
#ifdef WITH_CARDREADER
DEF_OPT_INT8("deprecated" , OFS(deprecated), 0),
#endif
DEF_OPT_INT8("audisabled" , OFS(audisabled), 0),
DEF_OPT_FUNC("auprovid" , 0, auprovid_fn),
DEF_OPT_FUNC("ratelimitecm" , 0, ratelimitecm_fn),
DEF_OPT_FUNC("ecmunique" , 0, ecmunique_fn),
DEF_OPT_FUNC("ratelimittime" , 0, ratelimittime_fn),
DEF_OPT_FUNC("srvidholdtime" , 0, srvidholdtime_fn),
DEF_OPT_FUNC("cooldown" , 0, cooldown_fn),
DEF_OPT_INT32("maxparallel" , OFS(maxparallel), 0),
DEF_OPT_FUNC("parallelfactor" , 0, parallelfactor_fn),
DEF_OPT_INT32("paralleltimeout" , OFS(paralleltimeout), 1000),
DEF_OPT_FUNC("cooldowndelay" , 0, cooldowndelay_fn),
DEF_OPT_FUNC("cooldowntime" , 0, cooldowntime_fn),
#ifdef READER_VIACCESS
DEF_OPT_UINT8("read_old_classes" , OFS(read_old_classes), 1),
#endif
DEF_LAST_OPT
};
static inline bool in_list(const char *token, const char *list[])
{
int i;
for(i = 0; list[i]; i++)
{
if(streq(token, list[i]))
{ return true; }
}
return false;
}
static bool reader_check_setting(const struct config_list *UNUSED(clist), void *config_data, const char *setting)
{
struct s_reader *reader = config_data;
// These are written only when the reader is physical reader
static const char *hw_only_settings[] =
{
"readnano", "resetcycle", "smargopatch", "autospeed", "sc8in1_dtrrts_patch", "boxid","fix07",
"fix9993", "rsakey", "deskey", "card_startdate_basemonth", "card_startdate_baseyear", "card_expiredate_basemonth", "card_expiredate_baseyear", "ins7e", "ins42", "ins7e11", "ins2e06", "k1_generic", "k1_unique", "force_irdeto", "boxkey",
"atr", "detect", "nagra_read", "mhz", "cardmhz", "readtiers", "read_old_classes", "use_gpio", "needsglobalfirst",
#ifdef READER_NAGRA_MERLIN
"mod1", "idird", "cmd0eprov", "mod2", "key3588", "key3460", "key3310", "data50", "mod50", "nuid", "forcepair", "otpcsc", "otacsc", "cwpkcaid", "headermode", "cwekey0", "cwekey1", "cwekey2", "cwekey3", "cwekey4", "cwekey5", "cwekey6", "cwekey7", "cwekey8", "cwekey9", "cwekey10", "cwekey11", "cwekey12", "cwekey13", "cwekey14", "cwekey15", "cwekey16",
#endif
#if defined(READER_NAGRA)
"cak63nuid", "cak63cwekey",
#endif
#if defined(READER_DRE) || defined(READER_DRECAS)
"exec_cmd_file",
#endif
#ifdef READER_CONAX
"cwpkkey",
#endif
#if defined(READER_TONGFANG)
"tongfang3_deskey", "tongfang3_calibsn", "stbid", "tongfang_boxid",
#endif
#ifdef WITH_AZBOX
"mode",
#endif
"resetalways", "deprecated", "ndsversion",
0
};
// These are written only when the reader is network reader
static const char *network_only_settings[] =
{
"user", "inactivitytimeout", "reconnecttimeout",
0
};
if(is_network_reader(reader))
{
if(in_list(setting, hw_only_settings))
{ return false; }
}
else
{
if(in_list(setting, network_only_settings))
{ return false; }
}
// These are not written in the config file
static const char *deprecated_settings[] =
{
"cooldowndelay", "cooldowntime",
0
};
if(in_list(setting, deprecated_settings))
{ return false; }
// Special settings for NEWCAMD
static const char *newcamd_settings[] =
{
"disableserverfilter", "connectoninit",
0
};
if(reader->typ != R_NEWCAMD && in_list(setting, newcamd_settings))
{ return false; }
#ifdef MODULE_CCCAM
// These are written only when the reader is CCCAM
static const char *cccam_settings[] =
{
"cccversion", "cccmaxhops", "cccmindown", "cccwantemu", "ccckeepalive",
"cccreconnect",
0
};
// Special settings for CCCAM
if(reader->typ != R_CCCAM)
{
if(in_list(setting, cccam_settings))
{ return false; }
}
else if(streq(setting, "ccchop"))
{
return false;
}
#endif
#ifdef MODULE_PANDORA
// Special settings for PANDORA
if(reader->typ != R_PANDORA && streq(setting, "pand_send_ecm"))
{ return false; }
#endif
#ifdef MODULE_GBOX
// These are written only when the reader is GBOX
static const char *gbox_settings[] =
{
"gbox_max_distance", "gbox_max_ecm_send", "gbox_reshare", "cccam_reshare", "force_remm","ccc_gbx_reshare_ident","send_offline_cmd",
0
};
if(reader->typ != R_GBOX)
{
if(in_list(setting, gbox_settings))
{ return false; }
}
#endif
return true; // Write the setting
}
void chk_reader(char *token, char *value, struct s_reader *rdr)
{
if(config_list_parse(reader_opts, token, value, rdr))
{ return; }
else if(token[0] != '#')
{ fprintf(stderr, "Warning: keyword '%s' in reader section not recognized\n", token); }
}
void reader_set_defaults(struct s_reader *rdr)
{
config_list_set_defaults(reader_opts, rdr);
}
int32_t init_readerdb(void)
{
configured_readers = ll_create("configured_readers");
FILE *fp = open_config_file(cs_srvr);
if(!fp)
{ return 1; }
int32_t tag = 0;
char *value, *token;
if(!cs_malloc(&token, MAXLINESIZE))
{ return 1; }
struct s_reader *rdr;
if(!cs_malloc(&rdr, sizeof(struct s_reader)))
{
NULLFREE(token);
return 1;
}
ll_append(configured_readers, rdr);
while(fgets(token, MAXLINESIZE, fp))
{
int32_t l;
if((l = cs_strlen(trim(token))) < 3)
{ continue; }
if((token[0] == '[') && (token[l - 1] == ']'))
{
token[l - 1] = 0;
tag = (!strcmp("reader", strtolower(token + 1)));
if(rdr->label[0] && rdr->typ)
{
struct s_reader *newreader;
if(cs_malloc(&newreader, sizeof(struct s_reader)))
{
ll_append(configured_readers, newreader);
rdr = newreader;
}
}
reader_set_defaults(rdr);
continue;
}
if(!tag)
{ continue; }
if(!(value = strchr(token, '=')))
{ continue; }
*value++ = '\0';
chk_reader(trim(strtolower(token)), trim(value), rdr);
}
NULLFREE(token);
LL_ITER itr = ll_iter_create(configured_readers);
while((rdr = ll_iter_next(&itr))) // build active readers list
{
reader_fixups_fn(rdr);
module_reader_set(rdr);
}
fclose(fp);
return (0);
}
void free_reader(struct s_reader *rdr)
{
NULLFREE(rdr->emmfile);
if(rdr->parallel_slots || rdr->parallel_slots_prov)
{ cs_lock_destroy(__func__, &rdr->parallel_lock); }
NULLFREE(rdr->parallel_slots);
NULLFREE(rdr->parallel_slots_prov);
ll_destroy_data(&rdr->blocked_services);
ecm_whitelist_clear(&rdr->ecm_whitelist);
ecm_hdr_whitelist_clear(&rdr->ecm_hdr_whitelist);
ftab_clear(&rdr->fallback_percaid);
ftab_clear(&rdr->localcards);
ftab_clear(&rdr->fchid);
ftab_clear(&rdr->ftab);
ftab_clear(&rdr->disablecrccws_only_for);
#ifdef MODULE_GBOX
ftab_clear(&rdr->ccc_gbx_reshare_ident);
#endif
NULLFREE(rdr->cltab.aclass);
NULLFREE(rdr->cltab.bclass);
caidtab_clear(&rdr->ctab);
#ifdef CS_CACHEEX
cecspvaluetab_clear(&rdr->cacheex.filter_caidtab);
#ifdef CS_CACHEEX_AIO
caidtab_clear(&rdr->cacheex.localgenerated_only_caidtab);
caidtab_clear(&rdr->cacheex.localgenerated_only_in_caidtab);
ftab_clear(&rdr->cacheex.lg_only_tab);
ftab_clear(&rdr->cacheex.lg_only_in_tab);
caidvaluetab_clear(&rdr->cacheex.cacheex_nopushafter_tab);
#endif
#endif
lb_destroy_stats(rdr);
cs_clear_entitlement(rdr);
ll_destroy(&rdr->ll_entitlements);
if(rdr->csystem && rdr->csystem->card_done)
rdr->csystem->card_done(rdr);
NULLFREE(rdr->csystem_data);
ll_destroy_data(&rdr->blockemmbylen);
ll_destroy_data(&rdr->emmstat);
#ifdef READER_VIACCESS
aes_clear_entries(&rdr->aes_list);
#endif
config_list_gc_values(reader_opts, rdr);
add_garbage(rdr);
}
int32_t free_readerdb(void)
{
int count = 0;
struct s_reader *rdr;
LL_ITER itr = ll_iter_create(configured_readers);
while((rdr = ll_iter_next(&itr)))
{
free_reader(rdr);
count++;
}
cs_log("readerdb %d readers freed", count);
ll_destroy(&configured_readers);
return count;
}
int32_t write_server(void)
{
FILE *f = create_config_file(cs_srvr);
if(!f)
{ return 1; }
struct s_reader *rdr;
LL_ITER itr = ll_iter_create(configured_readers);
while((rdr = ll_iter_next(&itr)))
{
if(rdr->label[0])
{
fprintf(f, "[reader]\n");
config_list_apply_fixups(reader_opts, rdr);
config_list_save_ex(f, reader_opts, rdr, cfg.http_full_cfg, reader_check_setting);
fprintf(f, "\n");
}
}
return flush_config_file(f, cs_srvr);
}
void reload_readerdb(void)
{
struct s_reader *rdr;
LL_ITER itr = ll_iter_create(configured_readers);
while((rdr = ll_iter_next(&itr)))
{
// disable the current reader
rdr->enable = 0;
restart_cardreader(rdr,1);
}
free_readerdb(); // release the old readerdb
init_readerdb(); // reload the new readerdb
init_cardreader(); // start the readers
}