diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 diff --git a/.gitmodules b/.gitmodules old mode 100755 new mode 100644 diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100755 new mode 100644 index 86a08f6..c27d1cb --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -517,7 +517,6 @@ if (CONFIG_STREAMRELAY MATCHES "Y" AND NOT MODULE_STREAMRELAY EQUAL 1) set (LIBDVBCSA_LIBRARY "${LIBDVBCSADIR}/libdvbcsa.so") else (LIBDVBCSADIR) check_include_file ("dvbcsa/dvbcsa.h" FOUND_LIBDVBCSA) - set(HAVE_LIBDVBCSA ${FOUND_LIBDVBCSA}) find_library (LIBADVBCSA_LIBRARY NAMES libdvbcsa.a) find_library (LIBDVBCSA_LIBRARY NAMES dvbcsa) endif (LIBDVBCSADIR) @@ -547,22 +546,6 @@ endif (CONFIG_STREAMRELAY MATCHES "Y" AND NOT MODULE_STREAMRELAY EQUAL 1) # Manage config.h based on command line parameters # Manipulate config file based on given parameters and read unset parameters -execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_EMU OUTPUT_VARIABLE CONFIG_WITH_EMU OUTPUT_STRIP_TRAILING_WHITESPACE) -if (CONFIG_WITH_EMU MATCHES "Y" AND NOT WITH_EMU EQUAL 1) - add_definitions ("-DWITH_EMU") - set (WITH_EMU "1") - message (STATUS " EMU is added by config compiling with EMU") -endif (CONFIG_WITH_EMU MATCHES "Y" AND NOT WITH_EMU EQUAL 1) - -if (WITH_EMU) - execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --enabled WITH_SOFTCAM OUTPUT_VARIABLE CONFIG_WITH_SOFTCAM OUTPUT_STRIP_TRAILING_WHITESPACE) - if (CONFIG_WITH_SOFTCAM MATCHES "Y" AND NOT WITH_SOFTCAM EQUAL 1) - add_definitions ("-DWITH_SOFTCAM") - set (WITH_SOFTCAM "1") - message (STATUS " SOFTCAM is added by config linking SoftCam.Key") - endif (CONFIG_WITH_SOFTCAM MATCHES "Y" AND NOT WITH_SOFTCAM EQUAL 1) -endif (WITH_EMU) - execute_process (COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/config.sh --show-valid OUTPUT_VARIABLE config_vars_string OUTPUT_STRIP_TRAILING_WHITESPACE) string(REGEX MATCHALL "[A-Z0-9_]+" config_vars ${config_vars_string}) @@ -905,24 +888,6 @@ endif( HAVE_LIBRT OR HAVE_LIBRT_STATIC) #-------------------------------------------------------------------------------- -if (NOT OSCamOperatingSystem MATCHES "Mac OS X") -if (NOT DEFINED ENV{ANDROID_NDK}) -if (NOT DEFINED ENV{ANDROID_STANDALONE_TOOLCHAIN}) - if (WITH_SOFTCAM) - if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) - execute_process (COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key ${CMAKE_CURRENT_BINARY_DIR}/SoftCam.Key) - else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) - execute_process (COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/SoftCam.Key) - endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/SoftCam.Key) - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--format=binary -Wl,SoftCam.Key -Wl,--format=default") - if (NOT OSCamOperatingSystem MATCHES "Windows/Cygwin") - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,noexecstack") - endif (NOT OSCamOperatingSystem MATCHES "Windows/Cygwin") - endif (WITH_SOFTCAM) -endif (NOT DEFINED ENV{ANDROID_STANDALONE_TOOLCHAIN}) -endif (NOT DEFINED ENV{ANDROID_NDK}) -endif (NOT OSCamOperatingSystem MATCHES "Mac OS X") - #----------------------- installation ----------------------------- file (GLOB config_files "${CMAKE_CURRENT_SOURCE_DIR}/Distribution/oscam.*") @@ -1035,11 +1000,4 @@ if (HAVE_LIBDVBCSA) endif(STATICLIBDVBCSA AND NOT LIBDVBCSADIR) endif (HAVE_LIBDVBCSA) -if (WITH_EMU) - message (STATUS " Compile with EMU support") - if (WITH_SOFTCAM) - message (STATUS " SoftCam.Key will be linked as well") - endif (WITH_SOFTCAM) -endif (WITH_EMU) - message (STATUS "") diff --git a/CODING.RULES.txt b/CODING.RULES.txt old mode 100755 new mode 100644 diff --git a/COPYING b/COPYING old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.ac b/Distribution/doc/example/oscam.ac old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.cacheex b/Distribution/doc/example/oscam.cacheex old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.cert b/Distribution/doc/example/oscam.cert old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.conf b/Distribution/doc/example/oscam.conf old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.dvbapi b/Distribution/doc/example/oscam.dvbapi old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.guess b/Distribution/doc/example/oscam.guess old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.ird b/Distribution/doc/example/oscam.ird old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.provid b/Distribution/doc/example/oscam.provid old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.server b/Distribution/doc/example/oscam.server old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.services b/Distribution/doc/example/oscam.services old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.srvid b/Distribution/doc/example/oscam.srvid old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.tiers b/Distribution/doc/example/oscam.tiers old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.user b/Distribution/doc/example/oscam.user old mode 100755 new mode 100644 diff --git a/Distribution/doc/example/oscam.whitelist b/Distribution/doc/example/oscam.whitelist old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/list_smargo.1.html b/Distribution/doc/html/list_smargo.1.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.1.html b/Distribution/doc/html/oscam.1.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.ac.5.html b/Distribution/doc/html/oscam.ac.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.cacheex.5.html b/Distribution/doc/html/oscam.cacheex.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.cert.5.html b/Distribution/doc/html/oscam.cert.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.conf.5.html b/Distribution/doc/html/oscam.conf.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.dvbapi.5.html b/Distribution/doc/html/oscam.dvbapi.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.guess.5.html b/Distribution/doc/html/oscam.guess.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.ird.5.html b/Distribution/doc/html/oscam.ird.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.provid.5.html b/Distribution/doc/html/oscam.provid.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.ratelimit.5.html b/Distribution/doc/html/oscam.ratelimit.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.server.5.html b/Distribution/doc/html/oscam.server.5.html old mode 100755 new mode 100644 index 48fc01d..3fb60c5 --- a/Distribution/doc/html/oscam.server.5.html +++ b/Distribution/doc/html/oscam.server.5.html @@ -840,13 +840,6 @@ set reader's CCcam reshare hop, default:0

-cccwantemu = 0|1 -

-1 = request to provide emu from CCCam server, too, default:0 -
- -

- ccckeepalive = 0|1

1 = send keepalive messages to keep connection to remote CCCam server up, default:0 diff --git a/Distribution/doc/html/oscam.services.5.html b/Distribution/doc/html/oscam.services.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.srvid.5.html b/Distribution/doc/html/oscam.srvid.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.srvid2.5.html b/Distribution/doc/html/oscam.srvid2.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.tiers.5.html b/Distribution/doc/html/oscam.tiers.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.user.5.html b/Distribution/doc/html/oscam.user.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/html/oscam.whitelist.5.html b/Distribution/doc/html/oscam.whitelist.5.html old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/list_smargo.1 b/Distribution/doc/man/list_smargo.1 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.1 b/Distribution/doc/man/oscam.1 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.ac.5 b/Distribution/doc/man/oscam.ac.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.cacheex.5 b/Distribution/doc/man/oscam.cacheex.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.cert.5 b/Distribution/doc/man/oscam.cert.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.conf.5 b/Distribution/doc/man/oscam.conf.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.dvbapi.5 b/Distribution/doc/man/oscam.dvbapi.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.guess.5 b/Distribution/doc/man/oscam.guess.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.ird.5 b/Distribution/doc/man/oscam.ird.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.provid.5 b/Distribution/doc/man/oscam.provid.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.ratelimit.5 b/Distribution/doc/man/oscam.ratelimit.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.server.5 b/Distribution/doc/man/oscam.server.5 old mode 100755 new mode 100644 index 3a94758..4b5db81 --- a/Distribution/doc/man/oscam.server.5 +++ b/Distribution/doc/man/oscam.server.5 @@ -649,11 +649,6 @@ set reader's CCcam reshare hop, default:0 \fBx\fP = resharing for direct peer and share level x .RE .PP -\fBcccwantemu\fP = \fB0\fP|\fB1\fP -.RS 3n -1 = request to provide emu from CCCam server, too, default:0 -.RE -.PP \fBccckeepalive\fP = \fB0\fP|\fB1\fP .RS 3n 1 = send keepalive messages to keep connection to remote CCCam server up, default:0 diff --git a/Distribution/doc/man/oscam.services.5 b/Distribution/doc/man/oscam.services.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.srvid.5 b/Distribution/doc/man/oscam.srvid.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.srvid2.5 b/Distribution/doc/man/oscam.srvid2.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.tiers.5 b/Distribution/doc/man/oscam.tiers.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.user.5 b/Distribution/doc/man/oscam.user.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/man/oscam.whitelist.5 b/Distribution/doc/man/oscam.whitelist.5 old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/list_smargo.txt b/Distribution/doc/txt/list_smargo.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.ac.txt b/Distribution/doc/txt/oscam.ac.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.cacheex.txt b/Distribution/doc/txt/oscam.cacheex.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.cert.txt b/Distribution/doc/txt/oscam.cert.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.conf.txt b/Distribution/doc/txt/oscam.conf.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.dvbapi.txt b/Distribution/doc/txt/oscam.dvbapi.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.guess.txt b/Distribution/doc/txt/oscam.guess.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.ird.txt b/Distribution/doc/txt/oscam.ird.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.provid.txt b/Distribution/doc/txt/oscam.provid.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.ratelimit.txt b/Distribution/doc/txt/oscam.ratelimit.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.server.txt b/Distribution/doc/txt/oscam.server.txt old mode 100755 new mode 100644 index 837835a..b4c2b21 --- a/Distribution/doc/txt/oscam.server.txt +++ b/Distribution/doc/txt/oscam.server.txt @@ -509,9 +509,6 @@ DESCRIPTIONS 0 = resharing for direct peer only x = resharing for direct peer and share level x - cccwantemu = 0|1 - 1 = request to provide emu from CCCam server, too, default:0 - ccckeepalive = 0|1 1 = send keepalive messages to keep connection to remote CCCam server up, default:0 diff --git a/Distribution/doc/txt/oscam.services.txt b/Distribution/doc/txt/oscam.services.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.srvid.txt b/Distribution/doc/txt/oscam.srvid.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.srvid2.txt b/Distribution/doc/txt/oscam.srvid2.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.tiers.txt b/Distribution/doc/txt/oscam.tiers.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.txt b/Distribution/doc/txt/oscam.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.user.txt b/Distribution/doc/txt/oscam.user.txt old mode 100755 new mode 100644 diff --git a/Distribution/doc/txt/oscam.whitelist.txt b/Distribution/doc/txt/oscam.whitelist.txt old mode 100755 new mode 100644 diff --git a/Distribution/monitor/mpcsmon-src-0.6.tar.bz2 b/Distribution/monitor/mpcsmon-src-0.6.tar.bz2 old mode 100755 new mode 100644 diff --git a/Distribution/monitor/mpcsmon.sh b/Distribution/monitor/mpcsmon.sh old mode 100755 new mode 100644 diff --git a/Makefile b/Makefile old mode 100755 new mode 100644 index 21e4a1d..297c18c --- a/Makefile +++ b/Makefile @@ -32,9 +32,6 @@ ifeq "$(shell ./config.sh --enabled WITH_SSL)" "Y" override USE_SSL=1 override USE_LIBCRYPTO=1 endif -ifeq "$(shell ./config.sh --enabled WITH_EMU)" "Y" - override USE_LIBCRYPTO=1 -endif ifdef USE_SSL override USE_LIBCRYPTO=1 endif @@ -374,32 +371,6 @@ SRC-$(CONFIG_CS_CACHEEX) += module-cccam-cacheex.c SRC-$(CONFIG_MODULE_CCCAM) += module-cccam.c SRC-$(CONFIG_MODULE_CCCSHARE) += module-cccshare.c SRC-$(CONFIG_MODULE_CONSTCW) += module-constcw.c -SRC-$(CONFIG_WITH_EMU) += module-emulator.c -SRC-$(CONFIG_WITH_EMU) += module-emulator-osemu.c -SRC-$(CONFIG_WITH_EMU) += module-emulator-biss.c -SRC-$(CONFIG_WITH_EMU) += module-emulator-cryptoworks.c -SRC-$(CONFIG_WITH_EMU) += module-emulator-director.c -SRC-$(CONFIG_WITH_EMU) += module-emulator-irdeto.c -SRC-$(CONFIG_WITH_EMU) += module-emulator-nagravision.c -SRC-$(CONFIG_WITH_EMU) += module-emulator-omnicrypt.c -SRC-$(CONFIG_WITH_EMU) += module-emulator-powervu.c -SRC-$(CONFIG_WITH_EMU) += module-emulator-viaccess.c -ifeq "$(CONFIG_WITH_EMU)" "y" -ifeq "$(CONFIG_WITH_SOFTCAM)" "y" -UNAME := $(shell uname -s) -ifneq ($(UNAME),Darwin) -ifndef ANDROID_NDK -ifndef ANDROID_STANDALONE_TOOLCHAIN -TOUCH_SK := $(shell touch SoftCam.Key) -override LDFLAGS += -Wl,--format=binary -Wl,SoftCam.Key -Wl,--format=default -ifneq ($(uname_S),Cygwin) -override LDFLAGS += -Wl,-z,noexecstack -endif -endif -endif -endif -endif -endif SRC-$(CONFIG_CS_CACHEEX) += module-csp.c SRC-$(CONFIG_CW_CYCLE_CHECK) += module-cw-cycle-check.c SRC-$(CONFIG_WITH_AZBOX) += module-dvbapi-azbox.c @@ -960,3 +931,6 @@ debug: all -include Makefile.extra -include Makefile.local + +EXTRA_LIBS += -lm +LIBS += $(EXTRA_LIBS) diff --git a/Makefile.extra b/Makefile.extra old mode 100755 new mode 100644 diff --git a/README.build b/README.build old mode 100755 new mode 100644 diff --git a/README.config b/README.config old mode 100755 new mode 100644 index 9b99c3c..03c70f7 --- a/README.config +++ b/README.config @@ -81,7 +81,7 @@ Examples: ./config.sh --create-cert ecdsa prime256v1 ca 'My OSCam Distribution' Available options: - addons: WEBIF WEBIF_LIVELOG WEBIF_JQUERY WITH_COMPRESS_WEBIF WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_SIGNING WITH_EMU WITH_SOFTCAM + addons: WEBIF WEBIF_LIVELOG WEBIF_JQUERY WITH_COMPRESS_WEBIF WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_SIGNING protocols: MODULE_CAMD33 MODULE_CAMD35 MODULE_CAMD35_TCP MODULE_NEWCAMD MODULE_CCCAM MODULE_CCCSHARE MODULE_GBOX MODULE_RADEGAST MODULE_SCAM MODULE_SERIAL MODULE_CONSTCW MODULE_PANDORA MODULE_GHTTP MODULE_STREAMRELAY readers: READER_NAGRA READER_NAGRA_MERLIN READER_IRDETO READER_CONAX READER_CRYPTOWORKS READER_SECA READER_VIACCESS READER_VIDEOGUARD READER_DRE READER_TONGFANG READER_BULCRYPT READER_GRIFFIN READER_DGCRYPT card_readers: CARDREADER_PHOENIX CARDREADER_INTERNAL CARDREADER_SC8IN1 CARDREADER_MP35 CARDREADER_SMARGO CARDREADER_DB2COM CARDREADER_STAPI CARDREADER_STAPI5 CARDREADER_STINGER CARDREADER_DRECAS diff --git a/README.dvbapi_protocol b/README.dvbapi_protocol old mode 100755 new mode 100644 diff --git a/README.md b/README.md old mode 100755 new mode 100644 index b4ab476..d47df45 --- a/README.md +++ b/README.md @@ -1,188 +1,81 @@ -# OSCam with AI Fake DCW Detector Test +# OSCam: Open Source Conditional Access Module -![AI Fake DCW Detector](images/image1.jpg) +[![GitLab Last Commit](https://img.shields.io/gitlab/last-commit/11?gitlab_url=https%3A%2F%2Fgit.streamboard.tv&style=for-the-badge)](https://git.streamboard.tv/common/oscam/-/commits/master) +[![GitLab Tag](https://img.shields.io/gitlab/v/tag/11?gitlab_url=https%3A%2F%2Fgit.streamboard.tv&style=for-the-badge)](https://git.streamboard.tv/common/oscam/-/tags) +[![GitLab License](https://img.shields.io/gitlab/license/11?gitlab_url=https%3A%2F%2Fgit.streamboard.tv&style=for-the-badge)](https://git.streamboard.tv/common/oscam/-/blob/master/COPYING) -## Overview +## Quick links -This repository contains a modified version of **OSCam** enhanced with an advanced **AI-inspired Fake DCW Detection and Voting System**. +- [Releases](https://git.streamboard.tv/common/oscam/-/commits/master) +- [GitLab repository](https://git.streamboard.tv/common/oscam) +- [Wiki](https://git.streamboard.tv/common/oscam/-/wikis/home) +- [Issue tracker](https://git.streamboard.tv/common/oscam/-/issues) +- [Support forum](https://board.streamboard.tv/forum/) -The goal of this system is to improve stability and reliability when multiple Control Word (CW) sources are available, such as: -- Local readers -- CacheEx peers -- CSP sources -- Virtual readers +## Releases -Instead of accepting the first CW received, this implementation collects multiple candidates and selects the most reliable one using weighted voting logic. +For the latest changes and release history, see the +[OSCam commits](https://git.streamboard.tv/common/oscam/-/commits/master) page. ---- +## GitLab repository -# 🔍 Problem It Solves +Project page: + https://git.streamboard.tv/common/oscam -In multi-reader or CacheEx environments, fake or unstable DCWs can appear. +## Building & Dependencies -Default behavior: -- First CW wins -- Possible glitches, freezing, or unstable decoding +For detailed information about building OSCam, cross-compilation for +different CPUs, required and optional dependencies, SSL support, hardware +modules, and platform-specific or distribution-specific notes, please +refer to the OSCam wiki: -With AI Fake DCW Detector: -- Multiple CWs are collected -- Sources are tracked -- Votes are counted -- Local readers can be weighted higher -- Majority or timeout logic is applied -- A reliable CW is selected +- [Wiki Home](https://git.streamboard.tv/common/oscam/-/wikis/home) -Result: +## License -✔ Reduced fake DCWs -✔ Increased decoding stability -✔ Better CacheEx reliability -✔ Smarter CW selection +OSCam: Open Source CAM ---- +Copyright (C) 2009-2026 OSCam developers -# 🧠 Core Functions +OSCam is based on the Streamboard mp-cardserver 0.9d by dukat and has been +extended and worked on by many more since then. -## 1️⃣ `cw_vote_add()` +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 function is called whenever a new CW is received. +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. -It: +You should have received a copy of the GNU General Public License along with +this program. If not, see . -- Compares the CW with existing candidates -- Increases vote count if it matches -- Adds a new candidate if it is unique -- Tracks: - - Total votes - - Local votes - - Reader source - - CW value -- Identifies whether the source is: - - Local reader - - Virtual reader - - CacheEx client - - CSP source +For the full text of the license, please see the +[COPYING](https://git.streamboard.tv/common/oscam/-/blob/master/COPYING) +file in the OSCam repository. -Each CW candidate is stored in a voting pool. +## Contributing ---- +Contributions are welcome. If you want to help improve OSCam: -## 2️⃣ `cw_vote_decide()` +- Browse the existing [issues](https://git.streamboard.tv/common/oscam/-/issues) + and open a new issue if you find a bug or have a feature request. +- Fork the [GitLab repository](https://git.streamboard.tv/common/oscam), + create a topic branch, and submit a merge request. +- Check the [OSCam wiki](https://git.streamboard.tv/common/oscam/-/wikis/home) + for additional project information and guidelines. -This function determines the final CW to use. - -### Decision Process - -1. Count total votes -2. Verify minimum vote requirement -3. Calculate effective score: -4. Check for majority (> 50%) -5. Apply timeout logic -6. Apply fallback strategy if necessary - -If a winner is found: - -- CW is copied into `er->cw` -- CacheEx hit statistics are updated -- Optional logging is performed - -If no clear winner exists, the system waits for more votes unless timeout rules apply. - ---- - -# ⚙ Configuration Options - -| Parameter | Description | -|------------|------------| -| `cwvote_enabled` | Enable/disable voting system | -| `cwvote_max_candidates` | Maximum CW candidates stored | -| `cwvote_compare_len` | Number of bytes used for CW comparison | -| `cwvote_local_weight` | Weight multiplier for local votes | -| `cwvote_min_votes` | Minimum required votes before decision | -| `cwvote_timeout` | Timeout in milliseconds | -| `cwvote_fallback` | Fallback mode (1 = best candidate, 2 = first candidate) | -| `cwvote_log_enabled` | Enable detailed debug logging | - ---- - -# 🏆 Winner Selection Logic - -A CW is selected if: - -- It has a strict majority (> 50%) -OR -- Timeout is reached and fallback mode allows selection - -If timeout occurs and: - -- `fallback = 1` → best scoring candidate is selected -- `fallback = 2` → first candidate (slot 0) is selected - ---- - -# 📊 Intelligent Source Awareness - -The system distinguishes between: - -- Trusted local readers -- Virtual readers -- CacheEx clients -- CSP sources - -Local readers can be weighted higher using `cwvote_local_weight`, improving reliability in hybrid environments. - ---- - -# 📈 CacheEx Integration - -If the winning CW originates from CacheEx: - -- `cwcacheexhit` counters are incremented -- Client and account statistics are updated -- Global hit statistics are updated - -This improves monitoring accuracy and performance analysis. - ---- - -# 🚀 Advantages - -- Detects and filters fake DCWs -- Improves stability in multi-reader setups -- Reduces glitching and freezing -- Configurable and flexible logic -- Seamlessly integrated into OSCam core - ---- - -# 🧩 AI Concept - -Although not based on machine learning, this system applies AI-style decision logic: - -- Aggregates multiple inputs -- Applies weighted scoring -- Uses majority validation -- Implements adaptive timeout fallback - -It behaves like deterministic intelligence for CW selection. - ---- - -# Intended Use - -Designed for: - -- Emulated environments -- CacheEx-heavy setups -- Multi-reader configurations -- Advanced OSCam tuning -- High-stability streaming environments - ---- - -# License - -Based on the OSCam open-source project. -This repository includes custom modifications implementing advanced CW voting and fake DCW detection logic. +## Help and Support +- Man pages and configuration examples are available in the + `Distribution/doc` directory of the source tree. +- For bug reports and feature requests, please use the + [GitLab issue tracker](https://git.streamboard.tv/common/oscam/-/issues). +- For community help and general discussion (mainly German and English), + visit the [support forum](https://board.streamboard.tv/forum/). +- For configuration guides, FAQs, and detailed documentation, see the + [OSCam wiki](https://git.streamboard.tv/common/oscam/-/wikis/home). diff --git a/caid, b/caid, new file mode 100644 index 0000000..e69de29 diff --git a/config.h b/config.h index df6f33a..2f44561 100644 --- a/config.h +++ b/config.h @@ -1,17 +1,15 @@ #ifndef CONFIG_H_ #define CONFIG_H_ -#define WITH_EMU 1 -#define WITH_SOFTCAM 1 #define WEBIF 1 #define WEBIF_LIVELOG 1 #define WEBIF_JQUERY 1 -#define WEBIF_WIKI 1 -//#define WITH_COMPRESS_WEBIF 1 +//#define WEBIF_WIKI 1 +#define WITH_COMPRESS_WEBIF 1 //#define WITH_SSL 1 #if defined(__linux__) || defined(__CYGWIN__) -#define HAVE_DVBAPI 1 -#define WITH_EXTENDED_CW 1 +//#define HAVE_DVBAPI 1 +//#define WITH_EXTENDED_CW 1 #endif //#define WITH_NEUTRINO 1 #define READ_SDT_CHARSETS 1 @@ -24,46 +22,46 @@ #define CW_CYCLE_CHECK 1 //#define LCDSUPPORT 1 //#define LEDSUPPORT 1 -#define IPV6SUPPORT 1 +//#define IPV6SUPPORT 1 //#define WITH_ARM_NEON 1 //#define WITH_SIGNING 1 -#define MODULE_MONITOR 1 +//#define MODULE_MONITOR 1 -#define MODULE_CAMD33 1 -#define MODULE_CAMD35 1 +//#define MODULE_CAMD33 1 +//#define MODULE_CAMD35 1 #define MODULE_CAMD35_TCP 1 #define MODULE_NEWCAMD 1 #define MODULE_CCCAM 1 #define MODULE_CCCSHARE 1 -#define MODULE_GBOX 1 -#define MODULE_RADEGAST 1 +//#define MODULE_GBOX 1 +//#define MODULE_RADEGAST 1 //#define MODULE_SERIAL 1 -#define MODULE_CONSTCW 1 +//#define MODULE_CONSTCW 1 //#define MODULE_PANDORA 1 -#define MODULE_GHTTP 1 +//#define MODULE_GHTTP 1 //#define MODULE_SCAM 1 -#define MODULE_STREAMRELAY 1 +//#define MODULE_STREAMRELAY 1 -#define WITH_CARDREADER 1 +//#define WITH_CARDREADER 1 #ifdef WITH_CARDREADER -#define READER_NAGRA 1 -#define READER_NAGRA_MERLIN 1 -#define READER_IRDETO 1 -#define READER_CONAX 1 -#define READER_CRYPTOWORKS 1 -#define READER_SECA 1 -#define READER_VIACCESS 1 -#define READER_VIDEOGUARD 1 -#define READER_DRE 1 -#define READER_TONGFANG 1 -#define READER_BULCRYPT 1 -#define READER_GRIFFIN 1 -#define READER_DGCRYPT 1 +//#define READER_NAGRA 1 +//#define READER_NAGRA_MERLIN 1 +//#define READER_IRDETO 1 +//#define READER_CONAX 1 +//#define READER_CRYPTOWORKS 1 +//#define READER_SECA 1 +//#define READER_VIACCESS 1 +//#define READER_VIDEOGUARD 1 +//#define READER_DRE 1 +//#define READER_TONGFANG 1 +//#define READER_BULCRYPT 1 +//#define READER_GRIFFIN 1 +//#define READER_DGCRYPT 1 -#define CARDREADER_PHOENIX 1 -#define CARDREADER_INTERNAL 1 +//#define CARDREADER_PHOENIX 1 +//#define CARDREADER_INTERNAL 1 //#define CARDREADER_MP35 1 //#define CARDREADER_SC8IN1 1 //#define CARDREADER_SMARGO 1 @@ -72,7 +70,7 @@ //#define CARDREADER_DRECAS 1 #ifdef WITH_PCSC -//#define CARDREADER_PCSC 1 +#define CARDREADER_PCSC 1 #endif #ifdef WITH_LIBUSB diff --git a/config.mak b/config.mak new file mode 100644 index 0000000..f0c9530 --- /dev/null +++ b/config.mak @@ -0,0 +1 @@ +EXTRA_LIBS += -lm diff --git a/config.sh b/config.sh old mode 100755 new mode 100644 index b0b56a1..eb21796 --- a/config.sh +++ b/config.sh @@ -1,6 +1,6 @@ #!/bin/sh -addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY WEBIF_WIKI WITH_COMPRESS_WEBIF WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_SIGNING WITH_EMU WITH_SOFTCAM" +addons="WEBIF WEBIF_LIVELOG WEBIF_JQUERY WEBIF_WIKI WITH_COMPRESS_WEBIF WITH_SSL HAVE_DVBAPI WITH_EXTENDED_CW WITH_NEUTRINO READ_SDT_CHARSETS CS_ANTICASC WITH_DEBUG MODULE_MONITOR WITH_LB CS_CACHEEX CS_CACHEEX_AIO CW_CYCLE_CHECK LCDSUPPORT LEDSUPPORT CLOCKFIX IPV6SUPPORT WITH_ARM_NEON WITH_SIGNING" protocols="MODULE_CAMD33 MODULE_CAMD35 MODULE_CAMD35_TCP MODULE_NEWCAMD MODULE_CCCAM MODULE_CCCSHARE MODULE_GBOX MODULE_RADEGAST MODULE_SCAM MODULE_SERIAL MODULE_CONSTCW MODULE_PANDORA MODULE_GHTTP MODULE_STREAMRELAY" readers="READER_NAGRA READER_NAGRA_MERLIN READER_IRDETO READER_CONAX READER_CRYPTOWORKS READER_SECA READER_VIACCESS READER_VIDEOGUARD READER_DRE READER_TONGFANG READER_BULCRYPT READER_GRIFFIN READER_DGCRYPT" card_readers="CARDREADER_PHOENIX CARDREADER_INTERNAL CARDREADER_SC8IN1 CARDREADER_MP35 CARDREADER_SMARGO CARDREADER_DB2COM CARDREADER_STAPI CARDREADER_STAPI5 CARDREADER_STINGER CARDREADER_DRECAS" @@ -9,42 +9,40 @@ defconfig=" CONFIG_WEBIF=y CONFIG_WEBIF_LIVELOG=y CONFIG_WEBIF_JQUERY=y -CONFIG_WEBIF_WIKI=y +# CONFIG_WEBIF_WIKI=n CONFIG_WITH_COMPRESS_WEBIF=y -CONFIG_WITH_SSL=y +# CONFIG_WITH_SSL=n CONFIG_HAVE_DVBAPI=y -CONFIG_WITH_EXTENDED_CW=y +# CONFIG_WITH_EXTENDED_CW=n # CONFIG_WITH_NEUTRINO=n CONFIG_READ_SDT_CHARSETS=y -CONFIG_CS_ANTICASC=y +# CONFIG_CS_ANTICASC=n CONFIG_WITH_DEBUG=y CONFIG_MODULE_MONITOR=y CONFIG_WITH_LB=y -CONFIG_CS_CACHEEX=y -CONFIG_CS_CACHEEX_AIO=y -CONFIG_CW_CYCLE_CHECK=y -CONFIG_LCDSUPPORT=y -CONFIG_LEDSUPPORT=y -CONFIG_CLOCKFIX=y -CONFIG_IPV6SUPPORT=y +# CONFIG_CS_CACHEEX=n +# CONFIG_CS_CACHEEX_AIO=n +# CONFIG_CW_CYCLE_CHECK=n +# CONFIG_LCDSUPPORT=n +# CONFIG_LEDSUPPORT=n +# CONFIG_CLOCKFIX=n +# CONFIG_IPV6SUPPORT=n # CONFIG_WITH_ARM_NEON=n -CONFIG_WITH_SIGNING=n -CONFIG_WITH_EMU=y -CONFIG_WITH_SOFTCAM=y -CONFIG_MODULE_CAMD33=n +# CONFIG_WITH_SIGNING=n +# CONFIG_MODULE_CAMD33=n CONFIG_MODULE_CAMD35=y CONFIG_MODULE_CAMD35_TCP=y CONFIG_MODULE_NEWCAMD=y CONFIG_MODULE_CCCAM=y CONFIG_MODULE_CCCSHARE=y CONFIG_MODULE_GBOX=y -CONFIG_MODULE_RADEGAST=y -CONFIG_MODULE_SERIAL=y -CONFIG_MODULE_CONSTCW=y -CONFIG_MODULE_PANDORA=y -CONFIG_MODULE_SCAM=y -CONFIG_MODULE_GHTTP=y -CONFIG_MODULE_STREAMRELAY=y +# CONFIG_MODULE_RADEGAST=n +# CONFIG_MODULE_SERIAL=n +# CONFIG_MODULE_CONSTCW=n +# CONFIG_MODULE_PANDORA=n +# CONFIG_MODULE_SCAM=n +# CONFIG_MODULE_GHTTP=n +# CONFIG_MODULE_STREAMRELAY=n CONFIG_WITH_CARDREADER=y CONFIG_READER_NAGRA_COMMON=y CONFIG_READER_NAGRA=y @@ -333,8 +331,8 @@ get_opts() { update_deps() { # Calculate dependencies - enabled_any $(get_opts readers) $(get_opts card_readers) WITH_EMU && enable_opt WITH_CARDREADER >/dev/null - disabled_all $(get_opts readers) $(get_opts card_readers) WITH_EMU && disable_opt WITH_CARDREADER >/dev/null + enabled_any $(get_opts readers) $(get_opts card_readers) && enable_opt WITH_CARDREADER >/dev/null + disabled_all $(get_opts readers) $(get_opts card_readers) && disable_opt WITH_CARDREADER >/dev/null disabled WEBIF && disable_opt WEBIF_LIVELOG >/dev/null disabled WEBIF && disable_opt WEBIF_JQUERY >/dev/null disabled WEBIF && disable_opt WEBIF_WIKI >/dev/null @@ -343,9 +341,6 @@ update_deps() { enabled_any CARDREADER_DB2COM CARDREADER_MP35 CARDREADER_SC8IN1 CARDREADER_STINGER && enable_opt CARDREADER_PHOENIX >/dev/null enabled CS_CACHEEX_AIO && enable_opt CS_CACHEEX >/dev/null enabled WITH_SIGNING && enable_opt WITH_SSL >/dev/null - enabled WITH_EMU && enable_opt READER_VIACCESS >/dev/null - enabled WITH_EMU && enable_opt MODULE_NEWCAMD >/dev/null - disabled WITH_EMU && disable_opt WITH_SOFTCAM >/dev/null } list_config() { @@ -397,9 +392,9 @@ list_config() { not_have_flag USE_LIBCRYPTO && echo "CONFIG_LIB_AES=y" || echo "# CONFIG_LIB_AES=n" enabled MODULE_CCCAM && echo "CONFIG_LIB_RC6=y" || echo "# CONFIG_LIB_RC6=n" not_have_flag USE_LIBCRYPTO && enabled MODULE_CCCAM && echo "CONFIG_LIB_SHA1=y" || echo "# CONFIG_LIB_SHA1=n" - enabled_any READER_DRE MODULE_SCAM READER_VIACCESS READER_NAGRA READER_NAGRA_MERLIN READER_VIDEOGUARD READER_CONAX READER_TONGFANG WITH_EMU && echo "CONFIG_LIB_DES=y" || echo "# CONFIG_LIB_DES=n" - enabled_any MODULE_CCCAM READER_NAGRA READER_NAGRA_MERLIN READER_SECA WITH_EMU && echo "CONFIG_LIB_IDEA=y" || echo "# CONFIG_LIB_IDEA=n" - not_have_flag USE_LIBCRYPTO && enabled_any READER_CONAX READER_CRYPTOWORKS READER_NAGRA READER_NAGRA_MERLIN WITH_EMU && echo "CONFIG_LIB_BIGNUM=y" || echo "# CONFIG_LIB_BIGNUM=n" + enabled_any READER_DRE MODULE_SCAM READER_VIACCESS READER_NAGRA READER_NAGRA_MERLIN READER_VIDEOGUARD READER_CONAX READER_TONGFANG && echo "CONFIG_LIB_DES=y" || echo "# CONFIG_LIB_DES=n" + enabled_any MODULE_CCCAM READER_NAGRA READER_NAGRA_MERLIN READER_SECA && echo "CONFIG_LIB_IDEA=y" || echo "# CONFIG_LIB_IDEA=n" + not_have_flag USE_LIBCRYPTO && enabled_any READER_CONAX READER_CRYPTOWORKS READER_NAGRA READER_NAGRA_MERLIN && echo "CONFIG_LIB_BIGNUM=y" || echo "# CONFIG_LIB_BIGNUM=n" enabled READER_NAGRA_MERLIN && echo "CONFIG_LIB_MDC2=y" || echo "# CONFIG_LIB_MDC2=n" enabled READER_NAGRA_MERLIN && echo "CONFIG_LIB_FAST_AES=y" || echo "# CONFIG_LIB_FAST_AES=n" enabled_any READER_NAGRA_MERLIN WITH_SIGNING && echo "CONFIG_LIB_SHA256=y" || echo "# CONFIG_LIB_SHA256=n" @@ -529,8 +524,6 @@ menu_addons() { IPV6SUPPORT "IPv6 support (experimental)" $(check_test "IPV6SUPPORT") \ WITH_ARM_NEON "ARM NEON (SIMD/MPE) support" $(check_test "WITH_ARM_NEON") \ WITH_SIGNING "Binary signing with X.509 certificate" $(check_test "WITH_SIGNING") \ - WITH_EMU "Emulator support" $(check_test "WITH_EMU") \ - WITH_SOFTCAM "Built-in SoftCam.Key" $(check_test "WITH_SOFTCAM") \ 2> ${tempfile} opt=${?} @@ -913,8 +906,7 @@ do ;; '-v'|'--oscam-version') version=`grep '^#define CS_VERSION' globals.h | cut -d\" -f2` - emuversion=`grep EMU_VERSION module-emulator-osemu.h | awk '{ print $3 }'` - echo $version-$emuversion + echo $version break ;; '--submodule') @@ -965,7 +957,7 @@ do break ;; '-c'|'--oscam-commit') - sha=`git log --no-merges 2>/dev/null | sed -n 1p | cut -d ' ' -f2 | cut -c1-8` + sha=`git log 2>/dev/null | sed -n 1p | cut -d ' ' -f2 | cut -c1-8` echo $sha break ;; diff --git a/cscrypt/CMakeLists.txt b/cscrypt/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/cscrypt/Makefile b/cscrypt/Makefile old mode 100755 new mode 100644 diff --git a/cscrypt/aes.c b/cscrypt/aes.c old mode 100755 new mode 100644 diff --git a/cscrypt/aes.h b/cscrypt/aes.h old mode 100755 new mode 100644 diff --git a/cscrypt/bn.h b/cscrypt/bn.h old mode 100755 new mode 100644 diff --git a/cscrypt/bn_add.c b/cscrypt/bn_add.c old mode 100755 new mode 100644 diff --git a/cscrypt/bn_asm.c b/cscrypt/bn_asm.c old mode 100755 new mode 100644 diff --git a/cscrypt/bn_ctx.c b/cscrypt/bn_ctx.c old mode 100755 new mode 100644 diff --git a/cscrypt/bn_div.c b/cscrypt/bn_div.c old mode 100755 new mode 100644 diff --git a/cscrypt/bn_exp.c b/cscrypt/bn_exp.c old mode 100755 new mode 100644 diff --git a/cscrypt/bn_lcl.h b/cscrypt/bn_lcl.h old mode 100755 new mode 100644 diff --git a/cscrypt/bn_lib.c b/cscrypt/bn_lib.c old mode 100755 new mode 100644 diff --git a/cscrypt/bn_mul.c b/cscrypt/bn_mul.c old mode 100755 new mode 100644 diff --git a/cscrypt/bn_print.c b/cscrypt/bn_print.c old mode 100755 new mode 100644 diff --git a/cscrypt/bn_shift.c b/cscrypt/bn_shift.c old mode 100755 new mode 100644 diff --git a/cscrypt/bn_sqr.c b/cscrypt/bn_sqr.c old mode 100755 new mode 100644 diff --git a/cscrypt/bn_word.c b/cscrypt/bn_word.c old mode 100755 new mode 100644 diff --git a/cscrypt/buffer.h b/cscrypt/buffer.h old mode 100755 new mode 100644 diff --git a/cscrypt/des.c b/cscrypt/des.c old mode 100755 new mode 100644 diff --git a/cscrypt/des.h b/cscrypt/des.h old mode 100755 new mode 100644 diff --git a/cscrypt/fast_aes.c b/cscrypt/fast_aes.c old mode 100755 new mode 100644 diff --git a/cscrypt/fast_aes.h b/cscrypt/fast_aes.h old mode 100755 new mode 100644 diff --git a/cscrypt/i_cbc.c b/cscrypt/i_cbc.c old mode 100755 new mode 100644 diff --git a/cscrypt/i_ecb.c b/cscrypt/i_ecb.c old mode 100755 new mode 100644 diff --git a/cscrypt/i_skey.c b/cscrypt/i_skey.c old mode 100755 new mode 100644 diff --git a/cscrypt/idea.h b/cscrypt/idea.h old mode 100755 new mode 100644 diff --git a/cscrypt/idea_lcl.h b/cscrypt/idea_lcl.h old mode 100755 new mode 100644 diff --git a/cscrypt/md5.c b/cscrypt/md5.c old mode 100755 new mode 100644 diff --git a/cscrypt/md5.h b/cscrypt/md5.h old mode 100755 new mode 100644 diff --git a/cscrypt/mdc2.c b/cscrypt/mdc2.c old mode 100755 new mode 100644 diff --git a/cscrypt/mdc2.h b/cscrypt/mdc2.h old mode 100755 new mode 100644 diff --git a/cscrypt/mem.c b/cscrypt/mem.c old mode 100755 new mode 100644 diff --git a/cscrypt/openssl_mods.h b/cscrypt/openssl_mods.h old mode 100755 new mode 100644 diff --git a/cscrypt/rc6.c b/cscrypt/rc6.c old mode 100755 new mode 100644 diff --git a/cscrypt/rc6.h b/cscrypt/rc6.h old mode 100755 new mode 100644 diff --git a/cscrypt/sha1.c b/cscrypt/sha1.c old mode 100755 new mode 100644 diff --git a/cscrypt/sha1.h b/cscrypt/sha1.h old mode 100755 new mode 100644 diff --git a/cscrypt/sha256.c b/cscrypt/sha256.c old mode 100755 new mode 100644 diff --git a/cscrypt/sha256.h b/cscrypt/sha256.h old mode 100755 new mode 100644 diff --git a/csctapi/CMakeLists.txt b/csctapi/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/csctapi/Makefile b/csctapi/Makefile old mode 100755 new mode 100644 diff --git a/csctapi/atr.c b/csctapi/atr.c old mode 100755 new mode 100644 diff --git a/csctapi/atr.h b/csctapi/atr.h old mode 100755 new mode 100644 diff --git a/csctapi/cardreaders.h b/csctapi/cardreaders.h old mode 100755 new mode 100644 index 6cd27da..4cda2ed --- a/csctapi/cardreaders.h +++ b/csctapi/cardreaders.h @@ -15,6 +15,5 @@ extern const struct s_cardreader cardreader_smartreader; extern const struct s_cardreader cardreader_stapi; extern const struct s_cardreader cardreader_stinger; extern const struct s_cardreader cardreader_drecas; -extern const struct s_cardreader cardreader_emu; #endif diff --git a/csctapi/icc_async.c b/csctapi/icc_async.c old mode 100755 new mode 100644 diff --git a/csctapi/icc_async.h b/csctapi/icc_async.h old mode 100755 new mode 100644 diff --git a/csctapi/ifd_amsmc.c b/csctapi/ifd_amsmc.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_azbox.c b/csctapi/ifd_azbox.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_cool.c b/csctapi/ifd_cool.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_db2com.c b/csctapi/ifd_db2com.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_db2com.h b/csctapi/ifd_db2com.h old mode 100755 new mode 100644 diff --git a/csctapi/ifd_drecas.c b/csctapi/ifd_drecas.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_drecas.h b/csctapi/ifd_drecas.h old mode 100755 new mode 100644 diff --git a/csctapi/ifd_mp35.c b/csctapi/ifd_mp35.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_pcsc.c b/csctapi/ifd_pcsc.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_phoenix.c b/csctapi/ifd_phoenix.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_phoenix.h b/csctapi/ifd_phoenix.h old mode 100755 new mode 100644 diff --git a/csctapi/ifd_sc8in1.c b/csctapi/ifd_sc8in1.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_sci.c b/csctapi/ifd_sci.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_sci_global.h b/csctapi/ifd_sci_global.h old mode 100755 new mode 100644 diff --git a/csctapi/ifd_sci_ioctl.h b/csctapi/ifd_sci_ioctl.h old mode 100755 new mode 100644 diff --git a/csctapi/ifd_smargo.c b/csctapi/ifd_smargo.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_smartreader.c b/csctapi/ifd_smartreader.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_smartreader_types.h b/csctapi/ifd_smartreader_types.h old mode 100755 new mode 100644 diff --git a/csctapi/ifd_stapi.c b/csctapi/ifd_stapi.c old mode 100755 new mode 100644 diff --git a/csctapi/ifd_stinger.c b/csctapi/ifd_stinger.c old mode 100755 new mode 100644 diff --git a/csctapi/io_serial.c b/csctapi/io_serial.c old mode 100755 new mode 100644 diff --git a/csctapi/io_serial.h b/csctapi/io_serial.h old mode 100755 new mode 100644 diff --git a/csctapi/protocol_t0.c b/csctapi/protocol_t0.c old mode 100755 new mode 100644 diff --git a/csctapi/protocol_t0.h b/csctapi/protocol_t0.h old mode 100755 new mode 100644 diff --git a/csctapi/protocol_t1.c b/csctapi/protocol_t1.c old mode 100755 new mode 100644 diff --git a/devtools/README b/devtools/README old mode 100755 new mode 100644 diff --git a/devtools/check_cmdline_opts.sh b/devtools/check_cmdline_opts.sh old mode 100755 new mode 100644 diff --git a/devtools/check_config_tables.sh b/devtools/check_config_tables.sh old mode 100755 new mode 100644 diff --git a/devtools/extract_config.sh b/devtools/extract_config.sh old mode 100755 new mode 100644 diff --git a/extapi/README b/extapi/README old mode 100755 new mode 100644 diff --git a/extapi/coolapi.h b/extapi/coolapi.h old mode 100755 new mode 100644 diff --git a/extapi/cygwin/SCardErr.h b/extapi/cygwin/SCardErr.h old mode 100755 new mode 100644 diff --git a/extapi/cygwin/WinSCard.h b/extapi/cygwin/WinSCard.h old mode 100755 new mode 100644 diff --git a/extapi/cygwin/WinSmCrd.h b/extapi/cygwin/WinSmCrd.h old mode 100755 new mode 100644 diff --git a/extapi/linux/README b/extapi/linux/README old mode 100755 new mode 100644 diff --git a/extapi/linux/serial.h b/extapi/linux/serial.h old mode 100755 new mode 100644 diff --git a/extapi/linux/tty_flags.h b/extapi/linux/tty_flags.h old mode 100755 new mode 100644 diff --git a/extapi/openxcas/libOpenXCASAPI.a b/extapi/openxcas/libOpenXCASAPI.a old mode 100755 new mode 100644 diff --git a/extapi/openxcas/openxcas.conf b/extapi/openxcas/openxcas.conf old mode 100755 new mode 100644 diff --git a/extapi/openxcas/openxcas_api.h b/extapi/openxcas/openxcas_api.h old mode 100755 new mode 100644 diff --git a/extapi/openxcas/openxcas_message.h b/extapi/openxcas/openxcas_message.h old mode 100755 new mode 100644 diff --git a/extapi/openxcas/openxcas_smartcard.h b/extapi/openxcas/openxcas_smartcard.h old mode 100755 new mode 100644 diff --git a/globals.h b/globals.h old mode 100755 new mode 100644 index 28d0674..c348c70 --- a/globals.h +++ b/globals.h @@ -365,19 +365,14 @@ #define CHECK_BIT(var,pos) (((var) & (1<<(pos)))? 1 : 0) -// CW VOTING -#define MAX_VOTE_CANDIDATES 16 // Zwiększono dla obsługi większej liczby głosów -#define MIN_VOTES_REQUIRED 2 -#define CW_COMPARE_LEN 14 - /* =========================== * constants * =========================== */ -#define SCM_URL "https://github.com/oscam-mirror/oscam-emu" +#define SCM_URL "https://git.streamboard.tv/common/oscam" #define WIKI_URL "https://git.streamboard.tv/common/oscam/-/wikis" -#define BOARD_URL "https://github.com/oscam-mirror/oscam-emu/discussions" +#define BOARD_URL "https://board.streamboard.tv" #ifndef CS_VERSION -#define CS_VERSION "2.26.01-11942" +#define CS_VERSION "2.26.02-11943" #endif #ifndef CS_GIT_COMMIT #define CS_GIT_COMMIT "a2b4c6d8" @@ -403,6 +398,9 @@ #define CS_CLIENT_HASHBUCKETS 32 #define CS_SERVICENAME_SIZE 32 +#define WEBIF_MAX_NODES 16 +extern uint32_t webif_last_nodeid[WEBIF_MAX_NODES]; + #define CS_ECMSTORESIZE 16 // use MD5() #define CS_EMMSTORESIZE 16 // use MD5() #define CS_CLIENT_TIMEOUT 5000 @@ -412,18 +410,14 @@ #define CS_ECM_RINGBUFFER_MAX 0x10 // max size for ECM last responsetimes ringbuffer. Keep this set to power of 2 values! // Support for multiple CWs per channel and other encryption algos -#define WITH_EXTENDED_CW 1 +//#define WITH_EXTENDED_CW 1 #define MAX_ECM_SIZE 1024 #define MAX_EMM_SIZE 1024 #define MAX_CMD_SIZE 0xff + 5 // maximum value from length byte + command header -#ifdef WITH_EMU -#define CS_EMMCACHESIZE 1024 // nr of EMMs that EMU reader will cache -#else #define CS_EMMCACHESIZE 512 // nr of EMMs that each reader will cache -#endif #define MSGLOGSIZE 64 // size of string buffer for a ecm to return messages #define D_TRACE 0x0001 // Generate very detailed error/trace messages per routine @@ -458,11 +452,10 @@ #define R_MOUSE 0x5 // Reader smartcard mouse /////// internal readers (Dreambox, Coolstream, IPBox) are all R_INTERNAL, they are determined compile-time #define R_INTERNAL 0x6 // Reader smartcard intern -#define R_SMART 0x7 // Reader smartcard (generic) /////// readers that do not reed baudrate setting and timings are guarded by reader itself (large buffer built in): AFTER R_SMART +#define R_SMART 0x7 // Smartreader+ #define R_PCSC 0x8 // PCSC #define R_DRECAS 0x9 // Reader DRECAS -#define R_EMU 0x17 // Reader EMU /////// proxy readers after R_CS378X #define R_CAMD35 0x20 // Reader cascading camd 3.5x #define R_CAMD33 0x21 // Reader cascading camd 3.3x @@ -900,13 +893,6 @@ typedef struct s_entitlement // contains entitlement Info uint32_t class; // the class needed for some systems time_t start; // startdate time_t end; // enddate -#ifdef WITH_EMU - bool isKey; - bool isData; - char name[8]; - uint8_t *key; - uint32_t keyLength; -#endif } S_ENTITLEMENT; struct s_client; @@ -916,7 +902,7 @@ struct cmd_packet_t; struct s_ecm_answer; struct demux_s; -#define DEFAULT_MODULE_BUFsize 1024 +#define DEFAULT_MODULE_BUFSIZE 1024 struct s_module { @@ -1041,26 +1027,6 @@ typedef struct cw_extendted_t #endif } EXTENDED_CW; -typedef struct s_cw_vote { - uint8_t cw[16]; - uint8_t votes; - uint8_t local_votes; - uint8_t has_cacheex_vote; // FIX #2: flaga czy głos pochodzi z cacheex - struct s_reader *voters[MAX_VOTE_CANDIDATES]; -} s_cw_vote; - -// CW Vote settings -typedef struct s_cw_vote_caid_data -{ - uint16_t caid; -} CW_VOTE_CAID_DATA; - -typedef struct s_cw_vote_caid_tab -{ - int32_t cvcnum; - CW_VOTE_CAID_DATA *cvcdata; -} CW_VOTE_CAID_TAB; - typedef struct ecm_request_t { uint8_t ecm[MAX_ECM_SIZE]; @@ -1131,6 +1097,7 @@ typedef struct ecm_request_t uint16_t cacheex_mode1_delay; // cacheex mode 1 delay uint8_t cacheex_hitcache; // =1 if wait_time due hitcache void *cw_cache; // pointer to cw stored in cache + uint8_t entropy_blocked; // =1 if entropy check blocked CW #endif #ifdef CS_CACHEEX_AIO int32_t ecm_time; // ecm-time in ms @@ -1154,10 +1121,9 @@ typedef struct ecm_request_t #ifdef HAVE_DVBAPI uint8_t adapter_index; #endif - uint32_t vote_pool_session; // sesja dla vote_pool - zapobiega wielokrotnemu czyszczeniu - struct s_cw_vote vote_pool[MAX_VOTE_CANDIDATES]; } ECM_REQUEST; + struct s_ecm_answer { uint8_t status; @@ -1550,7 +1516,6 @@ typedef struct ce_csp_t CAIDVALUETAB maxhop_lg_percaid; #endif CECSPVALUETAB filter_caidtab; - CAIDVALUETAB cacheex_nopushafter_tab; uint8_t allow_request; uint8_t allow_reforward; uint8_t drop_csp; @@ -1570,6 +1535,7 @@ typedef struct ce_csp_t uint8_t lg_only_in_aio_only; uint8_t lg_only_remote_settings; int32_t feature_bitfield; + CAIDVALUETAB cacheex_nopushafter_tab; char aio_version[CS_AIO_VERSION_LEN]; #endif } CECSP; @@ -1725,7 +1691,7 @@ struct s_reader // contains device info, reader info and card info #endif int32_t r_port; char r_usr[64]; - char r_pwd[64]; // Max length 63 + null terminator + char r_pwd[64]; int32_t l_port; CAIDTAB ctab; uint32_t boxid; @@ -1834,7 +1800,6 @@ struct s_reader // contains device info, reader info and card info char cc_build[7]; // cccam build number int8_t cc_maxhops; // cccam max distance int8_t cc_mindown; // cccam min downhops - int8_t cc_want_emu; // Schlocke: Client want to have EMUs, 0 - NO; 1 - YES uint32_t cc_id; int8_t cc_keepalive; int8_t cc_hop; // For non-cccam reader: hop for virtual cards @@ -2031,11 +1996,6 @@ struct s_reader // contains device info, reader info and card info #ifdef MODULE_GHTTP uint8_t ghttp_use_ssl; #endif -#ifdef WITH_EMU - FTAB emu_auproviders; // AU providers for Emu reader - int8_t emu_datecodedenabled; // date-coded keys for BISS - LLIST *ll_biss2_rsa_keys; // BISS2 RSA keys - Read from external PEM files -#endif #ifdef READER_CONAX uint8_t cnxlastecm; // == 0 - last ecm has not been paired ecm, > 0 last ecm has been paired ecm #endif @@ -2273,7 +2233,7 @@ struct s_config uint32_t ctimeout; uint32_t ftimeout; CAIDVALUETAB ftimeouttab; - CAIDVALUETAB ctimeouttab; // ← clienttimeout_percaid + CAIDVALUETAB ctimeouttab; // � clienttimeout_percaid uint32_t cmaxidle; int32_t ulparent; uint32_t delay; @@ -2485,17 +2445,6 @@ struct s_config int8_t double_check; // schlocke: Double checks each ecm+dcw from two (or more) readers FTAB double_check_caid; // do not store loadbalancer stats with providers for this caid - // CW Vote settings - int8_t cwvote_enabled; - int8_t cwvote_log_enabled; - int32_t cwvote_timeout; // w milisekundach, 0 = wyłączony - int32_t cwvote_min_votes; - float cwvote_local_weight; // waga local readera (np. 2.0) - int32_t cwvote_max_candidates; - int32_t cwvote_compare_len; // 8 lub 16 - int32_t cwvote_fallback; // 0=wait, 1=best anyway, 2=first CW - CW_VOTE_CAID_TAB cwvote_caids; - #ifdef HAVE_DVBAPI int8_t dvbapi_enabled; int8_t dvbapi_au; @@ -2585,10 +2534,6 @@ struct s_config #else #define DEFAULT_STREAM_SOURCE_PORT 8001 //Enigma2 #endif -#endif -#ifdef WITH_EMU - uint32_t emu_stream_ecm_delay; - int8_t emu_stream_emm_enabled; #endif int32_t max_cache_time; // seconds ecms are stored in ecmcwcache @@ -2623,6 +2568,7 @@ struct s_config CECSPVALUETAB cacheex_wait_timetab; CAIDVALUETAB cacheex_mode1_delay_tab; #ifdef CS_CACHEEX_AIO + CAIDVALUETAB cacheex_nopushafter_tab; uint8_t waittime_block_start; uint16_t waittime_block_time; #endif @@ -2642,24 +2588,15 @@ struct s_config uint8_t cacheex_lg_only_remote_settings; uint8_t cacheex_localgenerated_only; CAIDTAB cacheex_localgenerated_only_caidtab; - FTAB lg_only_tab; - uint8_t localgenerated_only_in; - CAIDTAB localgenerated_only_in_caidtab; - FTAB lg_only_in_tab; - uint8_t lg_only_in_aio_only; - uint8_t lg_only_remote_settings; - int32_t feature_bitfield; - char aio_version[CS_AIO_VERSION_LEN]; -#endif - CAIDVALUETAB cacheex_nopushafter_tab; + FTAB cacheex_lg_only_tab; uint8_t cacheex_localgenerated_only_in; CAIDTAB cacheex_localgenerated_only_in_caidtab; FTAB cacheex_lg_only_in_tab; uint8_t cacheex_lg_only_in_aio_only; CECSPVALUETAB cacheex_filter_caidtab; CECSPVALUETAB cacheex_filter_caidtab_aio; - uint32_t cacheex_push_lg_groups; - FTAB cacheex_lg_only_tab; + uint64_t cacheex_push_lg_groups; +#endif #endif #ifdef CW_CYCLE_CHECK @@ -2790,18 +2727,12 @@ bool boxtype_is(const char *boxtype); bool boxname_is(const char *boxname); const char *boxtype_get(void); const char *boxname_get(void); -static inline bool caid_is_fake(uint16_t caid) { return caid == 0xffff; } -static inline bool caid_is_biss(uint16_t caid) { return caid >> 8 == 0x26; } -static inline bool caid_is_biss_fixed(uint16_t caid) { return caid == 0x2600 || caid == 0x2602; } // fixed cw, fake ecm -static inline bool caid_is_biss_dynamic(uint16_t caid) { return caid == 0x2610; } // dynamic cw, real ecm and emm static inline bool caid_is_seca(uint16_t caid) { return caid >> 8 == 0x01; } static inline bool caid_is_viaccess(uint16_t caid) { return caid >> 8 == 0x05; } static inline bool caid_is_irdeto(uint16_t caid) { return caid >> 8 == 0x06; } static inline bool caid_is_videoguard(uint16_t caid) { return caid >> 8 == 0x09; } static inline bool caid_is_conax(uint16_t caid) { return caid >> 8 == 0x0B; } static inline bool caid_is_cryptoworks(uint16_t caid) { return caid >> 8 == 0x0D; } -static inline bool caid_is_powervu(uint16_t caid) { return caid >> 8 == 0x0E; } -static inline bool caid_is_director(uint16_t caid) { return caid >> 8 == 0x10; } static inline bool caid_is_betacrypt(uint16_t caid) { return caid >> 8 == 0x17; } static inline bool caid_is_nagra(uint16_t caid) { return caid >> 8 == 0x18; } static inline bool caid_is_bulcrypt(uint16_t caid) { return caid == 0x5581 || caid == 0x4AEE; } @@ -2819,8 +2750,4 @@ static inline uint8_t get_ecm_mode(const ECM_REQUEST *er) { #endif const char *get_cardsystem_desc_by_caid(uint16_t caid); -#ifdef WITH_EMU -FILTER *get_emu_prids_for_caid(struct s_reader *rdr, uint16_t caid); -#endif - #endif diff --git a/globals.h.bak b/globals.h.bak new file mode 100644 index 0000000..75ccdbc --- /dev/null +++ b/globals.h.bak @@ -0,0 +1,2749 @@ +#ifndef GLOBALS_H_ +#define GLOBALS_H_ + +#define _GNU_SOURCE // needed for PTHREAD_MUTEX_RECURSIVE on some plattforms and maybe other things; do not remove +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) +#include +#endif + +/* + * The following hack is taken from Linux: include/linux/kconfig.h + * Original comment follows: + * Getting something that works in C and CPP for an arg that may or may + * not be defined is tricky. Here, if we have "#define CONFIG_BOOGER 1" + * we match on the placeholder define, insert the "0," for arg1 and generate + * the triplet (0, 1, 0). Then the last step cherry picks the 2nd arg (a one). + * When CONFIG_BOOGER is not defined, we generate a (... 1, 0) pair, and when + * the last step cherry picks the 2nd arg, we get a zero. + */ +#define __ARG_PLACEHOLDER_1 0, +#define config_enabled(cfg) _config_enabled(cfg) +#define _config_enabled(value) __config_enabled(__ARG_PLACEHOLDER_##value) +#define __config_enabled(arg1_or_junk) ___config_enabled(arg1_or_junk 1, 0) +#define ___config_enabled(__ignored, val, ...) val + +#include "config.h" + +#if defined(WITH_SSL) && !defined(WITH_LIBCRYPTO) +# define WITH_LIBCRYPTO 1 +#endif + +/* For deprecated but still needed cryptography functions: + * 10002 corresponds to OpenSSL version 1.0.2*/ + +#define OPENSSL_API_COMPAT 10002 + +#if defined(__CYGWIN__) || defined(__arm__) || defined(__SH4__) || defined(__MIPS__) || defined(__MIPSEL__) || defined(__powerpc__) +# define CS_LOGFILE "/dev/tty" +#endif + +#if defined(__AIX__) || defined(__SGI__) || defined(__OSF__) || defined(__HPUX__) || defined(__SOLARIS__) || defined(__APPLE__) +# define NEED_DAEMON +#endif + +#if defined(__AIX__) || defined(__SGI__) || defined(__OSF__) || defined(__HPUX__) || defined(__SOLARIS__) || defined(__CYGWIN__) +# define NO_ENDIAN_H +#endif + +#if defined(__AIX__) || defined(__SGI__) +# define socklen_t unsigned long +#endif + +#if defined(__SOLARIS__) || defined(__FreeBSD__) || defined(__OpenBSD__) +# define BSD_COMP +#endif + +#if defined(__HPUX__) +# define _XOPEN_SOURCE_EXTENDED +#endif + +#if (defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && !defined(s6_addr32) +#define s6_addr32 __u6_addr.__u6_addr32 +#endif + +#ifdef __ANDROID__ +#ifndef in_port_t +#define in_port_t uint16_t +#endif +#define tcdrain(fd) ioctl(fd, TCSBRK, 1) +#endif + +#ifdef __uClinux__ +#define fork() 0 +#endif + +// Prevent warnings about openssl functions. Apple may consider 'openssl' +// deprecated but changing perfectly working portable code just because they +// introduced some proprietary API is not going to happen. +#if defined(__APPLE__) +#define __AVAILABILITY_MACROS_USES_AVAILABILITY 0 +#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6 +#endif + +#include "cscrypt/aes.h" + +#ifdef IPV6SUPPORT +#define IN_ADDR_T struct in6_addr +#define SOCKADDR sockaddr_storage +#define ADDR_ANY in6addr_any +#define DEFAULT_AF AF_INET6 +#else +#define IN_ADDR_T in_addr_t +#define SOCKADDR sockaddr_in +#define ADDR_ANY INADDR_ANY +#define DEFAULT_AF AF_INET +#endif + +#ifndef NO_ENDIAN_H +#if defined(__APPLE__) +#include +#define __BYTE_ORDER __DARWIN_BYTE_ORDER +#define __BIG_ENDIAN __DARWIN_BIG_ENDIAN +#define __LITTLE_ENDIAN __DARWIN_LITTLE_ENDIAN +#elif defined(__FreeBSD__) || defined(__OpenBSD__) +#include +#define __BYTE_ORDER _BYTE_ORDER +#define __BIG_ENDIAN _BIG_ENDIAN +#define __LITTLE_ENDIAN _LITTLE_ENDIAN +#else +#include +#include +#endif +#endif + +/* =========================== + * macros + * =========================== */ +// Prevent use of unsafe functions (doesn't work for MacOSX) +#if !defined(__APPLE__) +#define strcpy(a,b) UNSAFE_STRCPY_USE_CS_STRNCPY_INSTEAD() +#define sprintf(a,...) UNSAFE_SPRINTF_USE_SNPRINTF_INSTEAD() +#define strtok(a,b,c) UNSAFE_STRTOK_USE_STRTOK_R_INSTEAD() +#define gmtime(a) UNSAFE_GMTIME_NOT_THREADSAFE_USE_CS_GMTIME_R() +#define localtime(a) UNSAFE_LOCALTIME_NOT_THREADSAFE_USE_LOCALTIME_R() +#define asctime(a) UNSAFE_ASCTIME_NOT_THREADSAFE_USE_ASCTIME_R() +#define ctime(a) UNSAFE_CTIME_NOT_THREADSAFE_USE_CS_CTIME_R() +#define gethostbyaddr(a,b,c) UNSAFE_GETHOSTBYADDR_NOT_THREADSAFE_USE_GETADDRINFO() +#define gethostent(a) UNSAFE_GETHOSTENT_NOT_THREADSAFE() +#define getprotobyname(a) UNSAFE_GETPROTOBYNAME_NOT_THREADSAFE_USE_GETPROTOBYNAME_R() +#define getservbyname(a,b) UNSAFE_GETSERVBYNAME_NOT_THREADSAFE_USE_GETSERVBYNAME_R() +#define getservbyport(a,b) UNSAFE_GETSERVBYPORT_NOT_THREADSAFE_USE_GETSERVBYPORT_R() +#define getservent() UNSAFE_GETSERVENT_NOT_THREADSAFE_USE_GETSERVENT_R() +#define getnetbyname(a) UNSAFE_GETNETBYNAME_NOT_THREADSAFE_USE_GETNETBYNAME_R +#define getnetbyaddr(a,b) UNSAFE_GETNETBYADDR_NOT_THREADSAFE_USE_GETNETBYADDR_R +#define getnetent() UNSAFE_GETNETENT_NOT_THREADSAFE_USE_GETNETENT_R +#define getrpcbyname(a) UNSAFE_GETRPCBYNAME_NOT_THREADSAFE_USE_GETRPCBYNAME_R +#define getrpcbynumber(a) UNSAFE_GETRPCBYNUMBER_NOT_THREADSAFE_USE_GETRPCBYNUMBER_R +#define getrpcent() UNSAFE_GETRPCENT_NOT_THREADSAFE_USE_GETRPCENT_R +#define ctermid(a) UNSAFE_CTERMID_NOT_THREADSAFE_USE_CTERMID_R +#define tmpnam(a) UNSAFE_TMPNAM_NOT_THREADSAFE +#define tempnam(a,b) UNSAFE_TEMPNAM_NOT_THREADSAFE +#define getlogin() UNSAFE_GETLOGIN_NOT_THREADSAFE_USE_GETLOGIN_R +#define getpwnam(a) UNSAFE_GETPWNAM_NOT_THREADSAFE_USE_GETPWNAM_R +#define getpwent() UNSAFE_GETPWENT_NOT_THREADSAFE_USE_GETPWENT_R +#define fgetpwent(a) UNSAFE_FGETPWENT_NOT_THREADSAFE_USE_FGETPWENT_R +#ifndef __ANDROID__ +#define getpwuid(a) UNSAFE_GETPWUID_NOT_THREADSAFE_USE_GETPWUID_R +#endif +#define getspent() UNSAFE_GETSPENT_NOT_THREADSAFE_USE_GETSPENT_R +#define getspnam(a) UNSAFE_GETSPNAM_NOT_THREADSAFE_USE_GETSPNAM_R +#define fgetspent(a) UNSAFE_FGETSPENT_NOT_THREADSAFE_USE_FGETSPENT_R +#define getgrnam(a) UNSAFE_GETGRNAM_NOT_THREADSAFE_USE_GETGRNAM_R +#define getgrent() UNSAFE_GETGRENT_NOT_THREADSAFE_USE_GETGRENT_R +#define getgrgid(a) UNSAFE_GETGRGID_NOT_THREADSAFE_USE_GETGRGID_R +#define fgetgrent() UNSAFE_FGETGRENT_NOT_THREADSAFE_USE_FGETGRGID_R +#define fcvt(a,b,c,d) UNSAFE_FCVT_NOT_THREADSAFE_AND_DEPRECATED +#define ecvt(a,b,c,d) UNSAFE_ECVT_NOT_THREADSAFE_AND_DEPRECATED +#define gcvt(a,b,c) UNSAFE_GCVT_NOT_THREADSAFE_AND_DEPRECATED +#define strptime(a,b,c) STRPTIME_NOT_EXISTS_ON_SOME_DM500_DB2() +#define ftime(a) FTIME_DEPRECATED() +#define timegm(a) TIMEGM_GNU_SPECIFIC_USE_CS_TIMEGM +#endif + +#ifdef UNUSED +#elif __GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +# define UNUSED(x) UNUSED_ ## x __attribute__((unused)) +#elif defined(__LCLINT__) +# define UNUSED(x) /*@unused@*/ x +#else +# define UNUSED(x) x +#endif + +#if __GNUC__ >= 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# define MUST_CHECK_RESULT __attribute__((warn_unused_result)) +#endif + +#ifdef OK +#undef OK +#endif + +#ifdef ERROR +#undef ERROR +#endif + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +#ifdef WITH_DEBUG +# define call(arg) \ + if(arg) \ + { \ + cs_log_dbg(D_TRACE, "ERROR, function call %s returns error.",#arg); \ + return ERROR; \ + } +#else +# define call(arg) \ + if(arg) \ + { \ + return ERROR; \ + } +#endif + +// checking if (X) free(X) unneccessary since freeing a null pointer doesnt do anything +#define NULLFREE(X) {void *tmpX=X; X=NULL; free(tmpX); } + +#ifdef __CYGWIN__ +#define cs_recv(a,b,c,d) cygwin_recv(a,b,c,d) +#else +#define cs_recv(a,b,c,d) recv(a,b,c,d) +#endif + +// safe wrappers to pthread functions +#define fprintf_stderr(fmt, params...) fprintf(stderr, fmt, ##params) + +#define SAFE_PTHREAD_1ARG(a, b, c) { \ + int32_t pter = a(b); \ + if(pter != 0) \ + { \ + c("FATAL ERROR: %s() failed in %s with error %d %s\n", #a, __func__, pter, strerror(pter)); \ + } } + +#define SAFE_MUTEX_LOCK(a) SAFE_PTHREAD_1ARG(pthread_mutex_lock, a, cs_log) +#define SAFE_MUTEX_UNLOCK(a) SAFE_PTHREAD_1ARG(pthread_mutex_unlock, a, cs_log) +#define SAFE_COND_SIGNAL(a) SAFE_PTHREAD_1ARG(pthread_cond_signal, a, cs_log) +#define SAFE_COND_BROADCAST(a) SAFE_PTHREAD_1ARG(pthread_cond_broadcast, a, cs_log) +#define SAFE_RWLOCK_RDLOCK(a) SAFE_PTHREAD_1ARG(pthread_rwlock_rdlock, a, cs_log) +#define SAFE_RWLOCK_WRLOCK(a) SAFE_PTHREAD_1ARG(pthread_rwlock_wrlock, a, cs_log) +#define SAFE_RWLOCK_UNLOCK(a) SAFE_PTHREAD_1ARG(pthread_rwlock_unlock, a, cs_log) +#define SAFE_ATTR_INIT(a) SAFE_PTHREAD_1ARG(pthread_attr_init, a, cs_log) +#define SAFE_MUTEXATTR_INIT(a) SAFE_PTHREAD_1ARG(pthread_mutexattr_init, a, cs_log) +#define SAFE_CONDATTR_INIT(a) SAFE_PTHREAD_1ARG(pthread_condattr_init, a, cs_log) + +#define SAFE_MUTEX_LOCK_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_mutex_lock, a, fprintf_stderr) +#define SAFE_MUTEX_UNLOCK_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_mutex_unlock, a, fprintf_stderr) +#define SAFE_COND_SIGNAL_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_cond_signal, a, fprintf_stderr) +#define SAFE_MUTEX_UNLOCK_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_mutex_unlock, a, fprintf_stderr) +#define SAFE_ATTR_INIT_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_attr_init, a, fprintf_stderr) +#define SAFE_CONDATTR_INIT_NOLOG(a) SAFE_PTHREAD_1ARG(pthread_condattr_init, a, fprintf_stderr) + +#define SAFE_PTHREAD_2ARG(a, b, c, d) { \ + int32_t pter = a(b, c); \ + if(pter != 0) \ + { \ + d("FATAL ERROR: %s() failed in %s with error %d %s\n", #a, __func__, pter, strerror(pter)); \ + } } + +#define SAFE_COND_WAIT(a,b) SAFE_PTHREAD_2ARG(pthread_cond_wait, a, b, cs_log) +#define SAFE_THREAD_JOIN(a,b) SAFE_PTHREAD_2ARG(pthread_join, a, b, cs_log) +#define SAFE_SETSPECIFIC(a,b) SAFE_PTHREAD_2ARG(pthread_setspecific, a, b, cs_log) +#define SAFE_MUTEXATTR_SETTYPE(a,b) SAFE_PTHREAD_2ARG(pthread_mutexattr_settype, a, b, cs_log) +#define SAFE_MUTEX_INIT(a,b) SAFE_PTHREAD_2ARG(pthread_mutex_init, a, b, cs_log) +#define SAFE_COND_INIT(a,b) SAFE_PTHREAD_2ARG(pthread_cond_init, a, b, cs_log) +#define SAFE_CONDATTR_SETCLOCK(a,b) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, cs_log) + +#define SAFE_MUTEX_INIT_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_mutex_init, a, b, fprintf_stderr) +#define SAFE_COND_INIT_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_cond_init, a, b, fprintf_stderr) +#define SAFE_THREAD_JOIN_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_join, a, b, fprintf_stderr) +#define SAFE_CONDATTR_SETCLOCK_NOLOG(a,b) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, fprintf_stderr) + +#define SAFE_PTHREAD_1ARG_R(a, b, c, d) { \ + int32_t pter = a(b); \ + if(pter != 0) \ + { \ + c("FATAL ERROR: %s() failed in %s (called from %s) with error %d %s\n", #a, __func__, d, pter, strerror(pter)); \ + } } + +#define SAFE_MUTEX_LOCK_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_lock, a, cs_log, b) +#define SAFE_MUTEX_UNLOCK_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_unlock, a, cs_log, b) +#define SAFE_COND_SIGNAL_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_cond_signal, a, cs_log, b) +#define SAFE_COND_BROADCAST_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_cond_broadcast, a, cs_log, b) +#define SAFE_CONDATTR_INIT_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_condattr_init, a, cs_log, b) + +#define SAFE_MUTEX_LOCK_NOLOG_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_lock, a, fprintf_stderr, b) +#define SAFE_MUTEX_UNLOCK_NOLOG_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_mutex_unlock, a, fprintf_stderr, b) +#define SAFE_CONDATTR_INIT_NOLOG_R(a, b) SAFE_PTHREAD_1ARG_R(pthread_condattr_init, a, fprintf_stderr, b) + +#define SAFE_PTHREAD_2ARG_R(a, b, c, d, e) { \ + int32_t pter = a(b, c); \ + if(pter != 0) \ + { \ + d("FATAL ERROR: %s() failed in %s (called from %s) with error %d %s\n", #a, __func__, e, pter, strerror(pter)); \ + } } + +#define SAFE_MUTEX_INIT_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_mutex_init, a, b, cs_log, c) +#define SAFE_COND_INIT_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_cond_init, a, b, cs_log, c) +#define SAFE_CONDATTR_SETCLOCK_R(a,b,c) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, cs_log, c) + +#define SAFE_MUTEX_INIT_NOLOG_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_mutex_init, a, b, fprintf_stderr, c) +#define SAFE_COND_INIT_NOLOG_R(a,b,c) SAFE_PTHREAD_2ARG_R(pthread_cond_init, a, b, fprintf_stderr, c) +#define SAFE_CONDATTR_SETCLOCK_NOLOG_R(a,b,c) SAFE_PTHREAD_2ARG(pthread_condattr_setclock, a, b, fprintf_stderr, c) + +#define SAFE_COND_TIMEDWAIT(a, b, c) { \ + int32_t pter; \ + if((c)->tv_nsec < 0) (c)->tv_nsec = 0; \ + if((c)->tv_nsec > 999999999) (c)->tv_nsec = 999999999; \ + pter = pthread_cond_timedwait(a, b, c); \ + if(pter != 0 && pter != ETIMEDOUT) \ + { \ + cs_log("FATAL ERROR: pthread_cond_timedwait failed in %s with error %d %s\n", __func__, pter, strerror(pter)); \ + } } + +#define SAFE_COND_TIMEDWAIT_R(a, b, c, d) { \ + int32_t pter; \ + if((c)->tv_nsec < 0) (c)->tv_nsec = 0; \ + if((c)->tv_nsec > 999999999) (c)->tv_nsec = 999999999; \ + pter = pthread_cond_timedwait(a, b, c); \ + if(pter != 0 && pter != ETIMEDOUT) \ + { \ + cs_log("FATAL ERROR: pthread_cond_timedwait failed in %s (called from %s) with error %d %s\n", __func__, d, pter, strerror(pter)); \ + } } + +#define SAFE_ATTR_SETSTACKSIZE(a,b) { \ + int32_t pter = pthread_attr_setstacksize(a, b); \ + if(pter != 0) \ + { \ + cs_log("WARNING: pthread_attr_setstacksize() failed in %s with error %d %s\n", __func__, pter, strerror(pter)); \ + } } + +#define SAFE_ATTR_SETSTACKSIZE_NOLOG(a,b) { \ + int32_t pter = pthread_attr_setstacksize(a, b); \ + if(pter != 0) \ + { \ + fprintf_stderr("WARNING: pthread_attr_setstacksize() failed in %s with error %d %s\n", __func__, pter, strerror(pter)); \ + } } + +#ifdef NO_PTHREAD_STACKSIZE +#undef SAFE_ATTR_SETSTACKSIZE +#undef SAFE_ATTR_SETSTACKSIZE_NOLOG +#define SAFE_ATTR_SETSTACKSIZE(a,b) +#define SAFE_ATTR_SETSTACKSIZE_NOLOG(a,b) +#endif + +#define CHECK_BIT(var,pos) (((var) & (1<<(pos)))? 1 : 0) + +/* =========================== + * constants + * =========================== */ +#define SCM_URL "https://git.streamboard.tv/common/oscam" +#define WIKI_URL "https://git.streamboard.tv/common/oscam/-/wikis" +#define BOARD_URL "https://board.streamboard.tv" +#ifndef CS_VERSION +#define CS_VERSION "2.26.02-11943" +#endif +#ifndef CS_GIT_COMMIT +#define CS_GIT_COMMIT "a2b4c6d8" +#endif +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO +#define CS_AIO_VERSION CS_VERSION +#define CS_AIO_VERSION_LEN (sizeof(CS_AIO_VERSION)) +#endif +#endif +#ifndef CS_TARGET +# define CS_TARGET "unknown" +#endif +#ifndef CS_CONFDIR +#define CS_CONFDIR "/usr/local/etc" +#endif +#ifndef CS_LOGFILE +#define CS_LOGFILE "/var/log/oscam.log" +#endif +#define CS_QLEN 128 // size of request queue +#define CS_MAXPROV 32 +#define CS_MAXPORTS 32 // max server ports +#define CS_CLIENT_HASHBUCKETS 32 +#define CS_SERVICENAME_SIZE 32 + +#define CS_ECMSTORESIZE 16 // use MD5() +#define CS_EMMSTORESIZE 16 // use MD5() +#define CS_CLIENT_TIMEOUT 5000 +#define CS_CLIENT_MAXIDLE 120 +#define CS_BIND_TIMEOUT 120 +#define CS_DELAY 0 +#define CS_ECM_RINGBUFFER_MAX 0x10 // max size for ECM last responsetimes ringbuffer. Keep this set to power of 2 values! + +// Support for multiple CWs per channel and other encryption algos +//#define WITH_EXTENDED_CW 1 + +#define MAX_ECM_SIZE 1024 +#define MAX_EMM_SIZE 1024 + +#define MAX_CMD_SIZE 0xff + 5 // maximum value from length byte + command header + +#define CS_EMMCACHESIZE 512 // nr of EMMs that each reader will cache +#define MSGLOGSIZE 64 // size of string buffer for a ecm to return messages + +#define D_TRACE 0x0001 // Generate very detailed error/trace messages per routine +#define D_ATR 0x0002 // Debug ATR parsing, dump of ecm, cw +#define D_READER 0x0004 // Debug Reader/Proxy Process +#define D_CLIENT 0x0008 // Debug Client Process +#define D_IFD 0x0010 // Debug IFD+protocol +#define D_DEVICE 0x0020 // Debug Reader I/O +#define D_EMM 0x0040 // Dumps EMM +#define D_DVBAPI 0x0080 // Debug DVBAPI +#define D_LB 0x0100 // Debug Loadbalancer/ECM handler +#define D_CACHEEX 0x0200 // Debug CACHEEX +#define D_CLIENTECM 0x0400 // Debug Client ECMs +#define D_CSP 0x0800 // Debug CSP +#define D_CWC 0x1000 // Debug CWC +#ifdef CS_CACHEEX_AIO +#define D_CW_CACHE 0x2000 // Debug CW Cache +#endif +#define D_ALL_DUMP 0xFFFF // dumps all + +#ifdef CS_CACHEEX_AIO +#define MAX_DEBUG_LEVELS 14 +#else +#define MAX_DEBUG_LEVELS 13 +#endif + +/////// phoenix readers which need baudrate setting and timings need to be guarded by OSCam: BEFORE R_MOUSE +#define R_DB2COM1 0x1 // Reader Dbox2 @ com1 +#define R_DB2COM2 0x2 // Reader Dbox2 @ com1 +#define R_SC8in1 0x3 // Reader Sc8in1 or MCR +#define R_MP35 0x4 // AD-Teknik Multiprogrammer 3.5 and 3.6 (only usb tested) +#define R_MOUSE 0x5 // Reader smartcard mouse +/////// internal readers (Dreambox, Coolstream, IPBox) are all R_INTERNAL, they are determined compile-time +#define R_INTERNAL 0x6 // Reader smartcard intern +/////// readers that do not reed baudrate setting and timings are guarded by reader itself (large buffer built in): AFTER R_SMART +#define R_SMART 0x7 // Smartreader+ +#define R_PCSC 0x8 // PCSC +#define R_DRECAS 0x9 // Reader DRECAS +/////// proxy readers after R_CS378X +#define R_CAMD35 0x20 // Reader cascading camd 3.5x +#define R_CAMD33 0x21 // Reader cascading camd 3.3x +#define R_NEWCAMD 0x22 // Reader cascading newcamd +#define R_RADEGAST 0x23 // Reader cascading radegast +#define R_CS378X 0x24 // Reader cascading camd 3.5x TCP +#define R_CONSTCW 0x25 // Reader for Constant CW +#define R_CSP 0x26 // Cache CSP +#define R_GHTTP 0x27 // Reader ghttp +#define R_SCAM 0x28 // Reader cascading scam +/////// peer to peer proxy readers after R_CCCAM +#define R_GBOX 0x30 // Reader cascading gbox +#define R_CCCAM 0x35 // Reader cascading cccam +#define R_PANDORA 0x36 // Reader cascading pandora +#define R_SERIAL 0x80 // Reader serial +#define R_IS_NETWORK 0x60 +#define R_IS_CASCADING 0xE0 + +#define is_network_reader(__X) (__X->typ & R_IS_NETWORK) +#define is_cascading_reader(__X) (__X->typ & R_IS_CASCADING) +#define is_smargo_reader(__X) (__X->crdr && strcmp(__X->crdr->desc, "smargo") == 0) + +// ECM rc codes: +/////// all found +#define E_FOUND 0 +#define E_CACHE1 1 +#define E_CACHE2 2 +#define E_CACHEEX 3 +/////// all notfound, some error or problem +#define E_NOTFOUND 4 // for selection of found, use < E_NOTFOUND +#define E_TIMEOUT 5 +#define E_SLEEPING 6 +#define E_FAKE 7 +#define E_INVALID 8 +#define E_CORRUPT 9 +#define E_NOCARD 10 +#define E_EXPDATE 11 +#define E_DISABLED 12 +#define E_STOPPED 13 // for selection of error, use <= E_STOPPED and exclude selection of found + +#define E_ALREADY_SENT 101 +#define E_WAITING 102 +#define E_99 99 // this code is undocumented +#define E_UNHANDLED 100 // for selection of unhandled, use >= E_UNHANDLED + +#define CS_MAX_MOD 20 +#define MOD_CONN_TCP 1 +#define MOD_CONN_UDP 2 +#define MOD_CONN_NET 3 +#define MOD_CONN_SERIAL 4 +#define MOD_NO_CONN 8 + +#define EMM_UNIQUE 1 +#define EMM_SHARED 2 +#define EMM_GLOBAL 4 +#define EMM_UNKNOWN 8 + +// Listener Types +#define LIS_CAMD33TCP 1 +#define LIS_CAMD35UDP 2 +#define LIS_CAMD35TCP 4 +#define LIS_NEWCAMD 8 +#define LIS_CCCAM 16 +#define LIS_GBOX 32 +#define LIS_RADEGAST 64 +#define LIS_DVBAPI 128 +#define LIS_CONSTCW 256 +#define LIS_SERIAL 1024 +#define LIS_CSPUDP 2048 +#define LIS_SCAM 4096 + +// EMM types: +#define UNKNOWN 0 +#define UNIQUE 1 +#define SHARED 2 +#define GLOBAL 3 + +#define NCD_AUTO 0 +#define NCD_524 1 +#define NCD_525 2 + +// moved from reader-common.h +#define UNKNOWN 0 +#define CARD_NEED_INIT 1 +#define CARD_INSERTED 2 +#define CARD_FAILURE 3 +#define NO_CARD 4 +#define READER_DEVICE_ERROR 5 + +// moved from stats +#define DEFAULT_REOPEN_SECONDS 30 +#define DEFAULT_MIN_ECM_COUNT 5 +#define DEFAULT_MAX_ECM_COUNT 500 +#define DEFAULT_NBEST 1 +#define DEFAULT_NFB 1 +#define DEFAULT_RETRYLIMIT 0 +#define DEFAULT_LB_MODE 0 +#define DEFAULT_LB_STAT_CLEANUP 336 +#define DEFAULT_UPDATEINTERVAL 240 +#define DEFAULT_LB_AUTO_BETATUNNEL 1 +#define DEFAULT_LB_AUTO_BETATUNNEL_MODE 0 +#define DEFAULT_LB_AUTO_BETATUNNEL_PREFER_BETA 50 + +#define DEFAULT_MAX_CACHE_TIME 15 +#define DEFAULT_MAX_HITCACHE_TIME 15 + +#define DEFAULT_LB_AUTO_TIMEOUT 0 +#define DEFAULT_LB_AUTO_TIMEOUT_P 30 +#define DEFAULT_LB_AUTO_TIMEOUT_T 300 + +enum {E1_GLOBAL = 0, E1_USER, E1_READER, E1_SERVER, E1_LSERVER}; + +// LB blocking events: +enum {E2_GLOBAL = 0, E2_GROUP, E2_CAID, E2_IDENT, E2_CLASS, E2_CHID, E2_QUEUE, E2_OFFLINE, + E2_SID, E2_CCCAM_NOCARD, + // From here only LB nonblocking events: + E2_CCCAM_NOK1, E2_CCCAM_NOK2, E2_CCCAM_LOOP, E2_WRONG_CHKSUM, E2_RATELIMIT + }; + +#define LB_NONBLOCK_E2_FIRST E2_CCCAM_NOK1 + +#define CTA_RES_LEN 512 + +#define MAX_ATR_LEN 33 // max. ATR length +#define MAX_HIST 15 // max. number of historical characters + +#define MAX_SIDBITS 64 // max services +#define SIDTABBITS uint64_t // 64bit type for services, if a system does not support this type, +// please use a define and define it as uint32_t / MAX_SIDBITS 32 + +#define BAN_UNKNOWN 1 // Failban mask for anonymous/ unknown contact +#define BAN_DISABLED 2 // Failban mask for Disabled user +#define BAN_SLEEPING 4 // Failban mask for sleeping user +#define BAN_DUPLICATE 8 // Failban mask for duplicate user + +#define MAX_HTTP_DYNDNS 3 // maximum allowed Dyndns addresses for webif access + +#define CHECK_WAKEUP 1 +#define CHECK_ANTICASCADER 2 +#define CHECK_ECMCACHE 3 + +#define AVAIL_CHECK_CONNECTED 0 +#define AVAIL_CHECK_LOADBALANCE 1 + +#define ECM_FMT_LEN 109 // 64 +#define CXM_FMT_LEN 209 // 160 + +#define LB_MAX_STAT_TIME 10 + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) +#define OSCAM_SIGNAL_WAKEUP SIGCONT +#else +#define OSCAM_SIGNAL_WAKEUP SIGRTMAX-2 +#endif + +#define READER_ACTIVE 0x01 +#define READER_FALLBACK 0x02 +#define READER_LOCAL 0x04 +#define READER_CACHEEX 0x08 + +#define REQUEST_SENT 0x10 +#define REQUEST_ANSWERED 0x20 + +#define CW_MODE_ONE_CW 0 +#define CW_MODE_MULTIPLE_CW 1 +#define CW_TYPE_VIDEO 0 +#define CW_TYPE_AUDIO 1 +#define CW_TYPE_DATA 2 +#define CW_ALGO_CSA 0 +#define CW_ALGO_DES 1 +#define CW_ALGO_AES128 2 +#define CW_ALGO_CSA_ALT 3 +#define CW_ALGO_MODE_ECB 0 +#define CW_ALGO_MODE_CBC 1 + +#define SIZE_SHORTDAY 8 +#define MAXALLOWEDTF 1001 // 10 allowed time frame slots for everyday + all [(3 + 1 + 10*(12) + 1)*8] +extern const char *shortDay[SIZE_SHORTDAY]; +extern const char *weekdstr; + +/* =========================== + * Default Values + * =========================== */ +#define DEFAULT_INACTIVITYTIMEOUT 0 +#define DEFAULT_TCP_RECONNECT_TIMEOUT 30 +#define DEFAULT_NCD_KEEPALIVE 0 + +#define DEFAULT_CC_MAXHOPS 10 +#define DEFAULT_CC_RESHARE -1 // Use global cfg +#define DEFAULT_CC_IGNRSHR -1 // Use global cfg +#define DEFAULT_CC_STEALTH -1 // Use global cfg +#define DEFAULT_CC_KEEPALIVE 0 +#define DEFAULT_CC_RECONNECT 12000 +#define DEFAULT_CC_RECV_TIMEOUT 2000 + +#define DEFAULT_AC_USERS -1 // Use global cfg +#define DEFAULT_AC_PENALTY -1 // Use global cfg + +// Return MPEG section length +#define SCT_LEN(sct) (3+((sct[1]&0x0f)<<8)+sct[2]) +// Used by readers +#define MAX_LEN 256 + +#define NO_CAID_VALUE 0xfffe +#define NO_PROVID_VALUE 0xfffffe +#define NO_SRVID_VALUE 0xfffe + +// If NULL return empty string +#define ESTR(x) ((x) ? (x) : "") + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + See: http://stackoverflow.com/questions/10269685/kernels-container-of-any-way-to-make-it-iso-conforming + http://www.kroah.com/log/linux/container_of.html +*/ +#define container_of(ptr, type, member) \ + ((type *) ((char *) (ptr) - offsetof(type, member) + \ + (&((type *) 0)->member == (ptr)) * 0)) + +/* =========================== + * global structures + * =========================== */ +struct timeb +{ + time_t time; + int64_t millitm; +}; + +typedef struct cs_mutexlock +{ + int32_t timeout; + pthread_mutex_t lock; + pthread_cond_t writecond, readcond; + const char *name; + int8_t flag; + int16_t writelock, readlock; +} CS_MUTEX_LOCK; + +#include "oscam-llist.h" + +typedef struct s_caidvaluetab_data +{ + uint16_t caid; + uint16_t value; +} CAIDVALUETAB_DATA; + +typedef struct s_caidvaluetab +{ + int32_t cvnum; + CAIDVALUETAB_DATA *cvdata; +} CAIDVALUETAB; + +typedef struct s_classtab +{ + uint8_t an; + uint8_t bn; + uint8_t *aclass; + uint8_t *bclass; +} CLASSTAB; + +typedef struct s_caidtab_data +{ + uint16_t caid; + uint16_t mask; + uint16_t cmap; +} CAIDTAB_DATA; + +typedef struct s_caidtab +{ + int32_t ctnum; + CAIDTAB_DATA *ctdata; +} CAIDTAB; + +typedef struct s_tuntab_data +{ + uint16_t bt_caidfrom; + uint16_t bt_caidto; + uint16_t bt_srvid; +} TUNTAB_DATA; + +typedef struct s_tuntab +{ + int32_t ttnum; + TUNTAB_DATA *ttdata; +} TUNTAB; + +typedef struct s_sidtab +{ + char label[64]; +#ifdef CS_CACHEEX_AIO + uint8_t disablecrccws_only_for_exception; + uint8_t no_wait_time; + uint8_t lg_only_exception; +#endif + uint16_t num_caid; + uint16_t num_provid; + uint16_t num_srvid; + uint16_t *caid; + uint32_t *provid; + uint16_t *srvid; + struct s_sidtab *next; +} SIDTAB; + +typedef struct s_filter +{ + uint16_t caid; + uint8_t nprids; + uint32_t prids[CS_MAXPROV]; +} FILTER; + +typedef struct s_ftab +{ + int32_t nfilts; + FILTER *filts; +} FTAB; + +typedef struct s_ncd_ftab +{ + int32_t nfilts; + FILTER filts[16]; +} NCD_FTAB; + +struct ncd_port +{ + bool ncd_key_is_set; + uint8_t ncd_key[14]; + NCD_FTAB ncd_ftab; +}; + +typedef struct s_port +{ + int32_t fd; + int32_t s_port; + struct ncd_port *ncd; // newcamd specific settings +} PORT; + +typedef struct s_ptab +{ + int32_t nports; + PORT ports[CS_MAXPORTS]; +} PTAB; + +typedef struct aes_entry +{ + uint16_t keyid; + uint16_t caid; + uint32_t ident; + uint8_t plainkey[16]; + AES_KEY key; + struct aes_entry *next; +} AES_ENTRY; + +struct aes_keys +{ + AES_KEY aeskey_encrypt; // encryption key needed by monitor and used by camd33, camd35 + AES_KEY aeskey_decrypt; // decryption key needed by monitor and used by camd33, camd35 +}; + +struct s_ecm +{ + uint8_t ecmd5[CS_ECMSTORESIZE]; + uint8_t cw[16]; + uint16_t caid; + uint64_t grp; + struct s_reader *reader; + int32_t rc; + time_t time; +}; + +struct s_emmstat +{ + uint8_t emmd5[CS_EMMSTORESIZE]; + uint8_t type; + int32_t count; + struct timeb firstwritten; + struct timeb lastwritten; +}; + +struct s_emmcache +{ + uint8_t emmd5[CS_EMMSTORESIZE]; + uint8_t type; + uint16_t len; + uint8_t emm[MAX_EMM_SIZE]; + struct timeb firstseen; + struct timeb lastseen; +}; + +struct s_csystem_emm_filter +{ + uint8_t type; + uint8_t enabled; + uint8_t filter[16]; + uint8_t mask[16]; +}; + +typedef struct v_ban // Failban listmember +{ + int32_t v_count; + IN_ADDR_T v_ip; + int32_t v_port; + struct timeb v_time; + bool acosc_entry; + int32_t acosc_penalty_dur; + char *info; +} V_BAN; + +typedef struct s_cacheex_stat_entry // Cacheex stats listmember +{ + int32_t cache_count; + time_t cache_last; + uint16_t cache_caid; + uint16_t cache_srvid; + uint32_t cache_prid; + int8_t cache_direction; // 0 = push / 1 = got +#ifdef CS_CACHEEX_AIO + int32_t cache_count_lg; +#endif +} S_CACHEEX_STAT_ENTRY; + +typedef struct s_entitlement // contains entitlement Info +{ + uint64_t id; // the element ID + uint32_t type; // enumerator for tier,chid whatever + // 0="", 1="Package", 2="PPV-Event", 3="chid", 4="tier", 5 = "class", 6 = "PBM". 7 = "seca-admin" + uint16_t caid; // the caid of element + uint32_t provid; // the provid of element + uint32_t class; // the class needed for some systems + time_t start; // startdate + time_t end; // enddate +} S_ENTITLEMENT; + +struct s_client; +struct ecm_request_t; +struct emm_packet_t; +struct cmd_packet_t; +struct s_ecm_answer; +struct demux_s; + +#define DEFAULT_MODULE_BUFSIZE 1024 + +struct s_module +{ + const char *desc; + int8_t type; + int8_t large_ecm_support; + int16_t listenertype; + //int32_t s_port; + IN_ADDR_T s_ip; + uint16_t bufsize; + void *(*s_handler)(struct s_client *, uint8_t *, int32_t); + void (*s_init)(struct s_client *); + int32_t (*recv)(struct s_client *, uint8_t *, int32_t); + void (*send_dcw)(struct s_client *, struct ecm_request_t *); + void (*cleanup)(struct s_client *); + int32_t (*c_recv_chk)(struct s_client *, uint8_t *, int32_t *, uint8_t *, int32_t); + int32_t (*c_init)(struct s_client *); + int32_t (*c_send_ecm)(struct s_client *, struct ecm_request_t *); + int32_t (*c_send_emm)(struct emm_packet_t *); + int32_t (*c_available)(struct s_reader *, int32_t, struct ecm_request_t *); // Schlocke: available check for load-balancing, + // params: + // rdr (reader to check) + // int32_t checktype (0=return connected, 1=return loadbalance-avail) return int + void (*c_idle)(void); // Schlocke: called when reader is idle + void (*s_idle)(struct s_client *); + void (*s_peer_idle)(struct s_client *); + void (*c_card_info)(void); // Schlocke: request card infos + + int32_t (*c_capmt)(struct s_client *, struct demux_s *); + +#ifdef CS_CACHEEX + int32_t (*c_cache_push)(struct s_client *, struct ecm_request_t *); // Cache push + int32_t (*c_cache_push_chk)(struct s_client *, struct ecm_request_t *); // Cache push Node Check, 0=no push +#endif + int32_t c_port; + PTAB ptab; + int32_t num; +}; + +struct s_ATR; + +struct s_cardreader_settings +{ + uint32_t ETU; + uint32_t EGT; + uint8_t P; + uint32_t I; + uint32_t F; + uint32_t Fi; + uint8_t Di; + uint8_t Ni; + uint32_t WWT; + uint32_t BGT; + uint8_t D; +}; + +struct s_cardreader +{ + const char *desc; + int32_t (*reader_init)(struct s_reader *); + int32_t (*get_status)(struct s_reader *, int *); + int32_t (*activate)(struct s_reader *, struct s_ATR *); + int32_t (*transmit)(struct s_reader *, uint8_t *sent, uint32_t size, uint32_t expectedlen, uint32_t delay, uint32_t timeout); + int32_t (*receive)(struct s_reader *, uint8_t *data, uint32_t size, uint32_t delay, uint32_t timeout); + int32_t (*lock_init)(struct s_reader *); + void (*lock)(struct s_reader *); + void (*unlock)(struct s_reader *); + int32_t (*close)(struct s_reader *); + int32_t (*set_parity)(struct s_reader *, uint8_t parity); + int32_t (*write_settings)(struct s_reader *, struct s_cardreader_settings *s); + int32_t (*set_protocol)(struct s_reader *, uint8_t *params, uint32_t *length, uint32_t len_request); + int32_t (*set_baudrate)(struct s_reader *, uint32_t baud); // set only for readers which need baudrate setting and timings need to be guarded by OSCam + int32_t (*card_write)(struct s_reader *pcsc_reader, const uint8_t *buf, uint8_t *cta_res, uint16_t *cta_lr, int32_t l); + void (*display_msg)(struct s_reader *, char *msg); + + int32_t (*do_reset)(struct s_reader *, struct s_ATR *, int32_t (*rdr_activate_card)(struct s_reader *, struct s_ATR *, uint16_t deprecated), int32_t (*rdr_get_cardsystem)(struct s_reader *, struct s_ATR *)); + + bool (*set_DTS_RTS)(struct s_reader *, int32_t *dtr, int32_t *rts); + + int32_t typ; // fixme: workaround, remove when all old code is converted + + int8_t max_clock_speed; // 1 for reader->typ > R_MOUSE + int8_t need_inverse; // 0 = reader does inversing; 1 = inversing done by oscam + //io_serial config + int8_t flush; + int8_t read_written; // 1 = written bytes has to read from device + bool skip_extra_atr_parsing; + bool skip_t1_command_retries; + bool skip_setting_ifsc; +}; + +struct s_cardsystem +{ + const char *desc; + const uint16_t *caids; + int32_t (*card_init)(struct s_reader *reader, struct s_ATR *); + void (*card_done)(struct s_reader *reader); + int32_t (*card_info)(struct s_reader *); + void (*poll_status)(struct s_reader *); + int32_t (*do_ecm)(struct s_reader *, const struct ecm_request_t *, struct s_ecm_answer *); + int32_t (*do_emm_reassembly)(struct s_reader *, struct s_client *, struct emm_packet_t *); // Returns 1/true if the EMM is ready to be written in the card + int32_t (*do_emm)(struct s_reader *, struct emm_packet_t *); + int32_t (*do_rawcmd)(struct s_reader *, struct cmd_packet_t *); + void (*post_process)(struct s_reader *); + int32_t (*get_emm_type)(struct emm_packet_t *, struct s_reader *); + int32_t (*get_emm_filter)(struct s_reader *, struct s_csystem_emm_filter **, uint32_t *); + int32_t (*get_emm_filter_adv)(struct s_reader *, struct s_csystem_emm_filter **, uint32_t *, uint16_t, uint32_t, uint16_t, uint16_t, uint16_t, uint32_t); + int32_t (*get_tunemm_filter)(struct s_reader *, struct s_csystem_emm_filter **, uint32_t *); +}; + +typedef struct cw_extendted_t +{ +#ifdef WITH_EXTENDED_CW + uint8_t mode; + uint8_t audio[4][16]; // 4 x odd/even pairs of 8 byte CWs + uint8_t data[16]; // odd/even pair of 8 byte CW or 16 byte IV + uint8_t session_word[32]; // odd/even pair of 16 byte CW + uint8_t algo; // CSA, DES or AES128 + uint8_t algo_mode; // ECB or CBC +#else + uint8_t disabled; +#endif +} EXTENDED_CW; + +typedef struct ecm_request_t +{ + uint8_t ecm[MAX_ECM_SIZE]; + uint8_t cw[16]; + EXTENDED_CW cw_ex; + uint8_t ecmd5[CS_ECMSTORESIZE]; + int16_t ecmlen; + uint16_t caid; + uint16_t ocaid; // original caid, used for betatunneling + uint16_t srvid; + uint16_t onid; + uint16_t tsid; + uint16_t pmtpid; + uint32_t ens; // enigma namespace + uint32_t vpid; // videopid + uint16_t chid; + uint16_t pid; + uint16_t idx; + uint32_t prid; + struct s_reader *selected_reader; + struct s_ecm_answer *matching_rdr; // list of matching readers + const struct s_reader *fallback; // fallback is the first fallback reader in the list matching_rdr + struct s_client *client; // contains pointer to 'c' client while running in 'r' client + uint64_t grp; + int32_t msgid; // client pending table index + uint8_t stage; // processing stage in server module + int8_t rc; + uint8_t rcEx; + struct timeb tps; // incoming time stamp + int8_t btun; // mark er as betatunneled + uint16_t reader_avail; // count of available readers for ecm + uint16_t readers; // count of available used readers for ecm + uint16_t reader_requested; // count of real requested readers + uint16_t localreader_count; // count of selected local readers + uint16_t cacheex_reader_count; // count of selected cacheex mode-1 readers + uint16_t fallback_reader_count; // count of selected fb readers + uint16_t reader_count; // count of selected not fb readers + int8_t preferlocalcards; + int8_t checked; // for doublecheck + uint8_t cw_checked[16]; // for doublecheck + int8_t readers_timeout_check; // set to 1 after ctimeout occurs and readers not answered are checked + uint32_t client_timeout; // clienttimeout per CAID + struct s_reader *origin_reader; + +#if defined MODULE_CCCAM + void *origin_card; // CCcam preferred card! +#endif + +#if defined MODULE_GBOX + uint32_t gbox_crc; // rcrc for gbox, used to identify ECM task in peer responses + uint16_t gbox_cw_src_peer; + uint16_t gbox_ecm_src_peer; + uint8_t gbox_ecm_dist; + uint8_t gbox_ecm_status; + LLIST *gbox_cards_pending; // type gbox_card_pending +#endif + + void *src_data; + uint32_t csp_hash; // csp has its own hash + + struct s_client *cacheex_src; // Cacheex origin +#ifdef CS_CACHEEX + int8_t cacheex_pushed; // to avoid duplicate pushs + uint8_t csp_answered; // =1 if er get answer by csp + LLIST *csp_lastnodes; // last 10 Cacheex nodes atm cc-proto-only + uint32_t cacheex_wait_time; // cacheex wait time in ms + uint8_t cacheex_wait_time_expired; // =1 if cacheex wait_time expires + uint16_t cacheex_mode1_delay; // cacheex mode 1 delay + uint8_t cacheex_hitcache; // =1 if wait_time due hitcache + void *cw_cache; // pointer to cw stored in cache +#endif +#ifdef CS_CACHEEX_AIO + int32_t ecm_time; // ecm-time in ms + uint8_t localgenerated; // flag for local generated CW +#endif + uint32_t cw_count; + uint8_t from_csp; // =1 if er from csp cache + uint8_t from_cacheex; // =1 if er from cacheex client pushing cache + uint8_t from_cacheex1_client; // =1 if er from cacheex-1 client + char msglog[MSGLOGSIZE]; + uint8_t cwc_cycletime; + uint8_t cwc_next_cw_cycle; +#ifdef CW_CYCLE_CHECK + char cwc_msg_log[MSGLOGSIZE]; +#endif +#ifdef WITH_STAPI5 + char dev_name[20]; +#endif + struct ecm_request_t *parent; + struct ecm_request_t *next; +#ifdef HAVE_DVBAPI + uint8_t adapter_index; +#endif +} ECM_REQUEST; + + +struct s_ecm_answer +{ + uint8_t status; + struct s_reader *reader; + ECM_REQUEST *er; + int8_t rc; + uint8_t rcEx; + uint8_t cw[16]; + EXTENDED_CW cw_ex; + char msglog[MSGLOGSIZE]; + struct timeb time_request_sent; // using for evaluate ecm_time + int32_t ecm_time; + uint16_t tier; // only filled by local videoguard reader atm +#ifdef WITH_LB + int32_t value; + int32_t time; +#endif + struct s_ecm_answer *next; + CS_MUTEX_LOCK ecmanswer_lock; + struct s_ecm_answer *pending; + struct s_ecm_answer *pending_next; + bool is_pending; +}; + +struct s_acasc_shm +{ + uint16_t ac_count : 15; + uint16_t ac_deny : 1; +}; + +struct s_acasc +{ + uint16_t stat[10]; + uint8_t idx; // current active index in stat[] +}; + +struct s_cwresponse +{ + int32_t duration; + time_t timestamp; + int32_t rc; +}; + +struct s_cascadeuser +{ + uint16_t caid; + uint32_t prid; + uint16_t srvid; + time_t time; + int8_t cwrate; +}; + +typedef struct sidtabs +{ + SIDTABBITS ok; // positive services + SIDTABBITS no; // negative services +} SIDTABS; + +struct s_zap_list +{ + uint16_t caid; + uint32_t provid; + uint16_t chid; + uint16_t sid; + int8_t request_stage; + time_t lasttime; +}; + +// EMM reassemply +struct emm_rass +{ + int16_t emmlen; + int32_t provid; + uint8_t emm[MAX_EMM_SIZE]; +}; + +struct s_client +{ + uint32_t tid; + int8_t init_done; + pthread_mutex_t thread_lock; + int8_t thread_active; + int8_t kill; + int8_t kill_started; + LLIST *joblist; + IN_ADDR_T ip; + in_port_t port; + time_t login; // connection + time_t logout; // disconnection + time_t last; + time_t lastswitch; + time_t lastemm; + time_t lastecm; + time_t expirationdate; + uint32_t allowedtimeframe[SIZE_SHORTDAY][24][2]; // day[0-sun to 6-sat, 7-ALL],hours,minutes use as binary flags to reduce mem usage + uint8_t allowedtimeframe_set; // flag for internal use to mention if allowed time frame is used + int8_t c35_suppresscmd08; + uint8_t c35_sleepsend; + int8_t ncd_keepalive; + int8_t disabled; + uint64_t grp; + int8_t crypted; + int8_t dup; + LLIST *aureader_list; + int8_t autoau; + LLIST *ra_buf; // EMM reassembly buffer for viaccess + struct emm_rass *cw_rass; // EMM reassembly buffer for cryptoworks + int8_t monlvl; + CAIDTAB ctab; + TUNTAB ttab; + SIDTABS sidtabs; + SIDTABS lb_sidtabs; + int8_t typ; // first s_client is type s=starting (master) thread; type r = physical reader, type p = proxy reader both always have 1 s_reader struct allocated; type c = client (user logging in into oscam) type m = monitor type h = http server a = anticascader + uint8_t module_idx; + uint16_t last_srvid; + uint32_t last_provid; + uint16_t last_caid; + struct s_provid *last_providptr; + struct s_srvid *last_srvidptr; + uint32_t last_srvidptr_search_provid; + int32_t tosleep; + struct s_auth *account; + int32_t udp_fd; + struct SOCKADDR udp_sa; + socklen_t udp_sa_len; + int8_t tcp_nodelay; + int8_t log; + int32_t logcounter; + int32_t cwfound; // count found ECMs per client + int32_t cwcache; // count ECMs from cache1/2 per client + int32_t cwnot; // count not found ECMs per client + int32_t cwtun; // count betatunneled ECMs per client + int32_t cwignored; // count ignored ECMs per client + int32_t cwtout; // count timeouted ECMs per client + int32_t cwlastresptime; // last Responsetime (ms) +#ifdef CW_CYCLE_CHECK + int32_t cwcycledchecked; // count checked cwcycles per client + int32_t cwcycledok; // count pos checked cwcycles per client + int32_t cwcyclednok; // count neg checked cwcycles per client + int32_t cwcycledign; // count ign cwcycles per client +#endif + int32_t emmok; // count EMM ok + int32_t emmnok; // count EMM nok + int8_t pending; // number of ECMs pending +#ifdef CS_CACHEEX + int32_t cwcacheexpush; // count pushed ecms/cws + int32_t cwcacheexgot; // count got ecms/cws + int32_t cwcacheexhit; // count hit ecms/cws + LLIST *ll_cacheex_stats; // list for Cacheex statistics + int8_t cacheex_maxhop; + int32_t cwcacheexerr; // cw=00 or chksum wrong + int32_t cwcacheexerrcw; // same Hex, different CW + int16_t cwcacheexping; // peer ping in ms, only used by csp + int32_t cwc_info; // count of in/out comming cacheex ecms with CWCinfo + uint8_t cxnodeid_last[9]; // save previous nodeid + uint8_t cxnodeid_changer_detected; // save node-changer detection status +#ifdef CS_CACHEEX_AIO + int32_t cwcacheexgotlg; // count got localgenerated-flagged CWs + int32_t cwcacheexpushlg; // count pushed localgenerated-flagged CWs +#endif + uint8_t cacheex_needfilter; // flag for cachex mode 3 used with camd35 +#ifdef CS_CACHEEX_AIO + uint8_t cacheex_aio_checked; // flag for cacheex aio detection done +#endif +#endif +#ifdef CS_ANTICASC + struct s_zap_list client_zap_list[15]; // 15 last zappings from client used for ACoSC +#endif +#ifdef WEBIF + struct s_cwresponse cwlastresptimes[CS_ECM_RINGBUFFER_MAX]; // ringbuffer for last 20 times + int32_t cwlastresptimes_last; // ringbuffer pointer + int8_t wihidden; // hidden in webinterface status + char lastreader[64]; // last cw got from this reader +#endif + + uint8_t ucrc[4]; // needed by monitor and used by camd35 + uint32_t pcrc; // password crc + struct aes_keys *aes_keys; // used by camd33 and camd35 + uint16_t ncd_msgid; + uint16_t ncd_client_id; + uint8_t ncd_skey[16]; // Also used for camd35 Cacheex to store remote node id + +#ifdef MODULE_CCCAM + void *cc; +#endif + +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP) + uint8_t c35_extmode; +#endif +#endif + +#ifdef MODULE_GBOX + void *gbox; + uint16_t gbox_peer_id; +#endif + +#ifdef MODULE_GHTTP + void *ghttp; +#endif + + int32_t port_idx; // index in server ptab + int32_t ncd_server; // newcamd server + +#ifdef CS_ANTICASC + int32_t ac_fakedelay; // When this is -1, the global ac_fakedelay is used + uint16_t ac_limit; + int8_t ac_penalty; + struct s_acasc_shm acasc; +#endif + + FTAB fchid; + FTAB ftab; // user [caid] and ident filter + CLASSTAB cltab; + + int32_t pfd; // Primary FD, must be closed on exit + struct s_reader *reader; // points to s_reader when cl->typ='r' + + ECM_REQUEST *ecmtask; + + pthread_t thread; + +#ifdef MODULE_SERIAL + struct s_serial_client *serialdata; +#endif + // reader common + int32_t last_idx; + uint16_t idx; + + int8_t ncd_proto; + uint8_t ncd_header[12]; + + // camd35 + uint8_t upwd[64]; + int8_t is_udp; + int8_t stopped; + uint16_t lastcaid; + uint16_t lastsrvid; + int32_t lastpid; + int8_t disable_counter; + uint8_t lastserial[8]; + + // Failban value set bitwise - compared with BAN_ + int32_t failban; + + LLIST *cascadeusers; // s_cascadeuser + + int32_t n_request[2]; // count for number of request per minute by client + + void *work_mbuf; // Points to local data allocated in work_thread when the thread is running + void *work_job_data; // Points to current job_data when work_thread is running + +#ifdef MODULE_PANDORA + int32_t pand_autodelay; + uint8_t pand_send_ecm; + uint8_t pand_ignore_ecm; + uint8_t pand_md5_key[16]; +#endif + +#ifdef MODULE_SCAM + void *scam; +#endif + void *module_data; // private module data + + struct s_client *next; // make client a linked list + struct s_client *nexthashed; + + int8_t start_hidecards; +}; + +typedef struct s_ecm_whitelist_data +{ + uint16_t len; + uint16_t caid; + uint32_t ident; +} ECM_WHITELIST_DATA; + +typedef struct s_ecm_whitelist +{ + int32_t ewnum; + ECM_WHITELIST_DATA *ewdata; +} ECM_WHITELIST; + +typedef struct s_ecm_hdr_whitelist_data +{ + uint16_t len; + uint16_t caid; + uint32_t provid; + uint8_t header[20]; +} ECM_HDR_WHITELIST_DATA; + +typedef struct s_ecm_hdr_whitelist +{ + int32_t ehnum; + ECM_HDR_WHITELIST_DATA *ehdata; +} ECM_HDR_WHITELIST; + +// ratelimit +struct ecmrl +{ + struct timeb last; + uint8_t kindecm; + bool once; + uint8_t ecmd5[CS_ECMSTORESIZE]; + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t chid; + int32_t ratelimitecm; + int32_t ratelimittime; + int32_t srvidholdtime; +}; +#define MAXECMRATELIMIT 20 + +// maxparallel service tracking +struct s_parallel_slot +{ + uint16_t srvid; // service ID + struct timeb last_ecm; // time of last ECM for this service + int32_t ecm_interval; // measured interval between ECMs in ms + struct s_client *client; // client using this slot +}; + +// maxparallel blocked client tracking +struct s_blocked_client +{ + struct s_client *client; // client pointer + uint16_t srvid; // blocked service ID +}; + +#ifdef MODULE_SERIAL +struct ecmtw +{ + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t deg; + uint16_t freq; +}; +#endif + +typedef struct ce_csp_tab_data +{ + int32_t caid; + int32_t cmask; + int32_t prid; + int32_t srvid; + int16_t awtime; + int16_t dwtime; +} CECSPVALUETAB_DATA; + +typedef struct ce_csp_tab +{ + int32_t cevnum; + CECSPVALUETAB_DATA *cevdata; +} CECSPVALUETAB; + +typedef struct cacheex_check_cw_tab_data +{ + int32_t caid; + int32_t cmask; + int32_t prid; + int32_t srvid; + int8_t mode; + uint32_t counter; +} CWCHECKTAB_DATA; + +typedef struct cacheex_check_cw_tab +{ + int32_t cwchecknum; + CWCHECKTAB_DATA *cwcheckdata; + +} CWCHECKTAB; + +typedef struct cacheex_check_cw +{ + int8_t mode; + uint32_t counter; +} CWCHECK; + +typedef struct ce_csp_t +{ + int8_t mode; + int8_t maxhop; +#ifdef CS_CACHEEX_AIO + int8_t maxhop_lg; +#endif + CAIDVALUETAB maxhop_percaid; +#ifdef CS_CACHEEX_AIO + CAIDVALUETAB maxhop_lg_percaid; +#endif + CECSPVALUETAB filter_caidtab; + uint8_t allow_request; + uint8_t allow_reforward; + uint8_t drop_csp; + uint8_t allow_filter; +#ifdef CS_CACHEEX_AIO + uint8_t allow_maxhop; +#endif + uint8_t block_fakecws; +#ifdef CS_CACHEEX_AIO + uint8_t cw_check_for_push; + uint8_t localgenerated_only; + CAIDTAB localgenerated_only_caidtab; + FTAB lg_only_tab; + uint8_t localgenerated_only_in; + CAIDTAB localgenerated_only_in_caidtab; + FTAB lg_only_in_tab; + uint8_t lg_only_in_aio_only; + uint8_t lg_only_remote_settings; + int32_t feature_bitfield; + CAIDVALUETAB cacheex_nopushafter_tab; + char aio_version[CS_AIO_VERSION_LEN]; +#endif +} CECSP; + +struct s_emmlen_range +{ + int16_t min; + int16_t max; +}; + +typedef struct emm_packet_t +{ + uint8_t emm[MAX_EMM_SIZE]; + int16_t emmlen; + uint8_t caid[2]; + uint8_t provid[4]; + uint8_t hexserial[8]; // contains hexserial or SA of EMM + uint8_t type; + uint8_t skip_filter_check; + struct s_client *client; +} EMM_PACKET; + +typedef struct cmd_packet_t +{ + uint8_t cmd[MAX_CMD_SIZE]; + int16_t cmdlen; + struct s_client *client; +} CMD_PACKET; + +struct s_reader // contains device info, reader info and card info +{ + uint8_t keepalive; + uint8_t changes_since_shareupdate; + int32_t resetcycle; // ECM until reset + int32_t resetcounter; // actual count + uint32_t auprovid; // AU only for this provid + int8_t audisabled; // exclude reader from auto AU + struct timeb emm_last; // time of last successfully written emm + struct s_client *client; // pointer to 'r'client this reader is running in + LLIST *ll_entitlements; // entitlements + int8_t enable; + int8_t active; + int8_t dropbadcws; // Schlocke: 1=drops cw if checksum is wrong. 0=fix checksum (default) + int8_t disablecrccws; // 1=disable cw checksum test. 0=enable checksum check + uint64_t grp; + int8_t fallback; + FTAB fallback_percaid; + FTAB localcards; + FTAB disablecrccws_only_for; // ignore checksum for selected caid provid +#ifdef READER_CRYPTOWORKS + int8_t needsglobalfirst; // 0:Write one Global EMM for SHARED EMM disabled 1:Write one Global EMM for SHARED EMM enabled +#endif +#if defined(READER_NAGRA) + uint8_t cak63nuid[4]; + uint8_t cak63nuid_length; + uint8_t cak63cwekey[16]; + uint8_t cak63cwekey_length; +#endif +#ifdef READER_NAGRA_MERLIN + uint8_t mod1[112]; + uint8_t mod1_length; + uint8_t cmd0eprov[2]; + uint8_t cmd0eprov_length; + uint8_t mod2[112]; + uint8_t mod2_length; + uint8_t tmprsa[112]; + uint8_t data50[80]; + uint8_t data50_length; + uint8_t mod50[80]; + uint8_t mod50_length; + uint8_t key3588[136]; + uint8_t key3588_length; + uint8_t key60[96]; + uint8_t exp60[96]; + uint8_t key68[104]; + uint8_t exp68[104]; + uint8_t key3des[16]; + uint8_t klucz68[24]; + uint8_t pairtype; + uint8_t hasunique; + uint8_t key3460[96]; + uint8_t key3460_length; + uint8_t key3310[16]; + uint8_t key3310_length; + uint8_t cwekey[17][16]; + uint8_t cwekey_length[17]; + uint8_t idird[4]; + uint8_t idird_length; + uint8_t kdt05_00[216]; + uint8_t kdt05_10[208]; + uint8_t edata[255]; + uint8_t dt5num; + uint8_t out[255]; + uint8_t ideakey1[16]; + uint8_t block3[8]; + uint8_t v[8]; + uint8_t iout[8]; + uint32_t dword_83DBC; + uint8_t data2[4]; + uint8_t ecmheader[4]; + uint8_t timestmp1[4]; + uint8_t timestmp2[4]; + uint8_t cak7expo[0x11]; + uint8_t data[0x80]; + uint8_t step1[0x60]; + uint8_t step2[0x68]; + uint8_t step3[0x6c]; + uint8_t encrypted[0x68]; + uint8_t result[104]; + uint8_t stillencrypted[0x50]; + uint8_t resultrsa[0x50]; + uint32_t cak7_seq; + uint32_t needrestart; + uint8_t otpcsc[2]; + uint8_t otpcsc_length; + uint8_t otacsc[2]; + uint8_t otacsc_length; + uint8_t forcepair[1]; + uint8_t forcepair_length; + uint8_t cak7_camstate; + uint8_t cak7_aes_key[32]; + uint8_t cak7_aes_iv[16]; + int8_t forcecwswap; + int8_t evensa; + int8_t forceemmg; + int8_t cwpkota; + int8_t headermode; + struct timeb last_refresh; +#endif +#if defined(READER_DGCRYPT) || defined(READER_NAGRA_MERLIN) + uint8_t cardid[8]; +#endif +#ifdef CS_CACHEEX + CECSP cacheex; // CacheEx Settings + uint8_t cxnodeid_last[9]; // save previous nodeid + uint8_t cxnodeid_changer_detected; // save node-changer detection status +#endif + int32_t typ; + char label[64]; +#ifdef WEBIF + char *description; +#endif + char device[128]; +#ifdef WITH_CARDREADER + uint16_t slot; // in case of multiple slots like sc8in1; first slot = 1 + int32_t handle; // device handle + int64_t handle_nr; // device handle_nr for mutiple readers same driver + int32_t fdmc; // device handle for multicam + int32_t detect; + int32_t mhz; // actual clock rate of reader in 10khz steps + int32_t cardmhz; // standard clock speed your card should have in 10khz steps; normally 357 but for Irdeto cards 600 + int32_t divider; // PLL divider for internal readers +#endif + int32_t r_port; + char r_usr[64]; + char r_pwd[64]; + int32_t l_port; + CAIDTAB ctab; + uint32_t boxid; +#ifdef READER_TONGFANG + uint32_t tongfang_version; + uint8_t tongfang3_commkey[8]; + uint32_t tongfang3_calibsn; + uint32_t tongfang_boxid; + uint8_t tongfang3_deskey[8]; + uint8_t tongfang3_deskey_length; + uint8_t stbid[8]; + uint8_t stbid_length; +#endif +#ifdef READER_NAGRA_MERLIN + int8_t cak7_mode; + uint8_t cak7type; + uint8_t cwpkcaid[2]; + uint8_t cwpkcaid_length; + uint8_t nuid[4]; + uint8_t nuid_length; +#endif +#ifdef READER_NAGRA + int8_t nagra_read; // read nagra ncmed records: 0 Disabled (default), 1 read all records, 2 read valid records only + int8_t detect_seca_nagra_tunneled_card; +#endif +#ifdef READER_IRDETO + int8_t force_irdeto; +#endif +#ifdef WITH_CARDREADER + uint8_t boxkey[16]; // n3 boxkey 8 bytes, seca sessionkey 16 bytes, viaccess camid 4 bytes + uint8_t boxkey_length; + uint8_t rsa_mod[120]; // rsa modulus for nagra cards. + uint8_t rsa_mod_length; + uint8_t cwpk_mod[16]; // cwpk modulus for conax cards. + uint8_t cwpk_mod_length; + uint8_t des_key[128]; // 3des key for Viaccess 16 bytes, des key for Dre 128 bytes + uint8_t des_key_length; +#endif + uint8_t card_atr[64]; // ATR readed from card + int8_t card_atr_length; // length of ATR +#ifdef WITH_CARDREADER + uint8_t atr[64]; + int32_t atrlen; + int8_t seca_nagra_card; // seca nagra card +#endif + SIDTABS sidtabs; + SIDTABS lb_sidtabs; + uint8_t hexserial[8]; + int32_t nprov; + uint8_t prid[CS_MAXPROV][8]; + uint8_t sa[CS_MAXPROV][4]; // viaccess & seca +#if defined(READER_NAGRA) || defined(READER_NAGRA_MERLIN) + int32_t nsa; + int32_t nemm82u; + int32_t nemm84; + int32_t nemm84s; + int32_t nemm83u; + int32_t nemm83s; + int32_t nemm87; + uint8_t emm82u[CS_MAXPROV][7]; + uint8_t emm84[CS_MAXPROV][3]; + uint8_t emm84s[CS_MAXPROV][6]; + uint8_t emm83; + uint8_t emm83u[CS_MAXPROV][6]; + uint8_t emm83s[CS_MAXPROV][6]; + uint8_t emm87[CS_MAXPROV][6]; + uint8_t emm82; +#endif + uint8_t read_old_classes; // viaccess + uint8_t maturity; // viaccess & seca maturity level + uint16_t caid; + uint16_t cak7_emm_caid; + uint16_t b_nano; + uint16_t s_nano; + int8_t ecmcommand; // used for filtering nagra bad ecm commands + uint8_t ecmcommandcache[5]; // cachebuff for ecm commands + int32_t blockemm; + int32_t saveemm; + LLIST *blockemmbylen; + char *emmfile; + char pincode[5]; + int8_t logemm; + int8_t cachemm; + int16_t rewritemm; + int16_t deviceemm; // catch device specific emms (so far only used for viaccess) + int8_t card_status; +#ifdef WITH_CARDREADER + int8_t deprecated; // if 0 ATR obeyed, if 1 default speed (9600) is chosen; for devices that cannot switch baudrate +#endif + int8_t resetalways; // send reset after each commands (for pscs) + struct s_module ph; + const struct s_cardreader *crdr; + void *crdr_data; // Private card reader data + bool crdr_flush; // sci readers may disable flush per reader + const struct s_cardsystem *csystem; + void *csystem_data; // Private card system data + bool csystem_active; + uint8_t ncd_key[14]; + uint8_t ncd_skey[16]; + int8_t ncd_connect_on_init; + int8_t ncd_disable_server_filt; + int8_t ncd_proto; + int8_t currenthops; // number of hops (cccam & gbox) +#ifdef MODULE_CCCAM + char cc_version[7]; // cccam version + char cc_build[7]; // cccam build number + int8_t cc_maxhops; // cccam max distance + int8_t cc_mindown; // cccam min downhops + uint32_t cc_id; + int8_t cc_keepalive; + int8_t cc_hop; // For non-cccam reader: hop for virtual cards + int8_t cc_reshare; + int32_t cc_reconnect; // reconnect on ecm-request timeout +#endif + int8_t tcp_connected; + int32_t tcp_ito; // inactivity timeout + int32_t tcp_rto; // reconnect timeout + int32_t tcp_reconnect_delay; // max tcp connection block delay + uint8_t ipv4force; + uint8_t ipv6_connect_failed; + struct timeb tcp_block_connect_till; // time tcp connect ist blocked + int32_t tcp_block_delay; // incrementing block time + time_t last_g; // get (if last_s-last_g>tcp_rto - reconnect ) + time_t last_s; // send + time_t last_check; // last checked + time_t last_poll; // last poll + FTAB fchid; + FTAB ftab; + CLASSTAB cltab; + ECM_WHITELIST ecm_whitelist; + ECM_HDR_WHITELIST ecm_hdr_whitelist; + int32_t brk_pos; + int32_t msg_idx; + int32_t secatype; // 0=not determined, 2=seca2, 3=nagra(~seca3) this is only valid for localreaders! + uint32_t maxreadtimeout; // in us + uint32_t minreadtimeout; // in us + uint32_t maxwritetimeout; // in us + uint32_t minwritetimeout; // in us +#if defined(WEBIF) || defined(LCDSUPPORT) + int32_t emmwritten[4]; // count written EMM + int32_t emmskipped[4]; // count skipped EMM + int32_t emmerror[4]; // count error EMM + int32_t emmblocked[4]; // count blocked EMM + int32_t webif_emmwritten[4]; // count written EMM for webif reader info + int32_t webif_emmskipped[4]; // count skipped EMM for webif reader info + int32_t webif_emmerror[4]; // count error EMM for reader webif info + int32_t webif_emmblocked[4]; // count blocked EMM for reader webif info + int32_t lbvalue; // loadbalance Value +#endif +#ifdef WITH_AZBOX + int32_t azbox_mode; +#endif +#ifdef WITH_CARDREADER + int32_t use_gpio; // Should this reader use GPIO functions + int gpio_outen; // fd of opened /dev/gpio/outen + int gpio_out; // fd of opened /dev/gpio/out + int gpio_in; // fd of opened /dev/gpio/in + uint32_t gpio; // gpio addr + // variables from icc_async.h start + int32_t convention; // Convention of this ICC + uint8_t protocol_type; // Type of protocol + uint32_t current_baudrate; // (for overclocking uncorrected) baudrate to prevent unnecessary conversions from/to termios structure + double worketu; // in us for internal and external readers calculated (1/D)*(F/cardclock)*1000000 + uint32_t read_timeout; // Max timeout (ETU) to receive characters + uint32_t char_delay; // Delay (ETU) after transmiting each successive char + uint32_t block_delay; // Delay (ms) after starting to transmit + uint32_t BWT, CWT; // (for overclocking uncorrected) block waiting time, character waiting time, in ETU + // variables from io_serial.h + int32_t written; // keep score of how much bytes are written to serial port, since they are echoed back they have to be read + // variables from protocol_t1.h + uint16_t ifsc; // Information field size for the ICC + uint8_t ns; // Send sequence number + int16_t smartdev_found; + int16_t smart_type; + uint16_t statuscnt; + uint16_t modemstat; +#endif +#ifdef READER_CRYPTOWORKS + EMM_PACKET *last_g_emm; // last global EMM + bool last_g_emm_valid; // status of last global EMM +#endif + uint8_t rom[15]; + uint8_t irdId[4]; +#ifdef READER_VIDEOGUARD + uint8_t payload4C[15]; + uint16_t VgCredit; + uint16_t VgPin; + uint8_t VgFuse; + uint8_t VgCountryC[3]; + uint8_t VgRegionC[8]; + uint8_t VgLastPayload[6]; + int32_t card_startdate_basemonth; + int32_t card_startdate_baseyear; + int32_t card_expiredate_basemonth; + int32_t card_expiredate_baseyear; +#endif +#ifdef WITH_LB + int32_t lb_weight; // loadbalance weight factor, if unset, weight=100. The higher the value, the higher the usage-possibility + int8_t lb_force_fallback; // force this reader as fallback if fallback or fallback_percaid paramters set + int32_t lb_usagelevel; // usagelevel for loadbalancer + int32_t lb_usagelevel_ecmcount; + struct timeb lb_usagelevel_time; // time for counting ecms, this creates usagelevel + struct timeb lb_last; // time for oldest reader + LLIST *lb_stat; // loadbalancer reader statistics + CS_MUTEX_LOCK lb_stat_lock; + int32_t lb_stat_busy; // do not add while saving +#endif + time_t card_valid_to; + // ratelimit + int32_t ratelimitecm; + int32_t ratelimittime; // ratelimit time in ms (everything below 60 ms is converted to ms by applying *1000) + int8_t ecmunique; // check for matching ecm hash in ratelimitslot + int32_t srvidholdtime; // time in ms to keep srvid in ratelimitslot (during this time not checked for ecmunique!) + // (everything below 60 ms is converted to ms by applying *1000) + struct timeb lastdvbapirateoverride; + uint32_t ecmsok; +#ifdef CS_CACHEEX_AIO + uint32_t ecmsoklg; +#endif + uint32_t webif_ecmsok; + uint32_t ecmsnok; + uint32_t webif_ecmsnok; + uint32_t ecmstout; + uint32_t webif_ecmstout; + uint32_t ecmnotfoundlimit; // config setting. restart reader if ecmsnok >= ecmnotfoundlimit + int32_t ecmsfilteredhead; // count filtered ECM's by ECM Headerwhitelist + int32_t ecmsfilteredlen; // count filtered ECM's by ECM Whitelist + int32_t webif_ecmsfilteredhead; // count filtered ECM's by ECM Headerwhitelist to readers ecminfo + int32_t webif_ecmsfilteredlen; // count filtered ECM's by ECM Whitelist to readers ecm info + float ecmshealthok; +#ifdef CS_CACHEEX_AIO + float ecmshealthoklg; +#endif + float ecmshealthnok; + float ecmshealthtout; + int32_t cooldown[2]; + int32_t maxparallel; // max parallel active services, 0 = unlimited (default) + float parallelfactor; // zapping tolerance multiplier (default: 2.0) + int32_t paralleltimeout; // timeout buffer in ms after expected ECM (default: 1000) + struct s_parallel_slot *parallel_slots; // regular service tracking (size: maxparallel) + struct s_parallel_slot *parallel_slots_prov; // pending service tracking (size: maxparallel * parallelfactor, rounded) + LLIST *blocked_services; // list of s_blocked_client for dropped services + CS_MUTEX_LOCK parallel_lock; // lock for parallel_slots access + int8_t parallel_full; // flag: 1 if reader is at capacity limit + int8_t cooldownstate; + struct timeb cooldowntime; + struct ecmrl rlecmh[MAXECMRATELIMIT]; +#ifdef READER_VIDEOGUARD + int8_t ndsversion; // 0 auto (default), 1 NDS1, 12 NDS1+, 2 NDS2 + int8_t fix_07; + int8_t fix_9993; + int8_t readtiers; // method to get videoguard tiers + uint8_t ins7E[0x1A + 1]; + uint8_t ins7E11[0x01 + 1]; + uint8_t ins42[0x25 + 1]; + uint8_t ins2e06[0x04 + 1]; + uint8_t k1_generic[0x10 + 1]; // k1 for generic pairing mode + uint8_t k1_unique[0x10 + 1]; // k1 for unique pairing mode +#endif +#ifdef WITH_CARDREADER + int8_t smargopatch; + int8_t autospeed; // 1 clockspeed set according to atr f max + int8_t ins7e11_fast_reset; + uint8_t sc8in1_dtrrts_patch; // fix for kernel commit 6a1a82df91fa0eb1cc76069a9efe5714d087eccd +#endif + +#ifdef READER_VIACCESS + AES_ENTRY *aes_list; // multi AES linked list + uint8_t initCA28; // To set when CA28 succeed + uint32_t key_schedule1[32]; + uint32_t key_schedule2[32]; +#endif + +#if defined(READER_DRE) || defined(READER_DRECAS) + char *userscript; + uint32_t force_ua; +#endif + +#ifdef READER_DRECAS + char *stmkeys; +#endif + +#ifdef MODULE_GBOX + uint8_t gbox_maxdist; + uint8_t gbox_maxecmsend; + uint8_t gbox_reshare; + int8_t gbox_cccam_reshare; + char last_gsms[128]; + uint16_t gbox_remm_peer; + uint16_t gbox_gsms_peer; + uint8_t gbox_force_remm; + uint16_t gbox_cw_src_peer; + uint8_t gbox_crd_slot_lev; + FTAB ccc_gbx_reshare_ident; + uint8_t send_offline_cmd; + uint16_t nb_send_crds; +#endif + +#ifdef MODULE_PANDORA + uint8_t pand_send_ecm; +#endif +#ifdef MODULE_GHTTP + uint8_t ghttp_use_ssl; +#endif +#ifdef READER_CONAX + uint8_t cnxlastecm; // == 0 - last ecm has not been paired ecm, > 0 last ecm has been paired ecm +#endif + LLIST *emmstat; // emm stats + CS_MUTEX_LOCK emmstat_lock; + struct s_reader *next; +}; + +struct s_cpmap +{ + uint16_t caid; + uint32_t provid; + uint16_t sid; + uint16_t chid; + uint16_t dwtime; + struct s_cpmap *next; +}; + +struct s_auth +{ + char usr[64]; + char *pwd; +#ifdef WEBIF + char *description; +#endif + int8_t uniq; +#ifdef CS_CACHEEX + CECSP cacheex; // CacheEx Settings + uint8_t no_wait_time; + uint8_t disablecrccacheex; + FTAB disablecrccacheex_only_for; +#endif + int16_t allowedprotocols; + LLIST *aureader_list; + int8_t autoau; + uint8_t emm_reassembly; // 0 = OFF; 1 = OFF / DVBAPI = ON; 2 = ON (default) + int8_t monlvl; + uint64_t grp; + int32_t tosleep; + int32_t umaxidle; + CAIDTAB ctab; + SIDTABS sidtabs; + FTAB fchid; + FTAB ftab; // user [caid] and ident filter + CLASSTAB cltab; + TUNTAB ttab; + int8_t preferlocalcards; + uint32_t max_connections; +#ifdef CS_ANTICASC + int32_t ac_fakedelay; // When this is -1, the global ac_fakedelay is used + int32_t ac_users; // 0 - unlimited + int8_t ac_penalty; // 0 - log, >0 - fake dw + struct s_acasc ac_stat; + int8_t acosc_max_ecms_per_minute; // user value 0 - unlimited + int8_t acosc_max_active_sids; // user value 0 - unlimited + int8_t acosc_zap_limit; // user value 0 - unlimited + int8_t acosc_penalty; // user value penalty + int32_t acosc_penalty_duration; // user value how long is penalty activ in sek. + time_t acosc_penalty_until; + int8_t acosc_penalty_active; // 0-deaktiv 1-max_active_sids 2-zap_limit 3-max_ecms_per_minute 4-penaly_duration + int32_t acosc_delay; // user value + int8_t acosc_user_zap_count; + time_t acosc_user_zap_count_start_time; +#endif +#ifdef WITH_LB + int32_t lb_nbest_readers; // When this is -1, the global lb_nbest_readers is used + int32_t lb_nfb_readers; // When this is -1, the global lb_nfb_readers is used + CAIDVALUETAB lb_nbest_readers_tab; // like nbest_readers, but for special caids +#endif + IN_ADDR_T dynip; + char *dyndns; + time_t expirationdate; + time_t firstlogin; + uint32_t allowedtimeframe[SIZE_SHORTDAY][24][2]; // day[0-sun to 6-sat, 7-ALL],hours,minutes use as binary flags to reduce mem usage + uint8_t allowedtimeframe_set; // flag for internal use to mention if allowed time frame is used + int8_t c35_suppresscmd08; + uint8_t c35_sleepsend; + int8_t ncd_keepalive; +#ifdef MODULE_CCCAM + int32_t cccmaxhops; + int8_t cccreshare; + int8_t cccignorereshare; + int8_t cccstealth; +#endif + int8_t disabled; + int32_t failban; + + int32_t cwfound; + int32_t cwcache; + int32_t cwnot; + int32_t cwtun; + int32_t cwignored; + int32_t cwtout; +#ifdef CW_CYCLE_CHECK + int32_t cwcycledchecked; // count checked cwcycles per client + int32_t cwcycledok; // count pos checked cwcycles per client + int32_t cwcyclednok; // count neg checked cwcycles per client + int32_t cwcycledign; // count ign cwcycles per client + int8_t cwc_disable; // disable cwc checking for this Client +#endif + int32_t emmok; + int32_t emmnok; +#ifdef CS_CACHEEX + int32_t cwcacheexpush; // count pushed ecms/cws + int32_t cwcacheexgot; // count got ecms/cws + int32_t cwcacheexhit; // count hit ecms/cws + int32_t cwcacheexerr; // cw=00 or chksum wrong + int32_t cwcacheexerrcw; // Same Hex, different CW + int32_t cwc_info; // count of in/out comming cacheex ecms with CWCinfo + uint8_t cxnodeid_last[9]; // save previous nodeid + uint8_t cxnodeid_changer_detected; // save node-changer detection status +#ifdef CS_CACHEEX_AIO + int32_t cwcacheexgotlg; // count got localgenerated-flagged CWs + int32_t cwcacheexpushlg; // count pushed localgenerated-flagged CWs +#endif +#endif + struct s_auth *next; +}; + + +struct s_srvid_caid +{ + uint16_t caid; + uint16_t nprovid; + uint32_t *provid; +}; + +struct s_srvid +{ + uint16_t srvid; + int8_t ncaid; + struct s_srvid_caid *caid; + char *data; + const char *prov; + const char *name; + const char *type; + const char *desc; + struct s_srvid *next; +}; + +struct s_rlimit +{ + struct ecmrl rl; + struct s_rlimit *next; +}; + +struct s_cw +{ + uint8_t cw[16]; +}; + +struct s_fakecws +{ + uint32_t count; + struct s_cw *data; +}; + +#ifdef MODULE_SERIAL +struct s_twin +{ + struct ecmtw tw; + struct s_twin *next; +}; +#endif + +struct s_tierid +{ + uint16_t tierid; + int8_t ncaid; + uint16_t caid[10]; + char name[33]; + struct s_tierid *next; +}; + +struct s_provid +{ + uint16_t caid; + uint16_t nprovid; + uint32_t *provid; + char prov[33]; + char sat[33]; + char lang[33]; + struct s_provid *next; +}; + +struct s_ip +{ + IN_ADDR_T ip[2]; + struct s_ip *next; +}; + +struct s_global_whitelist +{ + uint32_t line; // linenr of oscam.whitelist file, starting with 1 + char type; // w or i or l + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t chid; + uint16_t pid; + uint16_t ecmlen; + uint16_t mapcaid; + uint32_t mapprovid; + struct s_global_whitelist *next; +}; + +struct s_cacheex_matcher +{ + uint32_t line; // linenr of oscam.Cacheex file, starting with 1 + char type; // m + uint16_t caid; + uint32_t provid; + uint16_t srvid; + uint16_t chid; + uint16_t pid; + uint16_t ecmlen; + + uint16_t to_caid; + uint32_t to_provid; + uint16_t to_srvid; + uint16_t to_chid; + uint16_t to_pid; + uint16_t to_ecmlen; + + int32_t valid_from; + int32_t valid_to; + + struct s_cacheex_matcher *next; +}; + +struct s_config +{ + int32_t nice; + uint32_t netprio; + uint32_t ctimeout; + uint32_t ftimeout; + CAIDVALUETAB ftimeouttab; + CAIDVALUETAB ctimeouttab; // clienttimeout_percaid + uint32_t cmaxidle; + int32_t ulparent; + uint32_t delay; + int32_t bindwait; + int32_t tosleep; + IN_ADDR_T srvip; + char *usrfile; + char *cwlogdir; + char *emmlogdir; + char *logfile; + char *mailfile; + int8_t disablecrccws; // 1=disable cw checksum test. 0=enable checksum check + FTAB disablecrccws_only_for; // ignore checksum for selected caid provid +#ifdef CS_CACHEEX_AIO + uint8_t cacheex_srcname_webif; // show cacheex-srcname not cache3 in log +#endif + uint8_t logtostdout; + uint8_t logtosyslog; + int8_t logduplicatelines; + int32_t initial_debuglevel; + char *sysloghost; + int32_t syslogport; +#if defined(WEBIF) || defined(MODULE_MONITOR) + uint32_t loghistorylines; +#endif + int8_t disablelog; + int8_t disablemail; + int8_t disableuserfile; + int8_t usrfileflag; + struct s_auth *account; + struct s_srvid *srvid[16]; + struct s_tierid *tierid; + struct s_provid *provid; + struct s_sidtab *sidtab; +#ifdef MODULE_MONITOR + int32_t mon_port; + IN_ADDR_T mon_srvip; + struct s_ip *mon_allowed; + uint8_t mon_level; +#endif + int32_t aulow; + int32_t hideclient_to; +#ifdef WEBIF + int32_t http_port; + IN_ADDR_T http_srvip; + char *http_user; + char *http_pwd; + char *http_css; + int8_t http_prepend_embedded_css; + char *http_jscript; + char *http_tpl; + char *http_piconpath; + char *http_script; +#ifndef WEBIF_JQUERY + char *http_extern_jquery; +#endif + int32_t http_refresh; + int32_t poll_refresh; + int8_t http_hide_idle_clients; + char *http_hide_type; + int8_t http_showpicons; + int8_t http_picon_size; + int8_t http_status_log; + int8_t http_showmeminfo; + int8_t http_showecminfo; + int8_t http_showloadinfo; + int8_t http_showuserinfo; + int8_t http_showreaderinfo; + int8_t http_showcacheexinfo; + struct s_ip *http_allowed; + int8_t http_readonly; + IN_ADDR_T http_dynip[MAX_HTTP_DYNDNS]; + uint8_t http_dyndns[MAX_HTTP_DYNDNS][64]; + int8_t http_use_ssl; + int8_t https_force_secure_mode; + int8_t https_auto_create_cert; + char *http_cert; + char *http_help_lang; + char *http_locale; + char *http_oscam_label; + int32_t http_emmu_clean; + int32_t http_emms_clean; + int32_t http_emmg_clean; +#endif + int8_t http_full_cfg; + int8_t http_overwrite_bak_file; + int32_t failbantime; + int32_t failbancount; + LLIST *v_list; // Failban list +#ifdef MODULE_CAMD33 + int32_t c33_port; + IN_ADDR_T c33_srvip; + uint8_t c33_key[16]; + int32_t c33_crypted; + int32_t c33_passive; + struct s_ip *c33_plain; +#endif +#if defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP) + int32_t c35_port; + IN_ADDR_T c35_srvip; + int8_t c35_tcp_suppresscmd08; + int8_t c35_udp_suppresscmd08; + PTAB c35_tcp_ptab; + IN_ADDR_T c35_tcp_srvip; +#endif + int8_t c35_suppresscmd08; // used in cccam module + int8_t getblockemmauprovid; + int32_t umaxidle; //User max Idle +#ifdef MODULE_NEWCAMD + PTAB ncd_ptab; + IN_ADDR_T ncd_srvip; + uint8_t ncd_key[14]; + int8_t ncd_keepalive; + int8_t ncd_mgclient; + struct s_ip *ncd_allowed; +#endif +#ifdef MODULE_RADEGAST + int32_t rad_port; + IN_ADDR_T rad_srvip; + struct s_ip *rad_allowed; + char *rad_usr; +#endif +#ifdef MODULE_CCCAM + uint16_t cc_port[CS_MAXPORTS]; + int8_t cc_reshare; + int8_t cc_ignore_reshare; + int32_t cc_update_interval; + IN_ADDR_T cc_srvip; + char cc_version[7]; + int8_t cc_minimize_cards; + int8_t cc_keep_connected; + int8_t cc_stealth; + int8_t cc_reshare_services; + int8_t cc_forward_origin_card; + uint8_t cc_fixed_nodeid[8]; + uint32_t cc_recv_timeout; // The poll() timeout parameter in ms. Default: DEFAULT_CC_RECV_TIMEOUT (2000 ms). +#endif +#ifdef MODULE_GBOX + #define GBOX_MY_VERS_DEF 0x2A + #define GBOX_MY_CPU_API_DEF 0x61 + #define GBOX_MAX_PROXY_CARDS 32 + #define GBOX_MAX_IGNORED_PEERS 16 + #define GBOX_MAX_BLOCKED_ECM 16 + #define GBOX_MAX_REMM_PEERS 8 + #define GBOX_MAX_DEST_PEERS 16 + #define GBOX_MAX_MSG_TXT 127 + uint16_t gbox_port[CS_MAXPORTS]; + char *gbox_hostname; + uint32_t gbox_reconnect; + uint32_t gbox_password; + unsigned long gbox_proxy_card[GBOX_MAX_PROXY_CARDS]; + int8_t gbox_proxy_cards_num; + uint32_t gbox_my_vers; + uint8_t gbox_my_cpu_api; + uint8_t gsms_dis; + uint8_t log_hello; + uint8_t dis_attack_txt; + char *gbox_tmp_dir; + uint8_t cc_gbx_reshare_en; + uint16_t gbox_ignored_peer[GBOX_MAX_IGNORED_PEERS]; + uint8_t gbox_ignored_peer_num; + uint16_t accept_remm_peer[GBOX_MAX_REMM_PEERS]; + uint8_t accept_remm_peer_num; + uint16_t gbox_block_ecm[GBOX_MAX_BLOCKED_ECM]; + uint8_t gbox_block_ecm_num; + uint8_t gbox_save_gsms; + uint8_t gbox_msg_type; + uint16_t gbox_dest_peers[GBOX_MAX_DEST_PEERS]; + uint8_t gbox_dest_peers_num; + char gbox_msg_txt[GBOX_MAX_MSG_TXT+1]; +#endif +#ifdef MODULE_SERIAL + char *ser_device; +#endif + int32_t max_log_size; + int8_t waitforcards; + int32_t waitforcards_extra_delay; + int8_t preferlocalcards; + int32_t reader_restart_seconds; // schlocke: reader restart auf x seconds, disable = 0 + int8_t dropdups; // drop duplicate logins + + + // Loadbalancer-Config: + int32_t lb_mode; // schlocke: reader loadbalancing mode + int32_t lb_auto_betatunnel; // automatic selection of betatunnel convertion based on learned data + int32_t lb_auto_betatunnel_mode; // automatic selection of betatunnel direction +#ifdef WITH_LB + int32_t lb_save; // schlocke: load/save statistics to file, save every x ecms + int32_t lb_nbest_readers; // count of best readers + int32_t lb_nfb_readers; // count of fallback readers + int32_t lb_min_ecmcount; // minimal ecm count to evaluate lbvalues + int32_t lb_max_ecmcount; // maximum ecm count before reseting lbvalues + int32_t lb_reopen_seconds; // time between retrying failed readers/caids/prov/srv + int8_t lb_reopen_invalid; // default=1; if 0, rc=E_INVALID will be blocked until stats cleaned + int8_t lb_force_reopen_always; // force reopening immediately all failing readers if no matching reader found + int32_t lb_retrylimit; // reopen only happens if reader response time > retrylimit + CAIDVALUETAB lb_retrylimittab; + CAIDVALUETAB lb_nbest_readers_tab; // like nbest_readers, but for special caids + CAIDTAB lb_noproviderforcaid; // do not store loadbalancer stats with providers for this caid + char *lb_savepath; // path where the stat file is save. Empty=default=/tmp/.oscam/stat + int32_t lb_stat_cleanup; // duration in hours for cleaning old statistics + int32_t lb_max_readers; // limit the amount of readers during learning + int32_t lb_auto_betatunnel_prefer_beta; // prefer-beta-over-nagra factor + int32_t lb_auto_timeout; // Automatic timeout by loadbalancer statistics + int32_t lb_auto_timeout_p; // percent added to avg time as timeout time + int32_t lb_auto_timeout_t; // minimal time added to avg time as timeout time +#endif + int32_t resolve_gethostbyname; + int8_t double_check; // schlocke: Double checks each ecm+dcw from two (or more) readers + FTAB double_check_caid; // do not store loadbalancer stats with providers for this caid + +#ifdef HAVE_DVBAPI + int8_t dvbapi_enabled; + int8_t dvbapi_au; + char *dvbapi_usr; + int8_t dvbapi_boxtype; + int8_t dvbapi_pmtmode; + int8_t dvbapi_requestmode; + int32_t dvbapi_listenport; // TCP port to listen instead of camd.socket (network mode, default=0 -> disabled) + IN_ADDR_T dvbapi_srvip; + SIDTABS dvbapi_sidtabs; + int32_t dvbapi_delayer; // delayer ms, minimum time to write cw + int8_t dvbapi_ecminfo_file; // Enable or disable ecm.info file creation + int8_t dvbapi_ecminfo_type; + int8_t dvbapi_read_sdt; + int8_t dvbapi_write_sdt_prov; + int8_t dvbapi_extended_cw_api; +#ifdef MODULE_STREAMRELAY + int8_t dvbapi_demuxer_fix; +#endif +#endif + +#ifdef CS_ANTICASC + int8_t ac_enabled; + int32_t ac_users; // num of users for account (0 - default) + int32_t ac_stime; // time to collect AC statistics (3 min - default) + int32_t ac_samples; // qty of samples + int8_t ac_penalty; // 0 - write to log + int32_t ac_fakedelay; // 100-1000 ms + int32_t ac_denysamples; + char *ac_logfile; + struct s_cpmap *cpmap; + int8_t acosc_enabled; + int8_t acosc_max_ecms_per_minute; // global value 0 - unlimited + int8_t acosc_max_active_sids; // global value 0 - unlimited + int8_t acosc_zap_limit; // global value 0 - unlimited + int32_t acosc_penalty_duration; // global value how long is penalty activ in sek. + int8_t acosc_penalty; // global value + int32_t acosc_delay; // global value +#endif + +#ifdef LEDSUPPORT + int8_t enableled; // 0=disabled led, 1=enable led for routers, 2=enable qboxhd led +#endif + +#ifdef LCDSUPPORT + int8_t enablelcd; + char *lcd_output_path; + int32_t lcd_hide_idle; + int32_t lcd_write_intervall; +#endif + +#ifdef MODULE_PANDORA + int8_t pand_skip_send_dw; + struct s_ip *pand_allowed; + char *pand_usr; + char *pand_pass; + int8_t pand_ecm; + int32_t pand_port; + IN_ADDR_T pand_srvip; +#endif + +#ifdef MODULE_SCAM + int32_t scam_port; + IN_ADDR_T scam_srvip; + struct s_ip *scam_allowed; +#endif + +#ifdef MODULE_STREAMRELAY + int8_t stream_relay_enabled; + int32_t stream_relay_port; + char *stream_relay_user; + CAIDTAB stream_relay_ctab; // use the stream server for these caids + char *stream_source_host; + int8_t stream_client_source_host; + int32_t stream_source_port; + char *stream_source_auth_user; + char *stream_source_auth_password; + uint32_t stream_relay_buffer_time; + int8_t stream_relay_reconnect_count; + int8_t stream_display_client; + int8_t stream_reuse_client; +#ifdef WEBIF + int8_t stream_hide_client; +#endif +#ifdef WITH_NEUTRINO +#define DEFAULT_STREAM_SOURCE_PORT 31339 //Neutrino +#else +#define DEFAULT_STREAM_SOURCE_PORT 8001 //Enigma2 +#endif +#endif + + int32_t max_cache_time; // seconds ecms are stored in ecmcwcache + int32_t max_hitcache_time; // seconds hits are stored in cspec_hitcache (to detect dyn wait_time) + + int8_t reload_useraccounts; + int8_t reload_readers; + int8_t reload_provid; + int8_t reload_services_ids; + int8_t reload_tier_ids; + int8_t reload_fakecws; + int8_t reload_ac_stat; + int8_t reload_log; + + int8_t block_same_ip; // 0=allow all, 1=block client requests to reader with same ip (default=1) + int8_t block_same_name; // 0=allow all, 1=block client requests to reader with same name (default=1) + +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO + uint32_t cw_cache_size; + uint32_t cw_cache_memory; + CWCHECKTAB cw_cache_settings; + + uint32_t ecm_cache_size; + uint32_t ecm_cache_memory; + int32_t ecm_cache_droptime; +#endif + uint8_t wait_until_ctimeout; + CWCHECKTAB cacheex_cwcheck_tab; + IN_ADDR_T csp_srvip; + int32_t csp_port; + CECSPVALUETAB cacheex_wait_timetab; + CAIDVALUETAB cacheex_mode1_delay_tab; +#ifdef CS_CACHEEX_AIO + CAIDVALUETAB cacheex_nopushafter_tab; + uint8_t waittime_block_start; + uint16_t waittime_block_time; +#endif + CECSP csp; // CSP Settings + uint8_t cacheex_enable_stats; // enable stats + + uint8_t cacheex_global_maxhop; + uint8_t cacheex_maxhop; + uint8_t cacheex_maxhop_lg; + + CAIDVALUETAB cacheex_maxhop_percaid; + CAIDVALUETAB cacheex_maxhop_lg_percaid; + + struct s_cacheex_matcher *cacheex_matcher; +#ifdef CS_CACHEEX_AIO + uint8_t cacheex_dropdiffs; + uint8_t cacheex_lg_only_remote_settings; + uint8_t cacheex_localgenerated_only; + CAIDTAB cacheex_localgenerated_only_caidtab; + FTAB cacheex_lg_only_tab; + uint8_t cacheex_localgenerated_only_in; + CAIDTAB cacheex_localgenerated_only_in_caidtab; + FTAB cacheex_lg_only_in_tab; + uint8_t cacheex_lg_only_in_aio_only; + CECSPVALUETAB cacheex_filter_caidtab; + CECSPVALUETAB cacheex_filter_caidtab_aio; + uint64_t cacheex_push_lg_groups; +#endif +#endif + +#ifdef CW_CYCLE_CHECK + int8_t cwcycle_check_enable; // on or off + CAIDTAB cwcycle_check_caidtab; // Caid for CW Cycle Check + int32_t keepcycletime; // how long stay the learned Cycletime in Memory + int32_t maxcyclelist; // max size of cwcyclelist + int8_t onbadcycle; // what to do on bad cwcycle + int8_t cwcycle_dropold; // what to do on old ecmd5/cw + int8_t cwcycle_sensitive; + int8_t cwcycle_allowbadfromffb; // allow Bad cycles from Fixed Fallbackreader + int8_t cwcycle_usecwcfromce; // Use CWC Info from Cacheex Sources for CWC Checking +#endif + + // Global whitelist: + struct s_global_whitelist *global_whitelist; + int8_t global_whitelist_use_l; + int8_t global_whitelist_use_m; + + char *ecmfmt; + char *pidfile; + + int32_t max_pending; + + // Ratelimit list + struct s_rlimit *ratelimit_list; + + // fake cws + struct s_fakecws fakecws[0x100]; + +#ifdef MODULE_SERIAL + struct s_twin *twin_list; +#endif +}; + +struct s_clientinit +{ + void *(*handler)(struct s_client *); + struct s_client *client; +}; + +struct s_clientmsg +{ + uint8_t msg[1024]; + int32_t len; + int32_t cmd; +}; + +typedef struct reader_stat_t +{ + int32_t rc; + uint16_t caid; + uint32_t prid; + uint16_t srvid; + uint32_t chid; + int16_t ecmlen; + + struct timeb last_received; + + int32_t ecm_count; + int32_t time_avg; + int32_t time_stat[LB_MAX_STAT_TIME]; + int32_t time_idx; + + int32_t fail_factor; +} READER_STAT; + +typedef struct cs_stat_query +{ + uint16_t caid; + uint32_t prid; + uint16_t srvid; + uint32_t chid; + int16_t ecmlen; +} STAT_QUERY; + +struct s_write_from_cache +{ + ECM_REQUEST *er_new; + ECM_REQUEST *er_cache; +}; + +/* =========================== + * global variables + * =========================== */ +extern pthread_key_t getclient; +extern struct s_client *first_client; +extern CS_MUTEX_LOCK config_lock; +extern CS_MUTEX_LOCK clientlist_lock; +extern CS_MUTEX_LOCK readerlist_lock; +extern struct s_reader *first_active_reader; // points to list of _active_ readers (enable = 1, deleted = 0) +extern LLIST *configured_readers; + +// These are used pretty much everywhere +extern struct s_config cfg; +extern uint16_t cs_dblevel; + +#include "oscam-log.h" +#include "oscam-log-reader.h" + +// Add here *only* funcs that are implemented in oscam.c and are called in other places +void cs_exit(int32_t sig); +void cs_exit_oscam(void); +void cs_restart_oscam(void); +int32_t cs_get_restartmode(void); + +void set_thread_name(const char *thread_name); +int32_t start_thread(char *nameroutine, void *startroutine, void *arg, pthread_t *pthread, int8_t detach, int8_t modify_stacksize); +int32_t start_thread_nolog(char *nameroutine, void *startroutine, void *arg, pthread_t *pthread, int8_t detach, int8_t modify_stacksize); +void kill_thread(struct s_client *cl); + +struct s_module *get_module(struct s_client *cl); +void module_reader_set(struct s_reader *rdr); + +// Until we find a better place for these (they are implemented in oscam-simples.h) +char *get_servicename(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_servicename_or_null(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_picon_servicename_or_null(struct s_client *cl, uint16_t srvid, uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +int32_t picon_servicename_remve_hd(char *buf, uint32_t buflen); +char *get_tiername(uint16_t tierid, uint16_t caid, char *buf); +char *get_tiername_defaultid(uint16_t tierid, uint16_t caid, char *buf); +char *get_provider(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_providername(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +char *get_providername_or_null(uint32_t provid, uint16_t caid, char *buf, uint32_t buflen); +void add_provider(uint16_t caid, uint32_t provid, const char *name, const char *sat, const char *lang); +const char *get_cl_lastprovidername(struct s_client *cl); +bool boxtype_is(const char *boxtype); +bool boxname_is(const char *boxname); +const char *boxtype_get(void); +const char *boxname_get(void); +static inline bool caid_is_seca(uint16_t caid) { return caid >> 8 == 0x01; } +static inline bool caid_is_viaccess(uint16_t caid) { return caid >> 8 == 0x05; } +static inline bool caid_is_irdeto(uint16_t caid) { return caid >> 8 == 0x06; } +static inline bool caid_is_videoguard(uint16_t caid) { return caid >> 8 == 0x09; } +static inline bool caid_is_conax(uint16_t caid) { return caid >> 8 == 0x0B; } +static inline bool caid_is_cryptoworks(uint16_t caid) { return caid >> 8 == 0x0D; } +static inline bool caid_is_betacrypt(uint16_t caid) { return caid >> 8 == 0x17; } +static inline bool caid_is_nagra(uint16_t caid) { return caid >> 8 == 0x18; } +static inline bool caid_is_bulcrypt(uint16_t caid) { return caid == 0x5581 || caid == 0x4AEE; } +static inline bool caid_is_dre(uint16_t caid) { return caid == 0x4AE0 || caid == 0x4AE1 || caid == 0x2710;} +static inline bool caid_is_tongfang(uint16_t caid) { return caid == 0x4A02; } +#if defined(WITH_EXTENDED_CW) || defined(MODULE_STREAMRELAY) +static inline bool select_csa_alt(const ECM_REQUEST *er) { + return (caid_is_videoguard(er->caid) && er->ecm[4] != 0 && (er->ecm[2] - er->ecm[4]) == 4); +} +#endif +#ifdef MODULE_STREAMRELAY +static inline uint8_t get_ecm_mode(const ECM_REQUEST *er) { + return (caid_is_videoguard(er->caid) && er->ecmlen >= 4) ? (er->ecm[er->ecmlen - 1] & 0x0F) : 0; +} +#endif +const char *get_cardsystem_desc_by_caid(uint16_t caid); + +#endif diff --git a/label b/label new file mode 100644 index 0000000..e69de29 diff --git a/minilzo/CMakeLists.txt b/minilzo/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/minilzo/Makefile b/minilzo/Makefile old mode 100755 new mode 100644 diff --git a/minilzo/README.LZO b/minilzo/README.LZO old mode 100755 new mode 100644 diff --git a/minilzo/lzoconf.h b/minilzo/lzoconf.h old mode 100755 new mode 100644 diff --git a/minilzo/lzodefs.h b/minilzo/lzodefs.h old mode 100755 new mode 100644 diff --git a/minilzo/minilzo.c b/minilzo/minilzo.c old mode 100755 new mode 100644 diff --git a/minilzo/minilzo.h b/minilzo/minilzo.h old mode 100755 new mode 100644 diff --git a/module-anticasc.c b/module-anticasc.c old mode 100755 new mode 100644 diff --git a/module-anticasc.h b/module-anticasc.h old mode 100755 new mode 100644 diff --git a/module-cacheex.c b/module-cacheex.c old mode 100755 new mode 100644 index 0df746a..2c62a5c --- a/module-cacheex.c +++ b/module-cacheex.c @@ -34,6 +34,44 @@ extern struct ecm_request_t *ecm_pushed_deleted; extern CS_MUTEX_LOCK ecmcache_lock; extern struct ecm_request_t *ecmcwcache; +/* 48/64 learning structure (defined in oscam-ecm.c) */ +#define CW_DETECT_MAX 8192 +#define CW_LEARN_THRESHOLD 5 +#define CW_CACHEEX_NODEID_MAX 5 + +typedef struct +{ + uint16_t caid; + uint32_t prid; + uint16_t srvid; + + uint8_t is64; + uint8_t is48; + + uint8_t learn64_count; + uint8_t learn48_count; + + uint8_t strict_logged; + + /* CacheEX learning: track unique nodeid (64-bit) */ + uint64_t cacheex_nodeid_48[CW_CACHEEX_NODEID_MAX]; + uint8_t cacheex_nodeid_48_count; + + uint64_t cacheex_nodeid_64[CW_CACHEEX_NODEID_MAX]; + uint8_t cacheex_nodeid_64_count; + + /* finalized flags (no more learning) */ + uint8_t cacheex_48_finalized; + uint8_t cacheex_64_finalized; + +} cw_detect_entry; + +/* Forward declares for 48/64 learning (from oscam-ecm.c) */ +typedef struct ecm_request_t ECM_REQUEST; +extern cw_detect_entry *cw_detect_get(ECM_REQUEST *er); +extern int cw_checksum_ok(uint8_t *cw); +extern int cacheex_learn_add_nodeid(uint64_t *nodeid_list, uint8_t *count, uint64_t new_nodeid); + // HIT CACHE functions ************************************************************** typedef struct hit_key_t { @@ -522,7 +560,7 @@ int8_t cacheex_maxhop(struct s_client *cl, ECM_REQUEST *er) return default_maxhop; } - // GLOBAL override OFF → reader only + // GLOBAL override OFF � reader only else { // Priority 1: reader percaid @@ -546,10 +584,11 @@ int8_t cacheex_maxhop(struct s_client *cl, ECM_REQUEST *er) } } - // NOT a cacheex reader → default + // NOT a cacheex reader � default return default_maxhop; } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef CS_CACHEEX_AIO int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er) @@ -582,7 +621,7 @@ int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er) return default_maxhop; } - // GLOBAL override OFF → reader only + // GLOBAL override OFF � reader only else { // Priority 1: reader percaid LG @@ -615,7 +654,7 @@ int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er) * Check if ECM hop count is within reader's configured maxhop limits * Returns 1 if hop is allowed, 0 if hop exceeds limit */ -static int8_t __attribute__((unused)) cacheex_check_maxhop(struct s_client *cl, ECM_REQUEST *er) +static int8_t cacheex_check_maxhop(struct s_client *cl, ECM_REQUEST *er) { // Get the hop count from csp_lastnodes list uint8_t hop = er->csp_lastnodes ? ll_count(er->csp_lastnodes) : 0; @@ -1016,9 +1055,7 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in } #endif - // Skip check for BISS1 - cw could be indeed zero - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero - if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid)) + if(chk_is_null_CW(er->cw)) { cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received null cw from %s", csp ? "csp" : username(cl)); cl->cwcacheexerr++; @@ -1027,9 +1064,7 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in return 0; } - // Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them) - // Don't check for BISS2 mode CA (ECM table is always 0x80) - if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0) + if(get_odd_even(er) == 0) { cs_log_dbg(D_CACHEEX, "push received ecm with null odd/even byte from %s", csp ? "csp" : username(cl)); cl->cwcacheexerr++; @@ -1065,6 +1100,98 @@ static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, in } } + ///////////////////////////////////////////////////////////// + // CACHEEX 48/64 LEARNING (reuse oscam-ecm.c structures) + ///////////////////////////////////////////////////////////// + + if (!csp && !er->entropy_blocked) + { + /* Get learning entry from shared cw_detect_table */ + extern cw_detect_entry cw_detect_table[]; + cw_detect_entry *e = cw_detect_get(er); + uint8_t checksum_ok = cw_checksum_ok(er->cw); + + /* Get CacheEX source nodeid */ + uint64_t src_nodeid = 0; + if (er->csp_lastnodes && ll_count(er->csp_lastnodes) > 0) + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + memcpy(&src_nodeid, remotenodeid, 8); + } + + uint8_t whitelist = chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) || + (cl->account && chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for)); + + ///////////////////////////////////////////////////////////// + // CACHEEX LEARNING: ONLY FOR WHITELIST CAIDs IN UNKNOWN STATE + ///////////////////////////////////////////////////////////// + + if (whitelist && src_nodeid) + { + /* Learn 64bit (invalid checksum) */ + if (!checksum_ok && !e->cacheex_64_finalized) + { + int finalized = cacheex_learn_add_nodeid( + e->cacheex_nodeid_64, + &e->cacheex_nodeid_64_count, + src_nodeid + ); + + if (finalized) + { + e->cacheex_64_finalized = 1; + e->is64 = 1; + cs_log("CACHEEX FINALIZED 64bit after %d unique nodes caid=%04X srvid=%04X", + CW_CACHEEX_NODEID_MAX, er->caid, er->srvid); + } + else + { + cs_log_dbg(D_CACHEEX, "CACHEEX LEARN 64bit (%d/%d nodes) caid=%04X srvid=%04X src_nodeid=%" PRIu64 "X", + e->cacheex_nodeid_64_count, CW_CACHEEX_NODEID_MAX, er->caid, er->srvid, src_nodeid); + } + } + + /* Learn 48bit (valid checksum) */ + if (checksum_ok && !e->cacheex_48_finalized) + { + int finalized = cacheex_learn_add_nodeid( + e->cacheex_nodeid_48, + &e->cacheex_nodeid_48_count, + src_nodeid + ); + + if (finalized) + { + e->cacheex_48_finalized = 1; + e->is48 = 1; + cs_log("CACHEEX FINALIZED 48bit after %d unique nodes caid=%04X srvid=%04X", + CW_CACHEEX_NODEID_MAX, er->caid, er->srvid); + } + else + { + cs_log_dbg(D_CACHEEX, "CACHEEX LEARN 48bit (%d/%d nodes) caid=%04X srvid=%04X src_nodeid=%" PRIu64 "X", + e->cacheex_nodeid_48_count, CW_CACHEEX_NODEID_MAX, er->caid, er->srvid, src_nodeid); + } + } + } + else if (!whitelist && checksum_ok) + { + /* STRICT MODE: only 48bit allowed, no learning */ + cs_log_dbg(D_CACHEEX, "CACHEEX STRICT 48bit (CAID outside whitelist) caid=%04X srvid=%04X", + er->caid, er->srvid); + } + else if (!whitelist && !checksum_ok) + { + /* STRICT MODE: reject 64bit, no learning */ + cs_log_dbg(D_CACHEEX, "CACHEEX STRICT REJECT 64bit (CAID outside whitelist) caid=%04X srvid=%04X", + er->caid, er->srvid); + cl->cwcacheexerr++; + if (cl->account) cl->account->cwcacheexerr++; + return 0; + } + } + er->grp |= cl->grp; // ok for mode2 reader too: cl->reader->grp er->rc = E_CACHEEX; er->cacheex_src = cl; diff --git a/module-cacheex.c.bak b/module-cacheex.c.bak new file mode 100644 index 0000000..0e0bc02 --- /dev/null +++ b/module-cacheex.c.bak @@ -0,0 +1,1700 @@ +#define MODULE_LOG_PREFIX "cacheex" + +#include "globals.h" + +#ifdef CS_CACHEEX + +#include "cscrypt/md5.h" +#include "module-cacheex.h" +#include "module-cw-cycle-check.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-conf.h" +#include "oscam-ecm.h" +#include "oscam-hashtable.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" +#ifdef CS_CACHEEX_AIO +#include "oscam-array.h" +#endif + +#define cs_cacheex_matcher "oscam.cacheex" + +extern uint8_t cc_node_id[8]; +extern uint8_t camd35_node_id[8]; + +uint8_t cacheex_peer_id[8]; + +extern CS_MUTEX_LOCK ecm_pushed_deleted_lock; +extern struct ecm_request_t *ecm_pushed_deleted; +extern CS_MUTEX_LOCK ecmcache_lock; +extern struct ecm_request_t *ecmcwcache; + +// HIT CACHE functions ************************************************************** + +typedef struct hit_key_t { + uint16_t caid; + uint32_t prid; + uint16_t srvid; +} HIT_KEY; + +typedef struct cache_hit_t { + HIT_KEY key; + struct timeb time; + struct timeb max_hitcache_time; + uint64_t grp; + uint64_t grp_last_max_hitcache_time; +#ifdef CS_CACHEEX_AIO + int32_t waittime_block; +#endif + node ht_node; + node ll_node; +} CACHE_HIT; + +static pthread_rwlock_t hitcache_lock; +static hash_table ht_hitcache; +static list ll_hitcache; +static bool cacheex_running; + +void cacheex_init_hitcache(void) +{ + init_hash_table(&ht_hitcache, &ll_hitcache); + if (pthread_rwlock_init(&hitcache_lock,NULL) != 0) + cs_log("Error creating lock hitcache_lock!"); + cacheex_running = true; +} + +void cacheex_free_hitcache(void) +{ + cacheex_running = false; + cacheex_cleanup_hitcache(true); + deinitialize_hash_table(&ht_hitcache); + pthread_rwlock_destroy(&hitcache_lock); +} + +static int cacheex_compare_hitkey(const void *arg, const void *obj) +{ + if(((const HIT_KEY*)arg)->caid==((const CACHE_HIT*)obj)->key.caid + && ((const HIT_KEY*)arg)->prid==((const CACHE_HIT*)obj)->key.prid + && ((const HIT_KEY*)arg)->srvid==((const CACHE_HIT*)obj)->key.srvid) + { + return 0; + } + return 1; +} + +static int32_t cacheex_check_hitcache(ECM_REQUEST *er, struct s_client *cl) +{ + CACHE_HIT *result; + HIT_KEY search; + memset(&search, 0, sizeof(HIT_KEY)); + search.caid = er->caid; + search.prid = er->prid; + search.srvid = er->srvid; + SAFE_RWLOCK_RDLOCK(&hitcache_lock); + result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + if(result){ + struct timeb now; + cs_ftime(&now); + int64_t gone = comp_timeb(&now, &result->time); + uint64_t grp = cl?cl->grp:0; + + if( + gone <= (cfg.max_hitcache_time*1000) + && + (!grp || !result->grp || (grp & result->grp)) +#ifdef CS_CACHEEX_AIO + && + result->waittime_block <= cfg.waittime_block_start +#endif + ) + { + SAFE_RWLOCK_UNLOCK(&hitcache_lock); + return 1; + } + } + SAFE_RWLOCK_UNLOCK(&hitcache_lock); + return 0; +} + +static void cacheex_add_hitcache(struct s_client *cl, ECM_REQUEST *er) +{ + if (!cfg.max_hitcache_time) // we don't want check/save hitcache + return; + if (!cfg.cacheex_wait_timetab.cevnum) + return; + uint32_t cacheex_wait_time = get_cacheex_wait_time(er,NULL); + if (!cacheex_wait_time) + return; + + CACHE_HIT *result; + HIT_KEY search; + + memset(&search, 0, sizeof(HIT_KEY)); + search.caid = er->caid; + search.prid = er->prid; + search.srvid = er->srvid; + + SAFE_RWLOCK_WRLOCK(&hitcache_lock); + + result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + if(!result) // not found, add it! + { + if(cs_malloc(&result, sizeof(CACHE_HIT))) + { + memset(result, 0, sizeof(CACHE_HIT)); + result->key.caid = er->caid; + result->key.prid = er->prid; + result->key.srvid = er->srvid; + cs_ftime(&result->max_hitcache_time); +#ifdef CS_CACHEEX_AIO + result->waittime_block = 0; +#endif + add_hash_table(&ht_hitcache, &result->ht_node, &ll_hitcache, &result->ll_node, result, &result->key, sizeof(HIT_KEY)); + } + } + + if(result) + { + if(cl) + { + result->grp |= cl->grp; + result->grp_last_max_hitcache_time |= cl->grp; + } + cs_ftime(&result->time); //always update time; + } + + SAFE_RWLOCK_UNLOCK(&hitcache_lock); +} + +static void cacheex_del_hitcache(struct s_client *cl, ECM_REQUEST *er) +{ + HIT_KEY search; + CACHE_HIT *result; + + memset(&search, 0, sizeof(HIT_KEY)); + search.caid = er->caid; + search.prid = er->prid; + search.srvid = er->srvid; + + if(cl && cl->grp) + { + result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + while(result) + { + result->grp &= ~cl->grp; + result->grp_last_max_hitcache_time &= ~cl->grp; + result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + } + } + + SAFE_RWLOCK_WRLOCK(&hitcache_lock); + search_remove_elem_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + SAFE_RWLOCK_UNLOCK(&hitcache_lock); +} + +void cacheex_cleanup_hitcache(bool force) +{ + CACHE_HIT *cachehit; + node *i,*i_next; + struct timeb now; + int64_t gone, gone_max_hitcache_time; + int32_t timeout = (cfg.max_hitcache_time + (cfg.max_hitcache_time / 2)) * 1000; // 1,5 + int32_t clean_grp = (cfg.max_hitcache_time * 1000); + + SAFE_RWLOCK_WRLOCK(&hitcache_lock); + i = get_first_node_list(&ll_hitcache); + while (i) + { + i_next = i->next; + cachehit = get_data_from_node(i); + + if(!cachehit) + { + i = i_next; + continue; + } + + cs_ftime(&now); + gone = comp_timeb(&now, &cachehit->time); + gone_max_hitcache_time = comp_timeb(&now, &cachehit->max_hitcache_time); + + if(force || gone>timeout +#ifdef CS_CACHEEX_AIO + || (cachehit->waittime_block > (cfg.waittime_block_time / 3 + 1)) +#endif + ) + { + remove_elem_list(&ll_hitcache, &cachehit->ll_node); + remove_elem_hash_table(&ht_hitcache, &cachehit->ht_node); + NULLFREE(cachehit); + } + else if(gone_max_hitcache_time >= clean_grp){ + cachehit->grp = cachehit->grp_last_max_hitcache_time; + cachehit->grp_last_max_hitcache_time = 0; + cs_ftime(&cachehit->max_hitcache_time); + } + +#ifdef CS_CACHEEX_AIO + if(cfg.waittime_block_start && (cachehit && cachehit->waittime_block >= cfg.waittime_block_start)) + { + cachehit->waittime_block++; + } +#endif + i = i_next; + } + SAFE_RWLOCK_UNLOCK(&hitcache_lock); +} + +static int32_t cacheex_ecm_hash_calc(uint8_t *buf, int32_t n) +{ + int32_t i, h = 0; + for(i = 0; i < n; i++) + { + h = 31 * h + buf[i]; + } + return h; +} + +void cacheex_update_hash(ECM_REQUEST *er) +{ + er->csp_hash = cacheex_ecm_hash_calc(er->ecm + 3, er->ecmlen - 3); +} + +void cacheex_free_csp_lastnodes(ECM_REQUEST *er) +{ + ll_destroy_free_data(&er->csp_lastnodes); +} + +void cacheex_set_csp_lastnode(ECM_REQUEST *er) +{ + er->csp_lastnodes = NULL; +} + +void cacheex_set_cacheex_src(ECM_REQUEST *ecm, struct s_client *cl) +{ + if(ecm->cacheex_src == cl) + ecm->cacheex_src = NULL; +} + +void cacheex_init_cacheex_src(ECM_REQUEST *ecm, ECM_REQUEST *er) +{ + if(!ecm->cacheex_src) + ecm->cacheex_src = er->cacheex_src; +} + +static void *chkcache_process(void) +{ + set_thread_name(__func__); + + time_t timeout; + struct ecm_request_t *er, *ecm; + uint8_t add_hitcache_er; + struct s_reader *cl_rdr; + struct s_reader *rdr; + struct s_ecm_answer *ea; + struct s_client *cex_src = NULL; + struct s_write_from_cache *wfc = NULL; + + while(cacheex_running) + { + cs_readlock(__func__, &ecmcache_lock); + for(er = ecmcwcache; er; er = er->next) + { + timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1); + if(er->tps.time < timeout) + { break; } + + if(er->rc < E_UNHANDLED || er->readers_timeout_check) // already answered + { continue; } + + // CHECK IF FOUND ECM IN CACHE + ecm = check_cache(er, er->client); + if(ecm) // found in cache + { + // check for add_hitcache + if(ecm->cacheex_src) // cw from cacheex + { + // only when no wait_time expires (or not wait_time) + if((er->cacheex_wait_time && !er->cacheex_wait_time_expired) || !er->cacheex_wait_time) + { + // add_hitcache already called, but we check if we have to call it for these (er) caid|prid|srvid + if(ecm->prid!=er->prid || ecm->srvid!=er->srvid) + { + // here we should be sure cex client has not been freed! + cex_src = ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill ? ecm->cacheex_src : NULL; + + if(cex_src) // add_hitcache only if client is really active + { + add_hitcache_er = 1; + cl_rdr = cex_src->reader; + + if(cl_rdr && cl_rdr->cacheex.mode == 2) + { + for(ea = er->matching_rdr; ea; ea = ea->next) + { + rdr = ea->reader; + if(cl_rdr == rdr && ((ea->status & REQUEST_ANSWERED) == REQUEST_ANSWERED)) + { + cs_log_dbg(D_CACHEEX | D_CSP | D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [CACHEEX] skip ADD self request!", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + + add_hitcache_er=0; // don't add hit cache, reader requested self + } + } + } + + // USE cacheex client (to get correct group) and ecm + // from requesting client (to get correct caid|prid|srvid)!!! + if(add_hitcache_er) + { + cacheex_add_hitcache(cex_src, er); + } + } + } + + } + else + { + // add_hitcache already called, but we have to remove it because cacheex not coming before wait_time + if(ecm->prid == er->prid && ecm->srvid == er->srvid) + { cacheex_del_hitcache(er->client, ecm); } + } + } + // END check for add_hitcache + + if(check_client(er->client)) + { + wfc = NULL; + if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache))) + { + NULLFREE(ecm); + continue; + } + + wfc->er_new = er; + wfc->er_cache = ecm; + + if(!add_job(er->client, ACTION_ECM_ANSWER_CACHE, wfc, sizeof(struct s_write_from_cache))) // write_ecm_answer_fromcache + { + NULLFREE(ecm); + continue; + } + } + else + { + NULLFREE(ecm); + } + } + } + cs_readunlock(__func__, &ecmcache_lock); + cs_sleepms(10); + } + + return NULL; +} + +void checkcache_process_thread_start(void) +{ + start_thread("chkcache_process", (void *)&chkcache_process, NULL, NULL, 1, 1); +} + +void cacheex_init(void) +{ + // Init random node id + get_random_bytes(cacheex_peer_id, 8); +#ifdef MODULE_CCCAM + memcpy(cacheex_peer_id, cc_node_id, 8); +#endif +#ifdef MODULE_CAMD35_TCP + memcpy(camd35_node_id, cacheex_peer_id, 8); +#endif +} + +void cacheex_clear_account_stats(struct s_auth *account) +{ + account->cwcacheexgot = 0; + account->cwcacheexpush = 0; + account->cwcacheexhit = 0; +#ifdef CS_CACHEEX_AIO + account->cwcacheexgotlg = 0; + account->cwcacheexpushlg = 0; +#endif +} + +void cacheex_clear_client_stats(struct s_client *client) +{ + client->cwcacheexgot = 0; + client->cwcacheexpush = 0; + client->cwcacheexhit = 0; +#ifdef CS_CACHEEX_AIO + client->cwcacheexgotlg = 0; + client->cwcacheexpushlg = 0; +#endif +} + +int32_t cacheex_add_stats(struct s_client *cl, uint16_t caid, uint16_t srvid, uint32_t prid, uint8_t direction +#ifdef CS_CACHEEX_AIO + , uint8_t localgenerated +#endif +) +{ + if(!cfg.cacheex_enable_stats) + { return -1; } + + // create list if doesn't exist + if(!cl->ll_cacheex_stats) + { cl->ll_cacheex_stats = ll_create("ll_cacheex_stats"); } + + time_t now = time((time_t *)0); + LL_ITER itr = ll_iter_create(cl->ll_cacheex_stats); + S_CACHEEX_STAT_ENTRY *cacheex_stats_entry; + + // check for existing entry + while((cacheex_stats_entry = ll_iter_next(&itr))) + { + if(cacheex_stats_entry->cache_srvid == srvid && + cacheex_stats_entry->cache_caid == caid && + cacheex_stats_entry->cache_prid == prid && + cacheex_stats_entry->cache_direction == direction) + { + // we already have this entry - just add count and time + cacheex_stats_entry->cache_count++; +#ifdef CS_CACHEEX_AIO + if(localgenerated) + cacheex_stats_entry->cache_count_lg++; +#endif + cacheex_stats_entry->cache_last = now; + return cacheex_stats_entry->cache_count; + } + } + + // if we land here we have to add a new entry + if(cs_malloc(&cacheex_stats_entry, sizeof(S_CACHEEX_STAT_ENTRY))) + { + cacheex_stats_entry->cache_caid = caid; + cacheex_stats_entry->cache_srvid = srvid; + cacheex_stats_entry->cache_prid = prid; + cacheex_stats_entry->cache_count = 1; +#ifdef CS_CACHEEX_AIO + if(localgenerated) + cacheex_stats_entry->cache_count_lg = 1; +#endif + cacheex_stats_entry->cache_last = now; + cacheex_stats_entry->cache_direction = direction; + ll_iter_insert(&itr, cacheex_stats_entry); + return 1; + } + return 0; +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +int8_t cacheex_maxhop(struct s_client *cl, ECM_REQUEST *er) +{ + const int8_t default_maxhop = 10; + uint16_t caid = er ? er->caid : 0; + + // Apply ONLY for CacheEX readers + if(cl && cl->reader && cl->reader->cacheex.mode > 0) + { + // GLOBAL override ON + if(cfg.cacheex_global_maxhop == 1) + { + // Priority 1: global percaid + if(caid > 0) + { + uint16_t per = caidvaluetab_get_value( + &cfg.cacheex_maxhop_percaid, + caid, + 0 + ); + + if(per > 0) + return (int8_t)per; + } + + // Priority 2: global maxhop + if(cfg.cacheex_maxhop > 0) + return cfg.cacheex_maxhop; + + return default_maxhop; + } + // GLOBAL override OFF reader only + else + { + // Priority 1: reader percaid + if(caid > 0) + { + uint16_t per = caidvaluetab_get_value( + &cl->reader->cacheex.maxhop_percaid, + caid, + 0 + ); + + if(per > 0) + return (int8_t)per; + } + + // Priority 2: reader maxhop + if(cl->reader->cacheex.maxhop > 0) + return cl->reader->cacheex.maxhop; + + return default_maxhop; + } + } + + // NOT a cacheex reader default + return default_maxhop; + +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef CS_CACHEEX_AIO +int8_t cacheex_maxhop_lg(struct s_client *cl, ECM_REQUEST *er) +{ + const int8_t default_maxhop = 10; + uint16_t caid = er ? er->caid : 0; + + // Apply ONLY for CacheEX readers + if(cl && cl->reader && cl->reader->cacheex.mode > 0) + { + // GLOBAL override ON + if(cfg.cacheex_global_maxhop == 1) + { + // Priority 1: global percaid LG + if(caid > 0) + { + uint16_t per = caidvaluetab_get_value( + &cfg.cacheex_maxhop_lg_percaid, + caid, + 0 + ); + + if(per > 0) + return (int8_t)per; + } + + // Priority 2: global maxhop LG + if(cfg.cacheex_maxhop_lg > 0) + return cfg.cacheex_maxhop_lg; + + return default_maxhop; + } + // GLOBAL override OFF reader only + else + { + // Priority 1: reader percaid LG + if(caid > 0) + { + uint16_t per = caidvaluetab_get_value( + &cl->reader->cacheex.maxhop_lg_percaid, + caid, + 0 + ); + + if(per > 0) + return (int8_t)per; + } + + // Priority 2: reader maxhop LG + if(cl->reader->cacheex.maxhop_lg > 0) + return cl->reader->cacheex.maxhop_lg; + + return default_maxhop; + } + } + + return default_maxhop; +} +#endif +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Check if ECM hop count is within reader's configured maxhop limits + * Returns 1 if hop is allowed, 0 if hop exceeds limit + */ +static int8_t cacheex_check_maxhop(struct s_client *cl, ECM_REQUEST *er) +{ + // Get the hop count from csp_lastnodes list + uint8_t hop = er->csp_lastnodes ? ll_count(er->csp_lastnodes) : 0; + + // Get the maximum allowed hop for this reader/ECM + int8_t max_hop; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + max_hop = cacheex_maxhop_lg(cl, er); + } + else +#endif + { + max_hop = cacheex_maxhop(cl, er); + } + + // Check if hop exceeds the limit + if(hop > max_hop) + { + debug_ecm(D_CACHEEX, "cacheex: reject ECM %04X@%06X/%04X hop %d > max_hop %d for reader %s", + er->caid, er->prid, er->srvid, hop, max_hop, username(cl)); + return 0; + } + + return 1; +} +#endif + +static void cacheex_cache_push_to_client(struct s_client *cl, ECM_REQUEST *er) +{ + add_job(cl, ACTION_CACHE_PUSH_OUT, er, 0); +} + +/** + * Check for NULL ecmd5 + **/ +static uint8_t checkECMD5(ECM_REQUEST *er) +{ + int8_t i; + for(i = 0; i < CS_ECMSTORESIZE; i++) + if(er->ecmd5[i]) { return 1; } + return 0; +} + +#ifdef CS_CACHEEX_AIO +static uint8_t chk_cwcheck(ECM_REQUEST *er, uint8_t cw_check_for_push) +{ + if(!cw_check_for_push) + return 1; + + CWCHECK check_cw; + check_cw = get_cwcheck(er); + + if(check_cw.mode && check_cw.counter > 1) + { + if(er->cw_count >= check_cw.counter) + { + return 1; + } + else + { + cs_log_dbg(D_CACHEEX, "push denied - cacheex_check_cw.counter: %u > er->cw_count: %u", check_cw.counter, er->cw_count); + return 0; + } + } + else + { + return 1; + } +} +#endif + +/** + * cacheex modes: + * + * cacheex=1 CACHE PULL: + * Situation: oscam A reader1 has cacheex=1, oscam B account1 has cacheex=1 + * oscam A gets a ECM request, reader1 send this request to oscam B, oscam B checks his cache + * a. not found in cache: return NOK + * a. found in cache: return OK+CW + * b. not found in cache, but found pending request: wait max cacheexwaittime and check again + * oscam B never requests new ECMs + * + * CW-flow: B->A + * + * cacheex=2 CACHE PUSH: + * Situation: oscam A reader1 has cacheex=2, oscam B account1 has cacheex=2 + * if oscam B gets a CW, its pushed to oscam A + * reader has normal functionality and can request ECMs + * + * Problem: oscam B can only push if oscam A is connected + * Problem or feature?: oscam A reader can request ecms from oscam B + * + * CW-flow: B->A + * + */ +void cacheex_cache_push(ECM_REQUEST *er) +{ + if(er->rc >= E_NOTFOUND) { return; } + + //cacheex=2 mode: push (server->remote) + struct s_client *cl; + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client->next; cl; cl = cl->next) + { + if(check_client(cl) && er->cacheex_src != cl) + { + if(get_module(cl)->num == R_CSP) // always send to csp cl + { + if(!er->cacheex_src || cfg.csp.allow_reforward) { cacheex_cache_push_to_client(cl, er); } // but not if the origin was cacheex (might loop) + } + else if(cl->typ == 'c' && !cl->dup && cl->account && cl->account->cacheex.mode == 2) // send cache over user + { + if(get_module(cl)->c_cache_push // cache-push able + && (!er->grp || (cl->grp & er->grp) +#ifdef CS_CACHEEX_AIO + || (er->localgenerated && ((cl->grp & cfg.cacheex_push_lg_groups) && strcmp(username(cl), username(er->cacheex_src)))) +#endif + ) // Group-check + /**** OUTGOING FILTER CHECK ***/ + && (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_name || strcmp(username(cl), er->selected_reader->label)) // check reader mode-1 loopback by same name + && (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_ip || (check_client(er->selected_reader->client) && !IP_EQUAL(cl->ip, er->selected_reader->client->ip))) // check reader mode-1 loopback by same ip + && (!cl->account->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check + && chk_ctab(er->caid, &cl->ctab) // Caid-check + && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->ftab)) // Ident-check (not for csp: prid=0 always!) + && chk_srvid(cl, er) // Service-check + && chk_csp_ctab(er, &cl->account->cacheex.filter_caidtab) // cacheex_ecm_filter +#ifdef CS_CACHEEX_AIO + && (er->localgenerated // lg-flag-check + || chk_srvid_localgenerated_only_exception(er) // lg-only-service-exception + || !(cl->account->cacheex.localgenerated_only // usr-lg-only + || ( + (cl->account->cacheex.feature_bitfield & 64) // cx-aio >= 9.2.6 => check ftab + && (chk_lg_only(er, &cl->account->cacheex.lg_only_tab) // usr-lg-only-ftab (feature 64) + || chk_lg_only(er, &cfg.cacheex_lg_only_tab)) // global-lg-only-ftab (feature 64) + ) + ) + ) + && (chk_cwcheck(er, cl->account->cacheex.cw_check_for_push)) // check cw_check-counter if enabled + && chk_nopushafter(er->caid, &cl->account->cacheex.cacheex_nopushafter_tab, er->ecm_time) // no push after check +#endif + ) + { + cacheex_cache_push_to_client(cl, er); + } + } + } + } + cs_readunlock(__func__, &clientlist_lock); + + //cacheex=3 mode: reverse push (reader->server) + cs_readlock(__func__, &readerlist_lock); + cs_readlock(__func__, &clientlist_lock); + struct s_reader *rdr; + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + cl = rdr->client; + if(check_client(cl) && er->cacheex_src != cl && rdr->cacheex.mode == 3) // send cache over reader + { + if(rdr->ph.c_cache_push // cache-push able + && (!er->grp || (rdr->grp & er->grp) +#ifdef CS_CACHEEX_AIO + || (er->localgenerated && ((rdr->grp & cfg.cacheex_push_lg_groups) && strcmp(username(cl), username(er->cacheex_src)))) +#endif + ) // Group-check + /**** OUTGOING FILTER CHECK ***/ + && (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_name || strcmp(username(cl), er->selected_reader->label)) // check reader mode-1 loopback by same name + && (!er->selected_reader || !cacheex_reader(er->selected_reader) || !cfg.block_same_ip || (check_client(er->selected_reader->client) && !IP_EQUAL(cl->ip, er->selected_reader->client->ip))) // check reader mode-1 loopback by same ip + && (!rdr->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check + && chk_ctab(er->caid, &rdr->ctab) // Caid-check + && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &rdr->ftab)) // Ident-check (not for csp: prid=0 always!) + && chk_srvid(cl, er) // Service-check + && chk_csp_ctab(er, &rdr->cacheex.filter_caidtab) // cacheex_ecm_filter +#ifdef CS_CACHEEX_AIO + && (er->localgenerated // lg-only-check + || chk_srvid_localgenerated_only_exception(er) // service-exception + || !(rdr->cacheex.localgenerated_only // rdr-lg-only + || ( + (rdr->cacheex.feature_bitfield & 64) // cx-aio >= 9.2.6 => check ftab + && (chk_lg_only(er, &rdr->cacheex.lg_only_tab) // rdr-lg-only-ftab (feature 64) + || chk_lg_only(er, &cfg.cacheex_lg_only_tab)) // global-lg-only-ftab (feature 64) + ) + ) + ) + && (chk_cwcheck(er, rdr->cacheex.cw_check_for_push)) // check cw_check-counter if enabled + && chk_nopushafter(er->caid, &rdr->cacheex.cacheex_nopushafter_tab, er->ecm_time) +#endif + ) // no push after check + { + cacheex_cache_push_to_client(cl, er); + } + } + } + cs_readunlock(__func__, &clientlist_lock); + cs_readunlock(__func__, &readerlist_lock); +} + +/**** INCOMING FILTER CHECK ***/ +uint8_t check_cacheex_filter(struct s_client *cl, ECM_REQUEST *er) +{ + + if(check_client(cl) && cl->typ == 'p' && cl->reader && cl->reader->cacheex.mode == 2 + && (!cl->reader->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check + && chk_ctab(er->caid, &cl->reader->ctab) // Caid-check + && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->reader->ftab)) // Ident-check (not for csp: prid=0 always!) + && chk_srvid(cl, er)) // Service-check + { + return 1; + } + + if(check_client(cl) && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 3 + && (!cl->account->cacheex.drop_csp || checkECMD5(er)) // cacheex_drop_csp-check + && chk_ctab(er->caid, &cl->ctab) // Caid-check + && (!checkECMD5(er) || chk_ident_filter(er->caid, er->prid, &cl->ftab)) // Ident-check (not for csp: prid=0 always!) + && chk_srvid(cl, er)) // Service-check + { + return 1; + } + free_ecm(er); + return 0; +} + +static struct s_cacheex_matcher *is_cacheex_matcher_matching(ECM_REQUEST *from_er, ECM_REQUEST *to_er) +{ + struct s_cacheex_matcher *entry = cfg.cacheex_matcher; + int8_t v_ok = (from_er && to_er) ? 2 : 1; + while(entry) + { + int8_t ok = 0; + if(from_er + && (!entry->caid || entry->caid == from_er->caid) + && (!entry->provid || entry->provid == from_er->prid) + && (!entry->srvid || entry->srvid == from_er->srvid) + && (!entry->chid || entry->chid == from_er->chid) + && (!entry->pid || entry->pid == from_er->pid) + && (!entry->ecmlen || entry->ecmlen == from_er->ecmlen)) + { ok++; } + + if(to_er + && (!entry->to_caid || entry->to_caid == to_er->caid) + && (!entry->to_provid || entry->to_provid == to_er->prid) + && (!entry->to_srvid || entry->to_srvid == to_er->srvid) + && (!entry->to_chid || entry->to_chid == to_er->chid) + && (!entry->to_pid || entry->to_pid == to_er->pid) + && (!entry->to_ecmlen || entry->to_ecmlen == to_er->ecmlen)) + { ok++; } + + if(ok == v_ok) + { + if(!from_er || !to_er || from_er->srvid == to_er->srvid) + { return entry; } + } + entry = entry->next; + } + return NULL; +} + +bool cacheex_is_match_alias(struct s_client *cl, ECM_REQUEST *er) +{ + return check_client(cl) && cl->account && cl->account->cacheex.mode == 1 && is_cacheex_matcher_matching(NULL, er); +} + +#ifdef WITH_DEBUG +static void log_cacheex_cw(ECM_REQUEST *er, char *reason) +{ + uint8_t *data; + uint8_t remotenodeid[8]; + data = ll_last_element(er->csp_lastnodes); + if(data) + { memcpy(remotenodeid, data, 8); } + else + { memset(remotenodeid, 0 , 8); } + + char buf_ecm[109]; + format_ecm(er, buf_ecm, 109); + cs_log_dbg(D_CACHEEX,"got pushed ecm [%s]: %s - odd/even 0x%x - CSP cw: %s - pushed from %s, at hop %d, origin node-id %" PRIu64 "X", + reason, buf_ecm, er->ecm[0], (checkECMD5(er)?"NO":"YES"), er->from_csp ? "csp" : username((er->cacheex_src?er->cacheex_src:er->client)), ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); +} +#endif + +// check if sky_ger 64 bit CW has valid checksum bytes and therefore is probably invalid +uint8_t check_nds_cwex(ECM_REQUEST *er) +{ + uint8_t k, csum; + uint8_t hit = 0; + uint8_t oe = checkCWpart(er->cw, 0) ? 0 : 8; + for(k = 0; k < 8; k += 4) + { + csum = ((er->cw[k + oe] + er->cw[k + oe + 1] + er->cw[k + oe + 2]) & 0xff); + if(er->cw[k + oe + 3] == csum) + { + hit++; + } + } + if(hit > 1) + { + return 1; + } + return 0; +} + +static int32_t cacheex_add_to_cache_int(struct s_client *cl, ECM_REQUEST *er, int8_t csp) +{ + if(er->rc >= E_NOTFOUND) { return 0; } + + if(!cl) + { return 0; } + if(!csp && cl->reader && cl->reader->cacheex.mode != 2) //from reader + { + cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl)); + return 0; + } + if(!csp && !cl->reader && cl->account && cl->account->cacheex.mode != 3) //from user + { + cs_log_dbg(D_CACHEEX, "CACHEX received, but disabled for %s", username(cl)); + return 0; + } + if(!csp && !cl->reader && !cl->account) //not active! + { + cs_log_dbg(D_CACHEEX, "CACHEX received, but invalid client state %s", username(cl)); + return 0; + } + + if(!cfg.disablecrccws && ((cl->typ == 'c' && cl->account && !cl->account->disablecrccacheex) || ( cl->typ == 'p' && cl->reader && !cl->reader->disablecrccws))) + { + uint8_t selectedForIgnChecksum = chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for); + if(cl->typ == 'c') + { + selectedForIgnChecksum += chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for); + } + if(cl->typ == 'p') + { + selectedForIgnChecksum += chk_if_ignore_checksum(er, &cl->reader->disablecrccws_only_for); + } + if(!selectedForIgnChecksum) + { + uint8_t i, c; + for(i = 0; i < 16; i += 4) + { + c = ((er->cw[i] + er->cw[i + 1] + er->cw[i + 2]) & 0xff); + + if(er->cw[i + 3] != c) + { + cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received cw with chksum error from %s", csp ? "csp" : username(cl)); + cl->cwcacheexerr++; + if(cl->account) + { cl->account->cwcacheexerr++; } + return 0; + } + } + } + } + +#ifdef CS_CACHEEX_AIO + if(caid_is_videoguard(er->caid)) + { + if(cl->typ == 'p' && chk_if_ignore_checksum(er, &cl->reader->disablecrccws_only_for) && !chk_srvid_disablecrccws_only_for_exception(er)) + { + if(check_nds_cwex(er)) + { + if(check_client(cl) && cl->reader && cl->reader->dropbadcws) + { + if (((D_CACHEEX) & cs_dblevel)) // avoid useless operations if debug is not enabled + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + + cs_log_dbg(D_CACHEEX, "Probably got pushed bad CW from cacheex reader: %s, caid %04X, srvid %04X - dropping CW, lg: %i, hop: %i, src-nodeid %" PRIu64 "X", cl->reader->label, er->caid, er->srvid, er->localgenerated, ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); + } + return 0; + } + else + { + if (((D_CACHEEX) & cs_dblevel)) // avoid useless operations if debug is not enabled + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + + cs_log_dbg(D_CACHEEX, "Probably got pushed bad CW from cacheex reader: %s, caid %04X, srvid %04X, lg: %i, hop: %i, src-nodeid %" PRIu64 "X", cl->reader->label, er->caid, er->srvid, er->localgenerated, ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); + } + } + } + } + + if(cl->typ == 'c' && chk_if_ignore_checksum(er, &cl->account->disablecrccacheex_only_for) && !chk_srvid_disablecrccws_only_for_exception(er)) + { + if(check_nds_cwex(er)) + { + if (((D_CACHEEX) & cs_dblevel)) // avoid useless operations if debug is not enabled + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + + cs_log_dbg(D_CACHEEX, "Probably got bad CW from cacheex user: %s, caid %04X, srvid %04X, lg: %i, hop: %i, src-nodeid %" PRIu64 "X", username(cl), er->caid, er->srvid, er->localgenerated, ll_count(er->csp_lastnodes), er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); + } + } + } + } +#endif + + if(chk_is_null_CW(er->cw)) + { + cs_log_dump_dbg(D_CACHEEX, er->cw, 16, "push received null cw from %s", csp ? "csp" : username(cl)); + cl->cwcacheexerr++; + if(cl->account) + { cl->account->cwcacheexerr++; } + return 0; + } + + if(get_odd_even(er) == 0) + { + cs_log_dbg(D_CACHEEX, "push received ecm with null odd/even byte from %s", csp ? "csp" : username(cl)); + cl->cwcacheexerr++; + if(cl->account) + { cl->account->cwcacheexerr++; } + return 0; + } + + if(!chk_halfCW(er, er->cw)) + { +#ifdef WITH_DEBUG + if(cs_dblevel & D_CACHEEX) + { + log_cacheex_cw(er, "bad half cw"); + } +#endif + cl->cwcacheexerr++; + if(cl->account) + { cl->account->cwcacheexerr++; } + return 0; + } + + if((csp && cfg.csp.block_fakecws) || (cl->reader && cl->reader->cacheex.block_fakecws) + || (!cl->reader && cl->account && cl->account->cacheex.block_fakecws)) + { + if(chk_is_fakecw(er->cw)) + { + cs_log_dbg(D_CACHEEX, "push received fake cw from %s", csp ? "csp" : username(cl)); + cl->cwcacheexerr++; + if(cl->account) + { cl->account->cwcacheexerr++; } + return 0; + } + } + + er->grp |= cl->grp; // ok for mode2 reader too: cl->reader->grp + er->rc = E_CACHEEX; + er->cacheex_src = cl; + er->selected_reader = cl->reader; + er->client = NULL; // No Owner! So no fallback! + + if(check_client(cl)) + { + cl->cwcacheexgot++; + if(cl->account) + { cl->account->cwcacheexgot++; } + first_client->cwcacheexgot++; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + cl->cwcacheexgotlg++; + if(cl->account) + cl->account->cwcacheexgotlg++; + first_client->cwcacheexgotlg++; + } +#endif + } + + cacheex_add_hitcache(cl, er); // we have to call it before add_cache, because in chk_process we could remove it! + add_cache(er); +#ifdef CS_CACHEEX_AIO + cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 1, er->localgenerated); +#else + cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 1); +#endif + + cs_writelock(__func__, &ecm_pushed_deleted_lock); + er->next = ecm_pushed_deleted; + ecm_pushed_deleted = er; + cs_writeunlock(__func__, &ecm_pushed_deleted_lock); + + return 1; // NO free, we have to wait cache push out stuff ends. +} + +void cacheex_add_to_cache(struct s_client *cl, ECM_REQUEST *er) +{ + er->from_cacheex = 1; + + // Check maxhop limit before adding to cache + // Get the hop count from csp_lastnodes list + uint8_t hop = er->csp_lastnodes ? ll_count(er->csp_lastnodes) : 0; + int8_t max_hop; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + max_hop = cacheex_maxhop_lg(cl, er); + } + else +#endif + { + max_hop = cacheex_maxhop(cl, er); + } + + if(hop > max_hop) + { + debug_ecm(D_CACHEEX, "cacheex: reject ECM %04X@%06X/%04X hop %d > max_hop %d for reader %s", + er->caid, er->prid, er->srvid, hop, max_hop, username(cl)); + free_push_in_ecm(er); + return; + } + + if(!cacheex_add_to_cache_int(cl, er, 0)) + { free_push_in_ecm(er); } +} + +void cacheex_add_to_cache_from_csp(struct s_client *cl, ECM_REQUEST *er) +{ + // Check maxhop limit before adding to cache + // Get the hop count from csp_lastnodes list + uint8_t hop = er->csp_lastnodes ? ll_count(er->csp_lastnodes) : 0; + int8_t max_hop; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + max_hop = cacheex_maxhop_lg(cl, er); + } + else +#endif + { + max_hop = cacheex_maxhop(cl, er); + } + + if(hop > max_hop) + { + debug_ecm(D_CACHEEX, "cacheex: reject ECM %04X@%06X/%04X hop %d > max_hop %d for reader %s", + er->caid, er->prid, er->srvid, hop, max_hop, username(cl)); + free_push_in_ecm(er); + return; + } + + if(!cacheex_add_to_cache_int(cl, er, 1)) + { free_push_in_ecm(er); } +} + +//Format: +//caid:prov:srvid:pid:chid:ecmlen=caid:prov:srvid:pid:chid:ecmlen[,validfrom,validto] +//validfrom: default=-2000 +//validto: default=4000 +//valid time if found in cache +static struct s_cacheex_matcher *cacheex_matcher_read_int(void) +{ + FILE *fp = open_config_file(cs_cacheex_matcher); + if(!fp) + { return NULL; } + + char token[1024]; + uint8_t type; + int32_t i, ret, count = 0; + struct s_cacheex_matcher *new_cacheex_matcher = NULL, *entry, *last = NULL; + uint32_t line = 0; + + while(fgets(token, sizeof(token), fp)) + { + line++; + if(cs_strlen(token) <= 1) { continue; } + if(token[0] == '#' || token[0] == '/') { continue; } + if(cs_strlen(token) > 100) { continue; } + + for(i = 0; i < (int)cs_strlen(token); i++) + { + if((token[i] == ':' || token[i] == ' ') && token[i + 1] == ':') + { + memmove(token + i + 2, token + i + 1, cs_strlen(token) - i + 1); + token[i + 1] = '0'; + } + if(token[i] == '#' || token[i] == '/') + { + token[i] = '\0'; + break; + } + } + + type = 'm'; + uint32_t caid = 0, provid = 0, srvid = 0, pid = 0, chid = 0, ecmlen = 0; + uint32_t to_caid = 0, to_provid = 0, to_srvid = 0, to_pid = 0, to_chid = 0, to_ecmlen = 0; + int32_t valid_from = -2000, valid_to = 4000; + + ret = sscanf(token, "%c:%4x:%6x:%4x:%4x:%4x:%4X=%4x:%6x:%4x:%4x:%4x:%4X,%4d,%4d", + &type, + &caid, &provid, &srvid, &pid, &chid, &ecmlen, + &to_caid, &to_provid, &to_srvid, &to_pid, &to_chid, &to_ecmlen, + &valid_from, &valid_to); + + type = tolower(type); + + if(ret < 7 || type != 'm') + { continue; } + + if(!cs_malloc(&entry, sizeof(struct s_cacheex_matcher))) + { + fclose(fp); + return new_cacheex_matcher; + } + count++; + entry->line = line; + entry->type = type; + entry->caid = caid; + entry->provid = provid; + entry->srvid = srvid; + entry->pid = pid; + entry->chid = chid; + entry->ecmlen = ecmlen; + entry->to_caid = to_caid; + entry->to_provid = to_provid; + entry->to_srvid = to_srvid; + entry->to_pid = to_pid; + entry->to_chid = to_chid; + entry->to_ecmlen = to_ecmlen; + entry->valid_from = valid_from; + entry->valid_to = valid_to; + + cs_log_dbg(D_TRACE, "cacheex-matcher: %c: %04X@%06X:%04X:%04X:%04X:%02X = %04X@%06X:%04X:%04X:%04X:%02X valid %d/%d", + entry->type, entry->caid, entry->provid, entry->srvid, entry->pid, entry->chid, entry->ecmlen, + entry->to_caid, entry->to_provid, entry->to_srvid, entry->to_pid, entry->to_chid, entry->to_ecmlen, + entry->valid_from, entry->valid_to); + + if(!new_cacheex_matcher) + { + new_cacheex_matcher = entry; + last = new_cacheex_matcher; + } + else + { + last->next = entry; + last = entry; + } + } + + if(count) + { cs_log("%d entries read from %s", count, cs_cacheex_matcher); } + + fclose(fp); + + return new_cacheex_matcher; +} + +void cacheex_load_config_file(void) +{ + struct s_cacheex_matcher *entry, *old_list; + + old_list = cfg.cacheex_matcher; + cfg.cacheex_matcher = cacheex_matcher_read_int(); + + while(old_list) + { + entry = old_list->next; + NULLFREE(old_list); + old_list = entry; + } +} + +CWCHECK get_cwcheck(ECM_REQUEST *er) +{ + int32_t i; + int8_t mode = 0; + int16_t counter = 1; + + for(i = 0; i < cfg.cacheex_cwcheck_tab.cwchecknum; i++) + { + CWCHECKTAB_DATA *d = &cfg.cacheex_cwcheck_tab.cwcheckdata[i]; + + if(i == 0 && d->caid <= 0) + { + mode = d->mode; + counter = d->counter; + continue; //check other, only valid for unset + } + + if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1)) + { + if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1) + { + if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1) + { + mode = d->mode; + counter = d->counter; + break; + } + } + + } + } + + //check for correct values + if(mode>2 || mode<0) mode=0; + if(counter<1) counter=1; + + CWCHECK check_cw; + memset(&check_cw, 0, sizeof(CWCHECK)); + check_cw.mode = mode; + check_cw.counter = counter; + + return check_cw; +} + +uint16_t get_cacheex_mode1_delay(ECM_REQUEST *er) +{ + return caidvaluetab_get_value(&cfg.cacheex_mode1_delay_tab, er->caid, 0); +} + +uint32_t get_cacheex_wait_time(ECM_REQUEST *er, struct s_client *cl) +{ + int32_t i, dwtime = -1, awtime = -1; + + for(i = 0; i < cfg.cacheex_wait_timetab.cevnum; i++) + { + CECSPVALUETAB_DATA *d = &cfg.cacheex_wait_timetab.cevdata[i]; + + if(i == 0 && d->caid <= 0) + { + dwtime = d->dwtime; + awtime = d->awtime; + continue; //check other, only valid for unset + } + + if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1)) + { + if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1) + { + if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1) + { + dwtime = d->dwtime; + awtime = d->awtime; + break; + } + } + + } + + } + if(awtime > 0 && (dwtime <= 0 || awtime==dwtime) ) //if awtime==dwtime useless check hitcache + { + return awtime; + } + if(cl == NULL) + { + if(dwtime < 0) + { dwtime = 0; } + return dwtime; + } + if(awtime > 0 || dwtime > 0) + { + //if found last in cache return dynwaittime else alwayswaittime + if(cacheex_check_hitcache(er,cl)) + { return dwtime >= awtime ? dwtime : awtime; } + else + { return awtime > 0 ? awtime : 0; } + } + return 0; +} + + +int32_t chk_csp_ctab(ECM_REQUEST *er, CECSPVALUETAB *tab) +{ + if(!er->caid || !tab->cevnum) + { return 1; } // nothing setup we add all + int32_t i; + for(i = 0; i < tab->cevnum; i++) + { + CECSPVALUETAB_DATA *d = &tab->cevdata[i]; + if(d->caid > 0) + { + if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1)) + { + if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1) + { + if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1) + { + return 1; + } + } + } + } + } + return 0; +} + +void cacheex_push_out(struct s_client *cl, ECM_REQUEST *er) +{ + int32_t res = 0, stats = -1; + struct s_reader *reader = cl->reader; + struct s_module *module = get_module(cl); + + // cc-nodeid-list-check + if(reader) + { + if(reader->ph.c_cache_push_chk && !reader->ph.c_cache_push_chk(cl, er)) + return; + res = reader->ph.c_cache_push(cl, er); +#ifdef CS_CACHEEX_AIO + stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0, er->localgenerated); +#else + stats = cacheex_add_stats(cl, er->caid, er->srvid, er->prid, 0); +#endif + } + else + { + if(module->c_cache_push_chk && !module->c_cache_push_chk(cl, er)) + return; + res = module->c_cache_push(cl, er); + } + debug_ecm(D_CACHEEX, "pushed ECM %s to %s res %d stats %d", buf, username(cl), res, stats); + cl->cwcacheexpush++; + if(cl->account) + { cl->account->cwcacheexpush++; } + first_client->cwcacheexpush++; + +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + cl->cwcacheexpushlg++; + if(cl->account) + cl->account->cwcacheexpushlg++; + first_client->cwcacheexpushlg++; + } +#endif +} + +bool cacheex_check_queue_length(struct s_client *cl) +{ + // Avoid full running queues: + if(ll_count(cl->joblist) <= 2000) + return 0; + + cs_log_dbg(D_TRACE, "WARNING: job queue %s %s has more than 2000 jobs! count=%d, dropped!", + cl->typ == 'c' ? "client" : "reader", username(cl), ll_count(cl->joblist)); + + // Thread down??? + SAFE_MUTEX_LOCK(&cl->thread_lock); + if(cl && !cl->kill && cl->thread && cl->thread_active) + { + // Just test for invalid thread id: + if(pthread_detach(cl->thread) == ESRCH) + { + cl->thread_active = 0; + cs_log_dbg(D_TRACE, "WARNING: %s %s thread died!", cl->typ == 'c' ? "client" : "reader", username(cl)); + } + } + SAFE_MUTEX_UNLOCK(&cl->thread_lock); + return 1; +} + +void cacheex_mode1_delay(ECM_REQUEST *er) +{ + if(!er->cacheex_wait_time_expired && er->cacheex_mode1_delay + && er->cacheex_reader_count > 0 && !er->stage && er->rc >= E_UNHANDLED) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex_mode1_delay timeout! ", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + + // setting stop_stage=1, we request only cacheex mode 1 readers. Others are requested at cacheex timeout! + request_cw_from_readers(er, 1); + } +} + +void cacheex_timeout(ECM_REQUEST *er) +{ + if(er->cacheex_wait_time_expired) + return; + er->cacheex_wait_time_expired = 1; + if(er->rc >= E_UNHANDLED) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex timeout! ", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + +#ifdef CS_CACHEEX_AIO + CACHE_HIT *result; + HIT_KEY search; + + memset(&search, 0, sizeof(HIT_KEY)); + search.caid = er->caid; + search.prid = er->prid; + search.srvid = er->srvid; + + SAFE_RWLOCK_WRLOCK(&hitcache_lock); + + result = find_hash_table(&ht_hitcache, &search, sizeof(HIT_KEY), &cacheex_compare_hitkey); + if(result) + { + if(cfg.waittime_block_start && (result->waittime_block <= cfg.waittime_block_start)) + { + result->waittime_block++; + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} waittime_block count: %u ", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, result->waittime_block); + } + } + + SAFE_RWLOCK_UNLOCK(&hitcache_lock); +#endif + // if check_cw mode=0, first try to get cw from cache without check counter! + CWCHECK check_cw = get_cwcheck(er); + if(!check_cw.mode) + { + struct ecm_request_t *ecm = NULL; + ecm = check_cache(er, er->client); + + if(ecm) // found in cache + { + struct s_write_from_cache *wfc = NULL; + if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache))) + { + NULLFREE(ecm); + return; + } + wfc->er_new = er; + wfc->er_cache = ecm; + if(!add_job(er->client, ACTION_ECM_ANSWER_CACHE, wfc, sizeof(struct s_write_from_cache))) // write_ecm_answer_fromcache + { NULLFREE(ecm); } + return; + } + } + + // check if "normal" readers selected, if not send NOT FOUND! + // cacheex1-client (having always no "normal" reader), + // or not-cacheex-1 client with no normal readers available (or filtered by LB) + if((er->reader_count + er->fallback_reader_count - er->cacheex_reader_count) <= 0) + { + if(!cfg.wait_until_ctimeout) + { + er->rc = E_NOTFOUND; + er->selected_reader = NULL; + er->rcEx = 0; + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} cacheex timeout: NO \"normal\" readers... not_found! ", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + + send_dcw(er->client, er); + return; + } + } + else + { + if(er->stage < 2) + { + debug_ecm(D_TRACE, "request for %s %s", username(er->client), buf); + request_cw_from_readers(er, 0); + } + } + } +} + +#ifdef CS_CACHEEX_AIO +char* cxaio_ftab_to_buf(FTAB *lg_only_ftab) +{ + int32_t i, k, l = 0, strncat_sz = 0; + char *ret; + char caid[5]; + char provid[7]; + char nprids[3]; + + // get size of return-val + for(i = 0; i < lg_only_ftab->nfilts; i++) + { + l += 4; // caid + l += 2; // nprid-counter + l += 6 * lg_only_ftab->filts[i].nprids; // prid/s + + if(!lg_only_ftab->filts[i].nprids) + { + l += 6; + } + } + + if(!cs_malloc(&ret, l * sizeof(char) + sizeof(char))) { + return ""; + } + + strncat_sz += l * sizeof(char) + sizeof(char); + + for(i = 0; i < lg_only_ftab->nfilts; i++) + { + snprintf(caid, 5, "%04X", lg_only_ftab->filts[i].caid); + if (!cs_strncat(ret, caid, strncat_sz)) { + cs_log("FIXME!"); + } + + if(!lg_only_ftab->filts[i].nprids) + { + if (!cs_strncat(ret, "01", strncat_sz)) { + cs_log("FIXME2!"); + } + snprintf(provid, 7, "000000"); + if (!cs_strncat(ret, provid, strncat_sz)) { + cs_log("FIXME3!"); + } + } + else + { + snprintf(nprids, 3, "%02X", lg_only_ftab->filts[i].nprids); + if (!cs_strncat(ret, nprids, strncat_sz)) { + cs_log("FIXME4!"); + } + } + + for(k = 0; k < lg_only_ftab->filts[i].nprids; k++) + { + snprintf(provid, 7, "%06X", lg_only_ftab->filts[i].prids[k]); + if (!cs_strncat(ret, provid, strncat_sz)) { + cs_log("FIXME5!"); + } + } + } + return ret; +} + +FTAB caidtab2ftab(CAIDTAB *ctab) +{ + int i; + FTAB ftab; + memset(&ftab, 0, sizeof(ftab)); + + for(i=0; ictnum; i++) + { + FILTER d; + memset(&d, 0, sizeof(d)); + d.caid = ctab->ctdata[i].caid; + d.prids[d.nprids] = NO_PROVID_VALUE; + d.nprids++; + ftab_add(&ftab, &d); + } + return ftab; +} + +void caidtab2ftab_add(CAIDTAB *lgonly_ctab, FTAB *lgonly_tab) +{ + int j, k, l, rc; + for(j = 0; j < lgonly_ctab->ctnum; j++) + { + CAIDTAB_DATA *d = &lgonly_ctab->ctdata[j]; + if(d->caid) + { + rc = 0; + if(lgonly_tab->nfilts) + { + for(k = 0; (k < lgonly_tab->nfilts); k++) + { + if(lgonly_tab->filts[k].caid != 0 && lgonly_tab->filts[k].caid == d->caid) + { + for(l = 0; (l < lgonly_tab->filts[k].nprids); l++) + { + if(lgonly_tab->filts[k].prids[l] == NO_PROVID_VALUE) + { + rc = 1; + break; + } + } + if(!rc) + { + lgonly_tab->filts[k].nprids = 1; + lgonly_tab->filts[k].prids[0] = NO_PROVID_VALUE; + rc = 1; + } + break; + } + } + } + if(!rc) // caid not found + { + FILTER df; + memset(&df, 0, sizeof(df)); + df.caid = d->caid; + df.prids[0] = NO_PROVID_VALUE; + df.nprids++; + ftab_add(lgonly_tab, &df); + } + } + } +} +#endif + +#endif // CS_CACHEEX diff --git a/module-cacheex.h b/module-cacheex.h old mode 100755 new mode 100644 diff --git a/module-cacheex.h.bak b/module-cacheex.h.bak new file mode 100644 index 0000000..1d6b80c --- /dev/null +++ b/module-cacheex.h.bak @@ -0,0 +1,78 @@ +#ifndef MODULE_CACHEEX_H_ +#define MODULE_CACHEEX_H_ + +static inline uint64_t cacheex_node_id(void *var) +{ + uint64_t *x = var; + return *x; +} + +uint32_t get_cacheex_wait_time(ECM_REQUEST *er, struct s_client *cl); +CWCHECK get_cwcheck(ECM_REQUEST *er); +uint16_t get_cacheex_mode1_delay(ECM_REQUEST *er); +int32_t chk_csp_ctab(ECM_REQUEST *er, CECSPVALUETAB *tab); +uint8_t check_cacheex_filter(struct s_client *cl, ECM_REQUEST *er); +void cacheex_add_to_cache(struct s_client *cl, ECM_REQUEST *er); +void cacheex_add_to_cache_from_csp(struct s_client *cl, ECM_REQUEST *er); +void cacheex_cache_push(ECM_REQUEST *er); +int32_t cacheex_add_stats(struct s_client *cl, uint16_t caid, uint16_t srvid, uint32_t prid, uint8_t direction +#ifdef CS_CACHEEX_AIO + , uint8_t localgenerated +#endif +); +int8_t cacheex_maxhop(struct s_client *cl); +#ifdef CS_CACHEEX_AIO +int8_t cacheex_maxhop_lg(struct s_client *cl); +#endif + +#ifdef CS_CACHEEX +extern void cacheex_init(void); +extern void cacheex_clear_account_stats(struct s_auth *account); +extern void cacheex_clear_client_stats(struct s_client *client); +extern void cacheex_load_config_file(void); +static inline bool cacheex_reader(struct s_reader *rdr) { return rdr ? (rdr->cacheex.mode == 1 ? 1 : 0) : 0; } +extern bool cacheex_is_match_alias(struct s_client *cl, ECM_REQUEST *er); +void cacheex_set_csp_lastnode(ECM_REQUEST *er); +void cacheex_set_cacheex_src(ECM_REQUEST *ecm, struct s_client *cl); +void cacheex_init_cacheex_src(ECM_REQUEST *ecm, ECM_REQUEST *er); +void cacheex_free_csp_lastnodes(ECM_REQUEST *er); +void checkcache_process_thread_start(void); +void cacheex_push_out(struct s_client *cl, ECM_REQUEST *er); +bool cacheex_check_queue_length(struct s_client *cl); +static inline int8_t cacheex_get_rdr_mode(struct s_reader *reader) { return reader ? reader->cacheex.mode : 0; } +void cacheex_init_hitcache(void); +void cacheex_free_hitcache(void); +void cacheex_cleanup_hitcache(bool force); +void cacheex_update_hash(ECM_REQUEST *er); +void cacheex_mode1_delay(ECM_REQUEST *er); +void cacheex_timeout(ECM_REQUEST *er); +#ifdef CS_CACHEEX_AIO +char* cxaio_ftab_to_buf(FTAB *lg_only_ftab); +FTAB caidtab2ftab(CAIDTAB *ctab); +void caidtab2ftab_add(CAIDTAB *lgonly_ctab, FTAB *lgonly_tab); +#define CACHEEX_FEATURES 127 +#endif +#else +static inline void cacheex_init(void) { } +static inline void cacheex_clear_account_stats(struct s_auth *UNUSED(account)) { } +static inline void cacheex_clear_client_stats(struct s_client *UNUSED(client)) { } +static inline void cacheex_load_config_file(void) { } +static inline bool cacheex_reader(struct s_reader *UNUSED(rdr)) { return false; } +static inline bool cacheex_is_match_alias(struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er)) { return false; } +static inline void cacheex_set_csp_lastnode(ECM_REQUEST *UNUSED(er)) { } +static inline void cacheex_free_csp_lastnodes(ECM_REQUEST *UNUSED(er)) { } +static inline void cacheex_set_cacheex_src(ECM_REQUEST *UNUSED(ecm), struct s_client *UNUSED(cl)) { } +static inline void cacheex_init_cacheex_src(ECM_REQUEST *UNUSED(ecm), ECM_REQUEST *UNUSED(er)) { } +static inline void checkcache_process_thread_start(void) { } +static inline void cacheex_push_out(struct s_client *UNUSED(cl), ECM_REQUEST *UNUSED(er)) { } +static inline bool cacheex_check_queue_length(struct s_client *UNUSED(cl)) { return 0; } +static inline int8_t cacheex_get_rdr_mode(struct s_reader *UNUSED(reader)) { return 0; } +static inline void cacheex_init_hitcache(void) { } +static inline void cacheex_free_hitcache(void) { } +static inline void cacheex_cleanup_hitcache(bool UNUSED(force)) { } +static inline void cacheex_update_hash(ECM_REQUEST *UNUSED(er)) { } +static inline void cacheex_mode1_delay(ECM_REQUEST *UNUSED(er)) { } +static inline void cacheex_timeout(ECM_REQUEST *UNUSED(er)) { } +#endif + +#endif diff --git a/module-camd33.c b/module-camd33.c old mode 100755 new mode 100644 diff --git a/module-camd35-cacheex.c b/module-camd35-cacheex.c old mode 100755 new mode 100644 index 5649336..b03989b --- a/module-camd35-cacheex.c +++ b/module-camd35-cacheex.c @@ -20,10 +20,10 @@ uint8_t camd35_node_id[8]; -#define CSP_HASH_SWAP(n) (((((uint32_t)(n) & 0xFF)) << 24) | \ - ((((uint32_t)(n) & 0xFF00)) << 8) | \ - ((((uint32_t)(n) & 0xFF0000)) >> 8) | \ - ((((uint32_t)(n) & 0xFF000000)) >> 24)) +#define CSP_HASH_SWAP(n) (((((uint32_t)(n) & 0xFF)) << 24) | \ + ((((uint32_t)(n) & 0xFF00)) << 8) | \ + ((((uint32_t)(n) & 0xFF0000)) >> 8) | \ + ((((uint32_t)(n) & 0xFF000000)) >> 24)) #ifdef CS_CACHEEX_AIO void camd35_cacheex_feature_trigger_in(struct s_client *cl, uint8_t *buf) @@ -783,7 +783,7 @@ void camd35_cacheex_feature_trigger(struct s_client *cl, int32_t feature, uint8_ uint8_t *ofs = buf + 20; memcpy(ofs, payload, size - 20); - camd35_send_without_timeout(cl, buf, size-20); // send adds +20 + camd35_send_without_timeout(cl, buf, size-20); //send adds +20 } void camd35_cacheex_feature_request_save(struct s_client *cl, uint8_t *buf) @@ -879,7 +879,7 @@ void camd35_cacheex_feature_request_save(struct s_client *cl, uint8_t *buf) camd35_cacheex_feature_trigger(cl, 2, 2); } - // flag 4 => set cacheex_ecm_filter (extended) + // // flag 4 => set cacheex_ecm_filter (extended) if(rdr->cacheex.feature_bitfield & 4) { camd35_cacheex_feature_trigger(cl, 4, 2); @@ -925,7 +925,7 @@ void camd35_cacheex_feature_request(struct s_client *cl) i2b_buf(2, CACHEEX_FEATURES, buf + i); // set feature-list here - camd35_send_without_timeout(cl, buf, 12); // send adds +20 + camd35_send_without_timeout(cl, buf, 12); //send adds +20 } void camd35_cacheex_feature_request_reply(struct s_client *cl, uint8_t *buf) @@ -941,7 +941,7 @@ void camd35_cacheex_feature_request_reply(struct s_client *cl, uint8_t *buf) i2b_buf(2, CACHEEX_FEATURES, rbuf + i); - camd35_send_without_timeout(cl, rbuf, 12); // send adds +20 + camd35_send_without_timeout(cl, rbuf, 12); //send adds +20 } #endif @@ -954,13 +954,13 @@ void camd35_cacheex_send_push_filter(struct s_client *cl, uint8_t mode) struct s_reader *rdr = cl->reader; int i = 20, j; CECSPVALUETAB *filter; - // maximum size: 20+255 + //maximum size: 20+255 uint8_t buf[20+242]; memset(buf, 0, sizeof(buf)); buf[0] = 0x3c; buf[1] = 0xf2; - // mode==2 send filters from rdr + //mode==2 send filters from rdr if(mode == 2 && rdr) { filter = &rdr->cacheex.filter_caidtab; @@ -970,7 +970,7 @@ void camd35_cacheex_send_push_filter(struct s_client *cl, uint8_t mode) filter = &cfg.cacheex_filter_caidtab; #endif } - // mode==3 send filters from acc + //mode==3 send filters from acc else if(mode == 3 && cl->typ == 'c' && cl->account) { filter = &cl->account->cacheex.filter_caidtab; @@ -1025,7 +1025,7 @@ void camd35_cacheex_send_push_filter(struct s_client *cl, uint8_t mode) } cs_log_dbg(D_CACHEEX, "cacheex: sending push filter request to %s", username(cl)); - camd35_send_without_timeout(cl, buf, 242); // send adds +20 + camd35_send_without_timeout(cl, buf, 242); //send adds +20 } /** @@ -1038,13 +1038,13 @@ static void camd35_cacheex_push_filter(struct s_client *cl, uint8_t *buf, uint8_ int32_t caid, cmask, provid, srvid; CECSPVALUETAB *filter; - // mode==2 write filters to acc + //mode==2 write filters to acc if(mode == 2 && cl->typ == 'c' && cl->account && cl->account->cacheex.mode == 2 && cl->account->cacheex.allow_filter == 1) { filter = &cl->account->cacheex.filter_caidtab; } - // mode==3 write filters to rdr + //mode==3 write filters to rdr else if(mode == 3 && rdr && rdr->cacheex.allow_filter == 1) { filter = &rdr->cacheex.filter_caidtab; @@ -1105,16 +1105,16 @@ static void camd35_cacheex_push_filter(struct s_client *cl, uint8_t *buf, uint8_ static int32_t camd35_cacheex_push_chk(struct s_client *cl, ECM_REQUEST *er) { if( - ll_count(er->csp_lastnodes) >= cacheex_maxhop(cl, er) // check max 10 nodes to push + ll_count(er->csp_lastnodes) >= cacheex_maxhop(cl) // check max 10 nodes to push #ifdef CS_CACHEEX_AIO - && (!er->localgenerated || (er->localgenerated && (ll_count(er->csp_lastnodes) >= cacheex_maxhop_lg(cl, er)))) // check maxhop_lg if cw is lg-flagged + && (!er->localgenerated || (er->localgenerated && (ll_count(er->csp_lastnodes) >= cacheex_maxhop_lg(cl)))) // check maxhop_lg if cw is lg-flagged #endif ) { #ifdef CS_CACHEEX_AIO - cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes(non-lg) or reached %d nodes(lg), no push", cacheex_maxhop(cl, er), cacheex_maxhop_lg(cl, er)); + cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes(non-lg) or reached %d nodes(lg), no push", cacheex_maxhop(cl), cacheex_maxhop_lg(cl)); #else - cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes, no push", cacheex_maxhop(cl, er)); + cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes, no push", cacheex_maxhop(cl)); #endif return 0; } @@ -1150,7 +1150,7 @@ static int32_t camd35_cacheex_push_chk(struct s_client *cl, ECM_REQUEST *er) } ll_li_destroy(li); - // node found, so we got it from there, do not push: + //node found, so we got it from there, do not push: if(node) { cs_log_dbg(D_CACHEEX, @@ -1158,7 +1158,7 @@ static int32_t camd35_cacheex_push_chk(struct s_client *cl, ECM_REQUEST *er) return 0; } - // check if cw is already pushed + //check if cw is already pushed if(check_is_pushed(er->cw_cache, cl)) { return 0; } @@ -1170,10 +1170,10 @@ static int32_t camd35_cacheex_push_chk(struct s_client *cl, ECM_REQUEST *er) static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t *er) { int8_t rc = (er->rc < E_NOTFOUND) ? E_FOUND : er->rc; - if(rc != E_FOUND && rc != E_UNHANDLED) { return -1; } // Maybe later we could support other rcs + if(rc != E_FOUND && rc != E_UNHANDLED) { return -1; } //Maybe later we could support other rcs - // E_FOUND : we have the CW, - // E_UNHANDLED : incoming ECM request + //E_FOUND : we have the CW, + //E_UNHANDLED : incoming ECM request if(cl->reader) { @@ -1191,10 +1191,10 @@ static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t (ll_count(er->csp_lastnodes) + 1) * 8; #endif uint8_t *buf; - if(!cs_malloc(&buf, size + 20)) // camd35_send() adds +20 + if(!cs_malloc(&buf, size + 20)) //camd35_send() adds +20 { return -1; } - buf[0] = 0x3f; // New Command: Cache-push + buf[0] = 0x3f; //New Command: Cache-push buf[1] = size & 0xff; buf[2] = size >> 8; buf[3] = rc; @@ -1202,7 +1202,7 @@ static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t i2b_buf(2, er->srvid, buf + 8); i2b_buf(2, er->caid, buf + 10); i2b_buf(4, er->prid, buf + 12); - // i2b_buf(2, er->idx, buf + 16); // Not relevant...? + //i2b_buf(2, er->idx, buf + 16); // Not relevant...? if(er->cwc_cycletime && er->cwc_next_cw_cycle < 2) { @@ -1222,27 +1222,27 @@ static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t uint8_t *ofs = buf + 20; - // write oscam ecmd5: - memcpy(ofs, er->ecmd5, sizeof(er->ecmd5)); // 16 + //write oscam ecmd5: + memcpy(ofs, er->ecmd5, sizeof(er->ecmd5)); //16 ofs += sizeof(er->ecmd5); - // write csp hashcode: + //write csp hashcode: i2b_buf(4, CSP_HASH_SWAP(er->csp_hash), ofs); ofs += 4; - // write cw: - memcpy(ofs, er->cw, sizeof(er->cw)); // 16 + //write cw: + memcpy(ofs, er->cw, sizeof(er->cw)); //16 ofs += sizeof(er->cw); - // write node count: + //write node count: *ofs = ll_count(er->csp_lastnodes) + 1; ofs++; - // write own node: + //write own node: memcpy(ofs, camd35_node_id, 8); ofs += 8; - // write other nodes: + //write other nodes: LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); uint8_t *node; while((node = ll_li_next(li))) @@ -1271,7 +1271,7 @@ static int32_t camd35_cacheex_push_out(struct s_client *cl, struct ecm_request_t static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf) { int8_t rc = buf[3]; - if(rc != E_FOUND && rc != E_UNHANDLED) // Maybe later we could support other rcs + if(rc != E_FOUND && rc != E_UNHANDLED) //Maybe later we could support other rcs { return; } ECM_REQUEST *er; @@ -1289,7 +1289,7 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf) er->caid = b2i(2, buf + 10); er->prid = b2i(4, buf + 12); er->pid = b2i(2, buf + 16); - er->ecm[0] = buf[19]!=0x80 && buf[19]!=0x81 ? 0 : buf[19]; // odd/even byte, usefull to send it over CSP and to check cw for swapping + er->ecm[0] = buf[19]!=0x80 && buf[19]!=0x81 ? 0 : buf[19]; //odd/even byte, usefull to send it over CSP and to check cw for swapping er->rc = rc; er->ecmlen = 0; @@ -1310,8 +1310,8 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf) uint8_t *ofs = buf + 20; - // Read ecmd5 - memcpy(er->ecmd5, ofs, sizeof(er->ecmd5)); // 16 + //Read ecmd5 + memcpy(er->ecmd5, ofs, sizeof(er->ecmd5)); //16 ofs += sizeof(er->ecmd5); if(!check_cacheex_filter(cl, er)) @@ -1329,7 +1329,7 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf) ) { cs_log_dbg(D_CACHEEX, "cacheex: received cache not matching cacheex_ecm_filter => pushing filter again"); - camd35_cacheex_send_push_filter(cl, 2); // get cache != cacheex_ecm_filter, send filter again - remote restarted + camd35_cacheex_send_push_filter(cl, 2); // get cache != cacheex_ecm_filter, send filter again - remote restarted if(cl->reader->cacheex.feature_bitfield & 4) camd35_cacheex_feature_trigger(cl, 4, 2); free_push_in_ecm(er); @@ -1352,28 +1352,28 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf) } #endif - // Read csp_hash: + //Read csp_hash: er->csp_hash = CSP_HASH_SWAP(b2i(4, ofs)); ofs += 4; - // Read cw: - memcpy(er->cw, ofs, sizeof(er->cw)); // 16 + //Read cw: + memcpy(er->cw, ofs, sizeof(er->cw)); //16 ofs += sizeof(er->cw); - // Check auf neues Format: + //Check auf neues Format: uint8_t *data; if(size > (sizeof(er->ecmd5) + sizeof(er->csp_hash) + sizeof(er->cw))) { - // Read lastnodes: + //Read lastnodes: uint8_t count = *ofs; ofs++; #ifndef CS_CACHEEX_AIO - // check max nodes: - if(count > cacheex_maxhop(cl, er)) + //check max nodes: + if(count > cacheex_maxhop(cl)) { - cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", (int32_t)count, cacheex_maxhop(cl, er), username(cl)); + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", (int32_t)count, cacheex_maxhop(cl), username(cl)); NULLFREE(er); return; } @@ -1400,10 +1400,10 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf) er->localgenerated = 1; cs_log_dbg(D_CACHEEX, "cacheex: received ECM with localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl)); - // check max nodes for lg flagged cw: - if(ll_count(er->csp_lastnodes) > cacheex_maxhop_lg(cl, er)) + //check max nodes for lg flagged cw: + if(ll_count(er->csp_lastnodes) > cacheex_maxhop_lg(cl)) { - cs_log_dbg(D_CACHEEX, "cacheex: received (lg) %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop_lg(cl, er), username(cl)); + cs_log_dbg(D_CACHEEX, "cacheex: received (lg) %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop_lg(cl), username(cl)); free_push_in_ecm(er); return; } @@ -1411,10 +1411,10 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf) // without localgenerated flag else { - // check max nodes: - if(ll_count(er->csp_lastnodes) > cacheex_maxhop(cl, er)) + //check max nodes: + if(ll_count(er->csp_lastnodes) > cacheex_maxhop(cl)) { - cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop(cl, er), username(cl)); + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop(cl), username(cl)); free_push_in_ecm(er); return; } @@ -1472,19 +1472,16 @@ static void camd35_cacheex_push_in(struct s_client *cl, uint8_t *buf) er->csp_lastnodes = ll_create("csp_lastnodes"); } - // store remote node id if we got one. The remote node is the first node in the node list + //store remote node id if we got one. The remote node is the first node in the node list data = ll_has_elements(er->csp_lastnodes); - if(data && !cl->ncd_skey[8]) // Ok, this is tricky, we use newcamd key storage for saving the remote node + if(data && !cl->ncd_skey[8]) //Ok, this is tricky, we use newcamd key storage for saving the remote node { - memcpy(cl->cxnodeid_last, data, 8); - cl->cxnodeid_last[8] = 1; - cl->cxnodeid_changer_detected = 0; memcpy(cl->ncd_skey, data, 8); - cl->ncd_skey[8] = 1; // Mark as valid node + cl->ncd_skey[8] = 1; //Mark as valid node } cs_log_dbg(D_CACHEEX, "cacheex: received cacheex from remote node id %" PRIu64 "X", cacheex_node_id(cl->ncd_skey)); - // for compatibility: add peer node if no node received (not working now, maybe later): + //for compatibility: add peer node if no node received (not working now, maybe later): if(!ll_count(er->csp_lastnodes) && cl->ncd_skey[8]) { if(!cs_malloc(&data, 8)) @@ -1572,11 +1569,6 @@ static void camd35_server_client_init(struct s_client *cl) if(!cl->init_done) { cl->cacheex_needfilter = 1; - - // inicjalizacja nodeid changer detection - cl->cxnodeid_changer_detected = 0; - memset(cl->cxnodeid_last, 0, sizeof(cl->cxnodeid_last)); - memset(cl->ncd_skey, 0, sizeof(cl->ncd_skey)); } } @@ -1585,39 +1577,13 @@ static void camd35_server_client_init(struct s_client *cl) */ static void camd35_cacheex_push_receive_remote_id(struct s_client *cl, uint8_t *buf) { - uint8_t new_nodeid[8]; - memcpy(new_nodeid, buf + 20, 8); - - // jeśli pierwszy raz, zapisz baseline - if (!cl->cxnodeid_last[8]) - { - memcpy(cl->cxnodeid_last, new_nodeid, 8); - cl->cxnodeid_last[8] = 1; - } - - // zapisz aktualny nodeid do struktury OSCam - memcpy(cl->ncd_skey, new_nodeid, 8); + memcpy(cl->ncd_skey, buf + 20, 8); cl->ncd_skey[8] = 1; - - // sprawdź czy zmieniony - if (memcmp(new_nodeid, cl->cxnodeid_last, 8) != 0) - { - cs_log("CACHEEX WARNING: NodeID changer detected for %s: new=%" PRIu64 "X old=%" PRIu64 "X", - username(cl), - cacheex_node_id(new_nodeid), - cacheex_node_id(cl->cxnodeid_last)); - - cl->cxnodeid_changer_detected = 1; - - // zapamiętaj nowy jako baseline - memcpy(cl->cxnodeid_last, new_nodeid, 8); - cl->cxnodeid_last[8] = 1; - } + cs_log_dbg(D_CACHEEX, "cacheex: received id answer from %s: %" PRIu64 "X", username(cl), cacheex_node_id(cl->ncd_skey)); } - void camd35_cacheex_init_dcw(struct s_client *client, ECM_REQUEST *er) { uint8_t *buf = er->src_data; // get orig request @@ -1643,7 +1609,7 @@ void camd35_cacheex_init_dcw(struct s_client *client, ECM_REQUEST *er) */ void camd35_cacheex_push_send_own_id(struct s_client *cl, uint8_t *mbuf) { - uint8_t rbuf[32]; // minimal size + uint8_t rbuf[32]; //minimal size if(!cl->crypted) { return; } cs_log_dbg(D_CACHEEX, "cacheex: received id request from node %" PRIu64 "X %s", cacheex_node_id(mbuf + 20), username(cl)); memset(rbuf, 0, sizeof(rbuf)); @@ -1652,20 +1618,20 @@ void camd35_cacheex_push_send_own_id(struct s_client *cl, uint8_t *mbuf) rbuf[2] = 0; memcpy(rbuf + 20, camd35_node_id, 8); cs_log_dbg(D_CACHEEX, "cacheex: sending own id %" PRIu64 "X request %s", cacheex_node_id(camd35_node_id), username(cl)); - camd35_send(cl, rbuf, 12); // send adds +20 + camd35_send(cl, rbuf, 12); //send adds +20 } bool camd35_cacheex_server(struct s_client *client, uint8_t *mbuf) { switch(mbuf[0]) { - case 0x3c: // Cache-push filter request + case 0x3c: // Cache-push filter request if(client->account && client->account->cacheex.mode==2){ camd35_cacheex_push_filter(client, mbuf, 2); } break; - case 0x3d: // Cache-push id request - camd35_cacheex_push_receive_remote_id(client, mbuf); // reader send request id with its nodeid, so we save it! + case 0x3d: // Cache-push id request + camd35_cacheex_push_receive_remote_id(client, mbuf); //reader send request id with its nodeid, so we save it! camd35_cacheex_push_send_own_id(client, mbuf); if(client->cacheex_needfilter && client->account && client->account->cacheex.mode==3){ camd35_cacheex_send_push_filter(client, 3); @@ -1679,20 +1645,20 @@ bool camd35_cacheex_server(struct s_client *client, uint8_t *mbuf) } #endif break; - case 0x3e: // Cache-push id answer + case 0x3e: // Cache-push id answer camd35_cacheex_push_receive_remote_id(client, mbuf); break; - case 0x3f: // Cache-push + case 0x3f: // Cache-push camd35_cacheex_push_in(client, mbuf); break; #ifdef CS_CACHEEX_AIO - case 0x40: // cacheex-features request + case 0x40: // cacheex-features request camd35_cacheex_feature_request_reply(client, mbuf); break; - case 0x41: // cacheex-features answer + case 0x41: // cacheex-features answer // camd35_cacheex_feature_request_save(client, mbuf); break; - case 0x42: // cacheex-feature trigger in + case 0x42: // cacheex-feature trigger in camd35_cacheex_feature_trigger_in(client, mbuf); break; #endif @@ -1707,16 +1673,16 @@ bool camd35_cacheex_recv_chk(struct s_client *client, uint8_t *buf) struct s_reader *rdr = client->reader; switch(buf[0]) { - case 0x3c: // Cache-push filter request + case 0x3c: // Cache-push filter request if(rdr->cacheex.mode==3){ camd35_cacheex_push_filter(client, buf, 3); } break; - case 0x3d: // Cache-push id request - camd35_cacheex_push_receive_remote_id(client, buf); // client send request id with its nodeid, so we save it! + case 0x3d: // Cache-push id request + camd35_cacheex_push_receive_remote_id(client, buf); //client send request id with its nodeid, so we save it! camd35_cacheex_push_send_own_id(client, buf); break; - case 0x3e: // Cache-push id answer + case 0x3e: // Cache-push id answer camd35_cacheex_push_receive_remote_id(client, buf); #ifdef CS_CACHEEX_AIO if(!client->cacheex_aio_checked && ((client->account && client->account->cacheex.mode > 0) || (client->reader && client->reader->cacheex.mode > 0))) @@ -1726,17 +1692,17 @@ bool camd35_cacheex_recv_chk(struct s_client *client, uint8_t *buf) } #endif break; - case 0x3f: // cache-push + case 0x3f: //cache-push camd35_cacheex_push_in(client, buf); break; #ifdef CS_CACHEEX_AIO - case 0x40: // cacheex-features request + case 0x40: // cacheex-features request camd35_cacheex_feature_request_reply(client, buf); break; - case 0x41: // cacheex-features answer + case 0x41: // cacheex-features answer // camd35_cacheex_feature_request_save(client, buf); break; - case 0x42: // cacheex-feature trigger in + case 0x42: // cacheex-feature trigger in camd35_cacheex_feature_trigger_in(client, buf); break; #endif @@ -1751,14 +1717,14 @@ bool camd35_cacheex_recv_chk(struct s_client *client, uint8_t *buf) */ void camd35_cacheex_push_request_remote_id(struct s_client *cl) { - uint8_t rbuf[32]; // minimal size + uint8_t rbuf[32];//minimal size memset(rbuf, 0, sizeof(rbuf)); rbuf[0] = 0x3d; rbuf[1] = 12; rbuf[2] = 0; memcpy(rbuf + 20, camd35_node_id, 8); cs_log_dbg(D_CACHEEX, "cacheex: sending id request to %s", username(cl)); - camd35_send(cl, rbuf, 12); // send adds +20 + camd35_send(cl, rbuf, 12); //send adds +20 } void camd35_cacheex_module_init(struct s_module *ph) diff --git a/module-camd35-cacheex.h b/module-camd35-cacheex.h old mode 100755 new mode 100644 diff --git a/module-camd35.c b/module-camd35.c old mode 100755 new mode 100644 diff --git a/module-camd35.h b/module-camd35.h old mode 100755 new mode 100644 diff --git a/module-cccam-cacheex.c b/module-cccam-cacheex.c old mode 100755 new mode 100644 index a915ef3..039f464 --- a/module-cccam-cacheex.c +++ b/module-cccam-cacheex.c @@ -1088,16 +1088,16 @@ static int32_t cc_cacheex_push_chk(struct s_client *cl, struct ecm_request_t *er } if( - ll_count(er->csp_lastnodes) >= cacheex_maxhop(cl, er) // check max 10 nodes to push + ll_count(er->csp_lastnodes) >= cacheex_maxhop(cl) // check max 10 nodes to push #ifdef CS_CACHEEX_AIO - && (!er->localgenerated || (er->localgenerated && (ll_count(er->csp_lastnodes) >= cacheex_maxhop_lg(cl, er)))) // check maxhop_lg if cw is lg-flagged + && (!er->localgenerated || (er->localgenerated && (ll_count(er->csp_lastnodes) >= cacheex_maxhop_lg(cl)))) // check maxhop_lg if cw is lg-flagged #endif ) { #ifdef CS_CACHEEX_AIO - cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes(non-lg) or reached %d nodes(lg), no push", cacheex_maxhop(cl, er), cacheex_maxhop_lg(cl, er)); + cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes(non-lg) or reached %d nodes(lg), no push", cacheex_maxhop(cl), cacheex_maxhop_lg(cl)); #else - cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes, no push", cacheex_maxhop(cl, er)); + cs_log_dbg(D_CACHEEX, "cacheex: nodelist reached %d nodes, no push", cacheex_maxhop(cl)); #endif return 0; } @@ -1426,10 +1426,10 @@ void cc_cacheex_push_in(struct s_client *cl, uint8_t *buf) #ifndef CS_CACHEEX_AIO // check max nodes - if(count > cacheex_maxhop(cl, er)) + if(count > cacheex_maxhop(cl)) { cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", - (int32_t)count, cacheex_maxhop(cl, er), username(cl)); + (int32_t)count, cacheex_maxhop(cl), username(cl)); NULLFREE(er); return; @@ -1467,9 +1467,9 @@ void cc_cacheex_push_in(struct s_client *cl, uint8_t *buf) cs_log_dbg(D_CACHEEX, "cacheex: received ECM with localgenerated flag %04X@%06X:%04X %s", er->caid, er->prid, er->srvid, username(cl)); //check max nodes for lg flagged cw: - if(ll_count(er->csp_lastnodes) > cacheex_maxhop_lg(cl, er)) + if(ll_count(er->csp_lastnodes) > cacheex_maxhop_lg(cl)) { - cs_log_dbg(D_CACHEEX, "cacheex: received (lg) %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop_lg(cl, er), username(cl)); + cs_log_dbg(D_CACHEEX, "cacheex: received (lg) %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop_lg(cl), username(cl)); free_push_in_ecm(er); return; } @@ -1478,9 +1478,9 @@ void cc_cacheex_push_in(struct s_client *cl, uint8_t *buf) else { //check max nodes: - if(ll_count(er->csp_lastnodes) > cacheex_maxhop(cl, er)) + if(ll_count(er->csp_lastnodes) > cacheex_maxhop(cl)) { - cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop(cl, er), username(cl)); + cs_log_dbg(D_CACHEEX, "cacheex: received %d nodes (max=%d), ignored! %s", ll_count(er->csp_lastnodes), cacheex_maxhop(cl), username(cl)); free_push_in_ecm(er); return; } diff --git a/module-cccam-cacheex.h b/module-cccam-cacheex.h old mode 100755 new mode 100644 diff --git a/module-cccam-data.h b/module-cccam-data.h old mode 100755 new mode 100644 diff --git a/module-cccam.c b/module-cccam.c old mode 100755 new mode 100644 index 0b66509..e52ce8a --- a/module-cccam.c +++ b/module-cccam.c @@ -1009,7 +1009,7 @@ int32_t cc_send_cli_data(struct s_client *cl) memcpy(buf, rdr->r_usr, sizeof(rdr->r_usr)); memcpy(buf + 20, cc->node_id, 8); - buf[28] = rdr->cc_want_emu; // <-- Client wants to have EMUs, 0 - NO; 1 - YES + buf[28] = 0; // <-- Client wants to have EMUs, 0 - NO; 1 - YES memcpy(buf + 29, rdr->cc_version, sizeof(rdr->cc_version)); // cccam version (ascii) memcpy(buf + 61, rdr->cc_build, sizeof(rdr->cc_build)); // build number (ascii) @@ -1544,8 +1544,7 @@ struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8 } if((ncard->caid == cur_er->caid // caid matches - || (rdr->cc_want_emu && (ncard->caid == (cur_er->caid & 0xFF00)))) - || lb_match) // or system matches if caid ends with 00 (needed for wantemu) + || lb_match)) // or system matches if caid ends with 00 { int32_t goodSidCount = ll_count(ncard->goodsids); int32_t badSidCount = ll_count(ncard->badsids); @@ -1584,7 +1583,7 @@ struct cc_card *get_matching_card(struct s_client *cl, ECM_REQUEST *cur_er, int8 } } - if(!(rdr->cc_want_emu) && caid_is_nagra(ncard->caid) && (!xcard || ncard->hop < xcard->hop)) + if(caid_is_nagra(ncard->caid) && (!xcard || ncard->hop < xcard->hop)) { xcard = ncard; // remember card (D+ / 1810 fix) if request has no provider, but card has } diff --git a/module-cccam.h b/module-cccam.h old mode 100755 new mode 100644 diff --git a/module-cccshare.c b/module-cccshare.c old mode 100755 new mode 100644 diff --git a/module-cccshare.h b/module-cccshare.h old mode 100755 new mode 100644 diff --git a/module-constcw.c b/module-constcw.c old mode 100755 new mode 100644 diff --git a/module-csp.c b/module-csp.c old mode 100755 new mode 100644 diff --git a/module-cw-cycle-check.c b/module-cw-cycle-check.c old mode 100755 new mode 100644 index 9afb237..51e9ed7 --- a/module-cw-cycle-check.c +++ b/module-cw-cycle-check.c @@ -123,9 +123,7 @@ static uint8_t checkvalidCW(ECM_REQUEST *er) { uint8_t ret = 1; - // Skip check for BISS1 - cw could be indeed zero - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero - if(chk_is_null_CW(er->cw) && !caid_is_biss(er->caid)) + if(chk_is_null_CW(er->cw)) { er->rc = E_NOTFOUND; } if(er->rc == E_NOTFOUND) @@ -837,43 +835,33 @@ uint8_t checkcwcycle(struct s_client *client, ECM_REQUEST *er, struct s_reader * snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc LEARN"); break; - case 8: // use Cyclecheck from CE Source - count_ok(client); - snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc OK(CE)"); - break; + case 8: // use Cyclecheck from CE Source + count_ok(client); + snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc OK(CE)"); + break; - case 9: // CWCYCLE NOK without counting - snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc NOK"); - if(cfg.onbadcycle > 0) // ignore ECM Request - { -#ifdef CS_CACHEEX_AIO - if(!er->localgenerated) - { -#endif - cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> drop cw (ECM Answer)", user, er_ecmf, c_reader); - return 0; -#ifdef CS_CACHEEX_AIO - } - else - { - cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> lg-flagged CW -> do nothing", user, er_ecmf, c_reader); //D_CWC| D_TRACE - break; - } -#endif - } - else // only logging - { - cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> do nothing", user, er_ecmf, c_reader); - break; - } + case 9: // CWCYCLE NOK without counting + + snprintf(er->cwc_msg_log, sizeof(er->cwc_msg_log), "cwc NOK"); + + if(cfg.onbadcycle > 0) + { + cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> DROP CW (LG included)", user, er_ecmf, c_reader); + return 0; + } + else + { + cs_log("cyclecheck [Bad CW Cycle already Counted] for: %s %s from: %s -> do nothing", user, er_ecmf, c_reader); + break; + } - } - return 1; } - +return 1; +} +#endif /* * */ -#endif + diff --git a/module-cw-cycle-check.h b/module-cw-cycle-check.h old mode 100755 new mode 100644 diff --git a/module-dvbapi-azbox.c b/module-dvbapi-azbox.c old mode 100755 new mode 100644 index 321f19e..7387f02 --- a/module-dvbapi-azbox.c +++ b/module-dvbapi-azbox.c @@ -312,9 +312,7 @@ void azbox_send_dcw(struct s_client *client, ECM_REQUEST *er) int32_t n; for(n = 0; n < 2; n++) { - // Skip check for BISS1 - cw could be indeed zero - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero - if(memcmp(er->cw + (n * 8), demux[0].last_cw[0][n], 8) && (memcmp(er->cw + (n * 8), nullcw, 8) != 0 || caid_is_biss(er->caid))) + if(memcmp(er->cw + (n * 8), demux[0].last_cw[0][n], 8) && (memcmp(er->cw + (n * 8), nullcw, 8) != 0)) { memcpy(demux[0].last_cw[0][n], er->cw + (n * 8), 8); memcpy(openxcas_cw + (n * 8), er->cw + (n * 8), 8); diff --git a/module-dvbapi-azbox.h b/module-dvbapi-azbox.h old mode 100755 new mode 100644 diff --git a/module-dvbapi-chancache.c b/module-dvbapi-chancache.c old mode 100755 new mode 100644 diff --git a/module-dvbapi-chancache.h b/module-dvbapi-chancache.h old mode 100755 new mode 100644 diff --git a/module-dvbapi-coolapi-legacy.c b/module-dvbapi-coolapi-legacy.c old mode 100755 new mode 100644 diff --git a/module-dvbapi-coolapi.c b/module-dvbapi-coolapi.c old mode 100755 new mode 100644 diff --git a/module-dvbapi-coolapi.h b/module-dvbapi-coolapi.h old mode 100755 new mode 100644 diff --git a/module-dvbapi-mca.c b/module-dvbapi-mca.c old mode 100755 new mode 100644 index cc399e4..8b4d44d --- a/module-dvbapi-mca.c +++ b/module-dvbapi-mca.c @@ -601,10 +601,8 @@ void mca_send_dcw(struct s_client *client, ECM_REQUEST *er) int32_t n; for(n = 0; n < 2; n++) { - // Skip check for BISS1 - cw could be indeed zero - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero if((memcmp(er->cw + (n * 8), demux[0].last_cw[0][0], 8) && memcmp(er->cw + (n * 8), demux[0].last_cw[0][1], 8)) - && (memcmp(er->cw + (n * 8), nullcw, 8) != 0 || caid_is_biss(er->caid))) + && (memcmp(er->cw + (n * 8), nullcw, 8) != 0)) { memcpy(demux[0].last_cw[0][n], er->cw + (n * 8), 8); memcpy(openxcas_cw + (n * 8), er->cw + (n * 8), 8); diff --git a/module-dvbapi-mca.h b/module-dvbapi-mca.h old mode 100755 new mode 100644 diff --git a/module-dvbapi-stapi.c b/module-dvbapi-stapi.c old mode 100755 new mode 100644 index 2264bfc..e136091 --- a/module-dvbapi-stapi.c +++ b/module-dvbapi-stapi.c @@ -730,10 +730,8 @@ int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *STREAMpids, int3 for(l = 0; l < 2; l++) { - // Skip check for BISS1 - cw could be indeed zero - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero if(memcmp(cw + (l * 8), demux[demux_id].last_cw[0][l], 8) != 0 - && (memcmp(cw + (l * 8), nullcw, 8) != 0 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID))) + && (memcmp(cw + (l * 8), nullcw, 8) != 0)) { for(n = 0; n < PTINUM; n++) { diff --git a/module-dvbapi-stapi.h b/module-dvbapi-stapi.h old mode 100755 new mode 100644 diff --git a/module-dvbapi-stapi5.c b/module-dvbapi-stapi5.c old mode 100755 new mode 100644 index 76f0a6f..2adf451 --- a/module-dvbapi-stapi5.c +++ b/module-dvbapi-stapi5.c @@ -731,10 +731,8 @@ int32_t stapi_write_cw(int32_t demux_id, uint8_t *cw, uint16_t *STREAMpids, int3 for(l = 0; l < 2; l++) { - // Skip check for BISS1 - cw could be indeed zero - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero if(memcmp(cw + (l * 8), demux[demux_id].last_cw[0][l], 8) != 0 - && (memcmp(cw + (l * 8), nullcw, 8) != 0 || caid_is_biss(demux[demux_id].ECMpids[pidnum].CAID))) + && (memcmp(cw + (l * 8), nullcw, 8) != 0)) { ErrorCode = oscam_sttkd_KeyWrite(tkd_desc_info[demux[demux_id].dev_index].key_hndl, l, cw + (l * 8)); diff --git a/module-dvbapi.c b/module-dvbapi.c old mode 100755 new mode 100644 index f02f323..49d71b4 --- a/module-dvbapi.c +++ b/module-dvbapi.c @@ -1887,14 +1887,7 @@ void dvbapi_start_emm_filter(int32_t demux_id) if(match) { - if(rdr->typ == R_EMU) - { - csystem = rdr->csystem; - } - else - { - csystem = get_cardsystem_by_caid(caid); - } + csystem = get_cardsystem_by_caid(caid); if(csystem) { if(caid != ncaid) @@ -1915,15 +1908,7 @@ void dvbapi_start_emm_filter(int32_t demux_id) } else if(csystem->get_emm_filter) { - if(rdr->typ == R_EMU) - { - csystem->get_emm_filter_adv(rdr, &dmx_filter, &filter_count, caid, provid, demux[demux_id].program_number, - demux[demux_id].tsid, demux[demux_id].onid, demux[demux_id].ens); - } - else - { - csystem->get_emm_filter(rdr, &dmx_filter, &filter_count); - } + csystem->get_emm_filter(rdr, &dmx_filter, &filter_count); } } else @@ -2595,6 +2580,7 @@ void dvbapi_stop_descrambling(int32_t demux_id, uint32_t msgid) } demux[demux_id].pidindex = -1; demux[demux_id].curindex = -1; + demux[demux_id].srvtype = -1; if(!dvbapi_listenport_active && cfg.dvbapi_boxtype != BOXTYPE_PC_NODMX) { @@ -2666,8 +2652,6 @@ int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked, er->vpid = demux[demux_id].ECMpids[pid].VPID; er->pmtpid = demux[demux_id].pmtpid; er->onid = demux[demux_id].onid; - er->tsid = demux[demux_id].tsid; - er->ens = demux[demux_id].ens; er->msgid = msgid; #ifdef WITH_STAPI5 @@ -2699,63 +2683,6 @@ int32_t dvbapi_start_descrambling(int32_t demux_id, int32_t pid, int8_t checked, match = 1; // so make it a match to try it! } - // BISS1 and BISS2 mode 1/E or FAKE caid - // ecm pid is fake, so send out one fake ecm request - // special treatment: if we asked the cw first without starting a filter, - // the cw request will be killed due to no ecmfilter started - if(caid_is_fake(demux[demux_id].ECMpids[pid].CAID) || caid_is_biss_fixed(demux[demux_id].ECMpids[pid].CAID)) - { - int32_t j, n; - er->ecmlen = 7; - er->ecm[0] = 0x80; // to pass the cache check it must be 0x80 or 0x81 - er->ecm[1] = 0x00; - er->ecm[2] = 0x04; - i2b_buf(2, er->srvid, er->ecm + 3); - i2b_buf(2, er->pmtpid, er->ecm + 5); - - for(j = 0, n = 7; j < demux[demux_id].STREAMpidcount; j++, n += 2) - { - i2b_buf(2, demux[demux_id].STREAMpids[j], er->ecm + n); - er->ecm[2] += 2; - er->ecmlen += 2; - } - - er->ens &= 0x0FFFFFFF; // clear top 4 bits (in case of DVB-T/C or garbage), prepare for flagging - er->ens |= 0xA0000000; // flag to emu: this is the namespace, not a pid - - i2b_buf(2, er->tsid, er->ecm + er->ecmlen); // place tsid after the last stream pid - i2b_buf(2, er->onid, er->ecm + er->ecmlen + 2); // place onid right after tsid - i2b_buf(4, er->ens, er->ecm + er->ecmlen + 4); // place namespace at the end of the ecm - - er->ecm[2] += 8; - er->ecmlen += 8; - - cs_log("Demuxer %d trying to descramble PID %d CAID %04X PROVID %06X ECMPID %04X ANY CHID PMTPID %04X VPID %04X", - demux_id, - pid, - demux[demux_id].ECMpids[pid].CAID, - demux[demux_id].ECMpids[pid].PROVID, - demux[demux_id].ECMpids[pid].ECM_PID, - demux[demux_id].pmtpid, - demux[demux_id].ECMpids[pid].VPID); - - demux[demux_id].curindex = pid; // set current pid to the fresh started one - dvbapi_start_filter(demux_id, - pid, - demux[demux_id].ECMpids[pid].ECM_PID, - demux[demux_id].ECMpids[pid].CAID, - demux[demux_id].ECMpids[pid].PROVID, - 0x80, - 0xF0, - 3000, - TYPE_ECM); - - started = 1; - request_cw(dvbapi_client, er, demux_id, 0); // do not register ecm since this try! - fake_ecm = 1; - break; // we started an ecmfilter so stop looking for next matching reader! - } - if(match) // if matching reader found check for irdeto cas if local irdeto card check if it received emms in last 60 minutes { if(caid_is_irdeto(er->caid)) // irdeto cas init irdeto_curindex to wait for first index (00) @@ -3865,7 +3792,7 @@ static void dvbapi_parse_pmt_ca_descriptor(int32_t demux_id, const uint8_t *buff ca_system_id = b2i(2, buffer); ca_pid = b2i(2, buffer + 2) & 0x1FFF; - if(ca_system_id == 0x0000 || (!caid_is_biss_fixed(ca_system_id) && !caid_is_fake(ca_system_id) && ca_pid == 0x1FFF)) + if(ca_system_id == 0x0000 || ca_pid == 0x1FFF) { return; // This is not a valid CAID or ECM pid } @@ -4312,6 +4239,7 @@ typedef struct demux_parameters uint16_t onid; uint16_t tsid; uint32_t ens; + int64_t srvtype; // service type mask from descriptor 0x85, or -1 if not present } demux_parameters_t; static void get_demux_parameters(const uint8_t *buffer, demux_parameters_t *parameters) @@ -4402,7 +4330,13 @@ static void get_demux_parameters(const uint8_t *buffer, demux_parameters_t *para } case SERVICE_TYPE_MASK: + { + if(descriptor_length == 0x04) + { + parameters->srvtype = b2i(4, buffer + pos + 2); + } break; + } case DEMUX_DEVICE: { @@ -4453,33 +4387,14 @@ static void dvbapi_capmt_notify(struct demux_s *dmx) static void dvbapi_prepare_descrambling(int32_t demux_id, uint32_t msgid) { - bool is_powervu = false, start_emm = true; + bool start_emm = true; char service_name[CS_SERVICENAME_SIZE]; - // The CA PMT should have given us enough info to determine if descrambling - // is possible. Parsing the (real) PMT is not necessary, unless we have a - // PowerVu encrypted channel or (for some weird reason) no stream pids at all. - // Actually, when no streams are available, we set the PMT pid as the 1st - // stream pid, so we have to check against that. Finally, if the PMT pid is - // not included in the CA PMT, we start the PAT filter instead. - -#ifdef WITH_EXTENDED_CW - uint8_t i; - for(i = 0; i < demux[demux_id].ECMpidcount; i++) - { - if(caid_is_powervu(demux[demux_id].ECMpids[i].CAID)) - { - is_powervu = true; - break; - } - } -#endif - if(demux[demux_id].pmtpid == 0) { dvbapi_start_pat_filter(demux_id); } - else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid || is_powervu) + else if(demux[demux_id].STREAMpids[0] == demux[demux_id].pmtpid) { dvbapi_start_pmt_filter(demux_id); } @@ -4565,6 +4480,7 @@ int32_t dvbapi_parse_capmt(const uint8_t *buffer, uint32_t length, int32_t connf bool is_update = false; demux_parameters_t parameters; memset(¶meters, 0, sizeof(parameters)); + parameters.srvtype = -1; #if defined WITH_COOLAPI || defined WITH_COOLAPI2 ca_pmt_list_management = CA_PMT_LIST_ONLY; @@ -4728,6 +4644,7 @@ int32_t dvbapi_parse_capmt(const uint8_t *buffer, uint32_t length, int32_t connf demux[demux_id].ens = parameters.ens; demux[demux_id].tsid = parameters.tsid; demux[demux_id].onid = parameters.onid; + demux[demux_id].srvtype = parameters.srvtype; demux[demux_id].stop_descrambling = false; demux[demux_id].running = false; demux[demux_id].sdt_filter = -1; @@ -5680,7 +5597,6 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer, if(filtertype == TYPE_ECM && curpid) { uint32_t chid = 0x10000; - int8_t pvu_skip = 0; ECM_REQUEST *er; if(len != 0) // len = 0 receiver encountered an internal bufferoverflow! @@ -5706,25 +5622,8 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer, return; } -#ifdef WITH_EMU - if(caid_is_powervu(curpid->CAID)) // ecm counter for powervu - { - pvu_skip = 1; - - if(sctlen - 11 > buffer[9]) - { - if(buffer[11 + buffer[9]] > curpid->pvu_counter - || (curpid->pvu_counter == 255 && buffer[11 + buffer[9]] == 0) - || ((curpid->pvu_counter - buffer[11 + buffer[9]]) > 5)) - { - curpid->pvu_counter = buffer[11 + buffer[9]]; - pvu_skip = 0; - } - } - } -#endif // wait for odd / even ecm change (only not for irdeto!) - if((curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID)) || pvu_skip) + if(curpid->table == buffer[0] && !caid_is_irdeto(curpid->CAID)) { if(!(er = get_ecmtask())) { @@ -6068,35 +5967,6 @@ void dvbapi_process_input(int32_t demux_id, int32_t filter_num, uint8_t *buffer, return; // just skip on internal buffer overflow } -#ifdef WITH_EMU - if(caid_is_director(demux[demux_id].demux_fd[filter_num].caid)) - { - uint32_t i; - uint32_t emmhash; - - if(sctlen < 4) - { - return; - } - - for(i = 0; i + 2 < sctlen; i++) - { - if(buffer[i] == 0xF0 && (buffer[i + 2] == 0xE1 || buffer[i + 2] == 0xE4)) - { - emmhash = (buffer[3] << 8) | buffer[sctlen - 2]; - if(demux[demux_id].demux_fd[filter_num].cadata == emmhash) - { - return; - } - - demux[demux_id].demux_fd[filter_num].cadata = emmhash; - dvbapi_process_emm(demux_id, filter_num, buffer, sctlen); - return; - } - } - return; - } -#endif // fix to handle more than one irdeto emm packet uint8_t *pbuf = buffer; int32_t done = 0; @@ -7326,10 +7196,8 @@ void dvbapi_write_cw(int32_t demux_id, int32_t pid, int32_t stream_id, uint8_t * for(n = 0; n < 2; n++) { // Check if cw has changed and if new cw is empty (all zeros) - // Skip check for BISS1 - cw could be indeed zero - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero if((memcmp(cw + (n * cw_length), demux[demux_id].last_cw[stream_id][n], cw_length) != 0 || cw_empty) - && (memcmp(cw + (n * cw_length), null_cw, cw_length) != 0 || caid_is_biss(demux[demux_id].ECMpids[pid].CAID))) + && (memcmp(cw + (n * cw_length), null_cw, cw_length) != 0)) { // prepare ca device uint32_t idx = dvbapi_ca_set_pid(demux_id, pid, stream_id, (algo == CA_ALGO_DES), msgid); @@ -7621,9 +7489,7 @@ void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er) // 0=matching ecm hash, 2=no filter, 3=table reset, 4=cache-ex response // Check only against last_cw[0] (index 0) - No need to check the rest - // Skip check for BISS1 - cw could be indeed zero - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero - if((status == 0 || status == 3 || status == 4) && er->rc < E_NOTFOUND && !caid_is_biss(er->caid)) + if((status == 0 || status == 3 || status == 4) && er->rc < E_NOTFOUND) { // check for matching control word if(memcmp(er->cw, demux[i].last_cw[0][0], 8) == 0 && @@ -7895,8 +7761,21 @@ void dvbapi_send_dcw(struct s_client *client, ECM_REQUEST *er) bool set_dvbapi_cw = true; if((cfg.stream_relay_ctab.ctnum == 0 || chk_ctab_ex(er->caid, &cfg.stream_relay_ctab)) && cfg.stream_relay_enabled) { - // streamserver set cw - set_dvbapi_cw = !stream_write_cw(er); + bool sr_handled = stream_write_cw(er); + + if(demux[i].srvtype >= 0 && !(demux[i].srvtype & 0x180)) + { + // E2 sent service type mask without streamrelay bits (type 7/8), + // so this is a live-TV/softcsa service. Always send CW via dvbapi, + // even if a dying streamrelay client still consumed it. + set_dvbapi_cw = true; + } + else + { + // No service type mask (old E2) or streamrelay bits set: + // preserve original behavior - let stream_write_cw() decide. + set_dvbapi_cw = !sr_handled; + } } if (set_dvbapi_cw) #endif @@ -8627,9 +8506,7 @@ int32_t dvbapi_check_ecm_delayed_delivery(int32_t demux_id, ECM_REQUEST *er) } // Check for null cw - // Skip check for BISS1 - cw could be indeed zero - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero - if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0 && !caid_is_biss(er->caid)) + if(memcmp(er->cw, nullcw, 8) == 0 && memcmp(er->cw + 8, nullcw, 8) == 0) { return 5; } diff --git a/module-dvbapi.h b/module-dvbapi.h old mode 100755 new mode 100644 index f35daf2..df2f20a --- a/module-dvbapi.h +++ b/module-dvbapi.h @@ -145,7 +145,7 @@ #define DEMUX_CA_MASK_ADAPTER 0x82 // deprecated - applications should use descriptors ADAPTER_DEVICE, DEMUX_DEVICE and CA_DEVICE instead #define ADAPTER_DEVICE 0x83 #define PMT_PID 0x84 -#define SERVICE_TYPE_MASK 0x85 // not used by OSCam +#define SERVICE_TYPE_MASK 0x85 #define DEMUX_DEVICE 0x86 #define CA_DEVICE 0x87 @@ -336,9 +336,6 @@ typedef struct filter_s uint32_t SlotHandle[10]; uint32_t BufferHandle[10]; #endif -#ifdef WITH_EMU - uint32_t cadata; -#endif } FILTERTYPE; #ifdef WITH_EXTENDED_CW @@ -371,9 +368,6 @@ typedef struct s_ecmpid uint32_t index[MAX_STREAM_INDICES]; // ca indices used for this ecm pid (index[0] holds ca index for STREAMmpids[0] and so on) uint32_t streams; // bit mask of STREAMpids enabled for this ECMpid uint32_t cadata; -#ifdef WITH_EMU - int16_t pvu_counter; -#endif } ECMPIDTYPE; typedef struct s_emmpid @@ -452,6 +446,7 @@ typedef struct demux_s struct timeb decstart; struct timeb decend; uint32_t msgid; + int64_t srvtype; // service type mask from CA PMT descriptor 0x85, or -1 if not present } DEMUXTYPE; typedef struct s_streampid diff --git a/module-gbox-cards.c b/module-gbox-cards.c old mode 100755 new mode 100644 diff --git a/module-gbox-cards.h b/module-gbox-cards.h old mode 100755 new mode 100644 diff --git a/module-gbox-helper.c b/module-gbox-helper.c old mode 100755 new mode 100644 diff --git a/module-gbox-helper.h b/module-gbox-helper.h old mode 100755 new mode 100644 diff --git a/module-gbox-remm.c b/module-gbox-remm.c old mode 100755 new mode 100644 diff --git a/module-gbox-remm.h b/module-gbox-remm.h old mode 100755 new mode 100644 diff --git a/module-gbox-sms.c b/module-gbox-sms.c old mode 100755 new mode 100644 diff --git a/module-gbox-sms.h b/module-gbox-sms.h old mode 100755 new mode 100644 diff --git a/module-gbox.c b/module-gbox.c old mode 100755 new mode 100644 diff --git a/module-gbox.h b/module-gbox.h old mode 100755 new mode 100644 diff --git a/module-ghttp.c b/module-ghttp.c old mode 100755 new mode 100644 diff --git a/module-lcd.c b/module-lcd.c old mode 100755 new mode 100644 diff --git a/module-lcd.h b/module-lcd.h old mode 100755 new mode 100644 diff --git a/module-led.c b/module-led.c old mode 100755 new mode 100644 diff --git a/module-led.h b/module-led.h old mode 100755 new mode 100644 diff --git a/module-monitor.c b/module-monitor.c old mode 100755 new mode 100644 diff --git a/module-monitor.h b/module-monitor.h old mode 100755 new mode 100644 diff --git a/module-newcamd-des.c b/module-newcamd-des.c old mode 100755 new mode 100644 diff --git a/module-newcamd-des.h b/module-newcamd-des.h old mode 100755 new mode 100644 index 29f96e4..7b04814 --- a/module-newcamd-des.h +++ b/module-newcamd-des.h @@ -6,9 +6,6 @@ #define DES_RIGHT 4 #define DES_HASH 8 -#define DES_ECM_CRYPT 0 -#define DES_ECM_HASH DES_HASH - void nc_des(uint8_t key[], uint8_t mode, uint8_t data[]); int nc_des_encrypt(uint8_t *buffer, int len, uint8_t *deskey); int nc_des_decrypt(uint8_t *buffer, int len, uint8_t *deskey); diff --git a/module-newcamd.c b/module-newcamd.c old mode 100755 new mode 100644 index 6495a30..679f25a --- a/module-newcamd.c +++ b/module-newcamd.c @@ -1118,13 +1118,6 @@ static int8_t newcamd_auth_client(IN_ADDR_T ip, uint8_t *deskey) // set userfilter for au enabled clients if(aureader) { -#ifdef WITH_EMU - if(aureader->typ == R_EMU) - { - usr_filter = *get_emu_prids_for_caid(aureader, cfg.ncd_ptab.ports[cl->port_idx].ncd->ncd_ftab.filts[0].caid); - } - else -#endif mk_user_au_ftab(aureader, &usr_filter); } @@ -1191,7 +1184,7 @@ static int8_t newcamd_auth_client(IN_ADDR_T ip, uint8_t *deskey) uint32_t rprid; found = 0; - if(pufilt->caid == aureader->caid && aureader->typ != R_EMU) + if(pufilt->caid == aureader->caid) { for(k = 0; k < aureader->nprov; k++) { diff --git a/module-newcamd.h b/module-newcamd.h old mode 100755 new mode 100644 diff --git a/module-pandora.c b/module-pandora.c old mode 100755 new mode 100644 diff --git a/module-radegast.c b/module-radegast.c old mode 100755 new mode 100644 diff --git a/module-scam.c b/module-scam.c old mode 100755 new mode 100644 diff --git a/module-serial.c b/module-serial.c old mode 100755 new mode 100644 diff --git a/module-stat.c b/module-stat.c old mode 100755 new mode 100644 index 741c66f..324c43b --- a/module-stat.c +++ b/module-stat.c @@ -901,7 +901,7 @@ void check_lb_auto_betatunnel_mode(ECM_REQUEST *er) uint16_t get_rdr_caid(struct s_reader *rdr) { - if(is_network_reader(rdr) || rdr->typ == R_EMU) + if(is_network_reader(rdr)) { return 0; // reader caid is not real caid } @@ -1292,7 +1292,7 @@ void stat_get_best_reader(ECM_REQUEST *er) for(ea = er->matching_rdr; ea; ea = ea->next) { rdr = ea->reader; - if(is_network_reader(rdr) || rdr->typ == R_EMU) // reader caid is not real caid + if(is_network_reader(rdr)) // reader caid is not real caid { prv = ea; continue; // proxy can convert or reject @@ -2094,13 +2094,14 @@ void lb_update_last(struct s_ecm_answer *ea_er, struct s_reader *reader) cs_ftime(&reader->lb_last); } -void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc, int32_t ecm_time) +void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc) { if(rc >= E_99 || cacheex_reader(rdr)) { return; } - if(!ecm_time) - ecm_time = cfg.ctimeout; + int32_t ecm_time = cfg.ctimeout; + if(ea->ecm_time && ea->rc <= E_NOTFOUND) + { ecm_time = ea->ecm_time; } add_stat(rdr, er, ecm_time, rc, ea->rcEx); } diff --git a/module-stat.h b/module-stat.h old mode 100755 new mode 100644 index 9fc64ae..90ec1b1 --- a/module-stat.h +++ b/module-stat.h @@ -15,7 +15,7 @@ void init_stat(void); void stat_finish(void); void load_stat_from_file(void); void lb_destroy_stats(struct s_reader *rdr); -void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc, int32_t ecm_time); +void send_reader_stat(struct s_reader *rdr, ECM_REQUEST *er, struct s_ecm_answer *ea, int8_t rc); void stat_get_best_reader(ECM_REQUEST *er); void lb_mark_last_reader(ECM_REQUEST *er); void check_lb_auto_betatunnel_mode(ECM_REQUEST *er); @@ -32,7 +32,7 @@ static inline void init_stat(void) { } static inline void stat_finish(void) { } static inline void load_stat_from_file(void) { } static inline void lb_destroy_stats(struct s_reader *UNUSED(rdr)) { } -static inline void send_reader_stat(struct s_reader *UNUSED(rdr), ECM_REQUEST *UNUSED(er), struct s_ecm_answer *UNUSED(ea), int8_t UNUSED(rc), int32_t UNUSED(ecm_time)) { } +static inline void send_reader_stat(struct s_reader *UNUSED(rdr), ECM_REQUEST *UNUSED(er), struct s_ecm_answer *UNUSED(ea), int8_t UNUSED(rc)) { } static inline void stat_get_best_reader(ECM_REQUEST *UNUSED(er)) { } static inline void lb_mark_last_reader(ECM_REQUEST *UNUSED(er)) { } static inline void check_lb_auto_betatunnel_mode(ECM_REQUEST *UNUSED(er)) { } diff --git a/module-streamrelay.c b/module-streamrelay.c old mode 100755 new mode 100644 index 17fb28f..b313a91 --- a/module-streamrelay.c +++ b/module-streamrelay.c @@ -15,12 +15,6 @@ #include "oscam-string.h" #include "oscam-time.h" -#ifdef WITH_EMU -#include "cscrypt/des.h" -#include "module-emulator-osemu.h" -#include "module-emulator-powervu.h" -#endif - #define STREAM_UNDEFINED 0x00 #define STREAM_VIDEO 0x01 #define STREAM_AUDIO 0x02 @@ -48,28 +42,12 @@ static pthread_mutex_t stream_server_mutex; static int32_t glistenfd, mod_idx, gconncount = 0, gconnfd[STREAM_SERVER_MAX_CONNECTIONS], stream_resptime[STREAM_SERVER_MAX_CONNECTIONS]; static char ecm_src[STREAM_SERVER_MAX_CONNECTIONS][9]; static IN_ADDR_T client_ip[STREAM_SERVER_MAX_CONNECTIONS], stream_host_ip[STREAM_SERVER_MAX_CONNECTIONS]; -#ifdef WITH_EMU -#define STATIC /* none */ -#else -#define STATIC static -#endif static in_port_t client_port[STREAM_SERVER_MAX_CONNECTIONS]; struct s_client *streamrelay_client[STREAM_SERVER_MAX_CONNECTIONS]; -STATIC pthread_mutex_t fixed_key_srvid_mutex; -STATIC uint16_t stream_cur_srvid[STREAM_SERVER_MAX_CONNECTIONS]; -STATIC stream_client_key_data key_data[STREAM_SERVER_MAX_CONNECTIONS]; - -#ifdef WITH_EMU -int8_t stream_server_thread_init = 0; -int8_t emu_stream_emm_enabled = 0; -uint8_t emu_stream_server_mutex_init = 0; -int8_t stream_server_has_ecm[EMU_STREAM_SERVER_MAX_CONNECTIONS]; -pthread_mutex_t emu_fixed_key_data_mutex[EMU_STREAM_SERVER_MAX_CONNECTIONS]; -emu_stream_client_key_data emu_fixed_key_data[EMU_STREAM_SERVER_MAX_CONNECTIONS]; -LLIST *ll_emu_stream_delayed_keys[EMU_STREAM_SERVER_MAX_CONNECTIONS]; -#endif - +static pthread_mutex_t fixed_key_srvid_mutex; +static uint16_t stream_cur_srvid[STREAM_SERVER_MAX_CONNECTIONS]; +static stream_client_key_data key_data[STREAM_SERVER_MAX_CONNECTIONS]; #if DVBCSA_KEY_ECM static uint16_t last_srvid[STREAM_SERVER_MAX_CONNECTIONS]; #endif @@ -164,48 +142,19 @@ static void radegast_client_ecm(stream_client_data *cdata) { send_to_radegast(outgoing_data, packet_len); } } } -#endif // MODULE_RADEGAST -#ifdef WITH_EMU void ParseEcmData(stream_client_data *cdata) { uint8_t *data = cdata->ecm_data; - uint8_t dcw[16]; uint16_t section_length = SCT_LEN(data); if (section_length < 11) { return; } - if (caid_is_powervu(cdata->caid)) - { - if (data[11] > cdata->ecm_nb || (cdata->ecm_nb == 255 && data[11] == 0) || ((cdata->ecm_nb - data[11]) > 5)) - { - cdata->ecm_nb = data[11]; - cdata->key.connid = cdata->connid; - powervu_ecm(data, dcw, NULL, cdata->srvid, cdata->caid, cdata->tsid, cdata->onid, cdata->ens, &cdata->key); - } - } -#ifdef MODULE_RADEGAST - else - { - cs_strncpy(ecm_src[cdata->connid], "radegast", sizeof(ecm_src[cdata->connid])); - radegast_client_ecm(cdata); - } -#endif + cs_strncpy(ecm_src[cdata->connid], "radegast", sizeof(ecm_src[cdata->connid])); + radegast_client_ecm(cdata); } -#else -#define ParseEcmData radegast_client_ecm -#endif - -#if DVBCSA_KEY_ECM -#define DVBCSA_HEADER_ECM 1 -#define dvbcsa_bs_key_set(a,b) dvbcsa_bs_key_set_ecm(ecm,a,b) -#else -#define DVBCSA_HEADER_ECM 0 -#endif -#ifndef STATIC_LIBDVBCSA -#define STATIC_LIBDVBCSA 0 -#endif +#endif // MODULE_RADEGAST static void write_cw(ECM_REQUEST *er, int32_t connid) { @@ -217,25 +166,13 @@ static void write_cw(ECM_REQUEST *er, int32_t connid) if (memcmp(er->cw, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) != 0) { if (has_dvbcsa_ecm) - { -#ifdef WITH_EMU - dvbcsa_bs_key_set(er->cw, key_data[connid].key[0][EVEN]); -#else - dvbcsa_bs_key_set(er->cw, key_data[connid].key[EVEN]); -#endif - } + { dvbcsa_bs_key_set(er->cw, key_data[connid].key[EVEN]); } } if (memcmp(er->cw + 8, "\x00\x00\x00\x00\x00\x00\x00\x00", 8) != 0) { if (has_dvbcsa_ecm) - { -#ifdef WITH_EMU - dvbcsa_bs_key_set(er->cw + 8, key_data[connid].key[0][ODD]); -#else - dvbcsa_bs_key_set(er->cw + 8, key_data[connid].key[ODD]); -#endif - } + { dvbcsa_bs_key_set(er->cw + 8, key_data[connid].key[ODD]); } } #if DVBCSA_KEY_ECM if (er->srvid != last_srvid[connid] && ecm != 0 && has_dvbcsa_ecm) @@ -248,11 +185,6 @@ static void write_cw(ECM_REQUEST *er, int32_t connid) last_srvid[connid] = er->srvid; } #endif -#ifdef WITH_EMU - SAFE_MUTEX_LOCK(&emu_fixed_key_data_mutex[connid]); - emu_fixed_key_data[connid].csa_used = 1; - SAFE_MUTEX_UNLOCK(&emu_fixed_key_data_mutex[connid]); -#endif } static void update_client_info(ECM_REQUEST *er, int32_t connid) @@ -283,20 +215,24 @@ static void update_client_info(ECM_REQUEST *er, int32_t connid) bool stream_write_cw(ECM_REQUEST *er) { int32_t i; - bool cw_written = false; - //SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); - for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) + if (er->rc == E_FOUND) { - if (stream_cur_srvid[i] == er->srvid) + bool cw_written = false; + //SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); + for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) { - write_cw(er, i); - update_client_info(er, i); - cw_written = true; - // don't return as there might be more connections for the same channel (e.g. recordings) + if (stream_cur_srvid[i] == er->srvid) + { + write_cw(er, i); + update_client_info(er, i); + cw_written = true; + // don't return as there might be more connections for the same channel (e.g. recordings) + } } + //SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); + return cw_written; } - //SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); - return cw_written; + return true; } static void SearchTsPackets(const uint8_t *buf, const uint32_t bufLength, uint16_t *packetSize, uint16_t *startOffset) @@ -426,9 +362,6 @@ static void ParsePatData(stream_client_data *cdata) { int32_t i; uint16_t srvid; -#ifdef __BISS__ - cdata->STREAMpidcount = 0; -#endif for (i = 8; i + 7 < SCT_LEN(cdata->pat_data); i += 4) { srvid = b2i(2, cdata->pat_data + i); @@ -445,21 +378,6 @@ static void ParsePatData(stream_client_data *cdata) } } -#ifdef WITH_EMU -static int8_t stream_client_get_caid(stream_client_data *cdata) -{ - uint32_t tmp1 = (cdata->srvid << 16) | cdata->pmt_pid; - uint8_t tmp2[2]; - - if (emu_find_key('A', tmp1, 0, "FAKE", tmp2, 2, 0, 0, 0, NULL)) - { - cdata->caid = b2i(2, tmp2); - return 1; - } - return 0; -} -#endif - static void ParseDescriptors(const uint8_t *buffer, const uint16_t info_length, uint8_t *type) { uint32_t i; @@ -627,9 +545,6 @@ static void ParsePmtData(stream_client_data *cdata) case 0xD1: case 0xEA: { -#ifdef WITH_EMU - cdata->video_pid = elementary_pid; -#endif cs_log_dbg(D_READER, "Stream client %i found video pid: 0x%04X (%i)", cdata->connid, elementary_pid, elementary_pid); stream_parse_pmt_ca_descriptor(cdata->pmt_data, i, 5, es_info_length, cdata); @@ -644,15 +559,6 @@ static void ParsePmtData(stream_client_data *cdata) case 0x2E: case 0x81: { -#ifdef WITH_EMU - if (cdata->audio_pid_count >= EMU_STREAM_MAX_AUDIO_SUB_TRACKS) - { - continue; - } - - cdata->audio_pids[cdata->audio_pid_count] = elementary_pid; - cdata->audio_pid_count++; -#endif cs_log_dbg(D_READER, "Stream client %i found audio pid: 0x%04X (%i)", cdata->connid, elementary_pid, elementary_pid); break; @@ -664,89 +570,17 @@ static void ParsePmtData(stream_client_data *cdata) uint8_t type = STREAM_UNDEFINED; ParseDescriptors(cdata->pmt_data + i + 5, es_info_length, &type); if (type == STREAM_AUDIO) - { -#ifdef WITH_EMU - if (cdata->audio_pid_count >= EMU_STREAM_MAX_AUDIO_SUB_TRACKS) - { - continue; - } - - cdata->audio_pids[cdata->audio_pid_count] = elementary_pid; - cdata->audio_pid_count++; -#endif - cs_log_dbg(D_READER, "Stream client %i found audio pid: 0x%04X (%i)", - cdata->connid, elementary_pid, elementary_pid); - } + { cs_log_dbg(D_READER, "Stream client %i found audio pid: 0x%04X (%i)", + cdata->connid, elementary_pid, elementary_pid); } else if (type == STREAM_TELETEXT) - { -#ifdef WITH_EMU - cdata->teletext_pid = elementary_pid; -#endif - cs_log_dbg(D_READER, "Stream client %i found teletext pid: 0x%04X (%i)", - cdata->connid, elementary_pid, elementary_pid); - } + { cs_log_dbg(D_READER, "Stream client %i found teletext pid: 0x%04X (%i)", + cdata->connid, elementary_pid, elementary_pid); } break; } } -#ifdef __BISS__ - cdata->STREAMpids[cdata->STREAMpidcount] = elementary_pid; - cdata->STREAMpidcount++; -#endif - } -#ifdef WITH_EMU - // If we haven't found a CAID for this service, - // search the keyDB for a fake one - if (cdata->caid == NO_CAID_VALUE && stream_client_get_caid(cdata) == 1) - { - cs_log_dbg(D_READER, "Stream client %i found fake caid: 0x%04X (%i)", - cdata->connid, cdata->caid, cdata->caid); - } -#endif -} - -#ifdef WITH_EMU -static void ParseCatData(stream_client_data *cdata) -{ - uint8_t *data = cdata->cat_data; - uint32_t i; - - for (i = 8; i < (b2i(2, data + 1) & 0xFFF) - 1; i += data[i + 1] + 2) - { - if (data[i] != 0x09) - { - continue; - } - - uint16_t caid = b2i(2, data + i + 2); - - if (caid_is_powervu(caid)) // add all supported caids here - { - if (cdata->caid == NO_CAID_VALUE) - { - cdata->caid = caid; - } - cdata->emm_pid = b2i(2, data + i + 4) & 0x1FFF;; - cs_log_dbg(D_READER, "Stream client %i found emm pid: 0x%04X (%i)", - cdata->connid, cdata->emm_pid, cdata->emm_pid); - break; - } } } -static void ParseEmmData(stream_client_data *cdata) -{ - uint32_t keysAdded = 0; - - emu_process_emm(NULL, cdata->caid, cdata->emm_data, &keysAdded); - - if (keysAdded) - { - //refresh_entitlements(rdr); - cs_log("Stream client %i found %i keys", cdata->connid, keysAdded); - } -} -#endif - static void ParseTsPackets(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) { uint8_t payloadStart; @@ -787,11 +621,7 @@ static void ParseTsPackets(stream_client_data *data, uint8_t *stream_buf, uint32 } } -#ifdef WITH_EMU -static void decrypt_csa(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], const uint8_t oddeven, const int32_t connid, uint8_t cw_type) -#else -static void decrypt_csa(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], const uint8_t oddeven, const int32_t connid) -#endif +static void decrypt(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], const uint8_t oddeven, const int32_t connid) { if (fill[oddeven] > 0) { @@ -809,531 +639,10 @@ static void decrypt_csa(struct dvbcsa_bs_batch_s *tsbbatch, uint16_t fill[2], co fill[oddeven] = 0; -#ifdef WITH_EMU - dvbcsa_bs_decrypt(key_data[connid].key[cw_type][oddeven], tsbbatch, 184); -#else dvbcsa_bs_decrypt(key_data[connid].key[oddeven], tsbbatch, 184); -#endif } } -#ifndef WITH_EMU -#define decrypt(a) decrypt_csa(tsbbatch, fill, a, data->connid) -#else // multiple cw -#define decrypt(a) decrypt_csa(tsbbatch, fill, a, data->connid, 0) -#define decrypt_pvu(a, b) decrypt_csa(tsbbatch, fill, a, data->connid, b) -#endif - -#ifdef WITH_EMU -static void DescrambleTsPacketsPowervu(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize, struct dvbcsa_bs_batch_s *tsbbatch) -{ - uint32_t i, j, tsHeader, *deskey; - uint16_t pid, offset, fill[2] = {0,0}; - uint8_t *pdata, payloadStart, oddeven = 0, cw_type = 0; - int8_t oddKeyUsed; - - for (i = 0; i < bufLength; i += packetSize) - { - tsHeader = b2i(4, stream_buf + i); - pid = (tsHeader & 0x1FFF00) >> 8; - payloadStart = (tsHeader & 0x400000) >> 22; - offset = (tsHeader & 0x20) ? 4 + stream_buf[i + 4] + 1 : 4; - - if (packetSize - offset < 1) - { - continue; - } - - if (emu_stream_emm_enabled && pid == 0x0001 && data->have_cat_data != 1) // Search the CAT for EMM pids - { - // set to null pid - stream_buf[i + 1] |= 0x1F; - stream_buf[i + 2] = 0xFF; - - ParseTsData(0x01, 0xFF, 8, &data->have_cat_data, data->cat_data, sizeof(data->cat_data), - &data->cat_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParseCatData, data); - continue; - } - - if (emu_stream_emm_enabled && data->emm_pid && pid == data->emm_pid) // Process the EMM data - { - // set to null pid - stream_buf[i + 1] |= 0x1F; - stream_buf[i + 2] = 0xFF; - - ParseTsData(0x80, 0xF0, 3, &data->have_emm_data, data->emm_data, sizeof(data->emm_data), - &data->emm_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParseEmmData, data); - continue; - } - - if (data->ecm_pid && pid == data->ecm_pid) // Process the ECM data - { - stream_server_has_ecm[data->connid] = 1; - // set to null pid - stream_buf[i + 1] |= 0x1F; - stream_buf[i + 2] = 0xFF; - ParseTsData(0x80, 0xFE, 3, &data->have_ecm_data, data->ecm_data, sizeof(data->ecm_data), - &data->ecm_data_pos, payloadStart, stream_buf + i + offset, packetSize - offset, ParseEcmData, data); - continue; - } - - if ((tsHeader & 0xC0) == 0) - { - continue; - } - - if (data->key.csa_used) - { - stream_buf[i + 3] &= 0x3f; // consider it decrypted now - oddeven = (tsHeader & 0xC0) == 0xC0 ? ODD: EVEN; - decrypt_pvu(oddeven == ODD ? EVEN : ODD, cw_type); - - if (pid == data->video_pid) // start with video pid, since it is most dominant - { - cw_type = PVU_CW_VID; - tsbbatch[fill[oddeven]].data = &stream_buf[i + offset]; - tsbbatch[fill[oddeven]].len = packetSize - offset; - fill[oddeven]++; - if (fill[oddeven] > cluster_size - 1) - { - decrypt_pvu(oddeven, cw_type); - } - } - else - { - if (cw_type != PVU_CW_VID) - { - decrypt_pvu(oddeven, PVU_CW_VID); - } - for (j = 0; j < data->audio_pid_count; j++) - { - if (pid == data->audio_pids[j]) - { - if (cw_type != PVU_CW_A1 + j) - { - decrypt_pvu(oddeven, PVU_CW_A1 + j); - } - cw_type = PVU_CW_A1 + j; - tsbbatch[fill[oddeven]].data = &stream_buf[i + offset]; - tsbbatch[fill[oddeven]].len = packetSize - offset; - fill[oddeven]++; - if (fill[oddeven] > cluster_size - 1) - { - decrypt_pvu(oddeven, cw_type); - } - } - } - } - } - else - { - oddKeyUsed = (tsHeader & 0xC0) == 0xC0 ? 1 : 0; - deskey = NULL; - - if (pid == data->video_pid) - { - deskey = data->key.pvu_des_ks[PVU_CW_VID][oddKeyUsed]; - } - else - { - for (j = 0; j < data->audio_pid_count; j++) - { - if (pid == data->audio_pids[j]) - { - deskey = data->key.pvu_des_ks[PVU_CW_A1 + j][oddKeyUsed]; - } - } - } - - if (deskey == NULL) - { - deskey = data->key.pvu_des_ks[PVU_CW_HSD][oddKeyUsed]; - } - - for (j = offset; j + 7 < 188; j += 8) - { - pdata = stream_buf + i + j; - des(pdata, deskey, 0); - } - - stream_buf[i + 3] &= 0x3F; - } - } - if (data->key.csa_used) { decrypt_pvu(oddeven, cw_type); } -} -static void DescrambleTsPacketsRosscrypt1(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) -{ - int8_t is_av_pid; - int32_t j; - - uint8_t scramblingControl; - uint16_t pid, offset; - uint32_t i, tsHeader; - - for (i = 0; i < bufLength; i += packetSize) - { - tsHeader = b2i(4, stream_buf + i); - pid = (tsHeader & 0x1FFF00) >> 8; - scramblingControl = tsHeader & 0xC0; - - if (tsHeader & 0x20) - { - offset = 4 + stream_buf[i + 4] + 1; - } - else - { - offset = 4; - } - - if (packetSize - offset < 1) - { - continue; - } - - if (scramblingControl == 0) - { - continue; - } - - if (!(stream_buf[i + 3] & 0x10)) - { - stream_buf[i + 3] &= 0x3F; - continue; - } - - is_av_pid = 0; - - if (pid == data->video_pid) - { - is_av_pid = 1; - } - else - { - for (j = 0; j < data->audio_pid_count; j++) - { - if (pid == data->audio_pids[j]) - { - is_av_pid = 1; - break; - } - } - } - - if (is_av_pid) - { - static uint8_t dyn_key[184]; - static uint8_t last_packet[184]; - - // Reset key on channel change - if (data->reset_key_data == 1) - { - memset(dyn_key, 0x00, 184); - memset(last_packet, 0x00, 184); - data->reset_key_data = 0; - } - - if (memcmp(last_packet, stream_buf + i + 4, 184) == 0) - { - if (memcmp(dyn_key, stream_buf + i + 4, 184) != 0) - { - memcpy(dyn_key, stream_buf + i + 4, 184); - } - } - else - { - memcpy(last_packet, stream_buf + i + 4, 184); - } - - for (j = 0; j < 184; j++) - { - stream_buf[i + 4 + j] ^= dyn_key[j]; - } - - stream_buf[i + 3] &= 0x3F; - } - } -} - -static void DescrambleTsPacketsCompel(emu_stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize) -{ - int8_t is_pes_pid; // any PES pid - int32_t j; - const int8_t limit = 4; - - uint8_t scramblingControl; - uint16_t pid, offset; - uint32_t i, tsHeader; - - for (i = 0; i < bufLength; i += packetSize) - { - tsHeader = b2i(4, stream_buf + i); - pid = (tsHeader & 0x1FFF00) >> 8; - scramblingControl = tsHeader & 0xC0; - - if (tsHeader & 0x20) - { - offset = 4 + stream_buf[i + 4] + 1; - } - else - { - offset = 4; - } - - if (packetSize - offset < 1) - { - continue; - } - - if (scramblingControl == 0) - { - continue; - } - - if (!(stream_buf[i + 3] & 0x10)) - { - stream_buf[i + 3] &= 0x3F; - continue; - } - - is_pes_pid = 0; - - if (pid == data->video_pid) - { - is_pes_pid = 1; - } - else if (pid == data->teletext_pid) - { - is_pes_pid = 1; - } - else - { - for (j = 0; j < data->audio_pid_count; j++) - { - if (pid == data->audio_pids[j]) - { - is_pes_pid = 1; - break; - } - } - } - - if (is_pes_pid) - { - static uint8_t dyn_key[184]; - static uint8_t found_key_bytes[184]; - static uint8_t found_key_bytes_count = 8; - static uint8_t lastScramblingControl = 0xFF; - - int8_t matches00 = 0; - int8_t matchesFF = 0; - int8_t last00_was_good = 0; - int8_t lastFF_was_good = 0; - - // Reset key when scrambling control changes from odd to even - // and vice versa (every ~53 seconds) or when we change channel - if (lastScramblingControl != scramblingControl) - { - memset(dyn_key, 0x00, 184); - memset(found_key_bytes, 0, 184); - found_key_bytes_count = 8; - lastScramblingControl = scramblingControl; - - //cs_log_dbg(D_READER, "resetting key data (scrambling control: %02X)", scramblingControl); - } - - for (j = 8; j < 184; j++) - { - if (found_key_bytes_count == 184) - { - break; - } - - if (stream_buf[i + 4 + j] == 0x00) - { - last00_was_good = 1; - matches00++; - - if (matches00 > limit && found_key_bytes[j] == 0) - { - dyn_key[j] = 0x00; - found_key_bytes[j] = 1; - found_key_bytes_count++; - } - } - else if (stream_buf[i + 4 + j] == 0x3F) - { - last00_was_good = 1; - matches00++; - - if (matches00 > limit && found_key_bytes[j] == 0) - { - dyn_key[j] = 0x3F; - found_key_bytes[j] = 1; - found_key_bytes_count++; - } - } - else - { - if (last00_was_good == 1) - { - last00_was_good = 0; - matches00--; - } - else - { - matches00 -= 2; - } - - if (matches00 < 0) - { - matches00 = 0; - } - } - - if (stream_buf[i + 4 + j] == 0xC0) - { - lastFF_was_good = 1; - matchesFF++; - - if (matchesFF > limit && found_key_bytes[j] == 0) - { - dyn_key[j] = 0x3F; - found_key_bytes[j] = 1; - found_key_bytes_count++; - } - } - else if (stream_buf[i + 4 + j] == 0xFF) - { - lastFF_was_good = 1; - matchesFF++; - - if (matchesFF > limit && found_key_bytes[j] == 0) - { - dyn_key[j] = 0x00; - found_key_bytes[j] = 1; - found_key_bytes_count++; - } - } - else - { - if (lastFF_was_good == 1) - { - lastFF_was_good = 0; - matchesFF--; - } - else - { - matchesFF -= 2; - } - - if (matchesFF < 0) - { - matchesFF = 0; - } - } - } - - for (j = 183; j >= 8; j--) - { - if (found_key_bytes_count == 184) - { - break; - } - - if (stream_buf[i + 4 + j] == 0x00) - { - last00_was_good = 1; - matches00++; - - if (matches00 > limit && found_key_bytes[j] == 0) - { - dyn_key[j] = 0x00; - found_key_bytes[j] = 1; - found_key_bytes_count++; - } - } - else if (stream_buf[i + 4 + j] == 0x3F) - { - last00_was_good = 1; - matches00++; - - if (matches00 > limit && found_key_bytes[j] == 0) - { - dyn_key[j] = 0x3F; - found_key_bytes[j] = 1; - found_key_bytes_count++; - } - } - else - { - if (last00_was_good == 1) - { - last00_was_good = 0; - matches00--; - } - else - { - matches00 -= 2; - } - - if (matches00 < 0) - { - matches00 = 0; - } - } - - if (stream_buf[i + 4 + j] == 0xC0) - { - lastFF_was_good = 1; - matchesFF++; - - if (matchesFF > limit && found_key_bytes[j] == 0) - { - dyn_key[j] = 0x3F; - found_key_bytes[j] = 1; - found_key_bytes_count++; - } - } - else if (stream_buf[i + 4 + j] == 0xFF) - { - lastFF_was_good = 1; - matchesFF++; - - if (matchesFF > limit && found_key_bytes[j] == 0) - { - dyn_key[j] = 0x00; - found_key_bytes[j] = 1; - found_key_bytes_count++; - } - } - else - { - if (lastFF_was_good == 1) - { - lastFF_was_good = 0; - matchesFF--; - } - else - { - matchesFF -= 2; - } - - if (matchesFF < 0) - { - matchesFF = 0; - } - } - } - - for (j = 8; j < 184; j++) - { - stream_buf[i + 4 + j] ^= dyn_key[j]; - } - } - - stream_buf[i + 3] &= 0x3F; // Clear scrambling bits - } -} -#endif // WITH_EMU +#define decrypt(a) decrypt(tsbbatch, fill, a, data->connid) static void DescrambleTsPackets(stream_client_data *data, uint8_t *stream_buf, uint32_t bufLength, uint16_t packetSize, struct dvbcsa_bs_batch_s *tsbbatch) { @@ -1356,31 +665,6 @@ static void DescrambleTsPackets(stream_client_data *data, uint8_t *stream_buf, u if (packetSize - offset < 1) { continue; } #ifdef MODULE_RADEGAST -#ifdef __BISS__ - if(data->ecm_pid == 0x1FFF && caid_is_biss_fixed(data->caid)) - { - uint32_t j, n; - uint16_t ecm_len = 7; - data->ecm_data[0] = 0x80; // to pass the cache check it must be 0x80 or 0x81 - data->ecm_data[1] = 0x00; - data->ecm_data[2] = 0x04; - i2b_buf(2, data->srvid, data->ecm_data + 3); - i2b_buf(2, data->pmt_pid, data->ecm_data + 5); - for(j = 0, n = 7; j < data->STREAMpidcount; j++, n += 2) - { - i2b_buf(2, data->STREAMpids[j], data->ecm_data + n); - data->ecm_data[2] += 2; - ecm_len += 2; - } - data->ens &= 0x0FFFFFFF; // clear top 4 bits (in case of DVB-T/C or garbage), prepare for flagging - data->ens |= 0xA0000000; // flag to emu: this is the namespace, not a pid - i2b_buf(2, data->tsid, data->ecm_data + ecm_len); // place tsid after the last stream pid - i2b_buf(2, data->onid, data->ecm_data + ecm_len + 2); // place onid right after tsid - i2b_buf(4, data->ens, data->ecm_data + ecm_len + 4); // place namespace at the end of the ecm - data->ecm_data[2] += 8; - ParseEcmData(data); - } else -#endif // __BISS__ if (data->ecm_pid && pid == data->ecm_pid) // Process the ECM data { // set to null pid @@ -1490,9 +774,6 @@ static void stream_client_disconnect(stream_client_conn_data *conndata) stream_cur_srvid[conndata->connid] = NO_SRVID_VALUE; #if DVBCSA_KEY_ECM last_srvid[conndata->connid] = NO_SRVID_VALUE; -#endif -#ifdef WITH_EMU - stream_server_has_ecm[conndata->connid] = 0; #endif SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); @@ -1589,12 +870,10 @@ static void *stream_client_handler(void *arg) uint8_t descrambling = 0; struct timeb start, end; -#ifdef WITH_EMU - int32_t cur_dvb_buffer_size, cur_dvb_buffer_wait; -#else + const int32_t cur_dvb_buffer_size = DVB_BUFFER_SIZE_CSA; const int32_t cur_dvb_buffer_wait = DVB_BUFFER_WAIT_CSA; -#endif + struct dvbcsa_bs_batch_s *tsbbatch = NULL; create_streamrelay_client(conndata); @@ -1668,25 +947,14 @@ static void *stream_client_handler(void *arg) if (data->srvid == 0) // We didn't get a srvid - Exit { break; } -#ifndef WITH_EMU key_data[conndata->connid].key[ODD] = dvbcsa_bs_key_alloc(); key_data[conndata->connid].key[EVEN] = dvbcsa_bs_key_alloc(); -#else - for (i = 0; i < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; i++) - { - key_data[conndata->connid].key[i][ODD] = dvbcsa_bs_key_alloc(); - key_data[conndata->connid].key[i][EVEN] = dvbcsa_bs_key_alloc(); - } -#endif if (!cs_malloc(&tsbbatch, (cluster_size + 1) * sizeof(struct dvbcsa_bs_batch_s))) { break; } SAFE_MUTEX_LOCK(&fixed_key_srvid_mutex); stream_cur_srvid[conndata->connid] = data->srvid; -#ifdef WITH_EMU - stream_server_has_ecm[conndata->connid] = 0; -#endif SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); cs_log("Stream client %i request. host=%s port=%d path=%s (%s)", conndata->connid, conndata->stream_host, cfg.stream_source_port, stream_path, !cfg.stream_client_source_host ? "config" : strchr(http_host,':') ? "http header" : "client ip"); @@ -1710,9 +978,6 @@ static void *stream_client_handler(void *arg) data->have_cat_data = 0; data->have_ecm_data = 0; data->have_emm_data = 0; -#ifdef WITH_EMU - data->reset_key_data = 1; -#endif while (!exit_oscam && clientStatus != -1 && streamConnectErrorCount < 3 && streamDataErrorCount < 15) @@ -1737,18 +1002,6 @@ static void *stream_client_handler(void *arg) && (streamConnectErrorCount < 3 || streamDataErrorCount < 15)) #endif { -#ifdef WITH_EMU - if (data->key.csa_used) - { - cur_dvb_buffer_size = EMU_DVB_BUFFER_SIZE_CSA; - cur_dvb_buffer_wait = EMU_DVB_BUFFER_WAIT_CSA; - } - else - { - cur_dvb_buffer_size = EMU_DVB_BUFFER_SIZE_DES; - cur_dvb_buffer_wait = EMU_DVB_BUFFER_WAIT_DES; - } -#endif if (!streamrelay_client[conndata->connid] || streamrelay_client[conndata->connid]->kill) { clientStatus = -1; @@ -1855,26 +1108,9 @@ static void *stream_client_handler(void *arg) clientStatus = -1; break; } - if (cfg.stream_relay_ctab.ctnum == 0 || chk_ctab_ex(data->caid, &cfg.stream_relay_ctab)) + if ((cfg.stream_relay_ctab.ctnum == 0 || chk_ctab_ex(data->caid, &cfg.stream_relay_ctab)) && data->caid != 0xA101 && data->caid != NO_CAID_VALUE) { -#ifdef WITH_EMU - if (caid_is_powervu(data->caid)) - { - DescrambleTsPacketsPowervu(data, stream_buf + startOffset, packetCount * packetSize, packetSize, tsbbatch); - } - else if (data->caid == 0xA101) // Rosscrypt1 - { - DescrambleTsPacketsRosscrypt1(data, stream_buf + startOffset, packetCount * packetSize, packetSize); - } - else if (data->caid == NO_CAID_VALUE) // Compel - { - DescrambleTsPacketsCompel(data, stream_buf + startOffset, packetCount * packetSize, packetSize); - } - else -#endif // WITH_EMU - { - DescrambleTsPackets(data, stream_buf + startOffset, packetCount * packetSize, packetSize, tsbbatch); - } + DescrambleTsPackets(data, stream_buf + startOffset, packetCount * packetSize, packetSize, tsbbatch); if (!descrambling) { if (cfg.stream_relay_buffer_time) @@ -1912,20 +1148,12 @@ static void *stream_client_handler(void *arg) NULLFREE(http_buf); NULLFREE(stream_buf); -#ifndef WITH_EMU + dvbcsa_bs_key_free(key_data[conndata->connid].key[ODD]); dvbcsa_bs_key_free(key_data[conndata->connid].key[EVEN]); key_data[conndata->connid].key[ODD] = NULL; key_data[conndata->connid].key[EVEN] = NULL; -#else - for (i = 0; i < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; i++) - { - dvbcsa_bs_key_free(key_data[conndata->connid].key[i][ODD]); - dvbcsa_bs_key_free(key_data[conndata->connid].key[i][EVEN]); - key_data[conndata->connid].key[i][ODD] = NULL; - key_data[conndata->connid].key[i][EVEN] = NULL; - } -#endif + NULLFREE(tsbbatch); NULLFREE(data); @@ -1984,9 +1212,6 @@ static void *stream_server(void) for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) { stream_cur_srvid[i] = NO_SRVID_VALUE; -#ifdef WITH_EMU - stream_server_has_ecm[i] = 0; -#endif } SAFE_MUTEX_UNLOCK(&fixed_key_srvid_mutex); @@ -2105,88 +1330,12 @@ void *streamrelay_handler(struct s_client *UNUSED(cl), uint8_t *UNUSED(mbuf), in snprintf(authtmp, sizeof(authtmp), "%s:%s", cfg.stream_source_auth_user, cfg.stream_source_auth_password); b64encode(authtmp, cs_strlen(authtmp), &stream_source_auth); } - -#ifdef WITH_EMU - emu_stream_emm_enabled = cfg.emu_stream_emm_enabled; -#endif - mod_idx = module_idx; start_thread("stream_server", stream_server, NULL, NULL, 1, 1); } return NULL; } -#if WITH_EMU -void *stream_key_delayer(void *UNUSED(arg)) -{ - int32_t i, j; - const uint8_t ecm = 0; - emu_stream_client_key_data *cdata; - LL_ITER it; - emu_stream_cw_item *item; - struct timeb t_now; - - while (!exit_oscam) - { - cs_ftime(&t_now); - - for (i = 0; i < STREAM_SERVER_MAX_CONNECTIONS; i++) - { - it = ll_iter_create(ll_emu_stream_delayed_keys[i]); - - while ((item = ll_iter_next(&it))) - { - if (comp_timeb(&t_now, &item->write_time) < 0) - { - break; - } - - SAFE_MUTEX_LOCK(&emu_fixed_key_data_mutex[i]); - - cdata = &emu_fixed_key_data[i]; - - for (j = 0; j < EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2; j++) - { - if (item->csa_used) - { - if (item->is_even) - { - dvbcsa_bs_key_set(item->cw[j], key_data[cdata->connid].key[j][EVEN]); - } - else - { - dvbcsa_bs_key_set(item->cw[j], key_data[cdata->connid].key[j][ODD]); - } - - cdata->csa_used = 1; - } - else - { - if (item->is_even) - { - des_set_key(item->cw[j], cdata->pvu_des_ks[j][0]); - } - else - { - des_set_key(item->cw[j], cdata->pvu_des_ks[j][1]); - } - - cdata->csa_used = 0; - } - } - SAFE_MUTEX_UNLOCK(&emu_fixed_key_data_mutex[i]); - - ll_iter_remove_data(&it); - } - } - - cs_sleepms(25); - } - - return NULL; -} -#endif - void stop_stream_server(void) { int32_t i; diff --git a/module-streamrelay.h b/module-streamrelay.h old mode 100755 new mode 100644 index 2f0e1b1..f43ff55 --- a/module-streamrelay.h +++ b/module-streamrelay.h @@ -10,47 +10,26 @@ #define DVB_BUFFER_WAIT_CSA 188*(DVB_MAX_TS_PACKETS-128) #define DVB_BUFFER_SIZE DVB_BUFFER_SIZE_CSA -#ifdef WITH_EMU -#define EMU_STREAM_MAX_AUDIO_SUB_TRACKS 4 -#define EMU_DVB_BUFFER_SIZE_CSA DVB_BUFFER_SIZE_CSA -#define EMU_DVB_BUFFER_WAIT_CSA DVB_BUFFER_WAIT_CSA -#define EMU_DVB_BUFFER_SIZE_DES 188*32 -#define EMU_DVB_BUFFER_WAIT_DES 188*29 -#define EMU_STREAM_SERVER_MAX_CONNECTIONS STREAM_SERVER_MAX_CONNECTIONS -#define emu_fixed_key_srvid_mutex fixed_key_srvid_mutex -#define emu_stream_cur_srvid stream_cur_srvid -#define emu_stream_client_data stream_client_data -#endif - -//#define __BISS__ -#ifdef __BISS__ -#define MAX_STREAM_PIDS 32 -#endif - #include "cscrypt/md5.h" #include +#if DVBCSA_KEY_ECM +#define DVBCSA_HEADER_ECM 1 +#define dvbcsa_bs_key_set(a,b) dvbcsa_bs_key_set_ecm(ecm,a,b) +#else +#define DVBCSA_HEADER_ECM 0 +#endif +#ifndef STATIC_LIBDVBCSA +#define STATIC_LIBDVBCSA 0 +#endif #define EVEN 0 #define ODD 1 typedef struct { -#ifdef WITH_EMU - struct dvbcsa_bs_key_s *key[EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2][2]; -#else struct dvbcsa_bs_key_s *key[2]; -#endif } stream_client_key_data; -#ifdef WITH_EMU -typedef struct -{ - uint32_t pvu_des_ks[EMU_STREAM_MAX_AUDIO_SUB_TRACKS + 2][2][32]; - int8_t csa_used; - int32_t connid; -} emu_stream_client_key_data; -#endif - typedef struct { int32_t connid; @@ -79,20 +58,7 @@ typedef struct uint16_t ecm_pid; uint16_t emm_pid; uint16_t pcr_pid; -#ifdef __BISS__ - uint8_t STREAMpidcount; - uint16_t STREAMpids[MAX_STREAM_PIDS]; -#endif uint8_t ecm_md5[MD5_DIGEST_LENGTH]; -#ifdef WITH_EMU - int16_t ecm_nb; - int8_t reset_key_data; - uint16_t video_pid; - uint16_t teletext_pid; - uint16_t audio_pids[EMU_STREAM_MAX_AUDIO_SUB_TRACKS]; - uint8_t audio_pid_count; - emu_stream_client_key_data key; -#endif } stream_client_data; void init_stream_server(void); @@ -100,30 +66,6 @@ void stop_stream_server(void); bool stream_write_cw(ECM_REQUEST *er); -#ifdef WITH_EMU -extern int8_t stream_server_thread_init; -extern pthread_mutex_t fixed_key_srvid_mutex; -extern uint16_t stream_cur_srvid[STREAM_SERVER_MAX_CONNECTIONS]; -extern int8_t stream_server_has_ecm[STREAM_SERVER_MAX_CONNECTIONS]; -extern uint8_t emu_stream_server_mutex_init; -extern bool has_dvbcsa_ecm; - -typedef struct -{ - struct timeb write_time; - int8_t csa_used; - int8_t is_even; - uint8_t cw[8][8]; -} emu_stream_cw_item; - -extern pthread_mutex_t emu_fixed_key_data_mutex[EMU_STREAM_SERVER_MAX_CONNECTIONS]; -extern stream_client_key_data key_data[STREAM_SERVER_MAX_CONNECTIONS]; -extern emu_stream_client_key_data emu_fixed_key_data[EMU_STREAM_SERVER_MAX_CONNECTIONS]; - -extern LLIST *ll_emu_stream_delayed_keys[EMU_STREAM_SERVER_MAX_CONNECTIONS]; -void *stream_key_delayer(void *arg); -#endif // WITH_EMU - #endif // MODULE_STREAMRELAY #endif // MODULE_STREAMRELAY_H_ diff --git a/module-webif-lib.c b/module-webif-lib.c old mode 100755 new mode 100644 diff --git a/module-webif-lib.h b/module-webif-lib.h old mode 100755 new mode 100644 diff --git a/module-webif-tpl.c b/module-webif-tpl.c old mode 100755 new mode 100644 index bd13862..7bdc6da --- a/module-webif-tpl.c +++ b/module-webif-tpl.c @@ -456,7 +456,6 @@ char *tpl_getUnparsedTpl(const char *name, int8_t removeHeader, const char *subd check_conf(WITH_SSL, ptr2); check_conf(WITH_STAPI, ptr2); check_conf(WITH_STAPI5, ptr2); - check_conf(WITH_EMU, ptr2); } // for if(ok == 0) { diff --git a/module-webif-tpl.h b/module-webif-tpl.h old mode 100755 new mode 100644 diff --git a/module-webif.c b/module-webif.c old mode 100755 new mode 100644 index 71f3411..3fcf8d9 --- a/module-webif.c +++ b/module-webif.c @@ -30,25 +30,18 @@ #include "oscam-string.h" #include "oscam-time.h" #include "oscam-work.h" -#include "oscam-ecm.h" - -#define WEBIF_MAX_NODES 512 -static uint64_t webif_last_nodeid[WEBIF_MAX_NODES]; - - #ifdef MODULE_GBOX #include "module-gbox-sms.h" #include "module-gbox.h" #include "module-gbox-cards.h" #endif - - - #ifdef WEBIF_WIKI #include "webif/pages_wiki.h" #endif +uint32_t webif_last_nodeid[WEBIF_MAX_NODES]; + extern const struct s_cardreader *cardreaders[]; extern char cs_confdir[]; extern uint32_t ecmcwcache_size; @@ -150,9 +143,8 @@ static bool use_srvid2 = false; #define MNU_GBX_FSTAINF 27 #define MNU_GBX_FEXPINF 28 #define MNU_GBX_INFOLOG 29 -#define MNU_CFG_FSOFTCAMKEY 30 -#define MNU_CFG_TOTAL_ITEMS 31 // sum of items above. Use it for "All inactive" in function calls too. +#define MNU_CFG_TOTAL_ITEMS 30 // sum of items above. Use it for "All inactive" in function calls too. static void set_status_info_var(struct templatevars *vars, char *varname, int no_data, char *fmt, double value) { @@ -465,7 +457,8 @@ static void refresh_oscam(enum refreshtypes refreshtype) case REFR_SERVER: cs_log("Refresh Server requested by WebIF from %s", cs_inet_ntoa(GET_IP())); - reload_global_config(); // Wczytaj ponownie globalną konfigurację + //kill(first_client->pid, SIGHUP); + //todo how I can refresh the server after global settings break; case REFR_SERVICES: @@ -617,55 +610,13 @@ static void webif_save_config(char *section, struct templatevars *vars, struct u } int i; int cnt = (*params).paramcount; - // First pass: check for checkbox values (value=1) to identify which checkboxes are checked - // This is needed because hidden fields (value=0) should not override checkbox values - bool *checkbox_checked = NULL; - if(cnt > 0 && !cs_malloc(&checkbox_checked, cnt * sizeof(bool))) - { - return; // Allocation failed, proceed with original behavior - } for(i = 0; i < cnt; i++) { char *token = (*params).params[i]; char *value = (*params).values[i]; if(!streq(token, "part") && !streq(token, "action")) - { - // Check if this is a checkbox with value=1 - if(strcmp(value, "1") == 0) - { - checkbox_checked[i] = true; - } - } + { config_set(section, token, value); } } - // Second pass: apply settings, skip value=0 if checkbox is checked (hidden field) - for(i = 0; i < cnt; i++) - { - char *token = (*params).params[i]; - char *value = (*params).values[i]; - if(!streq(token, "part") && !streq(token, "action")) - { - // Skip hidden field value=0 if checkbox is checked (value=1 exists) - if(strcmp(value, "0") == 0) - { - bool checkbox_will_be_checked = false; - int j; - for(j = i + 1; j < cnt; j++) - { - if(strcmp((*params).params[j], token) == 0 && strcmp((*params).values[j], "1") == 0) - { - checkbox_will_be_checked = true; - break; - } - } - if(checkbox_will_be_checked) - { - continue; // Skip this hidden field, checkbox will set it to 1 - } - } - config_set(section, token, value); - } - } - free(checkbox_checked); if(write_config() == 0) { tpl_addMsg(vars, "Configuration was saved."); @@ -791,20 +742,6 @@ static char *send_oscam_config_global(struct templatevars *vars, struct uriparam tpl_addVar(vars, TPLADD, "IGNCHKSUMONLYFORGLOBAL", value); free_mk_t(value); - tpl_addVar(vars, TPLADD, "CWVOTEENABLEDCHECKED", (cfg.cwvote_enabled == 1) ? "checked" : ""); - tpl_addVar(vars, TPLADD, "CWVOTELOGENABLEDCHECKED", (cfg.cwvote_log_enabled == 1) ? "checked" : ""); - tpl_printf(vars, TPLADD, "CWVOTETIMEOUT", "%d", cfg.cwvote_timeout); - tpl_printf(vars, TPLADD, "CWVOTEMINVOTES", "%d", cfg.cwvote_min_votes); - tpl_printf(vars, TPLADD, "CWVOTELOCALWEIGHT", "%.1f", cfg.cwvote_local_weight); - tpl_printf(vars, TPLADD, "CWVOTEMAXCANDIDATES", "%d", cfg.cwvote_max_candidates); - tpl_addVar(vars, TPLADD, "CWVOTECOMPARE8", (cfg.cwvote_compare_len == 8) ? "selected" : ""); - tpl_addVar(vars, TPLADD, "CWVOTECOMPARE16", (cfg.cwvote_compare_len == 16) ? "selected" : ""); - tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK0", (cfg.cwvote_fallback == 0) ? "selected" : ""); - tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK1", (cfg.cwvote_fallback == 1) ? "selected" : ""); - tpl_addVar(vars, TPLADD, "CWVOTEFALLBACK2", (cfg.cwvote_fallback == 2) ? "selected" : ""); - value = mk_t_cwvote_caidtab(&cfg.cwvote_caids); - tpl_addVar(vars, TPLADD, "CWVOTECAIDS", value); - free_mk_t(value); #ifdef CS_CACHEEX_AIO tpl_addVar(vars, TPLADD, "CACHEEXSRCNAME", (cfg.cacheex_srcname_webif == 1) ? "checked" : ""); #endif @@ -1408,11 +1345,6 @@ static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uri tpl_printf(vars, TPLADD, "STREAM_RELAY_BUFFER_TIME", "%d", cfg.stream_relay_buffer_time); tpl_printf(vars, TPLADD, "STREAM_RELAY_RECONNECT_COUNT", "%d", cfg.stream_relay_reconnect_count); -#ifdef WITH_EMU - tpl_printf(vars, TPLADD, "TMP", "STREAMEMMENABLEDSELECTED%d", cfg.emu_stream_emm_enabled); - tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); - tpl_printf(vars, TPLADD, "STREAM_ECM_DELAY", "%d", cfg.emu_stream_ecm_delay); -#endif tpl_printf(vars, TPLADD, "TMP", "STREAMCONFIGCLIENTSELECTED%d", cfg.stream_display_client); tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); @@ -2532,7 +2464,7 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam chk_reader("services", servicelabels, rdr); chk_reader("lb_whitelist_services", servicelabelslb, rdr); - if(is_network_reader(rdr) || rdr->typ == R_EMU) //physical readers make trouble if re-started + if(is_network_reader(rdr)) //physical readers make trouble if re-started { if(rdr) { @@ -3329,8 +3261,6 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare); tpl_printf(vars, TPLADD, "CCCRECONNECT", "%d", rdr->cc_reconnect); - if(rdr->cc_want_emu) - { tpl_addVar(vars, TPLADD, "CCCWANTEMUCHECKED", "checked"); } if(rdr->cc_keepalive) { tpl_addVar(vars, TPLADD, "KEEPALIVECHECKED", "checked"); } #endif @@ -3368,23 +3298,6 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam tpl_addVar(vars, TPLADD, "USERSCRIPT", rdr->userscript); #endif -#ifdef WITH_EMU - //emu_auproviders - value = mk_t_ftab(&rdr->emu_auproviders); - tpl_addVar(vars, TPLADD, "EMUAUPROVIDERS", value); - free_mk_t(value); - - // Date-coded BISS keys - if(!apicall) - { - tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "checked" : ""); - } - else - { - tpl_addVar(vars, TPLADD, "EMUDATECODEDENABLED", (rdr->emu_datecodedenabled == 1) ? "1" : "0"); - } -#endif - tpl_addVar(vars, TPLADD, "PROTOCOL", reader_get_type_desc(rdr, 0)); // Show only parameters which needed for the reader @@ -3406,9 +3319,6 @@ static char *send_oscam_reader_config(struct templatevars *vars, struct uriparam case R_CAMD35 : tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCAMD35BIT")); break; - case R_EMU : - tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGEMUBIT")); - break; case R_CS378X : tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCS378XBIT")); break; @@ -5446,34 +5356,9 @@ static char *send_oscam_entitlement(struct templatevars *vars, struct uriparams tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", "

New Structure:
"); char tbuffer[83]; -#ifdef WITH_EMU - char keyBuffer[1024]; -#endif int jsondelimiter = 0; while((item = ll_iter_next(&itr))) { -#ifdef WITH_EMU - if(item->isKey) - { - tpl_addVar(vars, TPLADD, "ENTSTARTDATE", ""); - tpl_addVar(vars, TPLADD, "ENTENDDATE", ""); - cs_hexdump(0, item->key, item->keyLength, keyBuffer, sizeof(keyBuffer)); - tpl_addVar(vars, TPLADD, "ENTEXPIERED", "e_valid"); - tpl_printf(vars, TPLADD, "ENTCAID", "%04X", item->caid); - tpl_printf(vars, TPLADD, "ENTPROVID", "%08X", item->provid); - tpl_addVar(vars, TPLADD, "ENTID", item->name); - tpl_addVar(vars, TPLADD, "ENTCLASS", keyBuffer); - if(item->isData) { tpl_addVar(vars, TPLADD, "ENTTYPE", "data"); } - else { tpl_addVar(vars, TPLADD, "ENTTYPE", "key"); } - tpl_addVar(vars, TPLADD, "ENTRESNAME", ""); - - if((strcmp(getParam(params, "hideexpired"), "1") != 0) || (item->end > now)) - { tpl_addVar(vars, TPLAPPEND, "READERENTENTRY", tpl_getTpl(vars, "ENTITLEMENTITEMBIT")); } - - continue; - } -#endif - localtime_r(&item->start, &start_t); localtime_r(&item->end, &end_t); @@ -5970,9 +5855,6 @@ static char *send_oscam_status(struct templatevars * vars, struct uriparams * pa filtered = (type == cl->typ) || (type == 'x' && (cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)); #else filtered = (type == cl->typ); -#endif -#ifdef WITH_EMU - if(type == 'e' && cl->typ == 'r' && cl->reader->typ == R_EMU) filtered = 1; #endif } } @@ -7564,9 +7446,6 @@ static char *send_oscam_files(struct templatevars * vars, struct uriparams * par { "stats.info", MNU_GBX_FSTAINF, FTYPE_GBOX }, // id 27 { "expired.info", MNU_GBX_FEXPINF, FTYPE_GBOX }, // id 28 { "info.log", MNU_GBX_INFOLOG, FTYPE_GBOX }, // id 29 -#endif -#ifdef WITH_EMU - { "SoftCam.Key", MNU_CFG_FSOFTCAMKEY,FTYPE_CONFIG }, // id 30 #endif { NULL, 0, 0 }, }; @@ -8039,11 +7918,7 @@ static char *send_oscam_EMM_running(struct templatevars * vars, struct uriparams else if(!proxy && rdr->csystem_active) // local active reader { csystem = rdr->csystem; - - if(rdr->typ != R_EMU) - { - caid = rdr->caid; - } + caid = rdr->caid; } if(csystem) @@ -8325,8 +8200,9 @@ static char *send_oscam_cacheex(struct templatevars * vars, struct uriparams * p tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); } } - - { tpl_addVar(vars, TPLADD, "IP", ""); } + + + { tpl_addVar(vars, TPLADD, "IP", ""); } // --- NODEID changer detection (persistent, based on cxnodeid_changer_detected) --- @@ -8348,7 +8224,20 @@ else webif_last_nodeid[idx] = current_node; // --- end detection --- - + if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); } + else if(cl->login > cl->logout) + { tpl_addVar(vars, TPLADD, "IP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "IP", ""); } + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", get_cacheex_node(cl)); + tpl_addVar(vars, TPLADD, "LEVEL", level[cl->account->cacheex.mode]); + tpl_printf(vars, TPLADD, "PUSH", "%d", cl->account->cwcacheexpush); + tpl_printf(vars, TPLADD, "GOT", "%d", cl->account->cwcacheexgot); + tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->account->cwc_info); + tpl_printf(vars, TPLADD, "HIT", "%d", cl->account->cwcacheexhit); + tpl_printf(vars, TPLADD, "ERR", "%d", cl->account->cwcacheexerr); + tpl_printf(vars, TPLADD, "ERRCW", "%d", cl->account->cwcacheexerrcw); #ifdef CS_CACHEEX_AIO tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->account->cwcacheexgotlg); tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->account->cwcacheexpushlg); @@ -9356,11 +9245,7 @@ static int32_t readRequest(FILE * f, IN_ADDR_T in, char **result, int8_t forcePl memcpy(*result + bufsize, buf2, n); bufsize += n; -#ifdef WITH_EMU - if(bufsize > 204800) // max request size 200kb -#else if(bufsize > 102400) // max request size 100kb -#endif { cs_log("error: too much data received from %s", cs_inet_ntoa(in)); NULLFREE(*result); diff --git a/module-webif.c.bak b/module-webif.c.bak new file mode 100644 index 0000000..f6f7f89 --- /dev/null +++ b/module-webif.c.bak @@ -0,0 +1,10260 @@ +#define MODULE_LOG_PREFIX "webif" + +#include "globals.h" + +#ifdef WEBIF +// +// OSCam HTTP server module +// +#include +#include "cscrypt/md5.h" +#include "module-anticasc.h" +#include "module-cacheex.h" +#include "module-cccam.h" +#include "module-cccam-data.h" +#include "module-dvbapi.h" +#include "module-newcamd.h" +#include "module-stat.h" +#include "module-webif.h" +#include "module-webif-lib.h" +#include "module-webif-tpl.h" +#include "oscam-conf-mk.h" +#include "oscam-config.h" +#include "oscam-files.h" +#include "oscam-garbage.h" +#include "oscam-cache.h" +#include "oscam-client.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-reader.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-work.h" +#ifdef MODULE_GBOX +#include "module-gbox-sms.h" +#include "module-gbox.h" +#include "module-gbox-cards.h" +#endif + +#ifdef WEBIF_WIKI +#include "webif/pages_wiki.h" +#endif + +extern const struct s_cardreader *cardreaders[]; +extern char cs_confdir[]; +extern uint32_t ecmcwcache_size; +extern uint32_t cfg_sidtab_generation; +extern int32_t exit_oscam; +extern uint8_t cacheex_peer_id[8]; + +extern char *entitlement_type[]; +extern char *RDR_CD_TXT[]; +int8_t isactive; +int32_t ssl_active = 0; +char noncekey[33]; +pthread_key_t getkeepalive; +static pthread_key_t getip; +pthread_key_t getssl; +static CS_MUTEX_LOCK http_lock; +CS_MUTEX_LOCK *lock_cs; + +static uint8_t useLocal = 1; +#define PRINTF_LOCAL_D useLocal ? "%'d" : "%d" +#define PRINTF_LOCAL_F useLocal ? "%'.0f" : "%.0f" +#define PRINTF_LOCAL_MB useLocal ? "%'.2f MB" : "%.2f MB" + +static int8_t httpthread_running = 0; +static pthread_t httpthread; +static int32_t sock; +enum refreshtypes { REFR_ACCOUNTS, REFR_READERS, REFR_CLIENTS, REFR_SERVER, REFR_ANTICASC, REFR_SERVICES }; + +//initialize structs for calculating cpu-usage depending on time between refresh of status_page +static struct pstat p_stat_cur; +static struct pstat p_stat_old; + +static bool use_srvid2 = false; + +/* constants for menuactivating */ +#define MNU_STATUS 0 +#define MNU_LIVELOG 1 +#define MNU_CONFIG 2 +#define MNU_READERS 3 +#define MNU_USERS 4 +#define MNU_SERVICES 5 +#define MNU_FILES 6 +#define MNU_FAILBAN 7 +#define MNU_CACHEEX 8 +#define MNU_SCRIPT 9 +#define MNU_SHUTDOWN 10 +#define MNU_TOTAL_ITEMS 11 // sum of items above + +/* constants for config.html submenuactivating */ +#define MNU_CFG_GLOBAL 0 +#define MNU_CFG_ANTICASC 1 +#define MNU_CFG_CACHE 2 +#define MNU_CFG_LOADBAL 3 +#define MNU_CFG_CAMD33 4 +#define MNU_CFG_CAMD35 5 +#define MNU_CFG_CAMD35TCP 6 +#define MNU_CFG_CCCAM 7 +#define MNU_CFG_NEWCAMD 8 +#define MNU_CFG_GBOX 9 +#define MNU_CFG_RADEGAST 10 +#define MNU_CFG_SCAM 11 +#define MNU_CFG_SERIAL 12 +#define MNU_CFG_DVBAPI 13 +#define MNU_CFG_LCD 14 +#define MNU_CFG_MONITOR 15 +#define MNU_CFG_WEBIF 16 +#define MNU_CFG_STREAMRELAY 17 + +/* constants for files.html submenuactivating */ +#define MNU_CFG_FVERSION 0 +#define MNU_CFG_FCONF 1 +#define MNU_CFG_FUSER 2 +#define MNU_CFG_FSERVER 3 +#define MNU_CFG_FSRVID 4 +#define MNU_CFG_FDVBAPI 5 +#define MNU_CFG_FACLOG 6 +#define MNU_CFG_FLOGFILE 7 +#define MNU_CFG_FUSERFILE 8 +#define MNU_CFG_FSERVICES 9 +#define MNU_CFG_FPROVID 10 +#define MNU_CFG_FTIERS 11 +#define MNU_CFG_FRATELIMIT 12 +#define MNU_CFG_FWHITELIST 13 +#define MNU_CFG_FSRVID2 14 +#define MNU_CFG_FFAKECWS 15 +#define MNU_CFG_FCSS 16 +#define MNU_CFG_FTWIN 17 +#define MNU_CFG_FKEYCW 18 + +/* constants for files.html for GBOX submenuactivating */ +#define MNU_GBX_FSCINF 19 +#define MNU_GBX_FSHRINF 20 +#define MNU_GBX_FSHRONL 21 +#define MNU_GBX_FVERS 22 +#define MNU_GBX_FATTACK 23 +#define MNU_GBX_FSMSLOG 24 +#define MNU_GBX_FSMSACK 25 +#define MNU_GBX_FSMSNACK 26 +#define MNU_GBX_FSTAINF 27 +#define MNU_GBX_FEXPINF 28 +#define MNU_GBX_INFOLOG 29 + +#define MNU_CFG_TOTAL_ITEMS 30 // sum of items above. Use it for "All inactive" in function calls too. + +static void set_status_info_var(struct templatevars *vars, char *varname, int no_data, char *fmt, double value) +{ + if (no_data) + tpl_addVar(vars, TPLADD, varname, "N/A"); + else + tpl_printf(vars, TPLADD, varname, fmt, value); +} + +/* +* Creates vars Memory/CPU/OSCAM info in for status_page +* if check_available == 0 N/A will be displayed +* Bit mapping +* mem 0 total, 1 used & free, 2 buff, cached & free incl. buff, 3 share +* swap 4 total, 5 used & free, +* proc 6 count +* cpu 7 load +* oscam 8 vsize & rssize, 9 cpu user, 10 cpu sys, 11 cpu sum, 12 cpu refreshtime +* unused 13 - 15 +*/ +static void set_status_info(struct templatevars *vars, struct pstat stats){ + set_status_info_var(vars, "MEM_CUR_TOTAL", stats.check_available & (1 << 0), PRINTF_LOCAL_MB , (double)stats.mem_total/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_FREE", stats.check_available & (1 << 1), PRINTF_LOCAL_MB , (double)stats.mem_free/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_USED", stats.check_available & (1 << 1), PRINTF_LOCAL_MB , (double)stats.mem_used/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_BUFF", stats.check_available & (1 << 2), PRINTF_LOCAL_MB , (double)stats.mem_buff/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_CACHED", stats.check_available & (1 << 2), PRINTF_LOCAL_MB , (double)stats.mem_cached/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_FREEM", stats.check_available & (1 << 2), PRINTF_LOCAL_MB , (double)stats.mem_freem/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_SHARE", stats.check_available & (1 << 3), PRINTF_LOCAL_MB , (double)stats.mem_share/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_TOTSW", stats.check_available & (1 << 4), PRINTF_LOCAL_MB , (double)stats.mem_total_swap/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_FRESW", stats.check_available & (1 << 5), PRINTF_LOCAL_MB , (double)stats.mem_free_swap/(1024.0*1024.0)); + set_status_info_var(vars, "MEM_CUR_USESW", stats.check_available & (1 << 5), PRINTF_LOCAL_MB , (double)stats.mem_used_swap/(1024.0*1024.0)); + + set_status_info_var(vars, "SERVER_PROCS", stats.check_available & (1 << 6), PRINTF_LOCAL_F , stats.info_procs); + + set_status_info_var(vars, "CPU_LOAD_0", stats.check_available & (1 << 7), "%.2f" , stats.cpu_avg[0]); + set_status_info_var(vars, "CPU_LOAD_1", stats.check_available & (1 << 7), "%.2f" , stats.cpu_avg[1]); + set_status_info_var(vars, "CPU_LOAD_2", stats.check_available & (1 << 7), "%.2f" , stats.cpu_avg[2]); + + set_status_info_var(vars, "OSCAM_VMSIZE", stats.check_available & (1 << 8), PRINTF_LOCAL_MB , (double)stats.vsize/(1024.0*1024.0)); + set_status_info_var(vars, "OSCAM_RSSSIZE", stats.check_available & (1 << 8), PRINTF_LOCAL_MB , (double)stats.rss/(1024.0*1024.0)); + set_status_info_var(vars, "OSCAM_CPU_USER", stats.check_available & (1 << 9), "%.2f %%" , stats.cpu_usage_user); + set_status_info_var(vars, "OSCAM_CPU_SYS", stats.check_available & (1 << 10), "%.2f %%" , stats.cpu_usage_sys); + double sum_cpu = stats.cpu_usage_sys + stats.cpu_usage_user; + set_status_info_var(vars, "OSCAM_CPU_SUM", stats.check_available & (1 << 11), "%.2f %%" , sum_cpu); + + if (stats.check_available & (1 << 12)) + { + tpl_addVar(vars, TPLADD, "OSCAM_REFRESH" , "N/A"); + } + else + { + tpl_printf(vars, TPLADD, "OSCAM_REFRESH" , "%02"PRId64":%02"PRId64":%02"PRId64"h", + stats.gone_refresh / 3600, + (stats.gone_refresh / 60) % 60, + stats.gone_refresh % 60); + } +} + +static void clear_account_stats(struct s_auth *account) +{ + account->cwfound = 0; + account->cwcache = 0; + account->cwnot = 0; + account->cwtun = 0; + account->cwignored = 0; + account->cwtout = 0; + account->emmok = 0; + account->emmnok = 0; +#ifdef CW_CYCLE_CHECK + account->cwcycledchecked = 0; + account->cwcycledok = 0; + account->cwcyclednok = 0; + account->cwcycledign = 0; +#endif + cacheex_clear_account_stats(account); +} + +static void clear_all_account_stats(void) +{ + struct s_auth *account = cfg.account; + while(account) + { + clear_account_stats(account); + account = account->next; + } +} + +#ifdef CS_CACHEEX +static void cacheex_clear_all_stats(void) +{ + struct s_auth *account = cfg.account; + while(account) + { + cacheex_clear_account_stats(account); + account = account->next; + } + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { + cacheex_clear_client_stats(cl); + ll_clear_data(cl->ll_cacheex_stats); + } + cacheex_clear_client_stats(first_client); +} +#endif + +static void clear_info_clients_stats(void) +{ + first_client->cwfound = 0; + first_client->cwcache = 0; + first_client->cwnot = 0; + first_client->cwtun = 0; + first_client->cwignored = 0; + first_client->cwtout = 0; + first_client->emmok = 0; + first_client->emmnok = 0; + cacheex_clear_client_stats(first_client); +} + +static void clear_info_readers_stats(void) +{ + int8_t i; + cs_writelock(__func__, &readerlist_lock); + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdr; + while((rdr = ll_iter_next(&itr))) + { + rdr->webif_ecmsok = 0; + rdr->webif_ecmsnok = 0; + rdr->webif_ecmstout = 0; + rdr->webif_ecmsfilteredhead = 0; + rdr->webif_ecmsfilteredlen = 0; + + for(i = 0; i < 4; i++) + { + rdr->webif_emmerror[i] = 0; + rdr->webif_emmwritten[i] = 0; + rdr->webif_emmskipped[i] = 0; + rdr->webif_emmblocked[i] = 0; + } + } + cs_writeunlock(__func__, &readerlist_lock); +} + +static void set_ecm_info(struct templatevars * vars) +{ + //if one of the stats overloaded, reset all stats! + if(first_client->cwfound<0 + || first_client->cwnot<0 + || first_client->cwignored<0 + || first_client->cwtout<0 + || first_client->cwcache<0 + || first_client->cwtun<0 + || first_client->emmok<0 + || first_client->emmnok<0 +#ifdef CS_CACHEEX + || first_client->cwcacheexgot<0 + || first_client->cwcacheexpush<0 + || first_client->cwcacheexhit<0 +#ifdef CS_CACHEEX_AIO + || first_client->cwcacheexgotlg<0 + || first_client->cwcacheexpushlg<0 +#endif +#endif + ){ + clear_info_clients_stats(); + } + //end reset stats + + int ecm = 0, emm = 0; + double ecmsum = first_client->cwfound + first_client->cwcache + first_client->cwnot + first_client->cwtout; //dont count TUN its included + if(ecmsum < 1) {ecmsum = 1; ecm = 1;} + double ecmpos = first_client->cwfound + first_client->cwcache; // dont count TUN its included + if(ecmpos < 1) {ecmpos = 1;} + double ecmneg = first_client->cwnot + first_client->cwtout; //dont count IGN its not part of neagtiv + if(ecmneg < 1) {ecmneg = 1;} + double emmsum = first_client->emmok + first_client->emmnok; + if(emmsum < 1) {emmsum = 1; emm = 1;} + + tpl_printf(vars, TPLADD, "TOTAL_ECM_MIN", "%d", first_client->n_request[0]); + tpl_printf(vars, TPLADD, "TOTAL_CW", PRINTF_LOCAL_F, !ecm ? ecmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_CWOK", PRINTF_LOCAL_F, (double)first_client->cwfound); + tpl_printf(vars, TPLADD, "TOTAL_CWNOK", PRINTF_LOCAL_F, (double)first_client->cwnot); + tpl_printf(vars, TPLADD, "TOTAL_CWIGN", PRINTF_LOCAL_F, (double)first_client->cwignored); + tpl_printf(vars, TPLADD, "TOTAL_CWTOUT", PRINTF_LOCAL_F, (double)first_client->cwtout); + tpl_printf(vars, TPLADD, "TOTAL_CWCACHE", PRINTF_LOCAL_F, (double)first_client->cwcache); + tpl_printf(vars, TPLADD, "TOTAL_CWTUN", PRINTF_LOCAL_F, (double)first_client->cwtun); + tpl_printf(vars, TPLADD, "TOTAL_CWPOS", PRINTF_LOCAL_F, (double)first_client->cwfound + (double)first_client->cwcache); + tpl_printf(vars, TPLADD, "TOTAL_CWNEG", PRINTF_LOCAL_F, (double)first_client->cwnot + (double)first_client->cwtout); + tpl_printf(vars, TPLADD, "TOTAL_EM", PRINTF_LOCAL_F, !emm ? emmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_EMOK", PRINTF_LOCAL_F, (double)first_client->emmok); + tpl_printf(vars, TPLADD, "TOTAL_EMNOK", PRINTF_LOCAL_F, (double)first_client->emmnok); + tpl_printf(vars, TPLADD, "REL_CWOK", "%.2f", (double)first_client->cwfound * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWNOK", "%.2f", (double)first_client->cwnot * 100 / ecmsum); + //tpl_printf(vars, TPLADD, "REL_CWIGN", "%.2f", (double)first_client->cwignored * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWTOUT", "%.2f", (double)first_client->cwtout * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWCACHE", "%.2f", (double)first_client->cwcache * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWTUN", "%.2f", (double)first_client->cwtun * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWPOS", "%.2f", (double)(first_client->cwfound + first_client->cwcache) * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_CWNEG", "%.2f", (double)(first_client->cwnot + first_client->cwtout) * 100 / ecmsum); + tpl_printf(vars, TPLADD, "REL_EMOK", "%.2f", (double)first_client->emmok * 100 / emmsum); + tpl_printf(vars, TPLADD, "REL_EMNOK", "%.2f", (double)first_client->emmnok * 100 / emmsum); + tpl_printf(vars, TPLADD, "REL_CWPOSOK", "%.2f", (double)first_client->cwfound * 100 / ecmpos); + tpl_printf(vars, TPLADD, "REL_CWPOSCACHE", "%.2f", (double)first_client->cwcache * 100 / ecmpos); + tpl_printf(vars, TPLADD, "REL_CWNEGNOK", "%.2f", (double)first_client->cwnot * 100 / ecmneg); + //tpl_printf(vars, TPLADD, "REL_CWNEGIGN", "%.2f", (double)first_client->cwignored * 100 / ecmneg); + tpl_printf(vars, TPLADD, "REL_CWNEGTOUT", "%.2f", (double)first_client->cwtout * 100 / ecmneg); + + double totalrdrneg = 0, totalrdrpos = 0; + double totalrdrok = 0, totalrdrnok = 0, totalrdrtout = 0; + double flen = 0, fhead = 0; + double teruk = 0, terg = 0, ters = 0, teruq = 0; + double twruk = 0, twrg = 0, twrs = 0, twruq = 0; + double tskuk = 0, tskg = 0, tsks = 0, tskuq = 0; + double tbluk = 0, tblg = 0, tbls = 0, tbluq = 0; + + cs_readlock(__func__, &readerlist_lock); + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdr; + while((rdr = ll_iter_next(&itr))) + { + if (rdr->webif_ecmsok) { totalrdrok += rdr->webif_ecmsok; } + if (rdr->webif_ecmsnok) { totalrdrnok += rdr->webif_ecmsnok; } + if (rdr->webif_ecmstout) { totalrdrtout += rdr->webif_ecmstout; } + + if (rdr->webif_ecmsfilteredlen) { flen += rdr->webif_ecmsfilteredlen; } + if (rdr->webif_ecmsfilteredhead) { fhead += rdr->webif_ecmsfilteredhead; } + + if (rdr->webif_emmerror[0]) { teruk += rdr->webif_emmerror[0]; } + if (rdr->webif_emmerror[1]) { teruq += rdr->webif_emmerror[1]; } + if (rdr->webif_emmerror[2]) { ters += rdr->webif_emmerror[2]; } + if (rdr->webif_emmerror[3]) { terg += rdr->webif_emmerror[3]; } + + if (rdr->webif_emmwritten[0]) { twruk += rdr->webif_emmwritten[0]; } + if (rdr->webif_emmwritten[1]) { twruq += rdr->webif_emmwritten[1]; } + if (rdr->webif_emmwritten[2]) { twrs += rdr->webif_emmwritten[2]; } + if (rdr->webif_emmwritten[3]) { twrg += rdr->webif_emmwritten[3]; } + + if (rdr->webif_emmskipped[0]) { tskuk += rdr->webif_emmskipped[0]; } + if (rdr->webif_emmskipped[1]) { tskuq += rdr->webif_emmskipped[1]; } + if (rdr->webif_emmskipped[2]) { tsks += rdr->webif_emmskipped[2]; } + if (rdr->webif_emmskipped[3]) { tskg += rdr->webif_emmskipped[3]; } + + if (rdr->webif_emmblocked[0]) { tbluk += rdr->webif_emmblocked[0]; } + if (rdr->webif_emmblocked[1]) { tbluq += rdr->webif_emmblocked[1]; } + if (rdr->webif_emmblocked[2]) { tbls += rdr->webif_emmblocked[2]; } + if (rdr->webif_emmblocked[3]) { tblg += rdr->webif_emmblocked[3]; } + } + cs_readunlock(__func__, &readerlist_lock); + + totalrdrneg = totalrdrnok + totalrdrtout; + totalrdrpos = totalrdrok; + ecmsum = totalrdrok + totalrdrnok + totalrdrtout; + + tpl_printf(vars, TPLADD, "TOTAL_CWOK_READERS", PRINTF_LOCAL_F, totalrdrok); + tpl_printf(vars, TPLADD, "TOTAL_CWNOK_READERS", PRINTF_LOCAL_F, totalrdrnok); + tpl_printf(vars, TPLADD, "TOTAL_CWTOUT_READERS", PRINTF_LOCAL_F, totalrdrtout); + tpl_printf(vars, TPLADD, "REL_CWOK_READERS", "%.2f", ecmsum ? totalrdrok * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "REL_CWNOK_READERS", "%.2f", ecmsum ? totalrdrnok * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "REL_CWTOUT_READERS", "%.2f", ecmsum ? totalrdrtout * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_CWPOS_READERS", PRINTF_LOCAL_F, totalrdrpos); + tpl_printf(vars, TPLADD, "TOTAL_CWNEG_READERS", PRINTF_LOCAL_F, totalrdrneg); + tpl_printf(vars, TPLADD, "REL_CWPOS_READERS", "%.2f", ecmsum ? totalrdrpos * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "REL_CWNEG_READERS", "%.2f", ecmsum ? totalrdrneg * 100 / ecmsum : 0); + tpl_printf(vars, TPLADD, "TOTAL_ELENR", PRINTF_LOCAL_F, flen); + tpl_printf(vars, TPLADD, "TOTAL_EHEADR", PRINTF_LOCAL_F, fhead); + tpl_printf(vars, TPLADD, "TOTAL_SUM_ALL_READERS_ECM", PRINTF_LOCAL_F, ecmsum); + + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORUK_READERS", PRINTF_LOCAL_F, teruk); + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORG_READERS", PRINTF_LOCAL_F, terg); + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORS_READERS", PRINTF_LOCAL_F, ters); + tpl_printf(vars, TPLADD, "TOTAL_EMMERRORUQ_READERS", PRINTF_LOCAL_F, teruq); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENUK_READERS", PRINTF_LOCAL_F, twruk); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENG_READERS", PRINTF_LOCAL_F, twrg); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENS_READERS", PRINTF_LOCAL_F, twrs); + tpl_printf(vars, TPLADD, "TOTAL_EMMWRITTENUQ_READERS", PRINTF_LOCAL_F, twruq); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDUK_READERS", PRINTF_LOCAL_F, tskuk); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDG_READERS", PRINTF_LOCAL_F, tskg); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDS_READERS", PRINTF_LOCAL_F, tsks); + tpl_printf(vars, TPLADD, "TOTAL_EMMSKIPPEDUQ_READERS", PRINTF_LOCAL_F, tskuq); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDUK_READERS", PRINTF_LOCAL_F, tbluk); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDG_READERS", PRINTF_LOCAL_F, tblg); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDS_READERS", PRINTF_LOCAL_F, tbls); + tpl_printf(vars, TPLADD, "TOTAL_EMMBLOCKEDUQ_READERS", PRINTF_LOCAL_F, tbluq); + + emmsum = teruk + terg + ters + teruq + twruk + twrg + twrs + twruq + tskuk + tskg + tsks + tskuq + tbluk + tblg + tbls + tbluq; + + tpl_printf(vars, TPLADD, "TOTAL_SUM_ALL_READERS_EMM", PRINTF_LOCAL_F, emmsum); +} + +static void refresh_oscam(enum refreshtypes refreshtype) +{ + + switch(refreshtype) + { + case REFR_ACCOUNTS: + cs_log("Refresh Accounts requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + cs_accounts_chk(); + break; + + case REFR_READERS: + cs_log("Refresh Readers requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + reload_readerdb(); + break; + + case REFR_CLIENTS: + cs_log("Refresh Clients requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + cs_reinit_clients(cfg.account); + break; + + case REFR_SERVER: + cs_log("Refresh Server requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + //kill(first_client->pid, SIGHUP); + //todo how I can refresh the server after global settings + break; + + case REFR_SERVICES: + cs_log("Refresh Services requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + //init_sidtab(); + cs_accounts_chk(); + break; + +#ifdef CS_ANTICASC + case REFR_ANTICASC: + cs_log("Refresh Anticascading requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + ac_init_stat(); + struct s_client *cl; + struct s_auth *account; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->typ == 'c' && (account = cl->account)) + { + cl->ac_limit = (account->ac_users * 100 + 80) * cfg.ac_stime; + } + } + break; +#endif + default: + break; + } +} +/* + * load historical values from ringbuffer and return it in the right order + * as string. Value should be freed with free_mk_t() + */ +static char *get_ecm_historystring(struct s_client *cl) +{ + + if(cl) + { + int32_t k, i, pos = 0, needed = 1, v; + char *value, *dot = ""; + int32_t ptr = cl->cwlastresptimes_last; + + needed = CS_ECM_RINGBUFFER_MAX * 6; //5 digits + delimiter + if(!cs_malloc(&value, needed)) { return ""; } + + k = ptr + 1; + for(i = 0; i < CS_ECM_RINGBUFFER_MAX; i++) + { + if(k >= CS_ECM_RINGBUFFER_MAX) + { k = 0; } + v = cl->cwlastresptimes[k].duration; + if(v > 0 && v < (int32_t)cfg.ctimeout * 5) + { + pos += snprintf(value + pos, needed - pos, "%s%d", dot, v); + dot = ","; + } + k++; + } + if(cs_strlen(value) == 0) + { + NULLFREE(value); + return ""; + } + else { return value; } + + } + else + { + return ""; + } +} + +static char *get_ecm_fullhistorystring(struct s_client *cl) +{ + + if(cl) + { + int32_t k, i, pos = 0, needed = 1, v; + char *value, *dot = ""; + int32_t ptr = cl->cwlastresptimes_last; + + needed = CS_ECM_RINGBUFFER_MAX * 20; //5 digits + : + returncode(2) + : + time(10) + delimiter + if(!cs_malloc(&value, needed)) { return ""; } + + k = ptr + 1; + for(i = 0; i < CS_ECM_RINGBUFFER_MAX; i++) + { + if(k >= CS_ECM_RINGBUFFER_MAX) + { k = 0; } + v = cl->cwlastresptimes[k].duration; + if(v > 0 && v < (int32_t)cfg.ctimeout * 5) + { + pos += snprintf(value + pos, needed - pos, "%s%d:%d:%" PRId64, dot, cl->cwlastresptimes[k].duration, cl->cwlastresptimes[k].rc, (int64_t)cl->cwlastresptimes[k].timestamp); + dot = ","; + } + k++; + } + + return (value); + + } + else + { + return ""; + } +} + +/* + * Set the active menu to a different CSS class + */ +static void setActiveMenu(struct templatevars *vars, int8_t active) +{ + int8_t i; + for(i = 0; i < MNU_TOTAL_ITEMS; i++) + { + tpl_printf(vars, TPLADD, "TMP", "MENUACTIVE%d", i); + if(i == active) + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "menu_selected"); } + else + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "menu"); } + } + #ifdef WEBIF_LIVELOG + tpl_addVar(vars, TPLADD, "LOGPAGEMENU", tpl_getTpl(vars, "LOGMENU")); + #endif +} + +/* + * Set the active submenu to a different CSS class + */ +static void setActiveSubMenu(struct templatevars *vars, int8_t active) +{ + int8_t i; + for(i = 0; i < MNU_CFG_TOTAL_ITEMS; i++) + { + tpl_printf(vars, TPLADD, "TMP", "CMENUACTIVE%d", i); + if(i == active) + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "configmenu_selected"); } + else + { tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "configmenu"); } + } +} + +static void webif_save_config(char *section, struct templatevars *vars, struct uriparams *params) +{ + if(!streq(getParam(params, "action"), "execute")) + { return; } + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No changes are possible!"); + return; + } + int i; + int cnt = (*params).paramcount; + for(i = 0; i < cnt; i++) + { + char *token = (*params).params[i]; + char *value = (*params).values[i]; + if(!streq(token, "part") && !streq(token, "action")) + { config_set(section, token, value); } + } + if(write_config() == 0) + { + tpl_addMsg(vars, "Configuration was saved."); + enum refreshtypes ref_type = REFR_SERVER; + if(streq(getParam(params, "part"), "anticasc")) + { ref_type = REFR_ANTICASC; } + refresh_oscam(ref_type); + } + else + { + tpl_addMsg(vars, "ERROR: Failed to write config file!!!"); + } +} + +static char *send_oscam_config_global(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_GLOBAL); + + webif_save_config("global", vars, params); + + if(IP_ISSET(cfg.srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.srvip)); } + tpl_printf(vars, TPLADD, "NICE", "%d", cfg.nice); + tpl_printf(vars, TPLADD, "BINDWAIT", "%d", cfg.bindwait); + + tpl_printf(vars, TPLADD, "TMP", "NETPRIO%d", cfg.netprio); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "PIDFILE", "%s", ESTR(cfg.pidfile)); + + + if(cfg.usrfile != NULL) { tpl_addVar(vars, TPLADD, "USERFILE", cfg.usrfile); } + if(cfg.disableuserfile == 0) { tpl_addVar(vars, TPLADD, "DISABLEUSERFILECHECKED", "checked"); } + if(cfg.usrfileflag == 1) { tpl_addVar(vars, TPLADD, "USERFILEFLAGCHECKED", "selected"); } + if(cfg.mailfile != NULL) { tpl_addVar(vars, TPLADD, "MAILFILE", cfg.mailfile); } + if(cfg.disablemail == 0) { tpl_addVar(vars, TPLADD, "DISABLEMAILCHECKED", "checked"); } + + char *value = mk_t_logfile(); + tpl_addVar(vars, TPLADD, "LOGFILE", value); + free_mk_t(value); + if(cfg.disablelog == 0) { tpl_addVar(vars, TPLADD, "DISABLELOGCHECKED", "checked"); } + tpl_printf(vars, TPLADD, "MAXLOGSIZE", "%d", cfg.max_log_size); + + tpl_addVar(vars, TPLADD, "LOGDUPSCHECKED", (cfg.logduplicatelines == 1) ? "checked" : ""); + tpl_printf(vars, TPLADD, "INITIALDEBUGLEVEL", "%u", cfg.initial_debuglevel); + + if(cfg.cwlogdir != NULL) { tpl_addVar(vars, TPLADD, "CWLOGDIR", cfg.cwlogdir); } + if(cfg.emmlogdir != NULL) { tpl_addVar(vars, TPLADD, "EMMLOGDIR", cfg.emmlogdir); } + tpl_addVar(vars, TPLADD, "ECMFMT", cfg.ecmfmt); + tpl_printf(vars, TPLADD, "LOGHISTORYLINES", "%u", cfg.loghistorylines); + if(cfg.sysloghost != NULL) { tpl_addVar(vars, TPLADD, "SYSLOGHOST", cfg.sysloghost); } + tpl_printf(vars, TPLADD, "SYSLOGPORT", "%u", cfg.syslogport); + + + tpl_printf(vars, TPLADD, "CLIENTTIMEOUT", "%u", cfg.ctimeout); + value = mk_t_caidvaluetab(&cfg.ctimeouttab); + tpl_addVar(vars, TPLADD, "CLIENTTIMEOUT_PERCAID", value); + free_mk_t(value); + tpl_printf(vars, TPLADD, "FALLBACKTIMEOUT", "%u", cfg.ftimeout); + tpl_printf(vars, TPLADD, "CLIENTMAXIDLE", "%u", cfg.cmaxidle); + + + value = mk_t_caidvaluetab(&cfg.ftimeouttab); + tpl_addVar(vars, TPLADD, "FALLBACKTIMEOUT_PERCAID", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "SLEEP", "%d", cfg.tosleep); + tpl_addVar(vars, TPLADD, "UNLOCKPARENTALCHECKED", (cfg.ulparent == 1) ? "checked" : ""); + + if(cfg.reload_useraccounts) { tpl_addVar(vars, TPLADD, "RELOADUSERACCOUNTSCHECKED", "checked"); } + if(cfg.reload_readers) { tpl_addVar(vars, TPLADD, "RELOADREADERSCHECKED", "checked"); } + if(cfg.reload_provid) { tpl_addVar(vars, TPLADD, "RELOADPROVIDCHECKED", "checked"); } + if(cfg.reload_services_ids) { tpl_addVar(vars, TPLADD, "RELOADSERVICESIDSCHECKED", "checked"); } + if(cfg.reload_tier_ids) { tpl_addVar(vars, TPLADD, "RELOADTIERUDSCHECKED", "checked"); } + if(cfg.reload_fakecws) { tpl_addVar(vars, TPLADD, "RELOADFAKECWSCHECKED", "checked"); } + if(cfg.reload_ac_stat) { tpl_addVar(vars, TPLADD, "RELOADACSTATCHECKED", "checked"); } + if(cfg.reload_log) { tpl_addVar(vars, TPLADD, "RELOADLOGCHECKED", "checked"); } + + if(cfg.block_same_ip) { tpl_addVar(vars, TPLADD, "BLOCKSAMEIPCHECKED", "checked"); } + if(cfg.block_same_name) { tpl_addVar(vars, TPLADD, "BLOCKSAMENAMECHECKED", "checked"); } + + if(cfg.waitforcards == 1) { tpl_addVar(vars, TPLADD, "WAITFORCARDSCHECKED", "checked"); } + tpl_printf(vars, TPLADD, "EXTRADELAY", "%d", cfg.waitforcards_extra_delay); + if(cfg.preferlocalcards == 1) + { + tpl_addVar(vars, TPLADD, "PREFERCACHEEX", "selected"); + } + else if(cfg.preferlocalcards == 2) + { + tpl_addVar(vars, TPLADD, "PREFERLOCALCARDS", "selected"); + } + + if(cfg.c35_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08", "checked"); } + + if(cfg.getblockemmauprovid > 0) + { + tpl_addVar(vars, TPLADD, "GETBLOCKEMMAUPROVID", "checked"); + } + + if(cfg.reader_restart_seconds) + { tpl_printf(vars, TPLADD, "READERRESTARTSECONDS", "%d", cfg.reader_restart_seconds); } + + tpl_addVar(vars, TPLADD, "DROPDUPSCHECKED", (cfg.dropdups == 1) ? "checked" : ""); + + if(cfg.resolve_gethostbyname == 1) + { tpl_addVar(vars, TPLADD, "RESOLVER1", "selected"); } + else + { tpl_addVar(vars, TPLADD, "RESOLVER0", "selected"); } + + tpl_printf(vars, TPLADD, "FAILBANTIME", "%d", cfg.failbantime); + tpl_printf(vars, TPLADD, "FAILBANCOUNT", "%d", cfg.failbancount); + + tpl_addVar(vars, TPLADD, "DCHECKCSELECTED", (cfg.double_check == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.double_check_caid); + tpl_addVar(vars, TPLADD, "DOUBLECHECKCAID", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "DISABLECRCCWSCHECKEDGLOBAL", (cfg.disablecrccws == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.disablecrccws_only_for); + tpl_addVar(vars, TPLADD, "IGNCHKSUMONLYFORGLOBAL", value); + free_mk_t(value); + +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "CACHEEXSRCNAME", (cfg.cacheex_srcname_webif == 1) ? "checked" : ""); +#endif + +#ifdef LEDSUPPORT + if(cfg.enableled == 1) + { tpl_addVar(vars, TPLADD, "ENABLELEDSELECTED1", "selected"); } + else if(cfg.enableled == 2) + { tpl_addVar(vars, TPLADD, "ENABLELEDSELECTED2", "selected"); } +#endif + + return tpl_getTpl(vars, "CONFIGGLOBAL"); +} + +#ifdef WITH_LB +static char *send_oscam_config_loadbalancer(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_LOADBAL); + + if(cs_strlen(getParam(params, "button")) > 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No changes are possible!"); + } + else + { + if(strcmp(getParam(params, "button"), "Load Stats") == 0) + { + clear_all_stat(); + load_stat_from_file(); + tpl_addMsg(vars, "Stats loaded from file"); + } + + if(strcmp(getParam(params, "button"), "Save Stats") == 0) + { + save_stat_to_file(1); + tpl_addMsg(vars, "Stats saved to file"); + } + + if(strcmp(getParam(params, "button"), "Clear Stats") == 0) + { + clear_all_stat(); + tpl_addMsg(vars, "Stats cleared completly"); + } + + if(strcmp(getParam(params, "button"), "Clear Timeouts") == 0) + { + clean_all_stats_by_rc(E_TIMEOUT, 0); + tpl_addMsg(vars, "Timeout cleared from Stats"); + } + + if(strcmp(getParam(params, "button"), "Clear Not Found") == 0) + { + clean_all_stats_by_rc(E_NOTFOUND, 0); + tpl_addMsg(vars, "Not Found cleared from Stats"); + } + + if(strcmp(getParam(params, "button"), "Clear Invalid") == 0) + { + clean_all_stats_by_rc(E_INVALID, 0); + tpl_addMsg(vars, "Invalid cleared from Stats"); + } + } + } + + webif_save_config("global", vars, params); + + tpl_printf(vars, TPLADD, "TMP", "LBMODE%d", cfg.lb_mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "LBSAVE", "%d", cfg.lb_save); + if(cfg.lb_savepath) { tpl_addVar(vars, TPLADD, "LBSAVEPATH", cfg.lb_savepath); } + + tpl_printf(vars, TPLADD, "LBNBESTREADERS", "%d", cfg.lb_nbest_readers); + char *value = mk_t_caidvaluetab(&cfg.lb_nbest_readers_tab); + tpl_addVar(vars, TPLADD, "LBNBESTPERCAID", value); + free_mk_t(value); + tpl_printf(vars, TPLADD, "LBNFBREADERS", "%d", cfg.lb_nfb_readers); + tpl_printf(vars, TPLADD, "LBMAXREADERS", "%d", cfg.lb_max_readers); + tpl_printf(vars, TPLADD, "LBMINECMCOUNT", "%d", cfg.lb_min_ecmcount); + tpl_printf(vars, TPLADD, "LBMAXECEMCOUNT", "%d", cfg.lb_max_ecmcount); + tpl_printf(vars, TPLADD, "LBRETRYLIMIT", "%d", cfg.lb_retrylimit); + + value = mk_t_caidvaluetab(&cfg.lb_retrylimittab); + tpl_addVar(vars, TPLADD, "LBRETRYLIMITS", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "LBREOPENSECONDS", "%d", cfg.lb_reopen_seconds); + tpl_printf(vars, TPLADD, "LBCLEANUP", "%d", cfg.lb_stat_cleanup); + + tpl_addVar(vars, TPLADD, "LBREOPENINVALID", (cfg.lb_reopen_invalid == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LBFORCEALWAYS", (cfg.lb_force_reopen_always == 1) ? "checked" : ""); + + value = mk_t_caidtab(&cfg.lb_noproviderforcaid); + tpl_addVar(vars, TPLADD, "LBNOPROVIDERFORCAID", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LBAUTOBETATUNNEL", (cfg.lb_auto_betatunnel == 1) ? "checked" : ""); + + if(cfg.lb_auto_betatunnel_mode) + { + tpl_printf(vars, TPLADD, "TMP", "LBAUTOBETATUNNELMODE%d", cfg.lb_auto_betatunnel_mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + + tpl_printf(vars, TPLADD, "LBPREFERBETA", "%d", cfg.lb_auto_betatunnel_prefer_beta); + + tpl_addVar(vars, TPLADD, "LBAUTOTIMEOUT", (cfg.lb_auto_timeout == 1) ? "checked" : ""); + + tpl_printf(vars, TPLADD, "LBAUTOTIMEOUTP", "%d", cfg.lb_auto_timeout_p); + tpl_printf(vars, TPLADD, "LBAUTOTIMEOUTT", "%d", cfg.lb_auto_timeout_t); + + tpl_addVar(vars, TPLADDONCE, "CONFIG_CONTROL", tpl_getTpl(vars, "CONFIGLOADBALANCERCTRL")); + + return tpl_getTpl(vars, "CONFIGLOADBALANCER"); +} +#endif + +#ifdef MODULE_CAMD33 +static char *send_oscam_config_camd33(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_CAMD33); + + webif_save_config("camd33", vars, params); + + if(cfg.c33_port) + { + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.c33_port); + if(IP_ISSET(cfg.c33_srvip)) { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.c33_srvip)); } + tpl_addVar(vars, TPLADD, "PASSIVECHECKED", (cfg.c33_passive == 1) ? "checked" : ""); + + for(i = 0; i < (int) sizeof(cfg.c33_key); ++i) { tpl_printf(vars, TPLAPPEND, "KEY", "%02X", cfg.c33_key[i]); } + char *value = mk_t_iprange(cfg.c33_plain); + tpl_addVar(vars, TPLADD, "NOCRYPT", value); + free_mk_t(value); + } + + return tpl_getTpl(vars, "CONFIGCAMD33"); +} +#endif + +#ifdef MODULE_CAMD35 +static char *send_oscam_config_camd35(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_CAMD35); + + webif_save_config("cs357x", vars, params); + + if(cfg.c35_port) + { + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.c35_port); + if(IP_ISSET(cfg.c35_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.c35_srvip)); } + + if(cfg.c35_udp_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08UDP", "checked"); } + + } + return tpl_getTpl(vars, "CONFIGCAMD35"); +} +#endif + +#ifdef MODULE_CAMD35_TCP +static char *send_oscam_config_camd35tcp(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_CAMD35TCP); + + webif_save_config("cs378x", vars, params); + + if((cfg.c35_tcp_ptab.nports > 0) && (cfg.c35_tcp_ptab.ports[0].s_port > 0)) + { + + char *value = mk_t_camd35tcp_port(); + tpl_addVar(vars, TPLADD, "PORT", value); + free_mk_t(value); + + if(IP_ISSET(cfg.c35_tcp_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.c35_tcp_srvip)); } + + if(cfg.c35_tcp_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08TCP", "checked"); } + } + return tpl_getTpl(vars, "CONFIGCAMD35TCP"); +} +#endif + +static char *send_oscam_config_cache(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_CACHE); + + webif_save_config("cache", vars, params); + + tpl_printf(vars, TPLADD, "CACHEDELAY", "%u", cfg.delay); + + tpl_printf(vars, TPLADD, "MAXCACHETIME", "%d", cfg.max_cache_time); + +#ifdef CS_CACHEEX + char *value = NULL; + +#ifdef CS_CACHEEX_AIO + value = mk_t_cacheex_cwcheck_valuetab(&cfg.cw_cache_settings); + tpl_addVar(vars, TPLADD, "CWCACHESETTINGS", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "CWCACHESIZE", "%d", cfg.cw_cache_size); + + tpl_printf(vars, TPLADD, "CWCACHEMEMORY", "%d", cfg.cw_cache_memory); + + tpl_printf(vars, TPLADD, "ECMCACHESIZE", "%d", cfg.ecm_cache_size); + + tpl_printf(vars, TPLADD, "ECMCACHEMEMORY", "%d", cfg.ecm_cache_memory); + + tpl_printf(vars, TPLADD, "ECMDROPTIME", "%d", cfg.ecm_cache_droptime); +#endif + + value = mk_t_cacheex_valuetab(&cfg.cacheex_wait_timetab); + tpl_addVar(vars, TPLADD, "WAIT_TIME", value); + free_mk_t(value); + +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "WAITTIME_BLOCK_START", "%d", cfg.waittime_block_start); + + tpl_printf(vars, TPLADD, "WAITTIME_BLOCK_TIME", "%d", cfg.waittime_block_time); +#endif + + value = mk_t_caidvaluetab(&cfg.cacheex_mode1_delay_tab); + tpl_addVar(vars, TPLADD, "CACHEEXMODE1DELAY", value); + free_mk_t(value); + +#ifdef CS_CACHEEX_AIO + value = mk_t_caidvaluetab(&cfg.cacheex_nopushafter_tab); + tpl_addVar(vars, TPLADD, "CACHEEXNOPUSHAFTER", value); + free_mk_t(value); +#endif + + tpl_printf(vars, TPLADD, "MAX_HIT_TIME", "%d", cfg.max_hitcache_time); + + tpl_addVar(vars, TPLADD, "CACHEEXSTATSSELECTED", (cfg.cacheex_enable_stats == 1) ? "checked" : ""); + + /* GLOBAL CACHEEX MAXHOP */ + tpl_printf(vars, TPLADD, "CACHEEX_GLOBAL_MAXHOP",cfg.cacheex_global_maxhop ? "checked" : ""); + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_GLOBAL", "%d", cfg.cacheex_maxhop); + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG_GLOBAL", "%d", cfg.cacheex_maxhop_lg); + + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_PERCAID", "%s", mk_t_caidvaluetab(&cfg.cacheex_maxhop_percaid)); + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG_PERCAID", "%s", mk_t_caidvaluetab(&cfg.cacheex_maxhop_lg_percaid)); + + tpl_addVar(vars, TPLADD, "WTTCHECKED", (cfg.wait_until_ctimeout == 1) ? "checked" : ""); + +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "CACHEEXDROPDIFFS", (cfg.cacheex_dropdiffs == 1) ? "checked" : ""); + + value = mk_t_group(cfg.cacheex_push_lg_groups); + tpl_addVar(vars, TPLADD, "CACHEEXPUSHLGGRPS", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LGONLYREMOTESETTINGSCHECKED", (cfg.cacheex_lg_only_remote_settings == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYCHECKED", (cfg.cacheex_localgenerated_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.cacheex_lg_only_tab); + tpl_addVar(vars, TPLADD, "LGONLYTAB", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYINCHECKED", (cfg.cacheex_localgenerated_only_in == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LGONLYINAIOONLYCHECKED", (cfg.cacheex_lg_only_in_aio_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&cfg.cacheex_lg_only_in_tab); + tpl_addVar(vars, TPLADD, "LGONLYINTAB", value); + free_mk_t(value); + + value = mk_t_cacheex_hitvaluetab(&cfg.cacheex_filter_caidtab); + tpl_addVar(vars, TPLADD, "CACHEEXECMFILTER", value); + free_mk_t(value); + + value = mk_t_cacheex_hitvaluetab(&cfg.cacheex_filter_caidtab_aio); + tpl_addVar(vars, TPLADD, "CACHEEXECMFILTERAIO", value); + free_mk_t(value); +#endif + + if(cfg.csp_port) + { tpl_printf(vars, TPLADD, "PORT", "%d", cfg.csp_port); } + + if(IP_ISSET(cfg.csp_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.csp_srvip)); } + + value = mk_t_cacheex_hitvaluetab(&cfg.csp.filter_caidtab); + tpl_addVar(vars, TPLADD, "CSP_ECM_FILTER", value); + free_mk_t(value); + + value = mk_t_cacheex_cwcheck_valuetab(&cfg.cacheex_cwcheck_tab); + tpl_addVar(vars, TPLADD, "CACHEEXCWCHECK", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "ARCHECKED", (cfg.csp.allow_request == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "ARFCHECKED", (cfg.csp.allow_reforward == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKFAKECWSCHECKED", (cfg.csp.block_fakecws == 1) ? "checked" : ""); +#endif + +#ifdef CW_CYCLE_CHECK +#ifndef CS_CACHEEX + char *value = NULL; +#endif + + tpl_addVar(vars, TPLADD, "CWCYCLECHECK", (cfg.cwcycle_check_enable == 1) ? "checked" : ""); + + value = mk_t_caidtab(&cfg.cwcycle_check_caidtab); + tpl_addVar(vars, TPLADD, "CWCYCLECHECKCAID", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "MAXCYCLELIST", "%d", cfg.maxcyclelist); + tpl_printf(vars, TPLADD, "KEEPCYCLETIME", "%d", cfg.keepcycletime); + + if(cfg.onbadcycle) + { + tpl_printf(vars, TPLADD, "TMP", "ONBADCYCLE%d", cfg.onbadcycle); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + + tpl_addVar(vars, TPLADD, "DROPOLD", (cfg.cwcycle_dropold == 1) ? "checked" : ""); + + if(cfg.cwcycle_sensitive) + { + tpl_printf(vars, TPLADD, "TMP", "CWCSEN%d", cfg.cwcycle_sensitive); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + + tpl_addVar(vars, TPLADD, "ALLOWBADFROMFFB", (cfg.cwcycle_allowbadfromffb == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "USECWCFROMCE", (cfg.cwcycle_usecwcfromce == 1) ? "checked" : ""); + + +#endif + +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "CONFIGCACHEAIO"); +#else + return tpl_getTpl(vars, "CONFIGCACHE"); +#endif +} + +#ifdef MODULE_NEWCAMD +static char *send_oscam_config_newcamd(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_NEWCAMD); + + webif_save_config("newcamd", vars, params); + + if((cfg.ncd_ptab.nports > 0) && (cfg.ncd_ptab.ports[0].s_port > 0)) + { + + char *value = mk_t_newcamd_port(); + tpl_addVar(vars, TPLADD, "PORT", value); + free_mk_t(value); + + if(IP_ISSET(cfg.ncd_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.ncd_srvip)); } + + for(i = 0; i < (int32_t)sizeof(cfg.ncd_key); i++) + { tpl_printf(vars, TPLAPPEND, "KEY", "%02X", cfg.ncd_key[i]); } + + value = mk_t_iprange(cfg.ncd_allowed); + tpl_addVar(vars, TPLADD, "ALLOWED", value); + free_mk_t(value); + + if(cfg.ncd_keepalive) + { tpl_addVar(vars, TPLADD, "KEEPALIVE", "checked"); } + if(cfg.ncd_mgclient) + { tpl_addVar(vars, TPLADD, "MGCLIENTCHK", "checked"); } + } + return tpl_getTpl(vars, "CONFIGNEWCAMD"); +} +#endif + +#ifdef MODULE_GBOX +static char *send_oscam_config_gbox(struct templatevars *vars, struct uriparams *params) +{ + uint8_t i=0; + char local_gbox_save_gsms[2],local_gbox_msg_type[3], local_gbox_dest_peers[GBOX_MAX_DEST_PEERS*5], tmp_gbox_dest_peers[GBOX_MAX_DEST_PEERS*5] ; + int n=0, len_gbox_save_gsms=0, len_gbox_msg_type=0, len_gbox_dest_peers=0, len_gbox_msg_txt=0; + char *ptr1, *saveptr1, *isbroadcast = NULL; + const char *s; + uint16_t gbox_dest_peers_tmp; + + setActiveSubMenu(vars, MNU_CFG_GBOX); + webif_save_config("gbox", vars, params); + /* + * Action when GetOnlinePeers is pressed + */ + if(streq(getParam(params, "action"), "Online peers")) + { + gbox_get_online_peers(); + // init var + len_gbox_save_gsms=cs_strlen(getParam(params, "gbox_msg_type")); + len_gbox_msg_type=cs_strlen(getParam(params, "gbox_msg_type")); + len_gbox_msg_txt=cs_strlen(getParam(params, "gbox_msg_txt")); + if(len_gbox_msg_txt>GBOX_MAX_MSG_TXT) { len_gbox_msg_txt=GBOX_MAX_MSG_TXT; } + // retrieve value from Webif + cs_strncpy(local_gbox_save_gsms, getParam(params, "gbox_save_gsms"), len_gbox_save_gsms+1); + cfg.gbox_save_gsms=atoi(local_gbox_save_gsms); + cs_strncpy(local_gbox_msg_type, getParam(params, "gbox_msg_type"), len_gbox_msg_type+1); + cfg.gbox_msg_type=atoi(local_gbox_msg_type); + cs_strncpy(cfg.gbox_msg_txt,getParam(params, "gbox_msg_txt"), len_gbox_msg_txt+1); + } + /* + * Action when ResetGSMS button is pressed + */ + if(streq(getParam(params, "action"), "resetallgsms")) + { + cfg.gbox_save_gsms = 0; + cfg.gbox_msg_type = 0; + for(i = 0; i < GBOX_MAX_DEST_PEERS; i++) + { + cfg.gbox_dest_peers[i]='\0'; + } + cfg.gbox_dest_peers_num=0; + for(i = 0; i < GBOX_MAX_MSG_TXT; i++) + { + cfg.gbox_msg_txt[i]='\0'; + } + tpl_addMsg(vars, "GBOX: Reset GSMS datas done!"); + } + /* + * Action when Send GSMS is pressed + */ + if(streq(getParam(params, "action"), "Send GSMS")) + { + // init var + len_gbox_msg_type=cs_strlen(getParam(params, "gbox_msg_type")); + len_gbox_dest_peers=cs_strlen(trim(getParam(params, "gbox_dest_peers"))); + len_gbox_msg_txt=cs_strlen(getParam(params, "gbox_msg_txt")); + if(len_gbox_msg_txt>GBOX_MAX_MSG_TXT) { len_gbox_msg_txt=GBOX_MAX_MSG_TXT; } + // retrieve value from Webif + cs_strncpy(local_gbox_msg_type, getParam(params, "gbox_msg_type"), len_gbox_msg_type+1); + cfg.gbox_msg_type=atoi(local_gbox_msg_type); + cs_strncpy(local_gbox_dest_peers, strtoupper(trim(getParam(params, "gbox_dest_peers"))), len_gbox_dest_peers+1); + cs_strncpy(tmp_gbox_dest_peers, strtoupper(trim(getParam(params, "gbox_dest_peers"))), len_gbox_dest_peers+1); + cs_strncpy(cfg.gbox_msg_txt,getParam(params, "gbox_msg_txt"), len_gbox_msg_txt+1); + n=0; + for (ptr1 = strtok_r(tmp_gbox_dest_peers, ",", &saveptr1); (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s=trim(ptr1); + if ((n < GBOX_MAX_DEST_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_dest_peers[n++] = a2i(trim(ptr1), cs_strlen(trim(ptr1))); } + } + cfg.gbox_dest_peers_num = n; + /* + Start sending GBox SMS + */ + if((cs_strlen(cfg.gbox_msg_txt) > 5)) + { + isbroadcast=strstr(local_gbox_dest_peers, "FFFF"); + if(isbroadcast == NULL) + { + n =0; + for (i = 0, ptr1 = strtok_r(local_gbox_dest_peers, ",", &saveptr1); (i < 4) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s=ptr1; + if ((n < GBOX_MAX_DEST_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { + gbox_dest_peers_tmp = a2i(ptr1, 4); + if(gbox_direct_send_gsms(gbox_dest_peers_tmp, cfg.gbox_msg_type, cfg.gbox_msg_txt)) { cs_log("GBOX message sent to[%04X] type[%d] text[%s] ", gbox_dest_peers_tmp, cfg.gbox_msg_type, cfg.gbox_msg_txt);} + n++; + } + } + tpl_addMsg(vars, "GBOX Send SMS: individual messages started."); + } + else + { + if(gbox_direct_send_gsms(0xFFFF, cfg.gbox_msg_type, cfg.gbox_msg_txt)) { cs_log("GBOX broadcast message sent type[%d] text[%s] ", cfg.gbox_msg_type, cfg.gbox_msg_txt);} + tpl_addMsg(vars, "GBOX Send SMS: broadcast started."); + } + } + else + { + cs_log("GBox SMS: destination peers or message text not specified or too short"); + tpl_addMsg(vars, "GBOX: Send SMS failed - error in input fields: dest peers or text message."); + } + } + + tpl_addVar(vars, TPLADD, "HOSTNAME", xml_encode(vars, cfg.gbox_hostname)); + char *value0 = mk_t_gbox_port(); + tpl_addVar(vars, TPLAPPEND, "PORT", value0); + free_mk_t(value0); + tpl_printf(vars, TPLADD, "MYGBOXPASSWORD", "%08X", cfg.gbox_password); + tpl_printf(vars, TPLADD, "MYGBOXID", "%04X", gbox_get_local_gbox_id()); + tpl_printf(vars, TPLADD, "GBOXRECONNECT", "%d", cfg.gbox_reconnect); + tpl_printf(vars, TPLADD, "GBOXMYVERS", "%02X", cfg.gbox_my_vers); + tpl_printf(vars, TPLAPPEND, "GBOXMYCPUAPI", "%02X", cfg.gbox_my_cpu_api); +#ifdef MODULE_CCCAM + if(cfg.cc_gbx_reshare_en == 1) { tpl_addVar(vars, TPLADD, "GBOXCCCRESHARE", "checked"); } + tpl_addVar(vars, TPLAPPEND, "CCCDEPENDINGCONFIG", tpl_getTpl(vars, "CCCAMRESHAREBIT")); +#endif + if(cfg.log_hello == 1) { tpl_addVar(vars, TPLADD, "GBOXLOGHELLO", "checked"); } + if(cfg.gsms_dis == 1) { tpl_addVar(vars, TPLADD, "GBOXGSMSDISABLE", "checked"); } + if(cfg.dis_attack_txt == 1) { tpl_addVar(vars, TPLADD, "GBOXDISATTACKTXT", "checked"); } + if(cfg.gbox_tmp_dir != NULL) { tpl_addVar(vars, TPLADD, "GBOXTMPDIR", cfg.gbox_tmp_dir); } + char *value1 = mk_t_gbox_proxy_card(); + tpl_addVar(vars, TPLAPPEND, "GBOXPROXYCARD", value1); + free_mk_t(value1); + char *value2 = mk_t_gbox_ignored_peer(); + tpl_addVar(vars, TPLAPPEND, "GBOXIGNOREDPEER", value2); + free_mk_t(value2); + char *value3 = mk_t_gbox_block_ecm(); + tpl_addVar(vars, TPLAPPEND, "GBOXBLOCKECM", value3); + free_mk_t(value3); + char *value4 = mk_t_accept_remm_peer(); + tpl_addVar(vars, TPLAPPEND, "GBOXACCEPTREMM", value4); + free_mk_t(value4); +/* + * GBOX SMS +*/ + tpl_addVar(vars, TPLADD, "GBOXSAVEGSMS", (cfg.gbox_save_gsms == 1) ? "checked" : ""); + if(cfg.gbox_msg_type == 0) + { + tpl_addVar(vars, TPLADD, "GBOXMSGTYPENORMAL", "selected"); + } + else if(cfg.gbox_msg_type == 1) + { + tpl_addVar(vars, TPLADD, "GBOXMSGTYPEOSD", "selected"); + } + char *gmsg_dest_peers = mk_t_gbox_dest_peers(); + tpl_addVar(vars, TPLADD, "GBOXMSGDESTPEERS", gmsg_dest_peers); + free_mk_t(gmsg_dest_peers); + tpl_addVar(vars, TPLADD, "GBOXMSGTXT", cfg.gbox_msg_txt); + + return tpl_getTpl(vars, "CONFIGGBOX"); +} +#endif + +#ifdef MODULE_RADEGAST +static char *send_oscam_config_radegast(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_RADEGAST); + + webif_save_config("radegast", vars, params); + + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.rad_port); + if(IP_ISSET(cfg.rad_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.rad_srvip)); } + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, cfg.rad_usr)); + + char *value = mk_t_iprange(cfg.rad_allowed); + tpl_addVar(vars, TPLADD, "ALLOWED", value); + free_mk_t(value); + + return tpl_getTpl(vars, "CONFIGRADEGAST"); +} +#endif + +#ifdef MODULE_SCAM +static char *send_oscam_config_scam(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_SCAM); + + webif_save_config("scam", vars, params); + + tpl_printf(vars, TPLADD, "PORT", "%d", cfg.scam_port); + if(IP_ISSET(cfg.scam_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.scam_srvip)); } + + char *value = mk_t_iprange(cfg.scam_allowed); + tpl_addVar(vars, TPLADD, "ALLOWED", value); + free_mk_t(value); + + return tpl_getTpl(vars, "CONFIGSCAM"); +} +#endif + +#ifdef MODULE_STREAMRELAY +static char *send_oscam_config_streamrelay(struct templatevars *vars, struct uriparams *params) +{ + char *value; + + setActiveSubMenu(vars, MNU_CFG_STREAMRELAY); + + webif_save_config("streamrelay", vars, params); + + tpl_printf(vars, TPLADD, "TMP", "STREAMRELAYENABLEDSELECTED%d", cfg.stream_relay_enabled); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_printf(vars, TPLADD, "STREAM_RELAY_PORT", "%d", cfg.stream_relay_port); + if(cfg.stream_relay_user) + { tpl_printf(vars, TPLADD, "STREAM_RELAY_USER", "%s", cfg.stream_relay_user); } + value = mk_t_caidtab(&cfg.stream_relay_ctab); + tpl_addVar(vars, TPLADD, "STREAM_RELAY_CTAB", value); + free_mk_t(value); + + tpl_printf(vars, TPLADD, "STREAM_SOURCE_HOST", "%s", cfg.stream_source_host); + tpl_addVar(vars, TPLADD, "STREAM_CLIENT_SOURCE_HOST", (cfg.stream_client_source_host == 1) ? "checked" : ""); + tpl_printf(vars, TPLADD, "STREAM_SOURCE_PORT", "%d", cfg.stream_source_port); + if(cfg.stream_source_auth_user) + { tpl_printf(vars, TPLADD, "STREAM_SOURCE_AUTH_USER", "%s", cfg.stream_source_auth_user); } + if(cfg.stream_source_auth_password) + { tpl_printf(vars, TPLADD, "STREAM_SOURCE_AUTH_PASSWORD", "%s", cfg.stream_source_auth_password); } + + tpl_printf(vars, TPLADD, "STREAM_RELAY_BUFFER_TIME", "%d", cfg.stream_relay_buffer_time); + tpl_printf(vars, TPLADD, "STREAM_RELAY_RECONNECT_COUNT", "%d", cfg.stream_relay_reconnect_count); + + tpl_printf(vars, TPLADD, "TMP", "STREAMCONFIGCLIENTSELECTED%d", cfg.stream_display_client); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_addVar(vars, TPLADD, "STREAM_REUSE_CLIENT", (cfg.stream_reuse_client == 1) ? "checked" : ""); +#ifdef WEBIF + tpl_addVar(vars, TPLADD, "STREAM_HIDE_CLIENT", (cfg.stream_hide_client == 1) ? "checked" : ""); +#endif + + return tpl_getTpl(vars, "CONFIGSTREAMRELAY"); +} +#endif + +#ifdef MODULE_CCCAM +static char *send_oscam_config_cccam(struct templatevars *vars, struct uriparams *params) +{ + + setActiveSubMenu(vars, MNU_CFG_CCCAM); + + if(strcmp(getParam(params, "button"), "Refresh list") == 0) + { + cs_log_dbg(D_TRACE, "Entitlements: Refresh Shares start"); +#ifdef MODULE_CCCSHARE + refresh_shares(); +#endif + cs_log_dbg(D_TRACE, "Entitlements: Refresh Shares finished"); + tpl_addMsg(vars, "Refresh Shares started"); + } + + webif_save_config("cccam", vars, params); + + if(streq(getParam(params, "action"), "execute") && !cfg.http_readonly) + { cc_update_nodeid(); } + + char *value = mk_t_cccam_port(); + tpl_addVar(vars, TPLAPPEND, "PORT", value); + free_mk_t(value); + + if(IP_ISSET(cfg.cc_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.cc_srvip)); } + + tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare); + + if(!strcmp((char *)cfg.cc_version, "2.0.11")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED0", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.1")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED1", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.2")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED2", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.3")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED3", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.1.4")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED4", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.2.0")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED5", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.2.1")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED6", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.3.0")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED7", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.3.1")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED8", "selected"); + } + else if(!strcmp((char *)cfg.cc_version, "2.3.2")) + { + tpl_addVar(vars, TPLADD, "VERSIONSELECTED9", "selected"); + } + + tpl_printf(vars, TPLADD, "UPDATEINTERVAL", "%d", cfg.cc_update_interval); + tpl_printf(vars, TPLADD, "RECV_TIMEOUT", "%u", cfg.cc_recv_timeout); + + tpl_addVar(vars, TPLADD, "STEALTH", (cfg.cc_stealth == 1) ? "checked" : ""); + + tpl_printf(vars, TPLADD, "NODEID", "%02X%02X%02X%02X%02X%02X%02X%02X", + cfg.cc_fixed_nodeid[0], cfg.cc_fixed_nodeid[1], cfg.cc_fixed_nodeid[2], cfg.cc_fixed_nodeid[3], + cfg.cc_fixed_nodeid[4], cfg.cc_fixed_nodeid[5], cfg.cc_fixed_nodeid[6], cfg.cc_fixed_nodeid[7]); + + tpl_printf(vars, TPLADD, "TMP", "MINIMIZECARDSELECTED%d", cfg.cc_minimize_cards); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "TMP", "RESHAREMODE%d", cfg.cc_reshare_services); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "TMP", "IGNRSHRSELECTED%d", cfg.cc_ignore_reshare); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_addVar(vars, TPLADD, "FORWARDORIGINCARD", (cfg.cc_forward_origin_card == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "KEEPCONNECTED", (cfg.cc_keep_connected == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADDONCE, "CONFIG_CONTROL", tpl_getTpl(vars, "CONFIGCCCAMCTRL")); + + return tpl_getTpl(vars, "CONFIGCCCAM"); +} +#endif + +static bool is_ext(const char *path, const char *ext) +{ + size_t lenpath = cs_strlen(path); + size_t lenext = cs_strlen(ext); + if(lenext > lenpath) + { return 0; } + return memcmp(path + lenpath - lenext, ext, lenext) == 0; +} + +static char *send_oscam_config_webif(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_WEBIF); + + webif_save_config("webif", vars, params); + + tpl_printf(vars, TPLADD, "HTTPPORT", "%s%d", cfg.http_use_ssl ? "+" : "", cfg.http_port); + if(IP_ISSET(cfg.http_srvip)) + { tpl_addVar(vars, TPLAPPEND, "SERVERIP", cs_inet_ntoa(cfg.http_srvip)); } + + tpl_addVar(vars, TPLADD, "HTTPUSER", cfg.http_user); + tpl_addVar(vars, TPLADD, "HTTPPASSWORD", cfg.http_pwd); + tpl_addVar(vars, TPLADD, "HTTPOSCAMLABEL", cfg.http_oscam_label); + + // css style selector + tpl_printf(vars, TPLADD, "CSSOPTIONS", "\t\t\t\t\t\t\n", + !cfg.http_css ? " selected" : ""); + + if(cfg.http_tpl) + { + char path[255]; + tpl_getFilePathInSubdir(cfg.http_tpl, "", "style", ".css", path, 255); + if(file_exists(path)) + tpl_printf(vars, TPLAPPEND, "CSSOPTIONS", "\t\t\t\t\t\t\n", + path, + cfg.http_css && strstr(cfg.http_css, path) ? " selected" : "", + path); + } + + struct dirent **namelist; + int count = scandir(cs_confdir, &namelist, 0, NULL); + + if( count >= 0 ) + { + for( i = 0 ; i < count; i++ ) + { + if(is_ext(namelist[i]->d_name, ".css")) + { + tpl_printf(vars, TPLAPPEND, "CSSOPTIONS", "\t\t\t\t\t\t\n", + cs_confdir, + namelist[i]->d_name, + cfg.http_css && strstr(cfg.http_css, namelist[i]->d_name) ? " selected" : "", + cs_confdir, namelist[i]->d_name); + } + free( namelist[i] ); + } + free(namelist); + } + + if(cfg.http_prepend_embedded_css) + { tpl_addVar(vars, TPLADD, "HTTPPREPENDEMBEDDEDCSS", "checked"); } + + tpl_addVar(vars, TPLADD, "HTTPHELPLANG", cfg.http_help_lang); + tpl_addVar(vars, TPLADD, "HTTPLOCALE", cfg.http_locale); + tpl_printf(vars, TPLADD, "HTTPEMMUCLEAN", "%d", cfg.http_emmu_clean); + tpl_printf(vars, TPLADD, "HTTPEMMSCLEAN", "%d", cfg.http_emms_clean); + tpl_printf(vars, TPLADD, "HTTPEMMGCLEAN", "%d", cfg.http_emmg_clean); + tpl_printf(vars, TPLADD, "HTTPREFRESH", "%d", cfg.http_refresh); + tpl_printf(vars, TPLADD, "HTTPPOLLREFRESH", "%d", cfg.poll_refresh); + tpl_addVar(vars, TPLADD, "HTTPTPL", cfg.http_tpl); + tpl_addVar(vars, TPLADD, "HTTPPICONPATH", cfg.http_piconpath); + tpl_addVar(vars, TPLADD, "HTTPSCRIPT", cfg.http_script); + tpl_addVar(vars, TPLADD, "HTTPJSCRIPT", cfg.http_jscript); +#ifndef WEBIF_JQUERY + tpl_addVar(vars, TPLADD, "HTTPEXTERNJQUERY", cfg.http_extern_jquery); +#endif + tpl_printf(vars, TPLADD, "HTTPPICONSIZE", "%d", cfg.http_picon_size); + + if(cfg.http_hide_idle_clients > 0) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + tpl_addVar(vars, TPLADD, "HTTPHIDETYPE", cfg.http_hide_type); + if(cfg.http_status_log > 0) { tpl_addVar(vars, TPLADD, "SHOWLOGCHECKED", "checked"); } + if(cfg.http_showpicons > 0) { tpl_addVar(vars, TPLADD, "SHOWPICONSCHECKED", "checked"); } + if(cfg.http_showmeminfo > 0) { tpl_addVar(vars, TPLADD, "SHOWMEMINFOCHECKED", "checked"); } + if(cfg.http_showuserinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWUSERINFOCHECKED", "checked"); } + if(cfg.http_showreaderinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWREADERINFOCHECKED", "checked"); } + if(cfg.http_showcacheexinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWCACHEEXINFOCHECKED", "checked"); } + if(cfg.http_showloadinfo > 0) { tpl_addVar(vars, TPLADD, "SHOWLOADINFOCHECKED", "checked"); } + if(cfg.http_showecminfo > 0) { tpl_addVar(vars, TPLADD, "SHOWECMINFOCHECKED", "checked"); } + + char *value = mk_t_iprange(cfg.http_allowed); + tpl_addVar(vars, TPLADD, "HTTPALLOW", value); + free_mk_t(value); + + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(cfg.http_dyndns[i][0]) + { + tpl_addVar(vars, TPLAPPEND, "HTTPDYNDNS", i > 0 ? "," : ""); + tpl_addVar(vars, TPLAPPEND, "HTTPDYNDNS", (char *)cfg.http_dyndns[i]); + } + } + + tpl_addVar(vars, TPLADD, "HTTPSAVEFULLSELECT", (cfg.http_full_cfg == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "HTTPOVERWRITEBAKFILE", (cfg.http_overwrite_bak_file == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "HTTPREADONLY", (cfg.http_readonly == 1) ? "checked" : ""); + + +#ifdef WITH_SSL + if(cfg.http_cert != NULL) { tpl_addVar(vars, TPLADD, "HTTPCERT", cfg.http_cert); } + tpl_addVar(vars, TPLADD, "HTTPFORCESECUREMODESELECT", (cfg.https_force_secure_mode == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "HTTPAUTOCREATECERTSELECT", (cfg.https_auto_create_cert == 1) ? "checked" : ""); +#endif + +#ifndef WEBIF_JQUERY + tpl_addVar(vars, TPLADDONCE, "CONFIGWEBIFJQUERY", tpl_getTpl(vars, "CONFIGWEBIFJQUERYBIT")); +#endif + + tpl_printf(vars, TPLADD, "AULOW", "%d", cfg.aulow); + tpl_printf(vars, TPLADD, "HIDECLIENTTO", "%d", cfg.hideclient_to); + + return tpl_getTpl(vars, "CONFIGWEBIF"); +} + +#ifdef LCDSUPPORT +static char *send_oscam_config_lcd(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_LCD); + + webif_save_config("lcd", vars, params); + + tpl_addVar(vars, TPLADD, "ENABLELCDSELECTED", (cfg.enablelcd == 1) ? "checked" : ""); + + if(cfg.lcd_output_path != NULL) + { tpl_addVar(vars, TPLADD, "LCDOUTPUTPATH", cfg.lcd_output_path); } + + tpl_addVar(vars, TPLADD, "LCDHIDEIDLE", (cfg.lcd_hide_idle == 1) ? "checked" : ""); + + tpl_printf(vars, TPLADD, "LCDREFRESHINTERVAL", "%d", cfg.lcd_write_intervall); + + return tpl_getTpl(vars, "CONFIGLCD"); +} +#endif + +#ifdef MODULE_MONITOR +static char *send_oscam_config_monitor(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_MONITOR); + + webif_save_config("monitor", vars, params); + + tpl_printf(vars, TPLADD, "MONPORT", "%d", cfg.mon_port); + if(IP_ISSET(cfg.mon_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.mon_srvip)); } + + tpl_printf(vars, TPLADD, "AULOW", "%d", cfg.aulow); + tpl_printf(vars, TPLADD, "HIDECLIENTTO", "%d", cfg.hideclient_to); + + char *value = mk_t_iprange(cfg.mon_allowed); + tpl_addVar(vars, TPLADD, "NOCRYPT", value); + free_mk_t(value); + + //Monlevel selector + tpl_printf(vars, TPLADD, "TMP", "MONSELECTED%d", cfg.mon_level); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + return tpl_getTpl(vars, "CONFIGMONITOR"); +} +#endif + +#ifdef MODULE_SERIAL +static char *send_oscam_config_serial(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_SERIAL); + + webif_save_config("serial", vars, params); + + if(cfg.ser_device) + { + char sdevice[cs_strlen(cfg.ser_device)]; + cs_strncpy(sdevice, cfg.ser_device, sizeof(sdevice)); + char *ptr, *saveptr1 = NULL; + char delimiter[2]; + delimiter[0] = 1; + delimiter[1] = '\0'; + for(ptr = strtok_r(sdevice, delimiter, &saveptr1); ptr; ptr = strtok_r(NULL, delimiter, &saveptr1)) + { + tpl_addVar(vars, TPLADD, "SERIALDEVICE", xml_encode(vars, ptr)); + tpl_addVar(vars, TPLAPPEND, "DEVICES", tpl_getTpl(vars, "CONFIGSERIALDEVICEBIT")); + } + } + + tpl_addVar(vars, TPLADD, "SERIALDEVICE", ""); + tpl_addVar(vars, TPLAPPEND, "DEVICES", tpl_getTpl(vars, "CONFIGSERIALDEVICEBIT")); + + return tpl_getTpl(vars, "CONFIGSERIAL"); +} +#endif + +#ifdef HAVE_DVBAPI +extern const char *boxdesc[]; + +static char *send_oscam_config_dvbapi(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + + setActiveSubMenu(vars, MNU_CFG_DVBAPI); + + webif_save_config("dvbapi", vars, params); + + if(cfg.dvbapi_enabled > 0) + { tpl_addVar(vars, TPLADD, "ENABLEDCHECKED", "checked"); } + + if(cfg.dvbapi_au > 0) + { tpl_addVar(vars, TPLADD, "AUCHECKED", "checked"); } + + if(cfg.dvbapi_delayer > 0) + { tpl_printf(vars, TPLADD, "DELAYER", "%d", cfg.dvbapi_delayer); } + + tpl_printf(vars, TPLADD, "BOXTYPE", "\n", cfg.dvbapi_boxtype == 0 ? " selected" : ""); + for(i = 1; i <= BOXTYPES; i++) + { + tpl_printf(vars, TPLAPPEND, "BOXTYPE", "%s\n", cfg.dvbapi_boxtype == i ? " selected" : "", boxdesc[i]); + } + + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, cfg.dvbapi_usr)); + + //PMT Mode + tpl_printf(vars, TPLADD, "TMP", "PMTMODESELECTED%d", cfg.dvbapi_pmtmode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + //Request Mode + tpl_printf(vars, TPLADD, "TMP", "REQMODESELECTED%d", cfg.dvbapi_requestmode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + //ecminfo_file + if(cfg.dvbapi_ecminfo_file > 0) + { tpl_addVar(vars, TPLADD, "ECMINFOFILECHECKED", "checked"); } + + //ecminfo_type + tpl_printf(vars, TPLADD, "TMP", "ECMINFOTYPESELECTED%d", cfg.dvbapi_ecminfo_type); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + //read_sdt + tpl_printf(vars, TPLADD, "TMP", "READSDTSELECTED%d", cfg.dvbapi_read_sdt); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + +#ifdef WITH_EXTENDED_CW + //extended_cw_api + tpl_printf(vars, TPLADD, "TMP", "EXTENDEDCWAPISELECTED%d", cfg.dvbapi_extended_cw_api); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); +#endif + + //write_sdt_prov + if(cfg.dvbapi_write_sdt_prov > 0) + { tpl_addVar(vars, TPLADD, "WRITESDTPROVCHECKED", "checked"); } + +#ifdef MODULE_STREAMRELAY + //demuxer_fix + if(cfg.dvbapi_demuxer_fix > 0) + { tpl_addVar(vars, TPLADD, "DEMUXERFIXCHECKED", "checked"); } +#endif + + //TCP listen port + if(cfg.dvbapi_listenport > 0) + { tpl_printf(vars, TPLADD, "LISTENPORT", "%d", cfg.dvbapi_listenport); } + + if(IP_ISSET(cfg.dvbapi_srvip)) + { tpl_addVar(vars, TPLADD, "SERVERIP", cs_inet_ntoa(cfg.dvbapi_srvip)); } + + return tpl_getTpl(vars, "CONFIGDVBAPI"); +} +#endif + +#ifdef CS_ANTICASC +static char *send_oscam_config_anticasc(struct templatevars *vars, struct uriparams *params) +{ + setActiveSubMenu(vars, MNU_CFG_ANTICASC); + + webif_save_config("anticasc", vars, params); + + if(cfg.ac_enabled > 0) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + tpl_printf(vars, TPLADD, "NUMUSERS", "%d", cfg.ac_users); + tpl_printf(vars, TPLADD, "SAMPLETIME", "%d", cfg.ac_stime); + tpl_printf(vars, TPLADD, "SAMPLES", "%d", cfg.ac_samples); + + tpl_printf(vars, TPLADD, "TMP", "PENALTY%d", cfg.ac_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + if(cfg.ac_logfile) + { tpl_addVar(vars, TPLADD, "ACLOGFILE", cfg.ac_logfile); } + tpl_printf(vars, TPLADD, "FAKEDELAY", "%d", cfg.ac_fakedelay); + tpl_printf(vars, TPLADD, "DENYSAMPLES", "%d", cfg.ac_denysamples); + + if(cfg.acosc_enabled == 1) + { tpl_addVar(vars, TPLADD, "ACOSC_CHECKED", "checked"); } + tpl_printf(vars, TPLADD, "ACOSC_MAX_ECMS_PER_MINUTE", "%d", cfg.acosc_max_ecms_per_minute); + tpl_printf(vars, TPLADD, "ACOSC_MAX_ACTIVE_SIDS", "%d", cfg.acosc_max_active_sids); + tpl_printf(vars, TPLADD, "ACOSC_ZAP_LIMIT", "%d", cfg.acosc_zap_limit); + tpl_printf(vars, TPLADD, "TMP", "ACOSC_PENALTY%d", cfg.acosc_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_printf(vars, TPLADD, "ACOSC_PENALTY_DURATION", "%d", cfg.acosc_penalty_duration); + tpl_printf(vars, TPLADD, "ACOSC_DELAY", "%d", cfg.acosc_delay); + + return tpl_getTpl(vars, "CONFIGANTICASC"); +} +#endif + +static char *send_oscam_config(struct templatevars *vars, struct uriparams *params) +{ + + setActiveMenu(vars, MNU_CONFIG); + + char *part = getParam(params, "part"); + if(!strcmp(part, "webif")) { return send_oscam_config_webif(vars, params); } +#ifdef MODULE_MONITOR + else if(!strcmp(part, "monitor")) { return send_oscam_config_monitor(vars, params); } +#endif +#ifdef LCDSUPPORT + else if(!strcmp(part, "lcd")) { return send_oscam_config_lcd(vars, params); } +#endif +#ifdef MODULE_CAMD33 + else if(!strcmp(part, "camd33")) { return send_oscam_config_camd33(vars, params); } +#endif +#ifdef MODULE_CAMD35 + else if(!strcmp(part, "camd35")) { return send_oscam_config_camd35(vars, params); } +#endif +#ifdef MODULE_CAMD35_TCP + else if(!strcmp(part, "camd35tcp")) { return send_oscam_config_camd35tcp(vars, params); } +#endif + else if(!strcmp(part, "cache")) { return send_oscam_config_cache(vars, params); } +#ifdef MODULE_NEWCAMD + else if(!strcmp(part, "newcamd")) { return send_oscam_config_newcamd(vars, params); } +#endif +#ifdef MODULE_RADEGAST + else if(!strcmp(part, "radegast")) { return send_oscam_config_radegast(vars, params); } +#endif +#ifdef MODULE_SCAM + else if(!strcmp(part, "scam")) { return send_oscam_config_scam(vars, params); } +#endif +#ifdef MODULE_STREAMRELAY + else if(!strcmp(part, "streamrelay")) { return send_oscam_config_streamrelay(vars, params); } +#endif +#ifdef MODULE_CCCAM + else if(!strcmp(part, "cccam")) { return send_oscam_config_cccam(vars, params); } +#endif +#ifdef MODULE_GBOX + else if(!strcmp(part, "gbox")) { return send_oscam_config_gbox(vars, params); } +#endif +#ifdef HAVE_DVBAPI + else if(!strcmp(part, "dvbapi")) { return send_oscam_config_dvbapi(vars, params); } +#endif +#ifdef CS_ANTICASC + else if(!strcmp(part, "anticasc")) { return send_oscam_config_anticasc(vars, params); } +#endif +#ifdef MODULE_SERIAL + else if(!strcmp(part, "serial")) { return send_oscam_config_serial(vars, params); } +#endif +#ifdef WITH_LB + else if(!strcmp(part, "loadbalancer")) { return send_oscam_config_loadbalancer(vars, params); } +#endif + else { return send_oscam_config_global(vars, params); } +} + +static void inactivate_reader(struct s_reader *rdr) +{ + struct s_client *cl = rdr->client; + if(cl) + { kill_thread(cl); } +} + +static bool picon_exists(char *name) +{ + char picon_name[255], path[255]; + char *tpl_path; + tpl_path = cfg.http_piconpath ? cfg.http_piconpath : cfg.http_tpl; + if(!tpl_path) + { return false; } + snprintf(picon_name, sizeof(picon_name) - 1, "IC_%s", name); + return cs_strlen(tpl_getTplPath(picon_name, tpl_path, path, sizeof(path) - 1)) && file_exists(path); +} + +static void clear_rdr_stats(struct s_reader *rdr) +{ + int i; + for(i = 0; i < 4; i++) + { + rdr->emmerror[i] = 0; + rdr->emmwritten[i] = 0; + rdr->emmskipped[i] = 0; + rdr->emmblocked[i] = 0; + } + rdr->ecmsok = 0; +#ifdef CS_CACHEEX_AIO + rdr->ecmsoklg = 0; +#endif + rdr->ecmsnok = 0; + rdr->ecmstout = 0; + rdr->ecmshealthok = 0; +#ifdef CS_CACHEEX_AIO + rdr->ecmshealthoklg = 0; +#endif + rdr->ecmshealthnok = 0; + rdr->ecmshealthtout = 0; + rdr->ecmsfilteredhead = 0; + rdr->ecmsfilteredlen = 0; +} + +static void clear_all_rdr_stats(void) +{ + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + clear_rdr_stats(rdr); + } +} + +static char *send_oscam_reader(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + struct s_reader *rdr; + int32_t i; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + char *status; + + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + if(!apicall) + { + if(strcmp(getParam(params, "action"), "resetallrdrstats") == 0) + { + clear_all_rdr_stats(); + } + } + + tpl_addVar(vars, TPLADD, "READERACTIONCOLS", config_enabled(WITH_LB) ? "6" : "5"); + + if(strcmp(getParam(params, "action"), "resetuserstats") == 0) + { + clear_info_clients_stats(); + } + if(strcmp(getParam(params, "action"), "resetreaderstats") == 0) + { + clear_info_readers_stats(); + } + if(strcmp(getParam(params, "action"), "reloadreaders") == 0) + { + if(!cfg.http_readonly) + { + refresh_oscam(REFR_READERS); + refresh_oscam(REFR_ACCOUNTS); + } + } + if((strcmp(getParam(params, "action"), "disable") == 0) || (strcmp(getParam(params, "action"), "enable") == 0)) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. Enabling or disabling readers is not possible!"); + } + else + { + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + if(strcmp(getParam(params, "action"), "enable") == 0) + { + if(!rdr->enable) + { + rdr->enable = 1; + } + } + else + { + if(rdr->enable) + { + rdr->enable = 0; + } + } + if(rdr->typ != R_GBOX) + { + restart_cardreader(rdr, 1); + } +#ifdef MODULE_GBOX + else + { + restart_gbox_peer(rdr->label, 0, 0); + cs_log("gbox -> you must restart oscam so that setting becomes effective"); + } +#endif + + cs_log("reader %s %s by WebIf", rdr->label, rdr->enable == 1 ? "enabled":"disabled"); + + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + +#ifdef MODULE_GBOX + if(!is_network_reader(rdr) && !rdr->enable) + { + gbx_local_card_stat(LOCALCARDDISABLED, 0); + } +#endif + } + } + } + + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No deletion will be made!"); + } + else + { + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + inactivate_reader(rdr); + ll_remove(configured_readers, rdr); + + free_reader(rdr); + + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + } + } + } + + if(strcmp(getParam(params, "action"), "reread") == 0) + { + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + struct s_client *cl = rdr->client; + //reset the counters + for(i = 0; i < 4; i++) + { + rdr->emmerror[i] = 0; + rdr->emmwritten[i] = 0; + rdr->emmskipped[i] = 0; + rdr->emmblocked[i] = 0; + } + + if(rdr->enable == 1 && cl && cl->typ == 'r') + { + add_job(cl, ACTION_READER_CARDINFO, NULL, 0); + } + } + } + + LL_ITER itr = ll_iter_create(configured_readers); + + if(!apicall) + { + for(i = 0, rdr = ll_iter_next(&itr); rdr && rdr->label[0]; rdr = ll_iter_next(&itr), i++) { ; } + tpl_printf(vars, TPLADD, "NEXTREADER", "Reader-%d", i); //Next Readername + } + + int jsondelimiter = 0; + int existing_insert = 0; + + int32_t total_readers = 0; + int32_t disabled_readers = 0; + int32_t active_readers = 0; + int32_t connected_readers = 0; + + ll_iter_reset(&itr); //going to iterate all configured readers + while((rdr = ll_iter_next(&itr))) + { +#ifdef CS_CACHEEX_AIO + const char *proto = reader_get_type_desc(rdr, 0); +#endif + struct s_client *cl = rdr->client; + if(rdr->label[0] && rdr->typ) + { +#ifdef CS_CACHEEX_AIO + char *new_proto; +#if defined(MODULE_CAMD35) || defined (MODULE_CAMD35_TCP) + if(rdr->cacheex.feature_bitfield || (cl && cl->c35_extmode > 1)) +#else + if(rdr->cacheex.feature_bitfield) +#endif + { + const char *aio_suffix = " (cx-aio)"; + + if(cs_malloc(&new_proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + if (!cs_strncat(new_proto, (char *)proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + if (!cs_strncat(new_proto, (char *)aio_suffix, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + } + } +#endif + total_readers += 1; + + // used for API and WebIf + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + + MD5((uint8_t *)rdr->label, cs_strlen(rdr->label), md5tmp); + int z; + tpl_addVar(vars, TPLADD, "LABELMD5","id_"); + for (z = 0; z < MD5_DIGEST_LENGTH; z++) + { + tpl_printf(vars, TPLAPPEND, "LABELMD5", "%02x", md5tmp[z]); + } +#ifdef MODULE_GBOX + if(apicall) + { + tpl_addVar(vars, TPLADD, "LASTGSMS", ""); + tpl_addVar(vars, TPLADD, "LASTGSMS", rdr->last_gsms); + } +#endif + if(apicall) + { + tpl_printf(vars, TPLADD, "PICONENABLED", "%d", cfg.http_showpicons?1:0); + } + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, rdr->label)); + if(!existing_insert) + { + tpl_printf(vars, TPLADD, "EXISTING_INS", "'%s'", urlencode(vars, rdr->label)); + existing_insert++; + }else + { + tpl_printf(vars, TPLAPPEND, "EXISTING_INS", ",'%s'", urlencode(vars, rdr->label)); + } + tpl_addVar(vars, TPLADD, "READERCLASS", rdr->enable ? "enabledreader" : "disabledreader"); + + if(rdr->enable) { active_readers += 1; } + else { disabled_readers += 1; } + + if(rdr->tcp_connected) + { + connected_readers += 1; + +#ifdef CS_CACHEEX_AIO + if(rdr->cacheex.feature_bitfield) + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", (const char*)new_proto); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", (const char*)new_proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", new_proto); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char*)new_proto); + } + } + + if(rdr->cacheex.feature_bitfield & 32) + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", rdr->cacheex.aio_version); + else if(cl->reader->cacheex.feature_bitfield) + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", "[cx-aio < 9.2.3]"); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", proto); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", proto); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char *)proto); + } + } + } +#else + tpl_addVar(vars, TPLADD, "CLIENTPROTO", reader_get_type_desc(rdr, 0)); + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", reader_get_type_desc(rdr, 0)); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", reader_get_type_desc(rdr, 0)); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s", reader_get_type_desc(rdr, 0)); + } + } +#endif + switch(rdr->card_status) + { + case CARD_INSERTED: + status = "online"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_connected"); + break; + + case NO_CARD: + case UNKNOWN: + case READER_DEVICE_ERROR: + case CARD_NEED_INIT: + case CARD_FAILURE: + default: + status = "connected"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_undefined"); + break; + } + + tpl_addVar(vars, TPLADD, "READERIP", cs_inet_ntoa(rdr->client->ip)); + } + else + { + /* default initial values */ + tpl_addVar(vars, TPLADDONCE, "RSTATUS", "offline"); + tpl_addVar(vars, TPLADDONCE, "READERIP", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOSORT", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOTITLE", ""); + tpl_addVar(vars, TPLADDONCE, "PROTOICON", ""); + + if(!is_network_reader(rdr) && rdr->enable) + { + switch(rdr->card_status) + { + case CARD_INSERTED: + status = "active"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_connected"); + break; + + case NO_CARD: + case UNKNOWN: + case READER_DEVICE_ERROR: + case CARD_NEED_INIT: + case CARD_FAILURE: + default: + status = "connected"; + tpl_addVar(vars, TPLADD, "RSTATUS", status); + tpl_addVar(vars, TPLADD, "READERCLASS", "r_undefined"); + break; + } + + tpl_addVar(vars, TPLADD, "CLIENTPROTO", reader_get_type_desc(rdr, 0)); + tpl_addVar(vars, TPLADD, "CLIENTPROTOSORT", reader_get_type_desc(rdr, 0)); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", reader_get_type_desc(rdr, 0)); + if(picon_exists(picon_name)) + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s", reader_get_type_desc(rdr, 0)); + } + } + } + } + + if(rdr->description) + tpl_printf(vars, TPLADD, "DESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, rdr->description)); + else + tpl_addVar(vars, TPLADD, "DESCRIPTION", ""); + + if(cfg.http_showpicons && !apicall) + { + tpl_addVar(vars, TPLADD, "READERBIT", tpl_getTpl(vars, picon_exists(xml_encode(vars, rdr->label)) ? "READERNAMEBIT" : "READERNOICON")); +#ifdef CS_CACHEEX_AIO + if(rdr->cacheex.feature_bitfield) + { + tpl_addVar(vars, TPLADD, "CLIENTPROTO", picon_exists(xml_encode(vars, (const char*)new_proto)) ? tpl_getTpl(vars, "READERCTYPBIT") : tpl_getTpl(vars, "READERCTYPNOICON")); + } + else + { +#endif + tpl_addVar(vars, TPLADD, "CLIENTPROTO", picon_exists(xml_encode(vars, reader_get_type_desc(rdr, 0))) ? tpl_getTpl(vars, "READERCTYPBIT") : tpl_getTpl(vars, "READERCTYPNOICON")); +#ifdef CS_CACHEEX_AIO + } +#endif + } + else + tpl_addVar(vars, TPLADD, "READERBIT", tpl_getTpl(vars, "READERLABEL")); + + char *value = mk_t_group(rdr->grp); + tpl_addVar(vars, TPLADD, "GROUPS", value); + free_mk_t(value); + tpl_printf(vars, TPLADD, "EMMERRORUK", PRINTF_LOCAL_D, rdr->emmerror[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMERRORG", PRINTF_LOCAL_D, rdr->emmerror[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMERRORS", PRINTF_LOCAL_D, rdr->emmerror[SHARED]); + tpl_printf(vars, TPLADD, "EMMERRORUQ", PRINTF_LOCAL_D, rdr->emmerror[UNIQUE]); + + tpl_printf(vars, TPLADD, "EMMWRITTENUK", PRINTF_LOCAL_D, rdr->emmwritten[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMWRITTENG", PRINTF_LOCAL_D, rdr->emmwritten[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMWRITTENS", PRINTF_LOCAL_D, rdr->emmwritten[SHARED]); + tpl_printf(vars, TPLADD, "EMMWRITTENUQ", PRINTF_LOCAL_D, rdr->emmwritten[UNIQUE]); + + tpl_printf(vars, TPLADD, "EMMSKIPPEDUK", PRINTF_LOCAL_D, rdr->emmskipped[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMSKIPPEDG", PRINTF_LOCAL_D, rdr->emmskipped[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMSKIPPEDS", PRINTF_LOCAL_D, rdr->emmskipped[SHARED]); + tpl_printf(vars, TPLADD, "EMMSKIPPEDUQ", PRINTF_LOCAL_D, rdr->emmskipped[UNIQUE]); + + tpl_printf(vars, TPLADD, "EMMBLOCKEDUK", PRINTF_LOCAL_D, rdr->emmblocked[UNKNOWN]); + tpl_printf(vars, TPLADD, "EMMBLOCKEDG", PRINTF_LOCAL_D, rdr->emmblocked[GLOBAL]); + tpl_printf(vars, TPLADD, "EMMBLOCKEDS", PRINTF_LOCAL_D, rdr->emmblocked[SHARED]); + tpl_printf(vars, TPLADD, "EMMBLOCKEDUQ", PRINTF_LOCAL_D, rdr->emmblocked[UNIQUE]); + + tpl_printf(vars, TPLADD, "ECMSOK", PRINTF_LOCAL_D, rdr->ecmsok); + tpl_printf(vars, TPLADD, "ECMSOKREL", " (%.2f %%)", rdr->ecmshealthok); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "ECMSOKLG", PRINTF_LOCAL_D, rdr->ecmsoklg); + tpl_printf(vars, TPLADD, "ECMSOKLGREL", " (%.2f %%)", rdr->ecmshealthoklg); +#endif + tpl_printf(vars, TPLADD, "ECMSNOK", PRINTF_LOCAL_D, rdr->ecmsnok); + tpl_printf(vars, TPLADD, "ECMSNOKREL", " (%.2f %%)",rdr->ecmshealthnok); + tpl_printf(vars, TPLADD, "ECMSTOUT", PRINTF_LOCAL_D, rdr->ecmstout); + tpl_printf(vars, TPLADD, "ECMSTOUTREL", " (%.2f %%)",rdr->ecmshealthtout); + tpl_printf(vars, TPLADD, "ECMSFILTEREDHEAD", PRINTF_LOCAL_D, rdr->ecmsfilteredhead); + tpl_printf(vars, TPLADD, "ECMSFILTEREDLEN", PRINTF_LOCAL_D, rdr->ecmsfilteredlen); +#ifdef WITH_LB + tpl_printf(vars, TPLADD, "LBWEIGHT", "%d", rdr->lb_weight); +#endif + if(!is_network_reader(rdr)) //reader is physical + { + tpl_addVar(vars, TPLADD, "REFRICO", "image?i=ICREF"); + tpl_addVar(vars, TPLADD, "READERREFRESH", tpl_getTpl(vars, "READERREFRESHBIT")); + tpl_addVar(vars, TPLADD, "ENTICO", "image?i=ICENT"); + tpl_addVar(vars, TPLADD, "ENTITLEMENT", tpl_getTpl(vars, "READERENTITLEBIT")); + } + else + { + tpl_addVar(vars, TPLADD, "READERREFRESH", ""); + if(rdr->typ == R_CCCAM) + { + tpl_addVar(vars, TPLADD, "ENTICO", "image?i=ICENT"); + tpl_addVar(vars, TPLADD, "ENTITLEMENT", tpl_getTpl(vars, "READERENTITLEBIT")); + } + else + { + tpl_addVar(vars, TPLADD, "ENTITLEMENT", ""); + } + } + + if(rdr->enable == 0) + { + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICENA"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Enable"); + tpl_addVar(vars, TPLADD, "SWITCH", "enable"); + tpl_addVar(vars, TPLADD, "WRITEEMM", ""); + } + else + { + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICDIS"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Disable"); + tpl_addVar(vars, TPLADD, "SWITCH", "disable"); + + tpl_addVar(vars, TPLADD, "EMMICO", "image?i=ICEMM"); + tpl_addVar(vars, TPLADD, "WRITEEMM", tpl_getTpl(vars, "READERWRITEEMMBIT")); + } + + if(!apicall) + { + // Add to WebIf Template +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, "READERLIST", tpl_getTpl(vars, "READERSBITAIO")); +#else + tpl_addVar(vars, TPLAPPEND, "READERLIST", tpl_getTpl(vars, "READERSBIT")); +#endif + } + else + { + + // used only for API + tpl_addVar(vars, TPLADD, "APIREADERENABLED", !rdr->enable ? "0" : "1"); + if(cl) + { + tpl_printf(vars, TPLADD, "APIREADERTYPE", "%c", cl->typ ? cl->typ : 'x'); + } + + if(apicall==1) + { + // Add to API Template + tpl_addVar(vars, TPLAPPEND, "APIREADERLIST", tpl_getTpl(vars, "APIREADERSBIT")); + } + if(apicall==2) + { + tpl_printf(vars, TPLAPPEND, "APIREADERLIST","%s%s",jsondelimiter?",":"", tpl_getTpl(vars, "JSONREADERBIT")); + jsondelimiter++; + } + } +#ifdef CS_CACHEEX_AIO + if(rdr->cacheex.feature_bitfield) + { + free(new_proto); + } +#endif + } + } + + tpl_printf(vars, TPLADD, "TOTAL_READERS", "%d", total_readers); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED_READERS", "%d", disabled_readers); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE_READERS", "%d", active_readers); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED_READERS", "%d", connected_readers); + + //CM info + tpl_addVar(vars, TPLADD, "DISPLAYUSERINFO", "hidden"); // no userinfo in readers + set_ecm_info(vars); + + if(!apicall) + { +#ifdef MODULE_CAMD33 + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CAMD35 + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CAMD35_TCP + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_NEWCAMD + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CCCAM + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_GBOX + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_RADEGAST + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_SERIAL + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_CONSTCW + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif +#ifdef MODULE_SCAM + tpl_addVar(vars, TPLAPPEND, "ADDPROTOCOL", "\n"); +#endif + + for(i = 0; cardreaders[i]; i++) + { + tpl_printf(vars, TPLAPPEND, "ADDPROTOCOL", "\n", xml_encode(vars, cardreaders[i]->desc)); + } +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "READERSAIO"); +#else + return tpl_getTpl(vars, "READERS"); +#endif + } + else + { + if(apicall == 1) + { + return tpl_getTpl(vars, "APIREADERS"); + } + else + { + return tpl_getTpl(vars, "JSONREADER"); + } + } +} + +static char *send_oscam_reader_config(struct templatevars *vars, struct uriparams *params) +{ + int32_t i; + int32_t apicall = 0; + char *reader_ = getParam(params, "label"); + char *value; + + struct s_reader *rdr; + + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + + if(strcmp(getParam(params, "action"), "Add") == 0) + { + // Add new reader + struct s_reader *newrdr; + if(!cs_malloc(&newrdr, sizeof(struct s_reader))) { return "0"; } + for(i = 0; i < (*params).paramcount; ++i) + { + if(strcmp((*params).params[i], "action")) + { chk_reader((*params).params[i], (*params).values[i], newrdr); } + } + module_reader_set(newrdr); + reader_ = newrdr->label; + reader_set_defaults(newrdr); + newrdr->enable = 0; // do not start the reader because must configured before + ll_append(configured_readers, newrdr); + tpl_addMsg(vars, "New Reader has been added with default settings"); + } + else if(strcmp(getParam(params, "action"), "Save") == 0) + { + + rdr = get_reader_by_label(getParam(params, "label")); + if(!rdr) + { return NULL; } + //if (is_network_reader(rdr)) + // inactivate_reader(rdr); //Stop reader before reinitialization + char servicelabels[1024] = ""; + char servicelabelslb[1024] = ""; + + for(i = 0; i < (*params).paramcount; ++i) + { + if((strcmp((*params).params[i], "reader")) && (strcmp((*params).params[i], "action"))) + { + if(!strcmp((*params).params[i], "services")) + { snprintf(servicelabels + cs_strlen(servicelabels), sizeof(servicelabels) - cs_strlen(servicelabels), "%s,", (*params).values[i]); } + else if(!strcmp((*params).params[i], "lb_whitelist_services")) + { snprintf(servicelabelslb + cs_strlen(servicelabelslb), sizeof(servicelabelslb) - cs_strlen(servicelabelslb), "%s,", (*params).values[i]); } + else + /*if(cs_strlen((*params).values[i]) > 0)*/ + { chk_reader((*params).params[i], (*params).values[i], rdr); } + } + //printf("param %s value %s\n",(*params).params[i], (*params).values[i]); + } + chk_reader("services", servicelabels, rdr); + chk_reader("lb_whitelist_services", servicelabelslb, rdr); + + if(is_network_reader(rdr)) //physical readers make trouble if re-started + { + if(rdr) + { + if(rdr->typ != R_GBOX) + { + restart_cardreader(rdr, 1); + } +#ifdef MODULE_GBOX + else + { + //cs_log("SAVE - single gbox reader %s restarted by WebIf", rdr->label); + restart_gbox_peer(rdr->label, 0, 0); + } +#endif + } + } + + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } else { tpl_addMsg(vars, "Reader config updated and saved"); } + + } + + rdr = get_reader_by_label(reader_); + if(!rdr) + { return NULL; } + + // Label, Description + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "DESCRIPTION", xml_encode(vars, rdr->description)); + + // enabled + if(!apicall) + { + tpl_addVar(vars, TPLADD, "ENABLED", (rdr->enable == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "ENABLEDVALUE", (rdr->enable == 1) ? "1" : "0"); + } + + tpl_addVar(vars, TPLADD, "PASSWORD", xml_encode(vars, rdr->r_pwd)); + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, rdr->r_usr)); + tpl_addVar(vars, TPLADD, "PASS", xml_encode(vars, rdr->r_pwd)); + + // Key Newcamd + for(i = 0; i < (int32_t)sizeof(rdr->ncd_key); i++) + { tpl_printf(vars, TPLAPPEND, "NCD_KEY", "%02X", rdr->ncd_key[i]); } + + // Pincode + tpl_addVar(vars, TPLADD, "PINCODE", rdr->pincode); + + // Emmfile Path + if(rdr->emmfile) { tpl_addVar(vars, TPLADD, "EMMFILE", (char *)rdr->emmfile); } + + // Inactivity timeout + tpl_printf(vars, TPLADD, "INACTIVITYTIMEOUT", "%d", rdr->tcp_ito); + + // Receive timeout + tpl_printf(vars, TPLADD, "RECEIVETIMEOUT", "%d", rdr->tcp_rto); + + // keepalive + tpl_addVar(vars, TPLADD, "RDRKEEPALIVE", (rdr->keepalive == 1) ? "checked" : ""); + + // Connect on init (newcamd) + if(!apicall) + { + tpl_addVar(vars, TPLADD, "CONNECTONINITCHECKED", (rdr->ncd_connect_on_init == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "CONNECTONINITCHECKED", (rdr->ncd_connect_on_init == 1) ? "1" : "0"); + } + + // Reset Cycle + tpl_printf(vars, TPLADD, "RESETCYCLE", "%d", rdr->resetcycle); + + // Disable Serverfilter + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DISABLESERVERFILTERCHECKED", (rdr->ncd_disable_server_filt == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DISABLESERVERFILTERVALUE", (rdr->ncd_disable_server_filt == 1) ? "1" : "0"); + } + +#ifdef MODULE_GHTTP + // Use SSL + if(!apicall) + { + tpl_addVar(vars, TPLADD, "USESSLCHECKED", (rdr->ghttp_use_ssl == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "USESSLVALUE", (rdr->ghttp_use_ssl == 1) ? "1" : "0"); + } +#endif + + // IPv4 force + if (!apicall) + { + tpl_addVar(vars, TPLADD, "IPV4FORCE", (rdr->ipv4force == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "IPV4FORCE", (rdr->ipv4force == 1) ? "1" : "0"); + } + + // Fallback + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FALLBACKCHECKED", (rdr->fallback == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FALLBACKVALUE", (rdr->fallback == 1) ? "1" : "0"); + } + + // Fallback per caid + value = mk_t_ftab(&rdr->fallback_percaid); + tpl_addVar(vars, TPLADD, "FALLBACK_PERCAID", value); + free_mk_t(value); + + // disable checksum test only for selected caid/provid + value = mk_t_ftab(&rdr->disablecrccws_only_for); + tpl_addVar(vars, TPLADD, "IGN_CHKSUM_ONLYFOR", value); + free_mk_t(value); + +#ifdef WITH_LB + tpl_addVar(vars, TPLADD, "LBFORCEFALLBACK", (rdr->lb_force_fallback == 1) ? "checked" : ""); +#endif + +#ifdef CS_CACHEEX + // Cacheex + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "CACHEEXSELECTED%d", rdr->cacheex.mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "CACHEEX", "%d", rdr->cacheex.mode); + } + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP", "%d", rdr->cacheex.maxhop); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG", "%d", rdr->cacheex.maxhop_lg); +#endif + value = mk_t_cacheex_hitvaluetab(&rdr->cacheex.filter_caidtab); + //if (cs_strlen(value) > 0) + tpl_printf(vars, TPLADD, "CACHEEX_ECM_FILTER", "%s", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "DCCHECKED", (rdr->cacheex.drop_csp == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "ARCHECKED", (rdr->cacheex.allow_request == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "AFCHECKED", (rdr->cacheex.allow_filter == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "AMCHECKED", (rdr->cacheex.allow_maxhop == 1) ? "checked" : ""); +#endif + tpl_addVar(vars, TPLADD, "BLOCKFAKECWSCHECKED", (rdr->cacheex.block_fakecws == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "USECWCHECKFORPUSHCHECKED", (rdr->cacheex.cw_check_for_push == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LGONLYREMOTESETTINGSCHECKED", (rdr->cacheex.lg_only_remote_settings == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYCHECKED", (rdr->cacheex.localgenerated_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&rdr->cacheex.lg_only_tab); + tpl_addVar(vars, TPLADD, "LGONLYTAB", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYINCHECKED", (rdr->cacheex.localgenerated_only_in == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LGONLYINAIOONLYCHECKED", (rdr->cacheex.lg_only_in_aio_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&rdr->cacheex.lg_only_in_tab); + tpl_addVar(vars, TPLADD, "LGONLYINTAB", value); + free_mk_t(value); + + value = mk_t_caidvaluetab(&rdr->cacheex.cacheex_nopushafter_tab); + tpl_addVar(vars, TPLADD, "CACHEEXNOPUSHAFTER", value); + free_mk_t(value); +#endif +#endif + + // BoxID + if(rdr->boxid) + { tpl_printf(vars, TPLADD, "BOXID", "%08X", rdr->boxid); } + +#ifdef READER_VIDEOGUARD + // Filt 07 + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FIX07CHECKED", (rdr->fix_07 == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FIX07VALUE", (rdr->fix_07 == 1) ? "1" : "0"); + } + + + // Fix 9993 + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FIX9993CHECKED", (rdr->fix_9993 == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FIX9993VALUE", (rdr->fix_9993 == 1) ? "1" : "0"); + } +#endif + + // Drop CWs with wrong checksum: + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DROPBADCWSCHECKED", (rdr->dropbadcws == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DROPBADCWSVALUE", (rdr->dropbadcws == 1) ? "1" : "0"); + } + + // Disable CWs checksum test: + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DISABLECRCCWSCHECKED", (rdr->disablecrccws == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DISABLECRCCWSVALUE", (rdr->disablecrccws == 1) ? "1" : "0"); + } + +#ifdef WITH_CARDREADER + // Set reader to use GPIO + if(!apicall) + { + tpl_addVar(vars, TPLADD, "USE_GPIOCHECKED", rdr->use_gpio ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "USE_GPIOVALUE", rdr->use_gpio ? "1" : "0"); + } +#endif + + // AUdisabled + if(!apicall) + { + tpl_addVar(vars, TPLADD, "AUDISABLED", (rdr->audisabled == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "AUDISABLEDVALUE", (rdr->audisabled == 1) ? "1" : "0"); + } + + // AUprovid + if(rdr->auprovid) + { tpl_printf(vars, TPLADD, "AUPROVID", "%06X", rdr->auprovid); } + + if(rdr->ecmnotfoundlimit) + { tpl_printf(vars, TPLADD, "ECMNOTFOUNDLIMIT", "%u", rdr->ecmnotfoundlimit); } + +#ifdef READER_IRDETO + // Force Irdeto + if(!apicall) + { + tpl_addVar(vars, TPLADD, "FORCEIRDETOCHECKED", (rdr->force_irdeto == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "FORCEIRDETOVALUE", (rdr->force_irdeto == 1) ? "1" : "0"); + } +#endif + +#ifdef READER_CRYPTOWORKS + // needsglobalfirst + if(!apicall) + { + tpl_addVar(vars, TPLADD, "NEEDSGLOBALFIRST", (rdr->needsglobalfirst == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "NEEDSGLOBALFIRST", (rdr->needsglobalfirst == 1) ? "1" : "0"); + } +#endif +#ifdef WITH_CARDREADER + // RSA Key + for(i = 0; i < rdr->rsa_mod_length; i++) + { tpl_printf(vars, TPLAPPEND, "RSAKEY", "%02X", rdr->rsa_mod[i]); } + + // 3DES Key + for(i = 0; i < rdr->des_key_length; i++) + { tpl_printf(vars, TPLAPPEND, "DESKEY", "%02X", rdr->des_key[i]); } + + // BoxKey + for(i = 0; i < rdr->boxkey_length ; i++) + { tpl_printf(vars, TPLAPPEND, "BOXKEY", "%02X", rdr->boxkey[i]); } +#endif +#ifdef READER_CONAX + for(i = 0; i < rdr->cwpk_mod_length; i++) + { tpl_printf(vars, TPLAPPEND, "CWPKKEY", "%02X", rdr->cwpk_mod[i]); } +#endif +#ifdef READER_NAGRA + // nuid (CAK6.3) + for(i = 0; i < rdr->cak63nuid_length; i++) + { tpl_printf(vars, TPLAPPEND, "CAK63NUID", "%02X", rdr->cak63nuid[i]); } + + // cwekey (CAK6.3) + for(i = 0; i < rdr->cak63cwekey_length; i++) + { tpl_printf(vars, TPLAPPEND, "CAK63CWEKEY", "%02X", rdr->cak63cwekey[i]); } +#endif + +#ifdef READER_NAGRA_MERLIN + int32_t j; + + // idird (CAK7) + for(i = 0; i < rdr->idird_length; i++) + { tpl_printf(vars, TPLAPPEND, "IDIRD", "%02X", rdr->idird[i]); } + + // cmd0e_provider (CAK7) + for(i = 0; i < rdr->cmd0eprov_length; i++) + { tpl_printf(vars, TPLAPPEND, "CMD0EPROV", "%02X", rdr->cmd0eprov[i]); } + + // mod1 (CAK7) + for(i = 0; i < rdr->mod1_length ; i++) + { tpl_printf(vars, TPLAPPEND, "MOD1", "%02X", rdr->mod1[i]); } + + // mod2 (CAK7) + for(i = 0; i < rdr->mod2_length ; i++) + { tpl_printf(vars, TPLAPPEND, "MOD2", "%02X", rdr->mod2[i]); } + + // key3588 (CAK7) + for(i = 0; i < rdr->key3588_length; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3588", "%02X", rdr->key3588[i]); } + + // key3310 (CAK7) + for(i = 0; i < rdr->key3310_length; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3310", "%02X", rdr->key3310[i]); } + + // key3460 (CAK7) + for(i = 0; i < rdr->key3460_length; i++) + { tpl_printf(vars, TPLAPPEND, "KEY3460", "%02X", rdr->key3460[i]); } + + // data50 (CAK7) + for(i = 0; i < rdr->data50_length; i++) + { tpl_printf(vars, TPLAPPEND, "DATA50", "%02X", rdr->data50[i]); } + + // mod50 (CAK7) + for(i = 0; i < rdr->mod50_length; i++) + { tpl_printf(vars, TPLAPPEND, "MOD50", "%02X", rdr->mod50[i]); } + + // nuid (CAK7) + for(i = 0; i < rdr->nuid_length; i++) + { tpl_printf(vars, TPLAPPEND, "NUID", "%02X", rdr->nuid[i]); } + + // OTP CSC (CAK7) + for(i = 0; i < rdr->otpcsc_length; i++) + { tpl_printf(vars, TPLAPPEND, "OTPCSC", "%02X", rdr->otpcsc[i]); } + + // OTA CSC (CAK7) + for(i = 0; i < rdr->otacsc_length; i++) + { tpl_printf(vars, TPLAPPEND, "OTACSC", "%02X", rdr->otacsc[i]); } + + // Force Pairing Type (CAK7) + for(i = 0; i < rdr->forcepair_length; i++) + { tpl_printf(vars, TPLAPPEND, "FORCEPAIR", "%02X", rdr->forcepair[i]); } + + // cwekeys (CAK7) + for(j = 0; j < 17; j++) + { + char key[9] = "CWEKEY"; + if(j > 9) + { + key[6] = '1'; + key[7] = '0' + (j - 10); + } + else + { + key[6] = '0' + j; + } + for(i = 0; i < rdr->cwekey_length[j]; i++) + { tpl_printf(vars, TPLAPPEND, key, "%02X", rdr->cwekey[j][i]); } + } + + // force_cw_swap + if(rdr->forcecwswap) + { tpl_addVar(vars, TPLADD, "FORCECWSWAPCHECKED", "checked"); } + + // only_even_SA + if(rdr->evensa) + { tpl_addVar(vars, TPLADD, "EVENSACHECKED", "checked"); } + + // force_EMM_82 + if(rdr->forceemmg) + { tpl_addVar(vars, TPLADD, "FORCEEMMGCHECKED", "checked"); } + + // OTA_CWPKs + if(rdr->cwpkota) + { tpl_addVar(vars, TPLADD, "CWPKOTACHECKED", "checked"); } + + tpl_printf(vars, TPLADD, "TMP", "NAGRACAK7HEADERMODE%d", rdr->headermode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + // CWPK CaID (CAK7) + for(i = 0; i < rdr->cwpkcaid_length ; i++) + { tpl_printf(vars, TPLAPPEND, "CWPKCAID", "%02X", rdr->cwpkcaid[i]); } + + // cak7_mode + if(rdr->cak7_mode) + { tpl_addVar(vars, TPLADD, "NAGRACAK7MODECHECKED", "checked"); } +#endif + +#ifdef READER_VIDEOGUARD + tpl_printf(vars, TPLADD, "TMP", "CARDSTARTDATEBASEMONTH%d", rdr->card_startdate_basemonth); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "CARDSTARTDATEBASEYEAR", "%d", rdr->card_startdate_baseyear); + + tpl_printf(vars, TPLADD, "TMP", "CARDEXPIREDATEBASEMONTH%d", rdr->card_expiredate_basemonth); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "CARDEXPIREDATEBASEYEAR", "%d", rdr->card_expiredate_baseyear); + + // ins7E + if(rdr->ins7E[0x1A]) + { + for(i = 0; i < 26 ; i++) { tpl_printf(vars, TPLAPPEND, "INS7E", "%02X", rdr->ins7E[i]); } + } + // ins42 + if(rdr->ins42[0x25]) + { + for(i = 0; i < 37 ; i++) { tpl_printf(vars, TPLAPPEND, "INS42", "%02X", rdr->ins42[i]); } + } + + // ins7E11 + if(rdr->ins7E11[0x01]) + { + tpl_printf(vars, TPLAPPEND, "INS7E11", "%02X", rdr->ins7E11[0]); + } + + // ins2e06 + if(rdr->ins2e06[0x04]) + { + for(i = 0; i < 4 ; i++) { tpl_printf(vars, TPLAPPEND, "INS2E06", "%02X", rdr->ins2e06[i]); } + } + + // k1 for generic pairing mode + if(rdr->k1_generic[0x10]) + { + for(i = 0; i < rdr->k1_generic[0x10] ; i++) { tpl_printf(vars, TPLAPPEND, "K1_GENERIC", "%02X", rdr->k1_generic[i]); } + } + + // k1 for unique pairing mode + if(rdr->k1_unique[0x10]) + { + for(i = 0; i < rdr->k1_unique[0x10] ; i++) { tpl_printf(vars, TPLAPPEND, "K1_UNIQUE", "%02X", rdr->k1_unique[i]); } + } +#endif + +#ifdef WITH_CARDREADER + // ATR + if(rdr->atr[0]) + for(i = 0; i < rdr->atrlen / 2; i++) + { tpl_printf(vars, TPLAPPEND, "ATR", "%02X", rdr->atr[i]); } +#endif + + // ECM Whitelist + value = mk_t_ecm_whitelist(&rdr->ecm_whitelist); + tpl_addVar(vars, TPLADD, "ECMWHITELIST", value); + free_mk_t(value); + + // ECM Header Whitelist + value = mk_t_ecm_hdr_whitelist(&rdr->ecm_hdr_whitelist); + tpl_addVar(vars, TPLADD, "ECMHEADERWHITELIST", value); + free_mk_t(value); + +#ifdef WITH_CARDREADER + // Deprecated + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DEPRECATEDCHECKED", (rdr->deprecated == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "DEPRECATEDVALUE", (rdr->deprecated == 1) ? "1" : "0"); + } + + // Smargopatch + if(!apicall) + { + tpl_addVar(vars, TPLADD, "SMARGOPATCHCHECKED", (rdr->smargopatch == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "SMARGOPATCHVALUE", (rdr->smargopatch == 1) ? "1" : "0"); + } + + // Autospeed + if(!apicall) + { + tpl_addVar(vars, TPLADD, "AUTOSPEEDCHECKED", (rdr->autospeed == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "AUTOSPEEDVALUE", (rdr->autospeed == 1) ? "1" : "0"); + } + // sc8in1 dtrrts patch + if(!apicall) + { + tpl_addVar(vars, TPLADD, "SC8IN1DTRRTSPATCHCHECKED", (rdr->sc8in1_dtrrts_patch == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "SC8IN1DTRRTSPATCHVALUE", (rdr->sc8in1_dtrrts_patch == 1) ? "1" : "0"); + } + +#ifdef READER_VIACCESS + if(!apicall) + { + tpl_addVar(vars, TPLADD, "READOLDCLASSES", (rdr->read_old_classes == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "READOLDCLASSES", (rdr->read_old_classes == 1) ? "1" : "0"); + } +#endif + + // Detect + if(rdr->detect & 0x80) + { tpl_printf(vars, TPLADD, "DETECT", "!%s", RDR_CD_TXT[rdr->detect & 0x7f]); } + else + { tpl_addVar(vars, TPLADD, "DETECT", RDR_CD_TXT[rdr->detect & 0x7f]); } +#endif + + // Ratelimit + if(rdr->ratelimitecm) + { + tpl_printf(vars, TPLADD, "RATELIMITECM", "%d", rdr->ratelimitecm); + // ECMUNIQUE + if(!apicall) + { + tpl_addVar(vars, TPLADD, "ECMUNIQUECHECKED", (rdr->ecmunique == 1) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "ECMUNIQUE", (rdr->ecmunique == 1) ? "1" : "0"); + } + tpl_printf(vars, TPLADD, "RATELIMITTIME", "%d", rdr->ratelimittime); + tpl_printf(vars, TPLADD, "SRVIDHOLDTIME", "%d", rdr->srvidholdtime); + } + // Cooldown + if(rdr->cooldown[0] && rdr->cooldown[1]) + { + tpl_printf(vars, TPLADD, "COOLDOWNDELAY", "%d", rdr->cooldown[0]); + tpl_printf(vars, TPLADD, "COOLDOWNTIME", "%d", rdr->cooldown[1]); + } + // Max parallel services + tpl_printf(vars, TPLADD, "MAXPARALLEL", "%d", rdr->maxparallel); + // Parallelfactor: format as float with dot (comma would break URL parameters) + tpl_printf(vars, TPLADD, "PARALLELFACTOR", "%.1f", rdr->parallelfactor >= 0 ? rdr->parallelfactor : 2.0); + tpl_printf(vars, TPLADD, "PARALLELTIMEOUT", "%d", rdr->paralleltimeout); +#ifdef WITH_CARDREADER + // Frequencies + tpl_printf(vars, TPLADD, "MHZ", "%d", rdr->mhz); + tpl_printf(vars, TPLADD, "CARDMHZ", "%d", rdr->cardmhz); +#endif + + // Device + if(!apicall) + { + tpl_addVar(vars, TPLADD, "DEVICE", xml_encode(vars, rdr->device)); + } + else + { + tpl_addVar(vars, TPLADD, "DEVICE", rdr->device); + } + + if(rdr->r_port) + { tpl_printf(vars, TPLAPPEND, "DEVICE", ",%d", rdr->r_port); } + if(rdr->l_port) + { + if(rdr->r_port) + { tpl_printf(vars, TPLAPPEND, "DEVICE", ",%d", rdr->l_port); } + else + { tpl_printf(vars, TPLAPPEND, "DEVICE", ",,%d", rdr->l_port); } + } + + // Group + value = mk_t_group(rdr->grp); + tpl_addVar(vars, TPLADD, "GRP", value); + free_mk_t(value); + +#ifdef WITH_LB + if(rdr->lb_weight) + { tpl_printf(vars, TPLADD, "LBWEIGHT", "%d", rdr->lb_weight); } +#endif + + //services + if(!apicall) + { + struct s_sidtab *sidtab = cfg.sidtab; + //build matrix + i = 0; + while(sidtab != NULL) + { + tpl_addVar(vars, TPLADD, "SIDLABEL", xml_encode(vars, sidtab->label)); + if(rdr->sidtabs.ok & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "READERCONFIGSIDOKBIT")); + if(rdr->sidtabs.no & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "READERCONFIGSIDNOBIT")); + if(rdr->lb_sidtabs.ok & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "READERCONFIGSIDLBOKBIT")); + sidtab = sidtab->next; + i++; + } + if(i){ + tpl_addVar(vars, TPLADD, "READERCONFIGSIDINS", tpl_getTpl(vars, "READERCONFIGSID")); + } + } + else + { + value = mk_t_service(&rdr->sidtabs); + if(cs_strlen(value) > 0) + { tpl_addVar(vars, TPLADD, "SERVICES", value); } + free_mk_t(value); + } + + // CAID + value = mk_t_caidtab(&rdr->ctab); + tpl_addVar(vars, TPLADD, "CAIDS", value); + free_mk_t(value); + +#ifdef READER_VIACCESS + // AESkeys + value = mk_t_aeskeys(rdr); + tpl_addVar(vars, TPLADD, "AESKEYS", value); + free_mk_t(value); +#endif + + //ident + value = mk_t_ftab(&rdr->ftab); + tpl_addVar(vars, TPLADD, "IDENTS", value); + free_mk_t(value); + + //CHID + value = mk_t_ftab(&rdr->fchid); + tpl_addVar(vars, TPLADD, "CHIDS", value); + free_mk_t(value); + + //Local cards + value = mk_t_ftab(&rdr->localcards); + tpl_addVar(vars, TPLADD, "LOCALCARDS", value); + free_mk_t(value); + + //class + value = mk_t_cltab(&rdr->cltab); + tpl_addVar(vars, TPLADD, "CLASS", value); + free_mk_t(value); + + if(rdr->cachemm || rdr->logemm) + { tpl_printf(vars, TPLADD, "EMMCACHE", "%d,%d,%d,%d", rdr->cachemm, rdr->rewritemm, rdr->logemm, rdr->deviceemm); } + + //savenano + value = mk_t_nano(rdr->s_nano); + tpl_addVar(vars, TPLADD, "SAVENANO", value); + free_mk_t(value); + + //blocknano + value = mk_t_nano(rdr->b_nano); + tpl_addVar(vars, TPLADD, "BLOCKNANO", value); + free_mk_t(value); + + // Blocke EMM + if(!apicall) + { + tpl_addVar(vars, TPLADD, "BLOCKEMMUNKNOWNCHK", (rdr->blockemm & EMM_UNKNOWN) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKEMMUNIQCHK", (rdr->blockemm & EMM_UNIQUE) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKEMMSHAREDCHK", (rdr->blockemm & EMM_SHARED) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "BLOCKEMMGLOBALCHK", (rdr->blockemm & EMM_GLOBAL) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "BLOCKEMMUNKNOWNVALUE", (rdr->blockemm & EMM_UNKNOWN) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "BLOCKEMMUNIQVALUE", (rdr->blockemm & EMM_UNIQUE) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "BLOCKEMMSHAREDVALUE", (rdr->blockemm & EMM_SHARED) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "BLOCKEMMGLOBALVALUE", (rdr->blockemm & EMM_GLOBAL) ? "1" : "0"); + } + + // Save EMM + if(!apicall) + { + tpl_addVar(vars, TPLADD, "SAVEEMMUNKNOWNCHK", (rdr->saveemm & EMM_UNKNOWN) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "SAVEEMMUNIQCHK", (rdr->saveemm & EMM_UNIQUE) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "SAVEEMMSHAREDCHK", (rdr->saveemm & EMM_SHARED) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "SAVEEMMGLOBALCHK", (rdr->saveemm & EMM_GLOBAL) ? "checked" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "SAVEEMMUNKNOWNVALUE", (rdr->saveemm & EMM_UNKNOWN) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "SAVEEMMUNIQVALUE", (rdr->saveemm & EMM_UNIQUE) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "SAVEEMMSHAREDVALUE", (rdr->saveemm & EMM_SHARED) ? "1" : "0"); + tpl_addVar(vars, TPLADD, "SAVEEMMGLOBALVALUE", (rdr->saveemm & EMM_GLOBAL) ? "1" : "0"); + } + + value = mk_t_emmbylen(rdr); + if(cs_strlen(value) > 0) + { tpl_addVar(vars, TPLADD, "BLOCKEMMBYLEN", value); } + free_mk_t(value); + +#ifdef MODULE_CCCAM + if(!strcmp(rdr->cc_version, "2.0.11")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED0", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.1")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED1", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.2")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED2", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.3")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED3", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.1.4")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED4", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.2.0")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED5", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.2.1")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED6", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.3.0")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED7", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.3.1")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED8", "selected"); + } + else if(!strcmp(rdr->cc_version, "2.3.2")) + { + tpl_addVar(vars, TPLADD, "CCCVERSIONSELECTED9", "selected"); + } +#endif + +#ifdef READER_VIDEOGUARD + tpl_printf(vars, TPLADD, "TMP", "NDSVERSION%d", rdr->ndsversion); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_printf(vars, TPLADD, "TMP", "NDSREADTIERS%d", rdr->readtiers); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); +#endif +#ifdef READER_NAGRA + tpl_printf(vars, TPLADD, "TMP", "NAGRAREAD%d", rdr->nagra_read); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + if(rdr->detect_seca_nagra_tunneled_card) + { tpl_addVar(vars, TPLADD, "NAGRADETECTSECACARDCHECKED", "checked"); } +#endif +#ifdef READER_TONGFANG + if(rdr->tongfang3_calibsn) + { tpl_printf(vars, TPLADD, "TONGFANGCALIBSN", "%08X", rdr->tongfang3_calibsn); } + + if(rdr->tongfang_boxid) + { tpl_printf(vars, TPLADD, "TONGFANGBOXID", "%08X", rdr->tongfang_boxid); } + + if(rdr->tongfang3_deskey_length > 0) + { + for(i = 0; i < rdr->tongfang3_deskey_length ; i++) + { + tpl_printf(vars, TPLAPPEND, "TONGFANGDESKEY", "%02X", rdr->tongfang3_deskey[i]); + } + } + + if(rdr->stbid_length > 0) + { + for(i = 0; i < rdr->stbid_length ; i++) + { + tpl_printf(vars, TPLAPPEND, "STBID", "%02X", rdr->stbid[i]); + } + } + +#endif + +#ifdef MODULE_CCCAM + tpl_printf(vars, TPLADD, "CCCMAXHOPS", "%d", rdr->cc_maxhops); + tpl_printf(vars, TPLADD, "CCCMINDOWN", "%d", rdr->cc_mindown); + tpl_printf(vars, TPLADD, "CCCRESHARE", "%d", rdr->cc_reshare); + tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare); + tpl_printf(vars, TPLADD, "CCCRECONNECT", "%d", rdr->cc_reconnect); + + if(rdr->cc_keepalive) + { tpl_addVar(vars, TPLADD, "KEEPALIVECHECKED", "checked"); } +#endif + +#ifdef MODULE_GBOX + tpl_printf(vars, TPLADD, "GBOXMAXDISTANCE", "%d", rdr->gbox_maxdist); + tpl_printf(vars, TPLADD, "GBOXMAXECMSEND", "%d", rdr->gbox_maxecmsend); + tpl_printf(vars, TPLADD, "GBOXRESHARE", "%d", rdr->gbox_reshare); + tpl_printf(vars, TPLADD, "PEERGBOXID", "%04X", gbox_convert_password_to_id((uint32_t)a2i(rdr->r_pwd, 4))); + tpl_addVar(vars, TPLADD, "PEERONLSTAT", (get_peer_onl_status(gbox_convert_password_to_id((uint32_t)a2i(rdr->r_pwd, 4)))) ? "checked" : ""); + tpl_printf(vars, TPLADD, "FORCEREMM", "%d", rdr->gbox_force_remm); + tpl_printf(vars, TPLADD, "CMDHERE", rdr->send_offline_cmd ? "checked" : ""); + + if(rdr->blockemm & 0x80) + { + tpl_addVar(vars, TPLADD, "REMMCNLDCHK", "checked"); + tpl_printf(vars, TPLADD, "GBOXREMMPEER", "%04X", rdr->gbox_remm_peer); + tpl_addVar(vars, TPLADD, "REMMUNIQCHK", (rdr->blockemm & EMM_UNIQUE) ? "" : "checked"); + tpl_addVar(vars, TPLADD, "REMMSHAREDCHK", (rdr->blockemm & EMM_SHARED) ? "" : "checked"); + tpl_addVar(vars, TPLADD, "REMMGLOBALCHK", (rdr->blockemm & EMM_GLOBAL) ? "" : "checked"); + tpl_addVar(vars, TPLADD, "REMMEMMUNKNOWNCHK", (rdr->blockemm & EMM_UNKNOWN) ? "" : "checked"); + } + if(rdr->gbox_gsms_peer) + { + tpl_printf(vars, TPLADD, "LASTGSMS", "%s", rdr->last_gsms); + tpl_printf(vars, TPLADD, "GBOXGSMSPEER", "%04X", rdr->gbox_gsms_peer); + } +#endif + +#ifdef READER_DRECAS + tpl_addVar(vars, TPLADD, "STMKEYS", rdr->stmkeys); +#endif + +#if defined(READER_DRE) || defined(READER_DRECAS) + tpl_addVar(vars, TPLADD, "USERSCRIPT", rdr->userscript); +#endif + + tpl_addVar(vars, TPLADD, "PROTOCOL", reader_get_type_desc(rdr, 0)); + + // Show only parameters which needed for the reader + switch(rdr->typ) + { + case R_CONSTCW: + case R_DB2COM1: + case R_DB2COM2: + case R_MOUSE : + case R_DRECAS : + case R_MP35: + case R_SC8in1 : + case R_SMART : + case R_INTERNAL: + case R_SERIAL : + case R_PCSC : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGSTDHWREADERBIT")); + break; + case R_CAMD35 : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCAMD35BIT")); + break; + case R_CS378X : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCS378XBIT")); + break; + case R_RADEGAST: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGRADEGASTBIT")); + break; + case R_SCAM: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGSCAMBIT")); + break; + case R_GHTTP: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGGHTTPBIT")); + break; + case R_GBOX: + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGGBOXBIT")); +#if defined (MODULE_CCCAM) && defined (MODULE_GBOX) + if(cfg.cc_gbx_reshare_en) + { + tpl_printf(vars, TPLADD, "GBOXCCCAMRESHARE", "%d", rdr->gbox_cccam_reshare); + value = mk_t_ftab(&rdr->ccc_gbx_reshare_ident); + tpl_addVar(vars, TPLADD, "CCCGBXRESHAREIDENT", value); + free_mk_t(value); + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "GBOXCCCAMRESHAREBIT")); + } +#endif + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERINFOGBOXREMM")); + break; + case R_NEWCAMD: + if(rdr->ncd_proto == NCD_525) + { + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGNCD525BIT")); + } + else if(rdr->ncd_proto == NCD_524) + { + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGNCD524BIT")); + } + break; +#ifdef MODULE_CCCAM + case R_CCCAM : + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGCCCAMBIT")); + break; +#endif + default : + tpl_addMsg(vars, "Error: protocol not resolvable"); + break; + + } + +#ifdef MODULE_CCCAM + if(rdr->typ != R_CCCAM && rdr->typ != R_GBOX) + { + tpl_printf(vars, TPLADD, "CCCHOP", "%d", rdr->cc_hop); + tpl_addVar(vars, TPLAPPEND, "READERDEPENDINGCONFIG", tpl_getTpl(vars, "READERCONFIGHOPBIT")); + } +#endif + +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "READERCONFIGAIO"); +#else + return tpl_getTpl(vars, "READERCONFIG"); +#endif +} + +static char *send_oscam_reader_stats(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + + int8_t error; + struct s_client *cl = NULL; + struct s_reader *rdr; + + rdr = get_reader_by_label(getParam(params, "label")); + error = (rdr ? 0 : 1); + + if(!error && rdr) + { + cl = rdr->client; + error = (cl ? 0 : 1); + } + + if(error) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROW", tpl_getTpl(vars, "READERSTATSROWBIT")); + if(!apicall) + { return tpl_getTpl(vars, "READERSTATS"); } + else + { return tpl_getTpl(vars, "APIREADERSTATS"); } + } + +#ifdef WITH_LB + char *stxt[] = {"found", "cache1", "cache2", "cache3", + "not found", "timeout", "sleeping", + "fake", "invalid", "corrupt", "no card", "expdate", + "disabled", "stopped" + }; + + if(strcmp(getParam(params, "action"), "resetstat") == 0) + { + char *rcs = getParam(params, "rc"); + int32_t retval = 0; + if(cs_strlen(rcs) > 0) + { + int8_t rc; + rc = atoi(rcs); + retval = clean_stat_by_rc(rdr, rc, 0); + cs_log("Reader %s stats %d %s entr%s deleted by WebIF from %s", + rdr->label, retval, stxt[rc], + retval == 1 ? "y" : "ies", + cs_inet_ntoa(GET_IP())); + } + else + { + clear_reader_stat(rdr); + cs_log("Reader %s stats resetted by WebIF from %s", rdr->label, cs_inet_ntoa(GET_IP())); + } + + } + + if(strcmp(getParam(params, "action"), "deleterecord") == 0) + { + char *record = getParam(params, "record"); + if(cs_strlen(record) > 0) + { + int32_t retval = 0; + uint32_t caid, provid, sid, cid, len; + sscanf(record, "%4x@%6x:%4x:%4x:%4x", &caid, &provid, &sid, &cid, &len); + retval = clean_stat_by_id(rdr, caid, provid, sid, cid, len); + cs_log("Reader %s stats %d entr%s deleted by WebIF from %s", + rdr->label, retval, + retval == 1 ? "y" : "ies", + cs_inet_ntoa(GET_IP())); + } + } + + if(strcmp(getParam(params, "action"), "updateecmlen") == 0) + { + update_ecmlen_from_stat(rdr); + write_server(); + } + +#endif + + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "LABEL", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "ENCODEDLABEL", urlencode(vars, rdr->label)); + + if(apicall) + { + int32_t i, emmcount = 0; + char *ttxt[] = {"unknown", "unique", "shared", "global"}; + + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "error"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmerror[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmerror[i]; + tpl_printf(vars, TPLADD, "TOTALERROR", "%d", emmcount); + } + emmcount = 0; + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "written"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmwritten[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmwritten[i]; + tpl_printf(vars, TPLADD, "TOTALWRITTEN", "%d", emmcount); + } + emmcount = 0; + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "skipped"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmskipped[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmskipped[i]; + tpl_printf(vars, TPLADD, "TOTALSKIPPED", "%d", emmcount); + } + emmcount = 0; + for(i = 0; i < 4; i++) + { + tpl_addVar(vars, TPLADD, "EMMRESULT", "blocked"); + tpl_addVar(vars, TPLADD, "EMMTYPE", ttxt[i]); + tpl_printf(vars, TPLADD, "EMMCOUNT", "%d", rdr->emmblocked[i]); + tpl_addVar(vars, TPLAPPEND, "EMMSTATS", tpl_getTpl(vars, "APIREADERSTATSEMMBIT")); + emmcount += rdr->emmblocked[i]; + tpl_printf(vars, TPLADD, "TOTALBLOCKED", "%d", emmcount); + } + } + + if(apicall) + { + char *txt = "UNDEF"; + switch(rdr->card_status) + { + case NO_CARD: + if(rdr->typ == R_GBOX) + { txt = "ONL no crd"; } + else + { txt = "OFF"; } + break; + case UNKNOWN: + txt = "UNKNOWN"; + break; + case READER_DEVICE_ERROR: + txt = "READER DEVICE ERROR"; + break; + case CARD_NEED_INIT: + if(rdr->typ == R_GBOX) + { txt = "OFFLINE"; } + else + { txt = "NEEDINIT"; } + break; + case CARD_INSERTED: + if(cl->typ == 'p') + { + if(rdr->typ == R_GBOX) + { txt = "ONL w/crd"; } + else + { txt = "CONNECTED"; } + } + else + { txt = "CARDOK"; } + break; + case CARD_FAILURE: + txt = "ERROR"; + break; + default: + txt = "UNDEF"; + } + tpl_addVar(vars, TPLADD, "READERSTATUS", txt); + tpl_printf(vars, TPLADD, "READERCAID", "%04X", rdr->caid); + } + + int32_t rowcount = 0; + uint64_t ecmcount = 0; + time_t lastaccess = 0; + +#ifdef WITH_LB + int32_t rc2hide = (-1); + if(cs_strlen(getParam(params, "hide")) > 0) + { rc2hide = atoi(getParam(params, "hide")); } + + int32_t rc2show = (-1); + if(cs_strlen(getParam(params, "show")) > 0) + { rc2show = atoi(getParam(params, "show")); } + + if(rdr->lb_stat) + { + int32_t statsize; + // @todo alno: sort by click, 0=ascending, 1=descending (maybe two buttons or reverse on second click) + READER_STAT **statarray = get_sorted_stat_copy(rdr, 0, &statsize); + char channame[CS_SERVICENAME_SIZE]; + for(; rowcount < statsize; ++rowcount) + { + READER_STAT *s = statarray[rowcount]; + if(!(s->rc == rc2hide) && ((rc2show == -1) || (s->rc == rc2show))) + { + struct tm lt; + localtime_r(&s->last_received.time, <); // fixme we need walltime! + ecmcount += s->ecm_count; + if(!apicall) + { + tpl_printf(vars, TPLADD, "CHANNEL", "%04X@%06X:%04X:%04X", s->caid, s->prid, s->srvid, s->chid); + tpl_addVar(vars, TPLADD, "CHANNELNAME", xml_encode(vars, get_servicename(cur_client(), s->srvid, s->prid, s->caid, channame, sizeof(channame)))); + tpl_printf(vars, TPLADD, "ECMLEN", "%04hX", s->ecmlen); + tpl_addVar(vars, TPLADD, "RC", stxt[s->rc]); + tpl_printf(vars, TPLADD, "TIME", PRINTF_LOCAL_D " ms", s->time_avg); + if(s->time_stat[s->time_idx]) + { tpl_printf(vars, TPLADD, "TIMELAST", PRINTF_LOCAL_D " ms", s->time_stat[s->time_idx]); } + else + { tpl_addVar(vars, TPLADD, "TIMELAST", ""); } + tpl_printf(vars, TPLADD, "COUNT", PRINTF_LOCAL_D, s->ecm_count); + + if(s->last_received.time) + { + tpl_printf(vars, TPLADD, "LAST", "%02d.%02d.%02d %02d:%02d:%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100, lt.tm_hour, lt.tm_min, lt.tm_sec); + + } + else + { + tpl_addVar(vars, TPLADD, "LAST", "never"); + } + } + else + { + tpl_printf(vars, TPLADD, "ECMCAID", "%04X", s->caid); + tpl_printf(vars, TPLADD, "ECMPROVID", "%06X", s->prid); + tpl_printf(vars, TPLADD, "ECMSRVID", "%04X", s->srvid); + tpl_printf(vars, TPLADD, "ECMLEN", "%04hX", s->ecmlen); + tpl_addVar(vars, TPLADD, "ECMCHANNELNAME", xml_encode(vars, get_servicename(cur_client(), s->srvid, s->prid, s->caid, channame, sizeof(channame)))); + tpl_printf(vars, TPLADD, "ECMTIME", PRINTF_LOCAL_D, s->time_avg); + tpl_printf(vars, TPLADD, "ECMTIMELAST", PRINTF_LOCAL_D, s->time_stat[s->time_idx]); + tpl_printf(vars, TPLADD, "ECMRC", "%d", s->rc); + tpl_addVar(vars, TPLADD, "ECMRCS", stxt[s->rc]); + if(s->last_received.time) + { + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", <); + tpl_addVar(vars, TPLADD, "ECMLAST", tbuffer); + } + else + { + tpl_addVar(vars, TPLADD, "ECMLAST", ""); + } + tpl_printf(vars, TPLADD, "ECMCOUNT", PRINTF_LOCAL_D, s->ecm_count); + + if(s->last_received.time > lastaccess) + { lastaccess = s->last_received.time; } + } + + if(!apicall) + { + if(s->rc == E_NOTFOUND) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROWNOTFOUND", tpl_getTpl(vars, "READERSTATSBIT")); + tpl_addVar(vars, TPLADD, "RESETA", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "READERSTATSNFHEADLINE", tpl_getTpl(vars, "READERSTATSROWNOTFOUNDBIT")); + } + else if(s->rc == E_TIMEOUT) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROWTIMEOUT", tpl_getTpl(vars, "READERSTATSBIT")); + tpl_addVar(vars, TPLADD, "RESETB", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "READERSTATSTOHEADLINE", tpl_getTpl(vars, "READERSTATSROWTIMEOUTBIT")); + } + else if(s->rc == E_INVALID) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATSROWINVALID", tpl_getTpl(vars, "READERSTATSBIT")); + tpl_addVar(vars, TPLADD, "RESETC", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "READERSTATSIVHEADLINE", tpl_getTpl(vars, "READERSTATSROWINVALIDBIT")); + } + else + { tpl_addVar(vars, TPLAPPEND, "READERSTATSROWFOUND", tpl_getTpl(vars, "READERSTATSBIT")); } + } + else + { + + tpl_addVar(vars, TPLAPPEND, "ECMSTATS", tpl_getTpl(vars, "APIREADERSTATSECMBIT")); + } + } + } + NULLFREE(statarray); + } + else +#endif + tpl_addVar(vars, TPLAPPEND, "READERSTATSROW", tpl_getTpl(vars, "READERSTATSNOSTATS")); + + tpl_printf(vars, TPLADD, "ROWCOUNT", "%d", rowcount); + + if(lastaccess > 0) + { + char tbuffer [30]; + struct tm lt; + localtime_r(&lastaccess, <); + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", <); + tpl_addVar(vars, TPLADD, "LASTACCESS", tbuffer); + } + else + { + tpl_addVar(vars, TPLADD, "LASTACCESS", ""); + } + + if(apicall) + { + if(cl) + { + char *value = get_ecm_historystring(cl); + tpl_addVar(vars, TPLADD, "ECMHISTORY", value); + free_mk_t(value); + } + } + + tpl_printf(vars, TPLADD, "TOTALECM", "%'" PRIu64, ecmcount); + + if(!apicall) + { return tpl_getTpl(vars, "READERSTATS"); } + else + { return tpl_getTpl(vars, "APIREADERSTATS"); } +} + +static char *send_oscam_user_config_edit(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + struct s_auth *account, *ptr, *chk; + char user[sizeof(first_client->account->usr)]; + + int32_t i; + int existing_insert = 0; + + if(!apicall) { setActiveMenu(vars, MNU_USERS); } + + if(strcmp(getParam(params, "action"), "Save As") == 0) { cs_strncpy(user, getParam(params, "newuser"), sizeof(user)); } + else { cs_strncpy(user, getParam(params, "user"), sizeof(user)); } + + account = NULL; + for(chk = cfg.account; chk != NULL; chk = chk->next) + { + if(strcmp(user, chk->usr) == 0) + { account = chk; } + if(!existing_insert) + { + tpl_printf(vars, TPLADD, "EXISTING_INS", "'%s'", urlencode(vars, chk->usr)); + existing_insert++; + } + else + { + tpl_printf(vars, TPLAPPEND, "EXISTING_INS", ",'%s'", urlencode(vars, chk->usr)); + } + } + + // Create a new user if it doesn't yet + if(account == NULL) + { + i = 1; + while(cs_strlen(user) < 1) + { + snprintf(user, sizeof(user) / sizeof(char) - 1, "NEWUSER%d", i); + for(account = cfg.account; account != NULL && strcmp(user, account->usr) != 0; account = account->next) { ; } + if(account != NULL) { user[0] = '\0'; } + ++i; + } + if(!cs_malloc(&account, sizeof(struct s_auth))) { return "0"; } + if(cfg.account == NULL) { cfg.account = account; } + else + { + for(ptr = cfg.account; ptr != NULL && ptr->next != NULL; ptr = ptr->next) { ; } + ptr->next = account; + } + account_set_defaults(account); + account->disabled = 1; + cs_strncpy((char *)account->usr, user, sizeof(account->usr)); + if(!account->grp) + { account->grp = 1; } + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + else if(strcmp(getParam(params, "action"), "Save As") == 0) { tpl_addMsg(vars, "New user has been added with cloned settings"); } + else { tpl_addMsg(vars, "New user has been added with default settings"); } + // no need to refresh anything here as the account is disabled by default and there's no client with this new account anyway! + } + + if((strcmp(getParam(params, "action"), "Save") == 0) || (strcmp(getParam(params, "action"), "Save As") == 0)) + { + char servicelabels[1024] = ""; + + for(i = 0; i < (*params).paramcount; i++) + { + if((strcmp((*params).params[i], "action")) && + (strcmp((*params).params[i], "user")) && + (strcmp((*params).params[i], "newuser")) && + (strcmp((*params).params[i], "part"))) + { + + if(!strcmp((*params).params[i], "services")) + { snprintf(servicelabels + cs_strlen(servicelabels), sizeof(servicelabels) - cs_strlen(servicelabels), "%s,", (*params).values[i]); } + else + { chk_account((*params).params[i], (*params).values[i], account); } + } + } + chk_account("services", servicelabels, account); + + refresh_oscam(REFR_CLIENTS); + + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + else if(strcmp(getParam(params, "action"), "Save As") != 0) { tpl_addMsg(vars, "User Account updated and saved"); } + } + + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, account->usr)); + tpl_addVar(vars, TPLADD, "PASSWORD", xml_encode(vars, account->pwd)); + tpl_addVar(vars, TPLADD, "DESCRIPTION", xml_encode(vars, account->description)); + + //Disabled + if(!apicall) + { + if(account->disabled) + { tpl_addVar(vars, TPLADD, "DISABLEDCHECKED", "checked"); } + } + else + { + tpl_printf(vars, TPLADD, "DISABLEDVALUE", "%d", account->disabled); + } + + //Expirationdate + struct tm timeinfo; + cs_gmtime_r(&account->expirationdate, &timeinfo); + char buf [80]; + strftime(buf, 80, "%Y-%m-%d", &timeinfo); + if(strcmp(buf, "1970-01-01")) { tpl_addVar(vars, TPLADD, "EXPDATE", buf); } + + //Allowed TimeFrame + char *allowedtf = mk_t_allowedtimeframe(account); + tpl_printf(vars, TPLADD, "ALLOWEDTIMEFRAME", "%s", allowedtf); + free_mk_t(allowedtf); + + //Group + char *value = mk_t_group(account->grp); + tpl_addVar(vars, TPLADD, "GROUPS", value); + free_mk_t(value); + + // allowed protocols + value = mk_t_allowedprotocols(account); + tpl_addVar(vars, TPLADD, "ALLOWEDPROTOCOLS", value); + free_mk_t(value); + + //Hostname + tpl_addVar(vars, TPLADD, "DYNDNS", xml_encode(vars, account->dyndns)); + + //Uniq + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "UNIQSELECTED%d", account->uniq); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "UNIQVALUE", "%d", account->uniq); + } + + //Sleep + if(!account->tosleep) { tpl_addVar(vars, TPLADD, "SLEEP", "0"); } + else { tpl_printf(vars, TPLADD, "SLEEP", "%d", account->tosleep); } + + //Monlevel selector + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "MONSELECTED%d", account->monlvl); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "MONVALUE", "%d", account->monlvl); + } + + //Au + if(account->autoau == 1) + { tpl_addVar(vars, TPLADD, "AUREADER", "1"); } + else if(account->aureader_list) + { + value = mk_t_aureader(account); + tpl_addVar(vars, TPLADD, "AUREADER", value); + free_mk_t(value); + } + + if(!apicall) + { + /* SERVICES */ + struct s_sidtab *sidtab = cfg.sidtab; + //build matrix + i = 0; + while(sidtab != NULL) + { + tpl_addVar(vars, TPLADD, "SIDLABEL", xml_encode(vars, sidtab->label)); + if(account->sidtabs.ok & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "USEREDITSIDOKBIT")); + if(account->sidtabs.no & ((SIDTABBITS)1 << i)) { tpl_addVar(vars, TPLADD, "CHECKED", "checked"); } + else { tpl_addVar(vars, TPLADD, "CHECKED", ""); } + tpl_addVar(vars, TPLAPPEND, "SIDS", tpl_getTpl(vars, "USEREDITSIDNOBIT")); + sidtab = sidtab->next; + i++; + } + if(i){ + tpl_addVar(vars, TPLADD, "USEREDITSIDINS", tpl_getTpl(vars, "USEREDITSID")); + } + } + else + { + value = mk_t_service(&account->sidtabs); + if(cs_strlen(value) > 0) + { tpl_addVar(vars, TPLADD, "SERVICES", value); } + free_mk_t(value); + } + + // CAID + value = mk_t_caidtab(&account->ctab); + tpl_addVar(vars, TPLADD, "CAIDS", value); + free_mk_t(value); + + //ident + value = mk_t_ftab(&account->ftab); + tpl_addVar(vars, TPLADD, "IDENTS", value); + free_mk_t(value); + + //CHID + value = mk_t_ftab(&account->fchid); + tpl_addVar(vars, TPLADD, "CHIDS", value); + free_mk_t(value); + + //class + value = mk_t_cltab(&account->cltab); + tpl_addVar(vars, TPLADD, "CLASS", value); + free_mk_t(value); + + //Betatunnel + value = mk_t_tuntab(&account->ttab); + tpl_addVar(vars, TPLADD, "BETATUNNELS", value); + free_mk_t(value); + + //SUPPRESSCMD08 + if(!apicall) + { + if(account->c35_suppresscmd08) + { tpl_addVar(vars, TPLADD, "SUPPRESSCMD08", "selected"); } + } + else + { + tpl_printf(vars, TPLADD, "SUPPRESSCMD08VALUE", "%d", account->c35_suppresscmd08); + } + + //Sleepsend + if(account->c35_sleepsend) + { + tpl_printf(vars, TPLADD, "SLEEPSEND", "selected"); + } + + //max_connections + tpl_printf(vars, TPLADD, "MAXCONNECTIONS", "%d", account->max_connections); + + //User Max Idle + tpl_printf(vars, TPLADD, "UMAXIDLE", "%d", account->umaxidle); + + //EMM Reassembly selector + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "EMMRSELECTED%d", account->emm_reassembly); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + } + else + { + tpl_printf(vars, TPLADD, "EMMRVALUE", "%d", account->emm_reassembly); + } + + //Prefer local cards + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "PREFERLOCALCARDS%d", account->preferlocalcards); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + char *tmp = NULL; + switch(cfg.preferlocalcards) + { + case -1: + tmp = "-1 - Use Global prefer local cards"; + break; + case 0: + tmp = "0 - local cards like proxied"; + break; + case 1: + tmp = "1 - prefer cache-ex then local cards"; + break; + case 2: + tmp = "2 - prefer local cards above cache-ex"; + break; + } + tpl_addVar(vars, TPLADD, "CFGPREFERLOCALCARDS", tmp); + } + else + { + tpl_printf(vars, TPLADD, "PREFERLOCALCARDSVALUE", "%d", account->preferlocalcards); + } + +#ifdef CS_CACHEEX + // Cacheex + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "CACHEEXSELECTED%d", account->cacheex.mode); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + } + else + { + tpl_printf(vars, TPLADD, "CACHEEX", "%d", account->cacheex.mode); + } + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP", "%d", account->cacheex.maxhop); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "CACHEEX_MAXHOP_LG", "%d", account->cacheex.maxhop_lg); +#endif + + value = mk_t_cacheex_hitvaluetab(&account->cacheex.filter_caidtab); + //if (cs_strlen(value) > 0) + tpl_printf(vars, TPLADD, "CACHEEX_ECM_FILTER", "%s", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "DCCHECKED", (account->cacheex.drop_csp == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "ARCHECKED", (account->cacheex.allow_request == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "AFCHECKED", (account->cacheex.allow_filter == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "AMCHECKED", (account->cacheex.allow_maxhop == 1) ? "checked" : ""); +#endif + tpl_addVar(vars, TPLADD, "BLOCKFAKECWSCHECKED", (account->cacheex.block_fakecws == 1) ? "checked" : ""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "USECWCHECKFORPUSHCHECKED", (account->cacheex.cw_check_for_push == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LGONLYREMOTESETTINGSCHECKED", (account->cacheex.lg_only_remote_settings == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYCHECKED", (account->cacheex.localgenerated_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&account->cacheex.lg_only_tab); + tpl_addVar(vars, TPLADD, "LGONLYTAB", value); + free_mk_t(value); + + tpl_addVar(vars, TPLADD, "LOCALGENERATEDONLYINCHECKED", (account->cacheex.localgenerated_only_in == 1) ? "checked" : ""); + + tpl_addVar(vars, TPLADD, "LGONLYINAIOONLYCHECKED", (account->cacheex.lg_only_in_aio_only == 1) ? "checked" : ""); + + value = mk_t_ftab(&account->cacheex.lg_only_in_tab); + tpl_addVar(vars, TPLADD, "LGONLYINTAB", value); + free_mk_t(value); + + value = mk_t_caidvaluetab(&account->cacheex.cacheex_nopushafter_tab); + tpl_addVar(vars, TPLADD, "CACHEEXNOPUSHAFTER", value); + free_mk_t(value); +#endif + tpl_addVar(vars, TPLADD, "NWTCHECKED", (account->no_wait_time == 1) ? "checked" : ""); + tpl_addVar(vars, TPLADD, "DISABLECRCCEX4USER", (account->disablecrccacheex == 1) ? "checked" : ""); + value = mk_t_ftab(&account->disablecrccacheex_only_for); + tpl_addVar(vars, TPLADD, "IGNCRCCEX4USERONLYFOR", value); + free_mk_t(value); + +#endif + +#ifdef CW_CYCLE_CHECK + tpl_addVar(vars, TPLADD, "USERCWCYCLEDISABLE", (account->cwc_disable == 1) ? "checked" : ""); +#endif + + //Keepalive + if(!apicall) + { + if(account->ncd_keepalive) + { tpl_addVar(vars, TPLADD, "KEEPALIVE", "checked"); } + } + else + { + tpl_printf(vars, TPLADD, "KEEPALIVEVALUE", "%d", account->ncd_keepalive); + } + +#ifdef CS_ANTICASC + tpl_printf(vars, TPLADD, "AC_USERS", "%d", account->ac_users); + tpl_printf(vars, TPLADD, "CFGNUMUSERS", "%d", cfg.ac_users); + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "PENALTY%d", account->ac_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + char *tmp = NULL; + switch(cfg.ac_penalty) + { + case 0: + tmp = "(0) Only write to log"; + break; + case 1: + tmp = "(1) NULL CW"; + break; + case 2: + tmp = "(2) Ban"; + break; + case 3: + tmp = "(3) Real CW delayed"; + break; + } + tpl_addVar(vars, TPLADD, "CFGPENALTY", tmp); + } + else + { + tpl_printf(vars, TPLADD, "PENALTYVALUE", "%d", account->ac_penalty); + } + tpl_printf(vars, TPLADD, "ACOSC_MAX_ECMS_PER_MINUTE", "%d", account->acosc_max_ecms_per_minute); + tpl_printf(vars, TPLADD, "ACOSC_MAX_ACTIVE_SIDS", "%d", account->acosc_max_active_sids); + tpl_printf(vars, TPLADD, "CFG_ACOSC_MAX_ECMS_PER_MINUTE", "%d", cfg.acosc_max_ecms_per_minute); + tpl_printf(vars, TPLADD, "CFG_ACOSC_MAX_ACTIVE_SIDS", "%d", cfg.acosc_max_active_sids); + tpl_printf(vars, TPLADD, "ACOSC_ZAP_LIMIT", "%d", account->acosc_zap_limit); + tpl_printf(vars, TPLADD, "CFG_ACOSC_ZAP_LIMIT", "%d", cfg.acosc_zap_limit); + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "ACOSC_PENALTY%d", account->acosc_penalty); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + char *tmp = NULL; + switch(cfg.acosc_penalty) + { + case -1: + tmp = "(-1) Use Global Value"; + break; + case 0: + tmp = "(0) Only write to log"; + break; + case 1: + tmp = "(1) NULL CW"; + break; + case 2: + tmp = "(2) Ban"; + break; + case 3: + tmp = "(3) CW delayed"; + break; + case 4: + tmp = "(4) Hidecards"; + break; + } + tpl_addVar(vars, TPLADD, "CFG_ACOSC_PENALTY", tmp); + } else + { + tpl_printf(vars, TPLADD, "ACOSC_PENALTYVALUE", "%d", account->acosc_penalty); + } + + tpl_printf(vars, TPLADD, "ACOSC_PENALTY_DURATION", "%d", account->acosc_penalty_duration); + tpl_printf(vars, TPLADD, "CFG_ACOSC_PENALTY_DURATION", "%d", cfg.acosc_penalty_duration); + tpl_printf(vars, TPLADD, "ACOSC_DELAY", "%d", account->acosc_delay); + tpl_printf(vars, TPLADD, "CFG_ACOSC_DELAY", "%d", cfg.acosc_delay); +#endif + +#ifdef MODULE_CCCAM + tpl_printf(vars, TPLADD, "CCCMAXHOPS", "%d", account->cccmaxhops); + tpl_printf(vars, TPLADD, "CCCRESHARE", "%d", account->cccreshare); + tpl_printf(vars, TPLADD, "RESHARE", "%d", cfg.cc_reshare); + + //CCcam Ignore Reshare + tpl_printf(vars, TPLADD, "TMP", "CCCIGNRSHRSELECTED%d", account->cccignorereshare); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + tpl_addVar(vars, TPLADD, "CFGIGNORERESHARE", + cfg.cc_ignore_reshare == 0 ? + "0 - use reshare level of Server" : "1 - use reshare level of Reader or User"); + + //CCcam Stealth Mode + tpl_printf(vars, TPLADD, "TMP", "CCCSTEALTHSELECTED%d", account->cccstealth); + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMP"), "selected"); + + tpl_addVar(vars, TPLADD, "STEALTH", cfg.cc_stealth ? "enable" : "disable"); +#endif + + //Failban + tpl_printf(vars, TPLADD, "FAILBAN", "%d", account->failban); + + if(!apicall) + { +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "USEREDITAIO"); +#else + return tpl_getTpl(vars, "USEREDIT"); +#endif + } + else + { + return tpl_getTpl(vars, "APIUSEREDIT"); + } + +} + +static void webif_add_client_proto(struct templatevars *vars, struct s_client *cl, const char *proto, int8_t apicall) +{ + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOSORT", ""); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOTITLE", ""); + tpl_addVar(vars, TPLADDONCE, "PROTOICON", ""); + if(!cl) { return; } +#ifdef MODULE_NEWCAMD + if(streq(proto, "newcamd") && cl->typ == 'c') + { + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%s (%s)", proto, newcamd_get_client_name(cl->ncd_client_id)); + tpl_printf(vars, TPLADD, "CLIENTPROTOSORT", "%s (%s)", proto, newcamd_get_client_name(cl->ncd_client_id)); + if(cfg.http_showpicons ) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s_%s", (char *)proto, newcamd_get_client_name(cl->ncd_client_id)); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "NCMDA", (char *)proto); + tpl_addVar(vars, TPLADD, "NCMDB", (char *)newcamd_get_client_name(cl->ncd_client_id)); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTONEWCAMDPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s_%s",(char *)proto, (char *)newcamd_get_client_name(cl->ncd_client_id)); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s_%s.tpl", proto, newcamd_get_client_name(cl->ncd_client_id)); + } + } + return; + } +#endif +#ifdef MODULE_CCCAM + if(strncmp(proto, "cccam", 5) == 0) + { + struct cc_data *cc = cl->cc; + if(cc && *cc->remote_version && *cc->remote_build) + { + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%s (%s-%s)", proto, cc->remote_version, cc->remote_build); + tpl_printf(vars, TPLADD, "CLIENTPROTOSORT", "%s (%s-%s)", proto, cc->remote_version, cc->remote_build); + if(cccam_client_multics_mode(cl)) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "Multics, revision r%d", cc->multics_version[0] | (cc->multics_version[1] << 8)); + } + else + { +#endif +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + if(cl->reader && cl->reader->cacheex.feature_bitfield) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->reader->cacheex.aio_version); + } + else if(cl->reader->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else if(cl->account && cl->account->cacheex.feature_bitfield) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->account->cacheex.aio_version); + } + else if(cl->account->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else + { +#endif +#endif +#ifdef MODULE_CCCAM + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", cc->extended_mode ? cc->remote_oscam : ""); +#endif +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + } +#endif +#endif +#ifdef MODULE_CCCAM + } + + if(cfg.http_showpicons) + { + char picon_name[32]; + + int8_t is_other_proto = 0; + if(cccam_client_multics_mode(cl)) { is_other_proto = 1; } + + switch(is_other_proto) + { + case 1: + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s_r_%d", proto, cc->multics_version[0] | (cc->multics_version[1] << 8)); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "CCA", (char *)proto); + tpl_addVar(vars, TPLADD, "CCB", "r"); + tpl_printf(vars, TPLADD, "CCC", "%d", cc->multics_version[0] | (cc->multics_version[1] << 8)); + tpl_addVar(vars, TPLADD, "CCD", ""); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOCCCAMPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s_r_%d",(char *)proto, cc->multics_version[0] | (cc->multics_version[1] << 8)); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "Multics, revision r%d missing icon: IC_%s_r_%d.tpl", + cc->multics_version[0] | (cc->multics_version[1] << 8), proto, cc->multics_version[0] | (cc->multics_version[1] << 8)); + } + break; + + default: + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s_%s_%s", proto, cc->remote_version, cc->remote_build); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "CCA", (char *)proto); + tpl_addVar(vars, TPLADD, "CCB", cc->remote_version); + tpl_addVar(vars, TPLADD, "CCC", cc->remote_build); +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + if(cl->reader && cl->reader->cacheex.feature_bitfield) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->reader->cacheex.aio_version); + } + else if(cl->reader->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else if(cl->account && cl->account->cacheex.feature_bitfield) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio %s]", (cc->extended_mode ? cc->remote_oscam : ""), cl->account->cacheex.aio_version); + } + else if(cl->account->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CCD", "%s [cx-aio < 9.2.3]", (cc->extended_mode ? cc->remote_oscam : "")); + } + } + else + { +#endif +#endif + tpl_addVar(vars, TPLADD, "CCD", cc->extended_mode ? cc->remote_oscam : ""); +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + } +#endif +#endif + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOCCCAMPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s_%s_%s",(char *)proto, cc->remote_version, cc->remote_build); + } + } + else + { +#endif +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + if(cl->reader && cl->reader->cacheex.feature_bitfield) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), cl->reader->cacheex.aio_version, proto, cc->remote_version, cc->remote_build); + } + else if(cl->reader->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), proto, cc->remote_version, cc->remote_build); + } + } + else if(cl->account && cl->account->cacheex.feature_bitfield) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio %s] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), cl->account->cacheex.aio_version, proto, cc->remote_version, cc->remote_build); + } + else if(cl->account->cacheex.feature_bitfield) + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s [cx-aio < 9.2.3] missing icon: IC_%s_%s_%s.tpl", (cc->extended_mode ? cc->remote_oscam : ""), proto, cc->remote_version, cc->remote_build); + } + } + else + { +#endif +#endif +#ifdef MODULE_CCCAM + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "%s missing icon: IC_%s_%s_%s.tpl", + cc->extended_mode ? cc->remote_oscam : "", proto, cc->remote_version, cc->remote_build); +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + } +#endif +#endif + } + break; + } + } + } + return; + } +#endif +#ifdef CS_CACHEEX_AIO +#if (defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP)) && defined(CS_CACHEEX) + if(strncmp(proto, "cs3", 3) == 0) + { + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", (char *)proto); + + char aiover[32]; + aiover[0] = '\0'; + + if(cl->account && cl->cacheex_aio_checked) + { + if(cl->account->cacheex.feature_bitfield & 32) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", cl->account->cacheex.aio_version); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else if(cl->account->cacheex.feature_bitfield) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", "[cx-aio: < 9.2.3]"); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", ""); + } + } + + if(cl->reader && cl->cacheex_aio_checked) + { + if(cl->reader->cacheex.feature_bitfield & 32) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", cl->reader->cacheex.aio_version); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else if(cl->reader->cacheex.feature_bitfield) + { + snprintf(aiover, sizeof(aiover) / sizeof(char) -1, "%s", "[cx-aio: < 9.2.3]"); + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", aiover); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTPROTOTITLE", ""); + } + } + + if(cfg.http_showpicons) + { + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", (char *)proto); + + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", proto); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "CAMD3A", (char *)proto); + if(aiover[0] == '\0') + tpl_addVar(vars, TPLADD, "AIOVER", ""); + else + tpl_printf(vars, TPLADD, "AIOVER", "[cx-aio %s]", aiover); + + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOCAMD3AIOPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char *)proto); + } + } + else + { + if(cl->account && cl->cacheex_aio_checked) + { + if(cl->account->cacheex.feature_bitfield & 32) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio %s]", proto, cl->account->cacheex.aio_version); + else if(cl->account->cacheex.feature_bitfield) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio < 9.2.3]", proto); + else + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl", proto); + } + + if(cl->reader && cl->cacheex_aio_checked) + { + if(cl->reader->cacheex.feature_bitfield & 32) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio %s]", proto, cl->reader->cacheex.aio_version); + else if(cl->reader->cacheex.feature_bitfield) + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl [cx-aio < 9.2.3]", proto); + else + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl", proto); + } + } + } + return; + } +#endif +#endif + +#ifdef HAVE_DVBAPI + if(streq(proto, "dvbapi") && cl->typ == 'c' && strcmp(dvbapi_get_client_name(), "")) + { + if (!apicall) + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%sclient: %s
protocol version: %d
", proto, dvbapi_get_client_name(), dvbapi_get_client_proto_version()); + else + tpl_printf(vars, TPLADD, "CLIENTPROTO", "%s (client: %s, protocol version: %d)", proto, dvbapi_get_client_name(), dvbapi_get_client_proto_version()); + tpl_printf(vars, TPLADD, "CLIENTPROTOSORT", "%s", proto); + return; + } +#endif + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTO", (char *)proto); + tpl_addVar(vars, TPLADDONCE, "CLIENTPROTOSORT", (char *)proto); + if(cfg.http_showpicons) + { + char picon_name[32]; + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", proto); + if(picon_exists(picon_name)) + { + if (!apicall) + { + tpl_addVar(vars, TPLADD, "OTHER", (char *)proto); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", tpl_getTpl(vars, "PROTOOTHERPIC")); + } + else + { + tpl_printf(vars, TPLADDONCE, "PROTOICON", "%s",(char *)proto); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTPROTOTITLE", "missing icon: IC_%s.tpl", proto); + } + } +} + +static void kill_account_thread(struct s_auth *account) +{ + struct s_client *cl; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->account == account) + { + if(get_module(cl)->type & MOD_CONN_NET) + { + kill_thread(cl); + } + else + { + cl->account = first_client->account; + } + } + } +} + +static char *send_oscam_user_config(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + struct s_auth *account; + struct s_client *cl; + char *user = getParam(params, "user"); + int32_t found = 0; + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + if(!apicall) + { + setActiveMenu(vars, MNU_USERS); + } + if(strcmp(getParam(params, "action"), "reinit") == 0) + { + if(!cfg.http_readonly) + { refresh_oscam(REFR_ACCOUNTS); } + } + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "WebIf is in readonly mode. No deletion will be made!"); + } + else + { + struct s_auth *account_prev = NULL; + + for(account = cfg.account; (account); account = account->next) + { + if(strcmp(account->usr, user) == 0) + { + if(account_prev == NULL) + { cfg.account = account->next; } + else + { account_prev->next = account->next; } + ll_clear(account->aureader_list); + kill_account_thread(account); + add_garbage(account); + found = 1; + break; + } + account_prev = account; + } + if(found > 0) + { + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + } + else { tpl_addMsg(vars, "Sorry but the specified user doesn't exist. No deletion will be made!"); } + } + } + + if((strcmp(getParam(params, "action"), "disable") == 0) || (strcmp(getParam(params, "action"), "enable") == 0)) + { + account = get_account_by_name(getParam(params, "user")); + if(account) + { + if(strcmp(getParam(params, "action"), "disable") == 0) + { + account->disabled = 1; + kill_account_thread(account); + } + else + { account->disabled = 0; } + if(write_userdb() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + } + else + { + tpl_addMsg(vars, "Sorry but the specified user doesn't exist. No deletion will be made!"); + } + } + + if(strcmp(getParam(params, "action"), "resetstats") == 0) + { + account = get_account_by_name(getParam(params, "user")); + if(account) { clear_account_stats(account); } + } + + if(strcmp(getParam(params, "action"), "resetuserstats") == 0) + { + clear_info_clients_stats(); + } + + if(strcmp(getParam(params, "action"), "resetreaderstats") == 0) + { + clear_info_readers_stats(); + } + + if(strcmp(getParam(params, "action"), "resetalluserstats") == 0) + { + clear_all_account_stats(); + } + + if((strcmp(getParam(params, "part"), "adduser") == 0) && (!cfg.http_readonly)) + { + tpl_addVar(vars, TPLAPPEND, "NEWUSERFORM", tpl_getTpl(vars, "ADDNEWUSER")); + } + + /* List accounts*/ + char *status, *expired, *classname, *lastchan; + time_t now = time((time_t *)0); + int32_t isec = 0, chsec = 0; + + char *filter = NULL; + int32_t clientcount = 0; + if(apicall) + { + filter = getParam(params, "label"); + } + int8_t grp_set = 0; + int8_t expdate_set = 0; + int32_t total_users = 0; + int32_t disabled_users = 0; + int32_t expired_users = 0; + int32_t expired_or_disabled_users = 0; + int32_t connected_users = 0; + int32_t online_users = 0; + int32_t casc_users = 0; + int32_t casc_users2 = 0; + int32_t n_request = 0; + int existing_insert = 0; + + for(account = cfg.account; (account); account = account->next) + { + if(account->expirationdate){ + expdate_set = 1; + } + + if(account->next){ + if(account->grp != account->next->grp){ + grp_set = 1; + } + } + if(expdate_set && grp_set) + break; + } + + for(account = cfg.account; (account); account = account->next) + { + //clear for next client + total_users++; + isactive = 1; + + status = "offline"; + classname = "offline"; + isec = 0; + chsec = 0; + + //reset caid/srevid template variables + tpl_addVar(vars, TPLADD, "CLIENTCAID", ""); + tpl_addVar(vars, TPLADD, "CLIENTPROVID", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVID", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNEL", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", ""); + tpl_addVar(vars, TPLADD, "USERMD5", ""); + tpl_addVar(vars, TPLADD, "CWLASTRESPONSET", ""); + tpl_addVar(vars, TPLADD, "CWLASTRESPONSETMS", ""); + tpl_addVar(vars, TPLADD, "CLIENTIP", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNELTITLE", ""); + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNELAPI", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEP", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEPAPI", ""); + tpl_addVar(vars, TPLADD, "IDLESECS", ""); + tpl_addVar(vars, TPLADD, "UNOTIFY", ""); + + if(account->expirationdate && account->expirationdate < now) + { + expired = " (expired)"; + classname = "expired"; + expired_users++; + isactive = 0; + } + else + { + expired = ""; + } + + if(account->disabled != 0) + { + expired = " (disabled)"; + classname = "disabled"; + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICENA"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Enable"); + tpl_addVar(vars, TPLADD, "SWITCH", "enable"); + disabled_users++; + isactive = 0; + } + else + { + tpl_addVar(vars, TPLADD, "SWITCHICO", "image?i=ICDIS"); + tpl_addVar(vars, TPLADD, "SWITCHTITLE", "Disable"); + tpl_addVar(vars, TPLADD, "SWITCH", "disable"); + } + if((account->expirationdate && account->expirationdate < now)||account->disabled != 0) + { + expired_or_disabled_users++; + } + + int32_t lastresponsetm = 0, latestactivity = 0; + const char *proto = ""; + double cwrate = 0.0, cwrate2 = 0.0; + + //search account in active clients + isactive = 0; + int16_t nrclients = 0; + struct s_client *latestclient = NULL; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->account && !strcmp(cl->account->usr, account->usr)) + { + if(cl->lastecm > latestactivity || cl->login > latestactivity) + { + if(cl->lastecm > cl->login) { latestactivity = cl->lastecm; } + else { latestactivity = cl->login; } + latestclient = cl; + } + nrclients++; + } + } + if(account->cwfound + account->cwnot + account->cwcache > 0) + { + cwrate = now - account->firstlogin; + cwrate /= (account->cwfound + account->cwnot + account->cwcache); + } + + casc_users = 0; + casc_users2 = 0; + int8_t conn = 0; + if(latestclient != NULL) + { + char channame[CS_SERVICENAME_SIZE]; + status = (!apicall) ? "connected" : "connected"; + if(account->expirationdate && account->expirationdate < now) { classname = "expired"; } + else { classname = "connected";conn = 1; } + + proto = client_get_proto(latestclient); + int clientcaid = latestclient->last_caid; + int clientsrvid = latestclient->last_srvid; + int clientprovid = latestclient->last_provid; + tpl_printf(vars, TPLADD, "CLIENTCAID", "%04X", clientcaid); + tpl_printf(vars, TPLADD, "CLIENTPROVID", "%06X", clientprovid); + tpl_printf(vars, TPLADD, "CLIENTSRVID", "%04X", clientsrvid); + + if(clientsrvid != NO_SRVID_VALUE || clientcaid != NO_CAID_VALUE) + { + lastchan = xml_encode(vars, get_servicename(latestclient, clientsrvid, clientprovid, clientcaid, channame, sizeof(channame))); + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", lastchan); + } + else + { + lastchan = ""; + tpl_addVar(vars, TPLADD, "LASTCHANNELSORT", "~~~~~"); + } + + tpl_addVar(vars, TPLADDONCE, "LASTCHANNEL", lastchan); + tpl_addVar(vars, TPLADD, "LASTCHANNELTITLE", lastchan); + + if(cfg.http_showpicons ) + { + char picon_name[128]; + char picon_channame[128]; + int8_t picon_ok = 0; + + get_picon_servicename_or_null(latestclient, clientsrvid, clientprovid, clientcaid, picon_channame, sizeof(picon_channame)); + if(picon_channame[0]) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%s", picon_channame); + picon_ok = picon_exists(picon_name); + + if(!picon_ok && picon_servicename_remve_hd(picon_channame, sizeof(picon_channame))) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%s", picon_channame); + picon_ok = picon_exists(picon_name); + } + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%04X_%06X_%04X", clientcaid, clientprovid, clientsrvid); + picon_ok = picon_exists(picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "%04X_%04X", clientcaid, clientsrvid); + picon_ok = picon_exists(picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char), "0000_%04X", clientsrvid); + picon_ok = picon_exists(picon_name); + } + if(picon_ok) + { + tpl_addVar(vars, TPLADDONCE, "LCA", picon_name); + tpl_addVar(vars, TPLADDONCE, "LCB", lastchan); + if(!apicall) + { + tpl_addVar(vars, TPLADDONCE, "LASTCHANNEL", tpl_getTpl(vars, "USERCONFIGLASTCHANEL")); + } + } + else + { + tpl_printf(vars, TPLADD, "LASTCHANNELTITLE", "missing icon: IC_%s.tpl", picon_name); + } + } + + lastresponsetm = latestclient->cwlastresptime; + if(IP_ISSET(latestclient->ip)) + { tpl_addVar(vars, TPLADD, "CLIENTIP", cs_inet_ntoa(latestclient->ip)); } + else if(latestclient->login > latestclient->logout) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "CLIENTIP", ""); } + connected_users++; + casc_users = ll_count(latestclient->cascadeusers); + LL_ITER it = ll_iter_create(latestclient->cascadeusers); + struct s_cascadeuser *cu; + while((cu = ll_iter_next(&it))) + { + if(cu->cwrate > 0) + { casc_users2++; } + } + if(latestactivity > 0) + { + isec = now - latestactivity; + chsec = latestclient->lastswitch ? now - latestclient->lastswitch : 0; + if(isec < cfg.hideclient_to) + { + isactive = 1; + status = (!apicall) ? "online" : "online"; + if(account->expirationdate && account->expirationdate < now) { classname = "expired"; } + else { classname = "online"; } + if(latestclient->cwfound + latestclient->cwnot + latestclient->cwcache > 0) + { + cwrate2 = now - latestclient->login; + cwrate2 /= (latestclient->cwfound + latestclient->cwnot + latestclient->cwcache); + tpl_printf(vars, TPLADDONCE, "CWRATE2", " (%.2f)", cwrate2); + online_users++; + } + } + } + } + + n_request = 0; + if(latestclient != NULL){ + n_request = latestclient->n_request[0]; + } + + tpl_addVar(vars, TPLADD, "EXPIREVIEW", expdate_set ? "" : "exp"); + tpl_addVar(vars, TPLADD, "GRPVIEW", grp_set ? "" : "grp"); +#ifdef CS_ANTICASC + tpl_addVar(vars, TPLADD, "ANTICASCVIEW", cfg.ac_enabled ? "" : "acas"); +#endif + tpl_printf(vars, TPLADD, "CWOK", PRINTF_LOCAL_D, account->cwfound); + tpl_printf(vars, TPLADD, "CWNOK", PRINTF_LOCAL_D, account->cwnot); + tpl_printf(vars, TPLADD, "CWIGN", PRINTF_LOCAL_D, account->cwignored); + tpl_printf(vars, TPLADD, "CWTOUT", PRINTF_LOCAL_D, account->cwtout); +#ifdef CW_CYCLE_CHECK + tpl_addVar(vars, TPLADD, "CWCCYCVIEW", cfg.cwcycle_check_enable ? "" : "cwc"); + tpl_printf(vars, TPLADD, "CWCYCLECHECKED", "%d", account->cwcycledchecked); + tpl_printf(vars, TPLADD, "CWCYCLEOK", "%d", account->cwcycledok); + tpl_printf(vars, TPLADD, "CWCYCLENOK", "%d", account->cwcyclednok); + tpl_printf(vars, TPLADD, "CWCYCLEIGN", "%d", account->cwcycledign); +#endif + tpl_printf(vars, TPLADD, "CWCACHE", PRINTF_LOCAL_D, account->cwcache); + tpl_printf(vars, TPLADD, "CWTUN", PRINTF_LOCAL_D, account->cwtun); + tpl_printf(vars, TPLADD, "EMMOK", PRINTF_LOCAL_D, account->emmok); + tpl_printf(vars, TPLADD, "EMMNOK", PRINTF_LOCAL_D, account->emmnok); + tpl_printf(vars, TPLADD, "CWRATE", "%.2f", cwrate); + tpl_printf(vars, TPLADD, "CASCUSERS", "%d", casc_users); + tpl_printf(vars, TPLADD, "CASCUSERS2", "%d", casc_users2); + tpl_printf(vars, TPLADD, "CASCUSERSCOMB", "%d/%d", casc_users, casc_users2); + tpl_printf(vars, TPLADD, "N_REQUEST_MIN", "%d", n_request); + + if(isactive > 0 || conn > 0) + { + if(casc_users+casc_users2>0) + { + tpl_printf(vars, TPLADD, "CWLASTRESPONSET", "%d", lastresponsetm); + tpl_printf(vars, TPLADD, "CWLASTRESPONSETMS", PRINTF_LOCAL_D " ms", lastresponsetm); + } + tpl_addVar(vars, TPLADD, "IDLESECS", sec2timeformat(vars, isec)); + } + + if(isactive > 0) + { + tpl_printf(vars, TPLADD, "CLIENTTIMEONCHANNELAPI", "%d", chsec); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", sec2timeformat(vars, chsec)); + + if(account->tosleep) + { + if(account->tosleep >0){ + tpl_printf(vars, TPLADD, "CLIENTTIMETOSLEEP", "Sleeping in %d minutes", account->tosleep - (chsec / 60)); + } else { + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEP", "Sleeping"); + } + tpl_printf(vars, TPLADD, "CLIENTTIMETOSLEEPAPI", "%d", account->tosleep - (chsec / 60)); + } else { + tpl_addVar(vars, TPLADD, "CLIENTTIMETOSLEEPAPI", "undefined"); + } + } +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CAMD35) || defined (MODULE_CAMD35_TCP) + if(latestclient != NULL && (latestclient->account->cacheex.feature_bitfield || latestclient->c35_extmode > 1)) +#else + if(latestclient != NULL && (latestclient->account->cacheex.feature_bitfield)) +#endif + { + const char *aio_suffix = " (cx-aio)"; + char *new_proto; + if(cs_malloc(&new_proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + if (!cs_strncat(new_proto, (char *)proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + if (cs_strncat(new_proto, (char *)aio_suffix, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + webif_add_client_proto(vars, latestclient, (const char *)new_proto, apicall); + } else { + cs_log("FIXME!"); + } + free(new_proto); + } + } else { +#endif + webif_add_client_proto(vars, latestclient, proto, apicall); +#ifdef CS_CACHEEX_AIO + } +#endif + + tpl_addVar(vars, TPLADD, "CLASSNAME", classname); + MD5((uint8_t *)account->usr, cs_strlen(account->usr), md5tmp); + int z; + tpl_addVar(vars, TPLADD, "USERMD5","id_"); + for (z = 0; z < MD5_DIGEST_LENGTH; z++) + { + tpl_printf(vars, TPLAPPEND, "USERMD5", "%02x", md5tmp[z]); + } + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, account->usr)); + tpl_addVar(vars, TPLADD, "USERNAMEENC", urlencode(vars, account->usr)); + if(!existing_insert) + { + tpl_printf(vars, TPLADD, "EXISTING_INS", "'%s'", urlencode(vars, account->usr)); + existing_insert++; + }else + { + tpl_printf(vars, TPLAPPEND, "EXISTING_INS", ",'%s'", urlencode(vars, account->usr)); + } + + if(account->description) + tpl_printf(vars, TPLADD, "DESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, account->description)); + else + tpl_addVar(vars, TPLADD, "DESCRIPTION", ""); + + if(cfg.http_showpicons && !apicall) + tpl_addVar(vars, TPLADD, "USERBIT", tpl_getTpl(vars, picon_exists(xml_encode(vars, account->usr)) ? "USERICON" : "USERNOICON")); + else + tpl_addVar(vars, TPLADD, "USERBIT", tpl_getTpl(vars, "USERLABEL")); + + char *value = mk_t_group(account->grp); + tpl_addVar(vars, TPLADD, "GROUPS", value); + free_mk_t(value); + tpl_addVar(vars, TPLADD, "STATUS", status); + tpl_addVar(vars, TPLAPPEND, "STATUS", expired); + + if(nrclients > 1) + { + tpl_printf(vars, TPLADD, "UNOTIFY", "%d", nrclients); + tpl_addVar(vars, TPLADDONCE, "CLIENTCOUNTNOTIFIER", tpl_getTpl(vars, "CLIENTCOUNTNOTIFIERBIT")); + } + + //Expirationdate + struct tm timeinfo; + cs_gmtime_r(&account->expirationdate, &timeinfo); + char buf [80]; + strftime(buf, 80, "%Y-%m-%d", &timeinfo); + if(strcmp(buf, "1970-01-01")) { tpl_addVar(vars, TPLADD, "EXPDATE", buf); } + else { tpl_addVar(vars, TPLADD, "EXPDATE", ""); } + + // append row to table template + if(!apicall) + { tpl_addVar(vars, TPLAPPEND, "USERCONFIGS", tpl_getTpl(vars, "USERCONFIGLISTBIT")); } + else if(!filter || strcmp(filter, account->usr) == 0 || strcmp(filter, "all") == 0 || cs_strlen(filter) == 0) + { + if(apicall == 1){ + tpl_addVar(vars, TPLAPPEND, "APIUSERCONFIGS", tpl_getTpl(vars, "APIUSERCONFIGLISTBIT")); + } else if (apicall == 2){ + tpl_printf(vars, TPLADD, "JSONDELIMITER", "%s", (total_users > 1)?",":""); + tpl_addVar(vars, TPLAPPEND, "APIUSERCONFIGS", tpl_getTpl(vars, "JSONUSERBIT")); + } + ++clientcount; + } + } + + tpl_printf(vars, TPLADD, "TOTAL_USERS", "%d", total_users); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED", "%d", disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_EXPIRED", "%d", expired_users); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE", "%d", total_users - expired_or_disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED", "%d", connected_users); + tpl_printf(vars, TPLADD, "TOTAL_ONLINE", "%d", online_users); + + //CM info + tpl_addVar(vars, TPLADD, "DISPLAYREADERINFO", "hidden"); // no readerinfo in users + set_ecm_info(vars); + + if(!apicall) + { return tpl_getTpl(vars, "USERCONFIGLIST"); } + else + { + if(!filter || clientcount > 0) + { + return tpl_getTpl(vars, (apicall==1)?"APIUSERCONFIGLIST":"JSONUSER"); + } + else + { + tpl_printf(vars, TPLADD, "APIERRORMESSAGE", "Invalid client %s", xml_encode(vars, filter)); + return tpl_getTpl(vars, "APIERROR"); + } + } + +} + +#define ENTITLEMENT_PAGE_SIZE 500 + +#ifdef MODULE_CCCAM +static void print_cards(struct templatevars *vars, struct uriparams *params, struct cc_card **cardarray, int32_t cardsize, + int8_t show_global_list, struct s_reader *rdr, int32_t offset, int32_t apicall) +{ + if(cardarray) + { + uint8_t serbuf[8]; + int32_t i, count = 0; + char provname[83]; + struct cc_card *card; + int32_t cardcount = 0; + int32_t providercount = 0; + int32_t nodecount = 0; + + char *provider = ""; + + // @todo alno: sort by click, 0=ascending, 1=descending (maybe two buttons or reverse on second click) + for(i = offset; i < cardsize; ++i) + { + card = cardarray[i]; + if(count == ENTITLEMENT_PAGE_SIZE) + { break; } + count++; + + if(!apicall) + { + if(show_global_list) + { rdr = card->origin_reader; } + if(rdr) + { tpl_printf(vars, TPLADD, "HOST", "%s:%d", xml_encode(vars, rdr->device), rdr->r_port); } + tpl_printf(vars, TPLADD, "CAID", "%04X", card->caid); + tpl_printf(vars, TPLADD, "CARDTYPE", "%02X", card->card_type); + } + else + { + tpl_printf(vars, TPLADD, "APICARDNUMBER", "%d", cardcount); + tpl_printf(vars, TPLADD, "APICAID", "%04X", card->caid); + tpl_printf(vars, TPLADD, "APICARDTYPE", "%02X", card->card_type); + } + + if(cc_UA_valid(card->hexserial)) //Add UA: + { + cc_UA_cccam2oscam(card->hexserial, serbuf, card->caid); + char tmp[20]; + tpl_printf(vars, TPLAPPEND, "HOST", "
\nUA_OSCam:%s", cs_hexdump(0, serbuf, 8, tmp, 20)); + tpl_printf(vars, TPLAPPEND, "HOST", "
\nUA_CCcam:%s", cs_hexdump(0, card->hexserial, 8, tmp, 20)); + } + if(!apicall) + { + int32_t n; + char channame[CS_SERVICENAME_SIZE]; + int8_t sidname = 0; + LL_ITER its = ll_iter_create(card->goodsids); + struct cc_srvid *srv; + n = 0; + if(strcmp(getParam(params, "button"), "Show detail list") == 0) + { sidname = 1; } + + tpl_addVar(vars, TPLADD, "SERVICESGOOD", ""); + while((srv = ll_iter_next(&its))) + { + if(sidname) + { + tpl_printf(vars, TPLAPPEND, "SERVICESGOOD", "%04X - %s
", srv->sid, xml_encode(vars, get_servicename(cur_client(), srv->sid, 0, card->caid, channame, sizeof(channame)))); + } else { + tpl_printf(vars, TPLAPPEND, "SERVICESGOOD", "%04X%s", srv->sid, ++n % 10 == 0 ? "
\n" : " "); + } + } + + its = ll_iter_create(card->badsids); + n = 0; + tpl_addVar(vars, TPLADD, "SERVICESBAD", ""); + while((srv = ll_iter_next(&its))) + { + if(sidname) + { + tpl_printf(vars, TPLAPPEND, "SERVICESBAD", "%04X - %s
", srv->sid, xml_encode(vars, get_servicename(cur_client(), srv->sid, 0, card->caid, channame, sizeof(channame)))); + } else { + tpl_printf(vars, TPLAPPEND, "SERVICESBAD", "%04X%s", srv->sid, ++n % 10 == 0 ? "
\n" : " "); + } + } + } + + tpl_addVar(vars, TPLADD, "SYSTEM", get_cardsystem_desc_by_caid(card->caid)); + + tpl_printf(vars, TPLADD, "SHAREID", "%08X", card->id); + tpl_printf(vars, TPLADD, "REMOTEID", "%08X", card->remote_id); + tpl_printf(vars, TPLADD, "UPHOPS", "%d", card->hop); + tpl_printf(vars, TPLADD, "MAXDOWN", "%d", card->reshare); + + LL_ITER pit = ll_iter_create(card->providers); + struct cc_provider *prov; + + providercount = 0; + + if(!apicall) + { tpl_addVar(vars, TPLADD, "PROVIDERS", ""); } + else + { tpl_addVar(vars, TPLADD, "PROVIDERLIST", ""); } + + while((prov = ll_iter_next(&pit))) + { + provider = xml_encode(vars, get_provider(prov->prov, card->caid, provname, sizeof(provname))); + + if(!apicall) + { + if(prov->sa[0] || prov->sa[1] || prov->sa[2] || prov->sa[3]) + { + tpl_printf(vars, TPLAPPEND, "PROVIDERS", "%s SA:%02X%02X%02X%02X
\n", provider, prov->sa[0], prov->sa[1], prov->sa[2], prov->sa[3]); + } + else + { + tpl_printf(vars, TPLAPPEND, "PROVIDERS", "%s
\n", provider); + } + } + else + { + if(prov->sa[0] || prov->sa[1] || prov->sa[2] || prov->sa[3]) + { tpl_printf(vars, TPLADD, "APIPROVIDERSA", "%02X%02X%02X%02X", prov->sa[0], prov->sa[1], prov->sa[2], prov->sa[3]); } + else + { tpl_addVar(vars, TPLADD, "APIPROVIDERSA", ""); } + tpl_printf(vars, TPLADD, "APIPROVIDERCAID", "%04X", card->caid); + tpl_printf(vars, TPLADD, "APIPROVIDERPROVID", "%06X", prov->prov); + tpl_printf(vars, TPLADD, "APIPROVIDERNUMBER", "%d", providercount); + tpl_addVar(vars, TPLADD, "APIPROVIDERNAME", xml_encode(vars, provider)); + tpl_addVar(vars, TPLAPPEND, "PROVIDERLIST", tpl_getTpl(vars, "APICCCAMCARDPROVIDERBIT")); + + } + providercount++; + tpl_printf(vars, TPLADD, "APITOTALPROVIDERS", "%d", providercount); + } + + LL_ITER nit = ll_iter_create(card->remote_nodes); + uint8_t *node; + + nodecount = 0; + if(!apicall) { tpl_addVar(vars, TPLADD, "NODES", ""); } + else { tpl_addVar(vars, TPLADD, "NODELIST", ""); } + + while((node = ll_iter_next(&nit))) + { + + if(!apicall) + { + tpl_printf(vars, TPLAPPEND, "NODES", "%02X%02X%02X%02X%02X%02X%02X%02X
\n", + node[0], node[1], node[2], node[3], node[4], node[5], node[6], node[7]); + } + else + { + tpl_printf(vars, TPLADD, "APINODE", "%02X%02X%02X%02X%02X%02X%02X%02X", node[0], node[1], node[2], node[3], node[4], node[5], node[6], node[7]); + tpl_printf(vars, TPLADD, "APINODENUMBER", "%d", nodecount); + tpl_addVar(vars, TPLAPPEND, "NODELIST", tpl_getTpl(vars, "APICCCAMCARDNODEBIT")); + } + nodecount++; + tpl_printf(vars, TPLADD, "APITOTALNODES", "%d", nodecount); + } + + if(!apicall) + { tpl_addVar(vars, TPLAPPEND, "CCCAMSTATSENTRY", tpl_getTpl(vars, "ENTITLEMENTCCCAMENTRYBIT")); } + else + { tpl_addVar(vars, TPLAPPEND, "CARDLIST", tpl_getTpl(vars, "APICCCAMCARDBIT")); } + + cardcount++; + } + // set previous Link if needed + if(offset >= ENTITLEMENT_PAGE_SIZE) + { + tpl_printf(vars, TPLAPPEND, "CONTROLS", " << PREVIOUS < ", + offset - ENTITLEMENT_PAGE_SIZE, + getParam(params, "globallist"), + urlencode(vars, getParam(params, "label"))); + } + + // set next link if needed + if(cardsize > count && offset < cardsize) + { + tpl_printf(vars, TPLAPPEND, "CONTROLS", " > NEXT >> ", + offset + ENTITLEMENT_PAGE_SIZE, + getParam(params, "globallist"), + urlencode(vars, getParam(params, "label"))); + } + + if(!apicall) + { + tpl_printf(vars, TPLADD, "TOTALS", "card count=%d", cardsize); + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTCCCAMBIT")); + } + else + { + tpl_printf(vars, TPLADD, "APITOTALCARDS", "%d", cardsize); + } + + } + else + { + if(!apicall) + { + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTGENERICBIT")); + tpl_addVar(vars, TPLADD, "LOGHISTORY", "no cards found
\n"); + } + else + { + tpl_printf(vars, TPLADD, "APITOTALCARDS", "%d", 0); + } + } + +} +#endif + +static char *send_oscam_entitlement(struct templatevars *vars, struct uriparams *params, int32_t apicall) +{ + if(!apicall) { setActiveMenu(vars, MNU_READERS); } + char *reader_ = getParam(params, "label"); +#ifdef MODULE_CCCAM +#ifdef MODULE_CCCSHARE + char *sharelist_ = getParam(params, "globallist"); + int32_t show_global_list = sharelist_ && sharelist_[0] == '1'; +#else + int32_t show_global_list = 0; +#endif + + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(show_global_list || cs_strlen(reader_) || (rdr && rdr->typ == R_CCCAM)) + { + + if(show_global_list || (rdr && rdr->typ == R_CCCAM && rdr->enable)) + { + + if(show_global_list) + { + tpl_addVar(vars, TPLADD, "READERNAME", "GLOBAL"); + tpl_addVar(vars, TPLADD, "APIHOST", "GLOBAL"); + tpl_addVar(vars, TPLADD, "APIHOSTPORT", "GLOBAL"); + } + else + { + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "APIHOST", xml_encode(vars, rdr->device)); + tpl_printf(vars, TPLADD, "APIHOSTPORT", "%d", rdr->r_port); + } + + int32_t offset = atoi(getParam(params, "offset")); //should be 0 if parameter is missed on very first call + int32_t cardsize; +#ifdef MODULE_CCCSHARE + if(show_global_list) + { + int32_t i; + LLIST **sharelist = get_and_lock_sharelist(); + LLIST *sharelist2 = ll_create("web-sharelist"); + for(i = 0; i < CAID_KEY; i++) + { + if(sharelist[i]) + { ll_putall(sharelist2, sharelist[i]); } + } + unlock_sharelist(); + struct cc_card **cardarray = get_sorted_card_copy(sharelist2, 0, &cardsize); + ll_destroy(&sharelist2); + print_cards(vars, params, cardarray, cardsize, 1, NULL, offset, apicall); + NULLFREE(cardarray); + } + else +#endif + { + struct s_client *rc = rdr->client; + struct cc_data *rcc = (rc) ? rc->cc : NULL; + if(rcc && rcc->cards) + { + struct cc_card **cardarray = get_sorted_card_copy(rcc->cards, 0, &cardsize); + print_cards(vars, params, cardarray, cardsize, 0, rdr, offset, apicall); + NULLFREE(cardarray); + } + } + + } + else + { +#else + if(cs_strlen(reader_)) + { + { + struct s_reader *rdr; +#endif + tpl_addVar(vars, TPLADD, "LOGHISTORY", "->"); + // normal non-cccam reader + + rdr = get_reader_by_label(reader_); + + if(rdr) + { + struct s_client *cl = rdr->client; + if(rdr->ll_entitlements) + { + + time_t now = time((time_t *)0); + + struct tm start_t, end_t; + LL_ITER itr = ll_iter_create(rdr->ll_entitlements); + S_ENTITLEMENT *item; + + tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", "

New Structure:
"); + char tbuffer[83]; + int jsondelimiter = 0; + while((item = ll_iter_next(&itr))) + { + localtime_r(&item->start, &start_t); + localtime_r(&item->end, &end_t); + + if(!apicall) + { strftime(tbuffer, 30, "%Y-%m-%d", &start_t); } + else + { strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &start_t); } + tpl_addVar(vars, TPLADD, "ENTSTARTDATE", tbuffer); + + if(!apicall) + { strftime(tbuffer, 30, "%Y-%m-%d", &end_t); } + else + { strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &end_t); } + tpl_addVar(vars, TPLADD, "ENTENDDATE", tbuffer); + + tpl_addVar(vars, TPLADD, "ENTEXPIERED", item->end > now ? "e_valid" : "e_expired"); + tpl_printf(vars, TPLADD, "ENTCAID", "%04X", item->caid); + tpl_printf(vars, TPLADD, "ENTPROVID", "%06X", item->provid); + tpl_printf(vars, TPLADD, "ENTID", "%08X%08X", (uint32_t)(item->id >> 32), (uint32_t)item->id); + tpl_printf(vars, TPLADD, "ENTCLASS", "%08X", item->class); + tpl_addVar(vars, TPLADD, "ENTTYPE", entitlement_type[item->type]); + + char *entresname; + entresname = xml_encode(vars, get_tiername((uint16_t)(item->id & 0xFFFF), item->caid, tbuffer)); + if(!tbuffer[0]) + { entresname = xml_encode(vars, get_provider(item->provid, item->caid, tbuffer, sizeof(tbuffer))); } + tpl_addVar(vars, TPLADD, "ENTRESNAME", entresname); + + if((strcmp(getParam(params, "hideexpired"), "1") != 0) || (item->end > now)) + { tpl_addVar(vars, TPLAPPEND, "READERENTENTRY", tpl_getTpl(vars, "ENTITLEMENTITEMBIT")); } + + if(apicall==2) + { + tpl_printf(vars, TPLAPPEND, "APIENTITLEMENTLIST","%s%s",jsondelimiter?",":"", tpl_getTpl(vars, "JSONENTITLEMENTBIT")); + jsondelimiter++; + } + } + } + + if(cl && cl->typ) + { tpl_printf(vars, TPLADD, "READERTYPE", "%c", cl->typ); } + else + { tpl_addVar(vars, TPLADD, "READERTYPE", "null"); } + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, rdr->label)); + + int8_t i, j; + for(i = 0; i < 15; i++) { tpl_printf(vars, TPLAPPEND, "READERROM", "%c", rdr->rom[i]); } + if(rdr->hexserial[0] || rdr->hexserial[1]) { i = 0; } + else { i = 2; } + if(rdr->hexserial[6] || rdr->hexserial[7]) { j = 8; } + else { j = 6; } + for(; i < j; i++) { tpl_printf(vars, TPLAPPEND, "READERSERIAL", "%02X%s", rdr->hexserial[i], i < j - 1 ? " " : ""); } + for(i = 0; i < rdr->nprov; i++) + { + for(j = 0; j < 4; j++) { tpl_printf(vars, TPLAPPEND, "READERPROVIDS", "%02X ", rdr->prid[i][j]); } + tpl_addVar(vars, TPLAPPEND, "READERPROVIDS", i == 0 ? "(sysid)
\n" : "       
\n"); + } + +#ifdef READER_VIDEOGUARD + //CountryCode Vg card + char add_nds_line = 0; + if(rdr->VgCountryC[0]) + { + for(i = 0; i < 3; i++) { tpl_printf(vars, TPLAPPEND, "READERCOUNTRYC", "%c", rdr->VgCountryC[i]); } + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READERCOUNTRYC", "n/a"); + } + + //regional code for Vg card + if(rdr->VgRegionC[0]) + { + for(i = 0; i < 8; i++) { tpl_printf(vars, TPLAPPEND, "READER_RCODE", "%c", rdr->VgRegionC[i]); } + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READER_RCODE", "n/a"); + } + + //Pin Vg card + if(rdr->VgPin) + { + tpl_printf(vars, TPLAPPEND, "READERPIN", "%04i", rdr->VgPin); + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READERPIN", "n/a"); + } + + //Fuse Vg card + if(rdr->VgFuse) + { + tpl_printf(vars, TPLAPPEND, "READERFUSE", "%02X", rdr->VgFuse); + add_nds_line = 1; + } + + if(caid_is_videoguard(rdr->caid)) + { + tpl_printf(vars, TPLAPPEND, "READERPAYLOAD", "%02X %02X %02X %02X %02X %02X", rdr->VgLastPayload[0], + rdr->VgLastPayload[1], rdr->VgLastPayload[2], rdr->VgLastPayload[3], rdr->VgLastPayload[4], rdr->VgLastPayload[5]); + add_nds_line = 1; + } + + //credit on Vg card + if(rdr->VgCredit) + { + tpl_printf(vars, TPLAPPEND, "READERCREDIT", "%i", rdr->VgCredit); + add_nds_line = 1; + } + else + { + tpl_addVar(vars, TPLADD, "READERCREDIT", "n/a"); + } +#endif + + if(rdr->card_valid_to) + { + struct tm vto_t; + char vtobuffer[30]; + localtime_r(&rdr->card_valid_to, &vto_t); + strftime(vtobuffer, 30, "%Y-%m-%d", &vto_t); + tpl_addVar(vars, TPLADD, "READERCARDVALIDTO", vtobuffer); + } + else + { + tpl_addVar(vars, TPLADD, "READERCARDVALIDTO", "n/a"); + } + + if(rdr->irdId[0]) + { + for(i = 0; i < 4; i++) { tpl_printf(vars, TPLAPPEND, "READERIRDID", "%02X ", rdr->irdId[i]); } + } + else + { + tpl_addVar(vars, TPLADD, "READERIRDID", "n/a"); + } + + if(rdr->card_atr_length) + for(i = 0; i < rdr->card_atr_length; i++) { tpl_printf(vars, TPLAPPEND, "READERATR", "%02X ", rdr->card_atr[i]); } + + if(caid_is_seca(rdr->caid) || caid_is_viaccess(rdr->caid)) + { + if(rdr->maturity == 0xF) + { + tpl_printf(vars, TPLAPPEND, "READERMATURITY", "%s ", "no limit"); + } + else + { + tpl_printf(vars, TPLAPPEND, "READERMATURITY", "%d+", rdr->maturity); + } + } + else + { + tpl_printf(vars, TPLAPPEND, "READERMATURITY", "%s ", "n/a"); + } + + if (rdr->csystem) + tpl_addVar(vars, TPLADD, "READERCSYSTEM", rdr->csystem->desc); + +#ifdef READER_VIDEOGUARD + if(add_nds_line) + { + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENTNDS", tpl_getTpl(vars, "ENTITLEMENTBITNDS")); + } +#endif + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTBIT")); + } + else + { + tpl_addMsg(vars, "Reader does not exist or is not started!"); + } + } + + } + else + { + tpl_addVar(vars, TPLADD, "ENTITLEMENTCONTENT", tpl_getTpl(vars, "ENTITLEMENTGENERICBIT")); + } + + if(!apicall) + { return tpl_getTpl(vars, "ENTITLEMENTS"); } + else + { + if(apicall==1) + { return tpl_getTpl(vars, "APICCCAMCARDLIST"); } + else + { return tpl_getTpl(vars, "JSONENTITLEMENTS"); } + } +} + +#ifdef WEBIF_LIVELOG +static char *send_oscam_logpoll(struct templatevars * vars, struct uriparams * params) +{ + + uint64_t lastid = 0; + +#ifdef WITH_DEBUG + tpl_addVar(vars, TPLADD, "LOG_DEBUGMENU", tpl_getTpl(vars, "LOGDEBUGMENU")); +#endif + tpl_addVar(vars, TPLADD, "LOG_SIZEMENU", tpl_getTpl(vars, "LOGSIZEMENU")); + tpl_addVar(vars, TPLADD, "TITLEADD1", "Move mouse over log-window to stop scroll"); + + if(strcmp(getParam(params, "lastid"), "start") == 0){ + setActiveMenu(vars, MNU_LIVELOG); + return tpl_getTpl(vars, "LOGPAGE"); + } + else + { + lastid = strtoull(getParam(params, "lastid"), NULL, 10); + } + + char *dot = ""; //Delimiter + +#ifdef WITH_DEBUG + char *debuglvl = getParam(params, "debug"); + if(cs_strlen(debuglvl) > 0) { + int32_t dblvl = atoi(debuglvl); + if(cs_dblevel != dblvl) { + if(dblvl >= 0 && dblvl <= 65535) { cs_dblevel = dblvl; } + cs_log("%s debug_level=%d", "all", cs_dblevel); + } + } + tpl_printf(vars, TPLAPPEND, "DATA","%s\"debug\":\"%d\"", dot, cs_dblevel); + dot = ","; + tpl_printf(vars, TPLAPPEND, "DATA","%s\"maxdebug\":\"%d\"",dot, MAX_DEBUG_LEVELS); +#endif + + if(cfg.loghistorylines == 0){ + tpl_printf(vars, TPLAPPEND, "DATA","%s\"logdisabled\":\"1\"",dot); + return tpl_getTpl(vars, "POLL"); + } + + if(log_history) + { + LL_ITER it = ll_iter_create(log_history); + struct s_log_history *hist; + + tpl_printf(vars, TPLAPPEND, "DATA", "%s\"lines\":[", dot); + + dot = ""; + + while((hist = (struct s_log_history*)ll_iter_next(&it))) + { + char p_usr[32]; + size_t pos1 = strcspn(hist->txt, "\t") + 1; + cs_strncpy(p_usr, hist->txt , pos1 > sizeof(p_usr) ? sizeof(p_usr) : pos1); + + char *p_txt = hist->txt + pos1; + + pos1 = strcspn(p_txt, "\n") + 1; + char str_out[pos1]; + cs_strncpy(str_out, p_txt, pos1); + uint64_t id = hist->counter; + + size_t b64_str_in = cs_strlen(xml_encode(vars, str_out)); + size_t b64_str_out = 32 + BASE64_LENGTH(b64_str_in); + char *b64_str_out_buf; + if(!cs_malloc(&b64_str_out_buf, b64_str_out)) + { continue; } + base64_encode(xml_encode(vars, str_out), b64_str_in, b64_str_out_buf, b64_str_out); + + if(id > lastid){ + tpl_printf(vars, TPLAPPEND, "DATA","%s{\"id\":\"%" PRIu64 "\",\"usr\":\"%s\",\"line\":\"%s\"}", + dot, + id, + urlencode(vars, xml_encode(vars, p_usr)), + b64_str_out_buf); + dot = ","; // next in Array with leading delimiter + } + NULLFREE(b64_str_out_buf); + } + } + + tpl_addVar(vars, TPLAPPEND, "DATA", "]"); + return tpl_getTpl(vars, "POLL"); +} +#endif + +static char *send_oscam_status(struct templatevars * vars, struct uriparams * params, int32_t apicall) +{ + const char *usr; + int32_t lsec, isec, chsec, con, cau = 0; + time_t now = time((time_t *)0); + struct tm lt; + int delimiter=0; + + if(!apicall) + { + setActiveMenu(vars, MNU_STATUS); + if (config_enabled(WITH_LB)) + tpl_addVar(vars, TPLADD, "STATUSCOL14HEAD","LB Value/"); + } + if(strcmp(getParam(params, "action"), "kill") == 0) + { + char *cptr = getParam(params, "threadid"); + struct s_client *cl = NULL; + if(cs_strlen(cptr) > 1) + { sscanf(cptr, "%p", (void **)(void *)&cl); } + + if(cl && is_valid_client(cl)) + { + if(is_dvbapi_usr(cl->account->usr)) + { + cs_log("WebIF from %s requests to kill dvbapi client %s -> ignoring!", cs_inet_ntoa(GET_IP()), cl->account->usr); + } + else + { + kill_thread(cl); + cs_log("Client %s killed by WebIF from %s", cl->account->usr, cs_inet_ntoa(GET_IP())); + } + } + } + + if(strcmp(getParam(params, "action"), "resetuserstats") == 0) + { + clear_info_clients_stats(); + } + if(strcmp(getParam(params, "action"), "resetreaderstats") == 0) + { + clear_info_readers_stats(); + } + if(strcmp(getParam(params, "action"), "restart") == 0) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + if(rdr->typ != R_GBOX) + { + add_job(rdr->client, ACTION_READER_RESTART, NULL, 0); + } +#ifdef MODULE_GBOX + else + { + restart_gbox_peer(rdr->label, 0, 0); + } +#endif + cs_log("Reader %s restarted by WebIF from %s", rdr->label, cs_inet_ntoa(GET_IP())); + } + } + + char *debuglvl = getParam(params, "debug"); + if(cs_strlen(debuglvl) > 0) + { +#ifndef WITH_DEBUG + cs_log("*** Warning: Debug Support not compiled in ***"); +#else + int32_t dblvl = atoi(debuglvl); + if(dblvl >= 0 && dblvl <= 65535) { cs_dblevel = dblvl; } + cs_log("%s debug_level=%d", "all", cs_dblevel); +#endif + } + + char *hide = getParam(params, "hide"); + if(cs_strlen(hide) > 0) + { + struct s_client *hideidx = NULL; + sscanf(hide, "%p", (void **)(void *)&hideidx); + + if(hideidx && is_valid_client(hideidx)) + { hideidx->wihidden = 1; } + } + + char *hideidle = getParam(params, "hideidle"); + if(cs_strlen(hideidle) > 0) + { + if(atoi(hideidle) == 2) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 's' || cl->typ == 'h' || cl->typ == 'm'){ + cl->wihidden = 0; + } + } + } + else if(atoi(hideidle) == 3) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 'r'){ + cl->wihidden = 0; + } + } + } + else if(atoi(hideidle) == 4) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 'p'){ + cl->wihidden = 0; + } + } + } + else if(atoi(hideidle) == 5) + { + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->typ == 'c'){ + cl->wihidden = 0; + } + } + } + else + { + int32_t oldval = cfg.http_hide_idle_clients; + config_set("webif", "httphideidleclients", hideidle); + if(oldval != cfg.http_hide_idle_clients) + { + refresh_oscam(REFR_SERVER); + } + } + } + + if(cfg.http_hide_idle_clients > 0) { tpl_addVar(vars, TPLADD, "HIDEIDLECLIENTSSELECTED1", "selected"); } + else { tpl_addVar(vars, TPLADD, "HIDEIDLECLIENTSSELECTED0", "selected"); } + + tpl_addVar(vars, TPLADD, "SRVIDFILE", use_srvid2 ? "oscam.srvid2" : "oscam.srvid"); + + int32_t user_count_all = 0, user_count_shown = 0, user_count_active = 0; + int32_t reader_count_all = 0, reader_count_conn = 0, reader_count_off = 0; + int32_t proxy_count_all = 0, proxy_count_conn = 0, proxy_count_off = 0; + int32_t server_count_all = 0, server_count_shown = 0, server_count_hidden = 0; + int32_t monitor_count_all = 0, monitor_count_shown = 0; + int32_t shown; + + int32_t total_readers = 0; + int32_t active_readers = 0; + int32_t disabled_readers = 0; + int32_t connected_readers = 0; + + struct s_client *cl; + int8_t filtered; + + cs_readlock(__func__, &readerlist_lock); + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client; cl ; cl = cl->next) + { +#ifdef CS_CACHEEX_AIO +#if defined(MODULE_CCCAM) && defined(CS_CACHEEX) + struct cc_data *cc = cl->cc; +#endif +#endif + if(cl->kill) { continue; } +#ifdef CS_CACHEEX + if(get_module(cl)->listenertype != LIS_CSPUDP) + { +#endif + + // Reset template variables + tpl_addVar(vars, TPLADD, "LBLRPSTRVALUE", ""); + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", ""); + tpl_addVar(vars, TPLADD, "LASTREADER", ""); + tpl_addVar(vars, TPLADD, "CLIENTPROTO", ""); + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIME", ""); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIMEHIST", ""); + tpl_addVar(vars, TPLADD, "UPICMISSING" , ""); + tpl_addVar(vars, TPLADD, "ENTITLEMENTS", ""); + + if(cl->typ == 'c') + { user_count_all++; } + else if(cl->typ == 'p') + { + proxy_count_all++; + if((cl->reader->typ == R_GBOX && cl->reader->card_status != CARD_INSERTED && cl->reader->card_status != NO_CARD) || + (cl->reader->typ != R_GBOX && cl->reader->card_status != CARD_INSERTED)) + { proxy_count_off++; } + } + else if(cl->typ == 'r') + { reader_count_all++; if(cl->reader->card_status != CARD_INSERTED) { reader_count_off++; } } + else if(cl->typ == 's' || cl->typ == 'h') + { server_count_all++; if(cl->wihidden) {server_count_hidden++;} } + else if(cl->typ == 'm') + { monitor_count_all++; } + + shown = 0; + if(cl->wihidden != 1) + { + filtered = !(cfg.http_hide_idle_clients != 1 || cl->typ != 'c' || (now - cl->lastecm) <= cfg.hideclient_to); + if(!filtered && cfg.http_hide_type) + { + char *p = cfg.http_hide_type; + while(*p && !filtered) + { + char type = *p++; +#ifdef CS_CACHEEX + filtered = (type == cl->typ) || (type == 'x' && (cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)); +#else + filtered = (type == cl->typ); +#endif + } + } + + if(!filtered) + { + if(cl->typ == 'c') + { + user_count_shown++; + if(cfg.http_hide_idle_clients != 1 && cfg.hideclient_to > 0 && (now - cl->lastecm) <= cfg.hideclient_to) + { + user_count_active++; + } + } + else if(cl->typ == 's' || cl->typ == 'h') + { + server_count_shown++; + } + else if(cl->typ == 'm') + { + monitor_count_shown++; + } + else if(cl->typ == 'r') + { + reader_count_conn++; + } + else if(cl->typ == 'p') + { + proxy_count_conn++; + } + + if(cl->typ == 'c' || cl->typ == 'r' || cl->typ == 'p') + { + if(cl->lastecm >= cl->login && cl->lastecm >= cl->logout) { isec = now - cl->lastecm; } + else if(cl->logout >= cl->login) { isec = now - cl->logout; } + else { isec = now - cl->login; } + } + else { isec = now - cl->last; } + + shown = 1; + lsec = now - cl->login; + chsec = now - cl->lastswitch; + usr = username(cl); + + if((cl->typ == 'r') || (cl->typ == 'p')) { usr = cl->reader->label; } + + if(cl->dup) { con = 2; } + else if((cl->tosleep) && (now - cl->lastswitch > cl->tosleep)) { con = 1; } + else { con = 0; } + + // no AU reader == 0 / AU ok == 1 / Last EMM > aulow == -1 + if(cl->typ == 'c' || cl->typ == 'p' || cl->typ == 'r') + { + if((cl->typ == 'c' && ll_count(cl->aureader_list) == 0) || ((cl->typ == 'p' || cl->typ == 'r') && cl->reader->audisabled)) { cau = 0; } + else if((now - cl->lastemm) / 60 > cfg.aulow) { cau = -1; } + else { cau = 1; } + + if(cau == 0) + { + tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", "OFF"); + } + else + { + if(cau == -1) + { tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", !apicall?"ON":""); } + else + { tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", !apicall?"ACTIVE":""); } + tpl_addVar(vars, TPLAPPEND, "CLIENTCAUHTTP", !apicall?"":""); + if(cl->typ == 'c') + { + struct s_reader *rdr; + LL_ITER itr = ll_iter_create(cl->aureader_list); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->audisabled) + { tpl_printf(vars, TPLAPPEND, "CLIENTCAUHTTP", "(%s)
", xml_encode(vars, rdr->label)); } + else + { tpl_printf(vars, TPLAPPEND, "CLIENTCAUHTTP", "%s
", xml_encode(vars, rdr->label)); } + } + } + else { tpl_addVar(vars, TPLAPPEND, "CLIENTCAUHTTP", xml_encode(vars, cl->reader->label)); } + tpl_addVar(vars, TPLAPPEND, "CLIENTCAUHTTP", !apicall?"
":""); + } + } + else + { + cau = 0; + tpl_addVar(vars, TPLADD, "CLIENTCAUHTTP", ""); + } + localtime_r(&cl->login, <); + + if(cl->typ == 'c' || cl->typ == 'm') + { + if(cl->account && cl->account->description) + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->account->description)); + else + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + else if(cl->typ == 'p' || cl->typ == 'r') + { + if(cl->reader && cl->reader->description) + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->reader->description)); + else + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + if(!apicall) + { + tpl_addVar(vars, TPLADD, "LBL", xml_encode(vars, usr)); + tpl_printf(vars, TPLADD, "CID", "%p", cl); + if(cl->typ == 'c' || cl->typ == 'm') + { + tpl_addVar(vars, TPLADD, "TARGET", "User"); + tpl_addVar(vars, TPLADD, "CSIDX", tpl_getTpl(vars, "STATUSKBUTTON")); + } + else if(cl->typ == 'p') + { + tpl_addVar(vars, TPLADD, "TARGET", "Proxy"); + tpl_addVar(vars, TPLADD, "LBLENC", urlencode(vars, usr)); + tpl_addVar(vars, TPLADD, "CSIDX", tpl_getTpl(vars, "STATUSRBUTTON")); + } + else if(cl->typ == 'r') + { + tpl_addVar(vars, TPLADD, "TARGET", "Reader"); + tpl_addVar(vars, TPLADD, "LBLENC", urlencode(vars, usr)); + tpl_addVar(vars, TPLADD, "CSIDX", tpl_getTpl(vars, "STATUSRBUTTON")); + } + else if (cl->typ == 'h' || cl->typ == 's') + { + tpl_addVar(vars, TPLADD, "TARGET", "Server"); + tpl_addVar(vars, TPLADD, "CSIDX", ""); + } + tpl_addVar(vars, TPLADD, "HIDEIDX", tpl_getTpl(vars, "STATUSHBUTTON")); + tpl_printf(vars, TPLADD, "CSIDXPLAIN", "id_%p", cl); + } + else + { + tpl_printf(vars, TPLADD, "HIDEIDX", "%p", cl); + tpl_printf(vars, TPLADD, "CSIDX", "id_%p", cl); + } + tpl_printf(vars, TPLADD, "CLIENTTYPE", "%c", cl->typ); + tpl_printf(vars, TPLADD, "CLIENTCNR", "%d", get_threadnum(cl)); + tpl_addVar(vars, TPLADD, "CLIENTUSER", xml_encode(vars, usr)); + + tpl_addVar(vars, TPLADD, "STATUSUSERICON", xml_encode(vars, usr)); + if (cl->typ == 'c' || cl->typ == 'm') + { + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, usr)); + tpl_addVar(vars, TPLADD, "USERENC", urlencode(vars, usr)); + } + else if (cl->typ == 'p' || cl->typ == 'r') + { + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, usr)); + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, usr)); + } + + bool picon_shown = false; + const char *status_user_icon_tpl = NULL; + + char picon_name[32]; + if(cfg.http_showpicons) + { + if(picon_exists(xml_encode(vars, usr))) + { + switch (cl->typ) + { + case 'm': // Fall through + case 'c': status_user_icon_tpl = "SUSERICON"; picon_shown = true; break; + case 'p': // Fall through + case 'r': status_user_icon_tpl = "SREADERICON"; picon_shown = true; break; + } + } + else + tpl_printf(vars, TPLADD, "UPICMISSING", "%smissing icon: IC_%s.tpl",!apicall?" ":"",xml_encode(vars, usr)); + } + + if (!picon_shown) + { + switch (cl->typ) + { + case 'm': // Fall through + case 'c': status_user_icon_tpl = "SUSER"; break; + case 'p': // Fall through + case 'r': status_user_icon_tpl = "SREADER"; break; + } + } + + if (status_user_icon_tpl) + tpl_addVar(vars, TPLADD, "STATUSUSERICON", tpl_getTpl(vars, status_user_icon_tpl)); + + tpl_printf(vars, TPLADD, "CLIENTCAU", "%d", cau); + if(!apicall) + { + if(cl->typ == 'c' || cl->typ == 'p' || cl->typ == 'r') + { + if(cl->crypted) { tpl_addVar(vars, TPLADD, "CLIENTCRYPTED", "ON"); } + else { tpl_addVar(vars, TPLADD, "CLIENTCRYPTED", "OFF"); } + } + else { tpl_addVar(vars, TPLADD, "CLIENTCRYPTED", ""); } + } + else { tpl_printf(vars, TPLADD, "CLIENTCRYPTED", "%d", cl->crypted); } + + if(cl->typ == 'r' && cl->reader && !is_network_reader(cl->reader)) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "local"); } + else if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "CLIENTIP", cs_inet_ntoa(cl->ip)); } + else if((cl->typ == 'p' || cl->typ == 'r') && cl->reader && cl->reader->tcp_connected) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "camd.socket"); } + else if(cl->typ == 'c' && cl->login > cl->logout) + { tpl_addVar(vars, TPLADD, "CLIENTIP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "CLIENTIP", ""); } + + tpl_printf(vars, TPLADD, "CLIENTPORT", "%d", cl->port); + const char *proto = client_get_proto(cl); +#ifdef CS_CACHEEX_AIO + if(cl && + ( (cl->typ == 'c' && cl->account && cl->account->cacheex.feature_bitfield) +#if defined(MODULE_CAMD35) || defined (MODULE_CAMD35_TCP) + || (cl->c35_extmode > 1) +#endif +#ifdef MODULE_CCCAM + || (cc && cc->extended_lg_flagged_cws) +#endif + || (cl->typ == 'p' && cl->reader && cl->reader->cacheex.feature_bitfield)) + ) + { + const char *aio_suffix = " (cx-aio)"; + char *new_proto; + if(cs_malloc(&new_proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + new_proto[0] = '\0'; + if (!cs_strncat(new_proto, (char *)proto, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + cs_log("FIXME!"); + } + if (cs_strncat(new_proto, (char *)aio_suffix, cs_strlen(proto)+cs_strlen(aio_suffix)+1)) { + webif_add_client_proto(vars, cl, (const char*)new_proto, apicall); + } else { + cs_log("FIXME!"); + } + free(new_proto); + } + } else { +#endif + webif_add_client_proto(vars, cl, proto, apicall); +#ifdef CS_CACHEEX_AIO + } +#endif + if(!apicall) + { + if((cl->typ != 'p' && cl->typ != 'r') || cl->reader->card_status == CARD_INSERTED) + { + tpl_printf(vars, TPLADD, "CLIENTLOGINDATE", "%02d.%02d.%02d
%02d:%02d:%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100, lt.tm_hour, lt.tm_min, lt.tm_sec); + tpl_addVar(vars, TPLADD, "CLIENTLOGINSECS", sec2timeformat(vars, lsec)); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTLOGINDATE", ""); + tpl_addVar(vars, TPLADD, "CLIENTLOGINSECS", ""); + } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTLOGINDATEFMT", "%02d.%02d.%02d %02d:%02d:%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100, lt.tm_hour, lt.tm_min, lt.tm_sec); + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", <); + tpl_addVar(vars, TPLADD, "CLIENTLOGINDATE", tbuffer); + tpl_printf(vars, TPLADD, "CLIENTLOGINSECS", "%d", lsec); + } + + //load historical values from ringbuffer + char *value = get_ecm_historystring(cl); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIMEHIST", value); + free_mk_t(value); + + if((isec < cfg.hideclient_to || cfg.hideclient_to == 0 || is_dvbapi_usr(cl->account->usr)) && (cl->typ == 'c' || cl->typ == 'p' || cl->typ == 'r')) + { + if(((cl->typ == 'c')) && (cl->lastreader[0])) + { + tpl_printf(vars, TPLADD, "MSVALUE", PRINTF_LOCAL_D, cl->cwlastresptime); + if(apicall) + { + tpl_addVar(vars, TPLADD, "LASTREADER", (cl->last_caid == NO_CAID_VALUE || isec > cfg.hideclient_to ) ? "" : cl->lastreader); + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, cl->lastreader)); + } + else + { +#ifdef WITH_LB + tpl_addVar(vars, TPLADD, "LBLVALUE", xml_encode(vars, cl->lastreader)); + if(strstr(cl->lastreader, " (cache)")) + { + char lastreader_tmp[cs_strlen(cl->lastreader) - 8]; + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", tpl_getVar(vars, "LBLRPSTRVALUE")); + cs_strncpy(lastreader_tmp, cl->lastreader, sizeof(lastreader_tmp)); + tpl_addVar(vars, TPLADD, "LBLVALUEENC", urlencode(vars, lastreader_tmp)); + tpl_addVar(vars, TPLADD, "LBLVALUETITLE", xml_encode(vars, lastreader_tmp)); + } + else + { + tpl_addVar(vars, TPLADD, "LBLVALUEENC", urlencode(vars, cl->lastreader)); + tpl_addVar(vars, TPLADD, "LBLVALUETITLE", xml_encode(vars, cl->lastreader)); + } + tpl_addVar(vars, TPLAPPEND, "CLIENTLBVALUE", tpl_getTpl(vars, "CLIENTLBLVALUEBIT")); +#else + tpl_printf(vars, TPLAPPEND, "CLIENTLBVALUE", "%s (%'d ms)", xml_encode(vars, cl->lastreader), cl->cwlastresptime); +#endif + } + if(cl->last_caid == NO_CAID_VALUE || isec > cfg.hideclient_to) tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", ""); + } + if(cl->last_caid != NO_CAID_VALUE || cl->last_srvid != NO_SRVID_VALUE) + { + char channame[CS_SERVICENAME_SIZE]; + const char *lastprovidername; + + get_servicename_or_null(cl, cl->last_srvid, cl->last_provid, cl->last_caid, channame, sizeof(channame)); + if(channame[0] == '\0') + { + cs_strncpy(channame, "unknown", sizeof(channame)); + } + + lastprovidername = get_cl_lastprovidername(cl); + + tpl_printf(vars, TPLADD, "CLIENTCAID", "%04X", cl->last_caid); + tpl_printf(vars, TPLADD, "CLIENTPROVID", "%06X", cl->last_provid); + tpl_printf(vars, TPLADD, "CLIENTSRVID", "%04X", cl->last_srvid); + tpl_printf(vars, TPLADD, "CLIENTSRVPROVIDER", "%s%s%s", (lastprovidername[0] != '\0' && strcmp(lastprovidername, " ")) ? " [" : "", xml_encode(vars, lastprovidername), (lastprovidername[0] != '\0' && strcmp(lastprovidername, " ")) ? "]" : ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVNAME", xml_encode(vars, channame)); + tpl_printf(vars, TPLADD, "CLIENTLASTRESPONSETIME", "%d", cl->cwlastresptime ? cl->cwlastresptime : 1); + tpl_addVar(vars, TPLADD, "CLIENTSRVTYPE", cl->last_srvidptr && cl->last_srvidptr->type ? xml_encode(vars, cl->last_srvidptr->type) : ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVDESCRIPTION", cl->last_srvidptr && cl->last_srvidptr->desc ? xml_encode(vars, cl->last_srvidptr->desc) : ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", sec2timeformat(vars, chsec)); + if(cfg.http_showpicons && cl->last_srvid) + { + char picon_channame[30]; + int8_t picon_ok = 0; + + get_picon_servicename_or_null(cl, cl->last_srvid, cl->last_provid, cl->last_caid, picon_channame, sizeof(picon_channame)); + if(picon_channame[0]) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", picon_channame); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + + if(!picon_ok && picon_servicename_remve_hd(picon_channame, sizeof(picon_channame))) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%s", picon_channame); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + } + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%04X_%06X_%04X", cl->last_caid, cl->last_provid, cl->last_srvid); + tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + picon_ok = picon_exists(picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "%04X_%04X", cl->last_caid, cl->last_srvid); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + } + if(!picon_ok) + { + snprintf(picon_name, sizeof(picon_name) / sizeof(char) - 1, "0000_%04X", cl->last_srvid); + picon_ok = picon_exists(picon_name); + if(picon_ok) tpl_addVar(vars, TPLADD, "PICONNAME", picon_name); + } + if(picon_ok) + { + tpl_addVar(vars, TPLADDONCE, "CURRENTPICON", tpl_getTpl(vars, "CLIENTCURRENTCHANNELPIC")); + } + else + { + tpl_addVar(vars, TPLADDONCE, "CURRENTPICON", tpl_getTpl(vars, "CLIENTCURRENTCHANNEL")); + tpl_addVar(vars, TPLADD, "PICONNAME", ""); + } + } + else + { + tpl_addVar(vars, TPLADDONCE, "CURRENTPICON", tpl_getTpl(vars, "CLIENTCURRENTCHANNELBIT")); + tpl_addVar(vars, TPLADD, "PICONNAME", "0000_0000"); + } + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTCAID", "0000"); + tpl_addVar(vars, TPLADD, "CLIENTPROVID", "000000"); + tpl_printf(vars, TPLADD, "CLIENTSRVID", "0000"); + } + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTCAID", "0000"); + tpl_addVar(vars, TPLADD, "CLIENTPROVID", "000000"); + tpl_addVar(vars, TPLADD, "CLIENTSRVID", "0000"); + tpl_addVar(vars, TPLADD, "CURRENTPICON", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVPROVIDER", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVNAME", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVTYPE", ""); + tpl_addVar(vars, TPLADD, "CLIENTSRVDESCRIPTION", ""); + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", ""); + tpl_addVar(vars, TPLADD, "CLIENTTIMEONCHANNEL", ""); + } + + if(!apicall) + { + tpl_addVar(vars, TPLADD, "CLIENTIDLESECS", sec2timeformat(vars, isec)); + + if((cl->typ != 'p' && cl->typ != 'r') || cl->reader->card_status == CARD_INSERTED) + { tpl_addVar(vars, TPLADD, "CLIENTIDLESECSCLASS", "idlesec_normal"); } + else + { tpl_addVar(vars, TPLADD, "CLIENTIDLESECSCLASS", "idlesec_alert"); } + } + else + { + tpl_printf(vars, TPLADD, "CLIENTIDLESECS", "%d", isec); + } + + if(con == 2) { tpl_addVar(vars, TPLADD, "CLIENTCON", "Duplicate"); } + else if(con == 1) { tpl_addVar(vars, TPLADD, "CLIENTCON", "Sleep"); } + else + { + struct s_reader *rdr = cl->reader; + char *txt = "OK"; + if(!rdr && (cl->typ == 'r' || cl->typ == 'p')) { txt = "UNKNOWN"; } + + else if(cl->typ == 'r' || cl->typ == 'p') //reader or proxy + { +#ifdef WITH_LB + if(rdr->lbvalue) + { + tpl_printf(vars, TPLADD, "LBLRPSTRVALUE", "%d", rdr->lbvalue); + } + else + { + tpl_addVar(vars, TPLADD, "LBLRPSTRVALUE", "no data"); + } + tpl_addVar(vars, TPLADD, "LBLRPVALUE", rdr->label); + tpl_addVar(vars, TPLADD, "LBLRPVALUEENC", urlencode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", tpl_getTpl(vars, "CLIENTLBLVALUERP")); +#else + tpl_addVar(vars, TPLADD, "CLIENTLBVALUE", tpl_getVar(vars, "LBLRPSTRVALUE")); +#endif + switch(rdr->card_status) + { + case NO_CARD: + if(rdr->typ == R_GBOX) + { txt = "ONL"; } + else + { txt = "OFF"; } + break; + case UNKNOWN: + txt = "UNKNOWN"; + break; + case READER_DEVICE_ERROR: + txt = "READER DEVICE ERROR"; + break; + case CARD_NEED_INIT: + if(rdr->typ == R_GBOX) + { txt = "OFFLINE"; } +#ifdef CS_CACHEEX + else if (cl->reader->cacheex.mode > 0) + { txt = "CCcam CacheEX"; } +#endif + else + { txt = "NEEDINIT"; } + break; + case CARD_INSERTED: + if(cl->typ == 'p') + { + if(rdr->typ == R_GBOX) + { txt = "ONL"; } + else + { txt = "CONNECTED"; } + } + else + { txt = "CARDOK"; } + break; + case CARD_FAILURE: + txt = "ERROR"; + break; + default: + txt = "UNDEF"; + } +#ifdef MODULE_GBOX + if(rdr->typ == R_GBOX) + { + struct gbox_peer *peer = cl->gbox; + char gbx_txt[45]; + memset(gbx_txt, 0, sizeof(gbx_txt)); + if(!strcmp(txt, "OFFLINE")) + { + snprintf(gbx_txt, sizeof(gbx_txt), "%s | ID: %04X", txt, peer->gbox.id); + } + else + { + snprintf(gbx_txt, sizeof(gbx_txt), "%s | crd: %d | ID: %04X", txt, gbox_count_peer_cards(peer->gbox.id), peer->gbox.id); + } + txt = gbx_txt; + } +#endif + } + + tpl_addVar(vars, TPLADD, "CLIENTCON", txt); + + if(rdr && (cl->typ == 'r')) //reader + { + if(rdr->ll_entitlements) + { + LL_ITER itr = ll_iter_create(rdr->ll_entitlements); + S_ENTITLEMENT *ent; + uint16_t total_ent = 0; + uint16_t active_ent = 0; + struct tm end_t; + tpl_addVar(vars, TPLADD, "TMPSPAN", ""); + while((ent = ll_iter_next(&itr))) + { + total_ent++; + if((ent->end > now) && (ent->type != 7)) + { + if(active_ent) {tpl_addVar(vars, TPLAPPEND, "TMPSPAN", "

");} + active_ent++; + localtime_r(&ent->end, &end_t); + tpl_printf(vars, TPLAPPEND, "TMPSPAN", "%04X@%06X
exp:%04d/%02d/%02d", + ent->caid, ent->provid, + end_t.tm_year + 1900, end_t.tm_mon + 1, end_t.tm_mday); + tpl_printf(vars, TPLAPPEND, "ENTITLEMENTS", "%s{\"caid\":\"%04X\",\"provid\":\"%06X\",\"exp\":\"%04d/%02d/%02d\"}", + active_ent > 1 ? ",": "", + ent->caid, ent->provid, + end_t.tm_year + 1900, end_t.tm_mon + 1, end_t.tm_mday); + } + } + tpl_printf(vars, TPLADD, "TOTENTITLEMENTS", "%d", total_ent); + if(((total_ent) && (active_ent == 0)) || (total_ent == 0)) + { + tpl_addVar(vars, TPLAPPEND, "TMPSPAN", "No active entitlements found"); + } + tpl_addVar(vars, TPLAPPEND, "TMPSPAN", "
"); + if(active_ent) + { + tpl_printf(vars, TPLADD, "TMP", "(%d entitlement%s)", active_ent, (active_ent != 1) ? "s" : ""); + } + else + { + tpl_addVar(vars, TPLADD, "TMP", "(no entitlements)"); + } + tpl_addVar(vars, TPLADD, "ENTLABEL", urlencode(vars, cl->reader->label)); + tpl_addVar(vars, TPLADD, "ENTVALUE", active_ent > 0 ? "" : "1"); + if (!apicall) tpl_addVar(vars, TPLAPPEND, "CLIENTCON", tpl_getTpl(vars, "FOUNDENTITLEMENTS")); + } + else + { + tpl_addVar(vars, TPLADD, "ENTLABEL", urlencode(vars, cl->reader->label)); + if (!apicall) tpl_addVar(vars, TPLAPPEND, "CLIENTCON", tpl_getTpl(vars, "NOENTITLEMENTS")); + } + } +#ifdef MODULE_CCCAM + if(!apicall || apicall == 2) + { + if(rdr && (cl->typ == 'r' || cl->typ == 'p') && strncmp(proto, "cccam", 5) == 0 && rdr->tcp_connected && rdr->card_status != CARD_FAILURE) + { + struct cc_data *rcc = cl->cc; + if(rcc) + { + LLIST *cards = rcc->cards; + if(cards) + { + int32_t cnt = ll_count(cards); + int32_t locals = rcc->num_hop1; + if(!apicall) + { + tpl_printf(vars, TPLADD, "TMP", "(%d of %d card%s)", locals, cnt, (cnt > 1) ? "s" : ""); + tpl_printf(vars, TPLADD, "CCCOUNT", "%d", cnt); + tpl_printf(vars, TPLADD, "CCCHOP1", "%d", rcc->num_hop1); + tpl_printf(vars, TPLADD, "CCCHOP2", "%d", rcc->num_hop2); + tpl_printf(vars, TPLADD, "CCCHOPX", "%d", rcc->num_hopx); + tpl_printf(vars, TPLADD, "CCCCURR", "%d", cl->reader->currenthops); + tpl_printf(vars, TPLADD, "CCCRES0", "%d", rcc->num_reshare0); + tpl_printf(vars, TPLADD, "CCCRES1", "%d", rcc->num_reshare1); + tpl_printf(vars, TPLADD, "CCCRES2", "%d", rcc->num_reshare2); + tpl_printf(vars, TPLADD, "CCCRESX", "%d", rcc->num_resharex); + tpl_addVar(vars, TPLADD, "TMPSPAN", tpl_getTpl(vars, "CCENTITLEMENTS")); + tpl_addVar(vars, TPLADD, "CCCLABEL", urlencode(vars, cl->reader->label)); + tpl_addVar(vars, TPLADD, "CCCRESHARE", rcc->num_reshare0 > 0 ? "1" : ""); + tpl_addVar(vars, TPLADD, "CCCTMP", tpl_getVar(vars, "TMP")); + tpl_addVar(vars, TPLADD, "CCCTMPSPAN", tpl_getVar(vars, "TMPSPAN")); + tpl_addVar(vars, TPLAPPEND, "CLIENTCON", tpl_getTpl(vars, "CCENTITLETOOLTIP")); + } + if (apicall == 2) + { + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, cl->reader->label)); + tpl_printf(vars, TPLADD, "ENTITLEMENTS", "{\"locals\":\"%d\",\"cccount\":\"%d\",\"ccchop1\":\"%d\",\"ccchop2\":\"%d\",\"ccchopx\":\"%d\",\"ccccurr\":\"%d\",\"cccres0\":\"%d\",\"cccres1\":\"%d\",\"cccres2\":\"%d\",\"cccresx\":\"%d\",\"cccreshare\":\"%s\"}", + locals, cnt, rcc->num_hop1, rcc->num_hop2, rcc->num_hopx, cl->reader->currenthops, + rcc->num_reshare0, rcc->num_reshare1, rcc->num_reshare2, rcc->num_resharex, + rcc->num_reshare0 > 0 ? "1" : ""); + } + cs_log_dbg(D_TRACE, "Reader %s has total %d local%s hop1 %d hopx %d from total of %d card%s", cl->reader->label, locals, (locals > 1) ? "s" : "", rcc->num_hop2, rcc->num_hopx, cnt, (cnt > 1) ? "s" : ""); + } + } + } + } +#endif + } + } + } + if(!apicall) + { + // select right suborder + if(cl->typ == 'c') + { + if(shown) { tpl_addVar(vars, TPLAPPEND, "CLIENTSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); } + if(cfg.http_hide_idle_clients != 1 && cfg.hideclient_to > 0) + { + tpl_printf(vars, TPLADD, "UCAC", "%d", user_count_active); + tpl_printf(vars, TPLADD, "CFGH", "%d", cfg.hideclient_to); + tpl_addVar(vars, TPLADD, "CHEADADD", tpl_getTpl(vars, "CLIENTHEADLINEADD")); + } + + tpl_printf(vars, TPLADD, "UCS", "%d", user_count_shown); + tpl_printf(vars, TPLADD, "UCA", "%d", user_count_all); + tpl_addVar(vars, TPLADD, "HIDEIDLE", "5"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "User"); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTHEADLINE")); + tpl_addVar(vars, TPLADD, "CLIENTOPTIONADD", tpl_getTpl(vars, "CLIENTHEADLINEBIT")); + tpl_addVar(vars, TPLADD, "CLIENTHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + tpl_addVar(vars, TPLADD, "CLIENTOPTIONADD", ""); + } + else if(cl->typ == 'r') + { + if(shown) + { + tpl_addVar(vars, TPLAPPEND, "READERSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); + } + + tpl_printf(vars, TPLADD, "RCC", "%d", reader_count_conn); + tpl_printf(vars, TPLADD, "RCA", "%d", reader_count_all); + + if(reader_count_off) + { + tpl_printf(vars, TPLADD, "RCO", "%d", reader_count_all-reader_count_off); + tpl_addVar(vars, TPLADD, "RHEADADD", tpl_getTpl(vars, "CLIENTRHEADLINEADD")); + } + + tpl_addVar(vars, TPLADD, "HIDEIDLE", "3"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "Reader"); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTRHEADLINE")); + tpl_addVar(vars, TPLADD, "READERHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + } + else if(cl->typ == 'p') + { + if(shown) + { + tpl_addVar(vars, TPLAPPEND, "PROXYSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); + } + + tpl_printf(vars, TPLADD, "PCC", "%d", proxy_count_conn); + tpl_printf(vars, TPLADD, "PCA", "%d", proxy_count_all); + + if(proxy_count_off) + { + tpl_printf(vars, TPLADD, "PCO", "%d", proxy_count_all-proxy_count_off); + tpl_addVar(vars, TPLADD, "PHEADADD", tpl_getTpl(vars, "CLIENTPHEADLINEADD")); + } + + tpl_addVar(vars, TPLADD, "HIDEIDLE", "4"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "Proxy"); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTPHEADLINE")); + tpl_addVar(vars, TPLADD, "PROXYHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + } + else if(cl->typ == 'm' || cl->typ == 's' || cl->typ == 'h') + { + if(shown) + { + tpl_addVar(vars, TPLAPPEND, "SERVERSTATUS", tpl_getTpl(vars, "CLIENTSTATUSBIT")); + } + + tpl_addVar(vars, TPLADD, "HIDEIDLE", "2"); + tpl_addVar(vars, TPLADD, "SHOWHIDDEN", "Server"); + + if(cl->typ == 's' || cl->typ == 'h') + { + tpl_printf(vars, TPLADD, "SCS", "%d", server_count_shown); + tpl_printf(vars, TPLADD, "SCA", "%d", server_count_all); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTSHEADLINE")); + + if(shown || cl->wihidden) + { + tpl_addVar(vars, TPLADD, "SERVERHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + } + } + else + { + tpl_addVar(vars, TPLADD, "SHOWHIDDENADD", " & Monitors"); + tpl_printf(vars, TPLADD, "MCS", "%d", monitor_count_shown); + tpl_printf(vars, TPLADD, "MCA", "%d", monitor_count_all); + tpl_addVar(vars, TPLADD, "MHEADADD", tpl_getTpl(vars, "CLIENTMHEADLINE")); + tpl_addVar(vars, TPLADD, "XHEAD", tpl_getTpl(vars, "CLIENTSHEADLINE")); + tpl_addVar(vars, TPLADD, "SERVERHEADLINE", tpl_getTpl(vars, "STATUSHEADLINE")); + tpl_addVar(vars, TPLADD, "SHOWHIDDENADD", ""); + } + } + } + else + { + if(shown) + { + if(apicall == 1) + { + tpl_addVar(vars, TPLAPPEND, "APISTATUSBITS", tpl_getTpl(vars, "APISTATUSBIT")); + } + + if(apicall == 2) + { + tpl_addVar(vars, TPLADD, "JSONARRAYDELIMITER", delimiter?",":""); + tpl_addVar(vars, TPLAPPEND, "JSONSTATUSBITS", tpl_getTpl(vars, "JSONSTATUSBIT")); + delimiter++; + } + } + } + +#ifdef CS_CACHEEX + } +#endif + + } + + LL_ITER itr = ll_iter_create(configured_readers); + struct s_reader *rdrr; + while((rdrr = ll_iter_next(&itr))) + { + if(rdrr->label[0] && rdrr->typ) + { + total_readers += 1; + + if(rdrr->enable) { active_readers += 1; } + else { disabled_readers += 1; } + + if(rdrr->tcp_connected) { connected_readers += 1; } + } + } + + cs_readunlock(__func__, &clientlist_lock); + cs_readunlock(__func__, &readerlist_lock); + + if(cfg.http_status_log || (apicall == 1 && strcmp(getParam(params, "appendlog"), "1") == 0)) + { + if(cfg.loghistorylines && log_history) + { + LL_ITER it = ll_iter_create(log_history); + struct s_log_history *hist; + + while((hist = (struct s_log_history*)ll_iter_next(&it))) + { + char p_usr[32]; + size_t pos1 = strcspn(hist->txt, "\t") + 1; + cs_strncpy(p_usr, hist->txt , pos1 > sizeof(p_usr) ? sizeof(p_usr) : pos1); + + char *p_txt = hist->txt + pos1; + + if(!apicall) + { + if(p_txt[0]) tpl_printf(vars, TPLAPPEND, "LOGHISTORY","\t\t%s\t\t
\n", xml_encode(vars, p_usr), xml_encode(vars, p_txt)); + } + else + { + tpl_addVar(vars, TPLAPPEND, "LOGHISTORY", p_txt); + } + } + } + else + { + tpl_addVar(vars, TPLADD, "LOGHISTORY", "loghistorylines is set to 0 in your configuration"); + } + } + +#ifdef CS_CACHEEX + char *getting = "\"Getting\""; + char *pushing = "\"Pushing\""; + + float cachesum = first_client ? first_client->cwcacheexgot : 1; + if(cachesum < 1) + { + cachesum = 1; + } + tpl_printf(vars, TPLADD, "TOTAL_CACHEXPUSH", "%d", first_client ? first_client->cwcacheexpush : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXPUSH_IMG", pushing); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXGOT", "%d", first_client ? first_client->cwcacheexgot : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXGOT_IMG", getting); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXHIT", "%d", first_client ? first_client->cwcacheexhit : 0); + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE", "%d", cache_size()); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE_LG", "%d", cache_size_lg()); +#endif + tpl_printf(vars, TPLADD, "REL_CACHEXHIT", "%.2f", (first_client ? first_client->cwcacheexhit : 0) * 100 / cachesum); + tpl_addVar(vars, TPLADD, "CACHEEXSTATS", tpl_getTpl(vars, "STATUSCACHEX")); +#endif + //User info + struct s_auth *account; + int32_t total_users = 0; + int32_t disabled_users = 0; + int32_t expired_users = 0; + int32_t expired_or_disabled_users = 0; + int32_t connected_users = 0; + int32_t online_users = 0; + + for(account = cfg.account; (account); account = account->next) + { + total_users++; + if(account->expirationdate && account->expirationdate < now) + { + expired_users++; + } + if(account->disabled != 0) + { + disabled_users++; + } + if((account->expirationdate && account->expirationdate < now)||account->disabled != 0) + { + expired_or_disabled_users++; + } + int32_t latestactivity = 0; + struct s_client *latestclient = NULL; + for(cl = first_client->next; cl ; cl = cl->next) + { + if(cl->account && !strcmp(cl->account->usr, account->usr)) + { + if(cl->lastecm > latestactivity || cl->login > latestactivity) + { + if(cl->lastecm > cl->login) { latestactivity = cl->lastecm; } + else { latestactivity = cl->login; } + latestclient = cl; + } + } + } + + if(latestclient != NULL) + { + connected_users++; + + if(latestactivity > 0) + { + if((now - latestactivity) < cfg.hideclient_to) + { + if(latestclient->cwfound + latestclient->cwnot + latestclient->cwcache > 0) + { + online_users++; + } + } + } + } + } + tpl_printf(vars, TPLADD, "TOTAL_USERS", "%d", total_users); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE", "%d", total_users - expired_or_disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_EXPIRED", "%d", expired_users); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED", "%d", disabled_users); + tpl_printf(vars, TPLADD, "TOTAL_ONLINE", "%d", online_users); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED", "%d", connected_users); + + tpl_printf(vars, TPLADD, "TOTAL_READERS", "%d", total_readers); + tpl_printf(vars, TPLADD, "TOTAL_DISABLED_READERS", "%d", disabled_readers); + tpl_printf(vars, TPLADD, "TOTAL_ACTIVE_READERS", "%d", active_readers); + tpl_printf(vars, TPLADD, "TOTAL_CONNECTED_READERS", "%d", connected_readers); + + //CM info + set_ecm_info(vars); + + //copy struct to p_stat_old for cpu_usage calculation + p_stat_old = p_stat_cur; + +/* + * check_available Bit mapping + * mem 0 total, 1 used & free, 2 buff, cached & free incl. buff, 3 share + * swap 4 total, 5 used & free, + * proc 6 count + * cpu 7 load + * oscam 8 vsize & rssize, 9 cpu user, 10 cpu sys, 11 cpu sum, 12 cpu refreshtime + * unused 13 - 15 +*/ + //Memory-CPU Info for linux based systems +#if defined(__linux__) + //get actual stats + if(!get_stats_linux(getpid(),&p_stat_cur)){ + if(p_stat_old.cpu_total_time != 0){ + calc_cpu_usage_pct(&p_stat_cur, &p_stat_old); + } + } + else{ + //something went wrong, so fill with "N/A" + p_stat_cur.check_available = 65535; + } +#else // if not linux, fill with "N/A" but probably in future gets filled also for other platforms + p_stat_cur.check_available = 65535; +#endif + set_status_info(vars, p_stat_cur); + + if(cfg.http_showmeminfo || cfg.http_showuserinfo || cfg.http_showreaderinfo || cfg.http_showloadinfo || cfg.http_showecminfo || (cfg.http_showcacheexinfo && config_enabled(CS_CACHEEX)) || (cfg.http_showcacheexinfo && config_enabled(CS_CACHEEX_AIO))){ + tpl_addVar(vars, TPLADD, "DISPLAYINFO", "visible"); + } + else{ + tpl_addVar(vars, TPLADD, "DISPLAYINFO", "hidden"); + } + + tpl_addVar(vars, TPLADD, "DISPLAYSYSINFO", cfg.http_showmeminfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYUSERINFO", cfg.http_showuserinfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYREADERINFO", cfg.http_showreaderinfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYLOADINFO", cfg.http_showloadinfo ?"visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYECMINFO", cfg.http_showecminfo ? "visible" : "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYECMINFO_READERS", cfg.http_showecminfo ? "visible" : "hidden"); + + if(cfg.http_showcacheexinfo == 1 && config_enabled(CS_CACHEEX)) + { + if (config_enabled(CS_CACHEEX_AIO)) + { + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXINFO", "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXAIOINFO", "visible"); + } + else + { + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXINFO", "visible"); + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXAIOINFO", "hidden"); + } + } + else{ + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXINFO", "hidden"); + tpl_addVar(vars, TPLADD, "DISPLAYCACHEEXAIOINFO", "hidden"); + } + +#ifdef WITH_DEBUG + if(cfg.http_status_log) + { + // Debuglevel Selector + int32_t i, lvl; + for(i = 0; i < MAX_DEBUG_LEVELS; i++) + { + lvl = 1 << i; + tpl_printf(vars, TPLADD, "TMPC", "DCLASS%d", lvl); + tpl_printf(vars, TPLADD, "TMPV", "DEBUGVAL%d", lvl); + if(cs_dblevel & lvl) + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugls"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel - lvl); + } + else + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugl"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel + lvl); + } + } + + if(cs_dblevel == D_ALL_DUMP) + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugls"); } + else + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugl"); } + + tpl_addVar(vars, TPLADD, "NEXTPAGE", "status.html"); + tpl_addVar(vars, TPLADD, "DCLASS", "debugl"); //default + tpl_printf(vars, TPLADD, "ACTDEBUG", "%d", cs_dblevel); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECTAIO")); +#else + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECT")); +#endif + } +#endif + + if(cfg.http_status_log) + tpl_addVar(vars, TPLADDONCE, "LOG_HISTORY", tpl_getTpl(vars, "LOGHISTORYBIT")); + + if(apicall) + { + if(apicall == 1) + { return tpl_getTpl(vars, "APISTATUS"); } + if(apicall == 2) + { + tpl_printf(vars, TPLADD, "UCS", "%d", user_count_shown); + tpl_printf(vars, TPLADD, "UCA", "%d", user_count_all); + if(cfg.http_hide_idle_clients != 1 && cfg.hideclient_to > 0){ + tpl_printf(vars, TPLADD, "UCAC", "%d", user_count_active); + } + tpl_printf(vars, TPLADD, "CFGH", "%d", cfg.hideclient_to); + tpl_printf(vars, TPLADD, "MCS", "%d", monitor_count_shown); + tpl_printf(vars, TPLADD, "MCA", "%d", monitor_count_all); + tpl_printf(vars, TPLADD, "SCS", "%d", server_count_shown); + tpl_printf(vars, TPLADD, "SCH", "%d", server_count_hidden); + tpl_printf(vars, TPLADD, "SCA", "%d", server_count_all); + tpl_printf(vars, TPLADD, "RCC", "%d", reader_count_conn); + tpl_printf(vars, TPLADD, "RCO", "%d", reader_count_off); + tpl_printf(vars, TPLADD, "RCA", "%d", reader_count_all); + tpl_printf(vars, TPLADD, "PCC", "%d", proxy_count_conn); + tpl_printf(vars, TPLADD, "PCO", "%d", proxy_count_off); + tpl_printf(vars, TPLADD, "PCA", "%d", proxy_count_all); + tpl_printf(vars, TPLADD, "PICONENABLED", "%d", cfg.http_showpicons?1:0); + tpl_printf(vars, TPLADD, "SRVIDFILE", "%s", use_srvid2 ? "oscam.srvid2" : "oscam.srvid"); + return tpl_getTpl(vars, "JSONSTATUS"); + } + } + + return tpl_getTpl(vars, "STATUS"); +} + +static char *send_oscam_services_edit(struct templatevars * vars, struct uriparams * params) +{ + struct s_sidtab *sidtab, *ptr; + char label[sizeof(cfg.sidtab->label)]; + int32_t i; + + setActiveMenu(vars, MNU_SERVICES); + + cs_strncpy(label, strtolower(getParam(params, "service")), sizeof(label)); + ++cfg_sidtab_generation; + for(sidtab = cfg.sidtab; sidtab != NULL && strcmp(label, sidtab->label) != 0; sidtab = sidtab->next) { ; } + + if(sidtab == NULL) + { + i = 1; + while(cs_strlen(label) < 1) + { + snprintf(label, sizeof(label) / sizeof(char) - 1, "newservice%d", i); + for(sidtab = cfg.sidtab; sidtab != NULL && strcmp(label, sidtab->label) != 0; sidtab = sidtab->next) { ; } + if(sidtab != NULL) { label[0] = '\0'; } + ++i; + } + if(!cs_malloc(&sidtab, sizeof(struct s_sidtab))) { return "0"; } + + if(cfg.sidtab == NULL) { cfg.sidtab = sidtab; } + else + { + for(ptr = cfg.sidtab; ptr != NULL && ptr->next != NULL; ptr = ptr->next) { ; } + ptr->next = sidtab; + } + cs_strncpy((char *)sidtab->label, label, sizeof(sidtab->label)); + ++cfg_sidtab_generation; + tpl_addMsg(vars, "New service has been added"); + // Adding is uncritical as the new service is appended to sidtabs.ok/sidtabs.no and accounts/clients/readers have zeros there + if(write_services() != 0) { tpl_addMsg(vars, "Writing services to disk failed!"); } + } + + if(strcmp(getParam(params, "action"), "Save") == 0) + { + for(i = 0; i < (*params).paramcount; i++) + { + if((strcmp((*params).params[i], "action")) && (strcmp((*params).params[i], "service"))) + { + chk_sidtab((*params).params[i], (*params).values[i], sidtab); + } + } + ++cfg_sidtab_generation; + tpl_addMsg(vars, "Services updated"); + // We don't need any refresh here as accounts/clients/readers sidtabs.ok/sidtabs.no are unaffected! + if(write_services() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + + for(sidtab = cfg.sidtab; sidtab != NULL && strcmp(label, sidtab->label) != 0; sidtab = sidtab->next) { ; } + } + + if(sidtab) + { + tpl_addVar(vars, TPLADD, "LABEL", xml_encode(vars, sidtab->label)); + tpl_addVar(vars, TPLADD, "LABELENC", urlencode(vars, sidtab->label)); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "DCRCCHECKED", (sidtab->disablecrccws_only_for_exception == 1) ? "checked" : "" ); + tpl_addVar(vars, TPLADD, "NWCHECKED", (sidtab->no_wait_time == 1) ? "checked" : "" ); + tpl_addVar(vars, TPLADD, "LGOECHECKED", (sidtab->lg_only_exception == 1) ? "checked" : "" ); +#endif + for(i = 0; i < sidtab->num_caid; i++) + { + if(i == 0) { tpl_printf(vars, TPLADD, "CAIDS", "%04X", sidtab->caid[i]); } + else { tpl_printf(vars, TPLAPPEND, "CAIDS", ",%04X", sidtab->caid[i]); } + } + for(i = 0; i < sidtab->num_provid; i++) + { + if(i == 0) { tpl_printf(vars, TPLADD, "PROVIDS", "%06X", sidtab->provid[i]); } + else { tpl_printf(vars, TPLAPPEND, "PROVIDS", ",%06X", sidtab->provid[i]); } + } + for(i = 0; i < sidtab->num_srvid; i++) + { + if(i == 0) { tpl_printf(vars, TPLADD, "SRVIDS", "%04X", sidtab->srvid[i]); } + else { tpl_printf(vars, TPLAPPEND, "SRVIDS", ",%04X", sidtab->srvid[i]); } + } + } +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "SERVICEEDITAIO"); +#else + return tpl_getTpl(vars, "SERVICEEDIT"); +#endif +} + +static void delete_from_SIDTABBITS(SIDTABBITS * orgsidtab, int32_t position, int32_t sidtablength) +{ + if(*orgsidtab) + { + int32_t i; + SIDTABBITS newsidtab = 0; + for(i = 0; i < position; ++i) + { + if(*orgsidtab & ((SIDTABBITS)1 << i)) + { newsidtab |= ((SIDTABBITS)1 << i); } + } + for(; i < sidtablength; ++i) + { + if(*orgsidtab & ((SIDTABBITS)1 << (i + 1))) + { newsidtab |= ((SIDTABBITS)1 << i); } + } + *orgsidtab = newsidtab; + } +} + +static char *send_oscam_services(struct templatevars * vars, struct uriparams * params) +{ + struct s_sidtab *sidtab; + char *service = getParam(params, "service"); + char channame[CS_SERVICENAME_SIZE]; + int32_t i, counter = 0; + + setActiveMenu(vars, MNU_SERVICES); + + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(cfg.http_readonly) + { + tpl_addMsg(vars, "Sorry, Webif is in readonly mode. No deletion will be made!"); + } + else + { + struct s_sidtab *sidtab_prev = NULL; + int32_t sidtablength = -1; + int32_t position = 0; + + // Calculate sidtablength before deletion so that updating sidtabs is faster + for(sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next) + { ++sidtablength; } + + for(sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next) + { + if(strcmp(sidtab->label, service) == 0) + { + struct s_auth *account; + struct s_client *cl; + struct s_reader *rdr; + + if(!sidtab_prev) + { cfg.sidtab = sidtab->next; } + else + { sidtab_prev->next = sidtab->next; } + + for(account = cfg.account; (account); account = account->next) + { + delete_from_SIDTABBITS(&account->sidtabs.ok, position, sidtablength); + delete_from_SIDTABBITS(&account->sidtabs.no, position, sidtablength); + + for(cl = first_client->next; cl ; cl = cl->next) + { + if(account == cl->account) + { + cl->sidtabs.ok = account->sidtabs.ok; + cl->sidtabs.no = account->sidtabs.no; + } + } + } + + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + delete_from_SIDTABBITS(&rdr->sidtabs.ok, position, sidtablength); + delete_from_SIDTABBITS(&rdr->sidtabs.no, position, sidtablength); + delete_from_SIDTABBITS(&rdr->lb_sidtabs.ok, position, sidtablength); + } + free_sidtab(sidtab); + ++counter; + break; + } + sidtab_prev = sidtab; + position++; + } + if(counter > 0) + { + ++cfg_sidtab_generation; + tpl_addMsg(vars, "Service has been deleted!"); + if(write_services() != 0) { tpl_addMsg(vars, "Writing services to disk failed!"); } + } + else { tpl_addMsg(vars, "Sorry but the specified service doesn't exist. No deletion will be made!"); } + } + } + + sidtab = cfg.sidtab; + // Show List + counter = 0; + while(sidtab != NULL) + { + tpl_addVar(vars, TPLADD, "SID", ""); + tpl_printf(vars, TPLADD, "SERVICENUM", "%d", counter + 1); + if((strcmp(getParam(params, "service"), sidtab->label) == 0) && (strcmp(getParam(params, "action"), "list") == 0)) + { + tpl_addVar(vars, TPLADD, "SIDCLASS", "sidlist"); + tpl_addVar(vars, TPLAPPEND, "SID", ""); + for(i = 0; i < sidtab->num_srvid; i++) + { + tpl_printf(vars, TPLAPPEND, "SID", "%04X : %s
", sidtab->srvid[i], + xml_encode(vars, get_servicename(cur_client(), sidtab->srvid[i], sidtab->num_provid ? sidtab->provid[0] : 0, + sidtab->num_caid ? sidtab->caid[0] : 0, channame, sizeof(channame)))); + } + } + else + { + tpl_addVar(vars, TPLADD, "SIDCLASS", ""); + tpl_printf(vars, TPLADD, "SID", "Show Services", urlencode(vars, sidtab->label)); + } + tpl_addVar(vars, TPLADD, "LABELENC", urlencode(vars, sidtab->label)); + tpl_addVar(vars, TPLADD, "LABEL", xml_encode(vars, sidtab->label)); + tpl_addVar(vars, TPLADD, "SIDLIST", tpl_getTpl(vars, "SERVICECONFIGSIDBIT")); + + tpl_addVar(vars, TPLAPPEND, "SERVICETABS", tpl_getTpl(vars, "SERVICECONFIGLISTBIT")); + sidtab = sidtab->next; + counter++; + } + if(counter >= MAX_SIDBITS) + { + tpl_addVar(vars, TPLADD, "BTNDISABLED", "DISABLED"); + tpl_addMsg(vars, "Maximum Number of Services is reached"); + } + return tpl_getTpl(vars, "SERVICECONFIGLIST"); +} + +static char *send_oscam_savetpls(struct templatevars * vars) +{ + if(cfg.http_tpl) + { + tpl_printf(vars, TPLADD, "CNT", "%d", tpl_saveIncludedTpls(cfg.http_tpl)); + tpl_addVar(vars, TPLADD, "PATH", cfg.http_tpl); + } + else { tpl_addVar(vars, TPLADD, "CNT", "0"); } + return tpl_getTpl(vars, "SAVETEMPLATES"); +} + +static char *send_oscam_shutdown(struct templatevars * vars, FILE * f, struct uriparams * params, int8_t apicall, int8_t *keepalive, char *extraheader) +{ + if(!apicall) { setActiveMenu(vars, MNU_SHUTDOWN); } + if(strcmp(strtolower(getParam(params, "action")), "shutdown") == 0) + { + *keepalive = 0; + if(!apicall) + { + char *CSS = tpl_getUnparsedTpl("CSS", 1, ""); + tpl_addVar(vars, TPLADD, "STYLESHEET", CSS); + NULLFREE(CSS); + tpl_printf(vars, TPLADD, "REFRESHTIME", "%d", SHUTDOWNREFRESH); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + tpl_printf(vars, TPLADD, "SECONDS", "%d", SHUTDOWNREFRESH); + char *result = tpl_getTpl(vars, "SHUTDOWN"); + send_headers(f, 200, "OK", extraheader, "text/html", 0, cs_strlen(result), NULL, 0); + webif_write(result, f); + cs_log("Shutdown requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + } + else + { + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "shutdown"); + cs_log("Shutdown requested by XMLApi from %s", cs_inet_ntoa(GET_IP())); + } + cs_exit_oscam(); + + if(!apicall) + { return "1"; } + else + { return tpl_getTpl(vars, "APICONFIRMATION"); } + + } + else if(strcmp(strtolower(getParam(params, "action")), "restart") == 0) + { + *keepalive = 0; + if(!apicall) + { + char *CSS = tpl_getUnparsedTpl("CSS", 1, ""); + tpl_addVar(vars, TPLADD, "STYLESHEET", CSS); + NULLFREE(CSS); + tpl_addVar(vars, TPLADD, "REFRESHTIME", "5"); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + tpl_addVar(vars, TPLADD, "SECONDS", "5"); + char *result = tpl_getTpl(vars, "SHUTDOWN"); + send_headers(f, 200, "OK", extraheader, "text/html", 0, cs_strlen(result), NULL, 0); + webif_write(result, f); + cs_log("Restart requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + } + else + { + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "restart"); + cs_log("Restart requested by XMLApi from %s", cs_inet_ntoa(GET_IP())); + } + cs_restart_oscam(); + + if(!apicall) + { return "1"; } + else + { return tpl_getTpl(vars, "APICONFIRMATION"); } + + } + else + { + return tpl_getTpl(vars, "PRESHUTDOWN"); + } +} + +static char *send_oscam_script(struct templatevars * vars, struct uriparams * params) +{ + setActiveMenu(vars, MNU_SCRIPT); + tpl_printf(vars, TPLADD, "SCRIPTOPTIONS", "\n"); + + if(!cfg.http_readonly && cfg.http_script) + { + struct dirent **namelist; + int count, i; + count = scandir(cfg.http_script, &namelist, 0, alphasort ); + if( count >= 0 ) + { + for( i = 0 ; i < count; i++ ) + { + if(is_ext(namelist[i]->d_name, ".script") || is_ext(namelist[i]->d_name, ".sh")) + { + tpl_printf(vars, TPLAPPEND, "SCRIPTOPTIONS", "\n",namelist[i]->d_name,namelist[i]->d_name); + } + free( namelist[i] ); + } + free(namelist); + } + + char *scriptname = getParam(params, "scriptname"); + char *scriptparam = getParam(params, "scriptparam"); + char system_str[256]; + struct stat s; + snprintf(system_str, sizeof(system_str), "%s/%s", cfg.http_script, scriptname); + + if(!stat(system_str,&s)) + { + if(s.st_mode & S_IFREG) + { + if(s.st_mode & S_IXUSR) + { + int32_t rc = -1; + FILE *fp; + char buf[256]; + + if((scriptparam != NULL) && (sizeof(scriptparam) > 0)) + { + cs_strncat(system_str, " ", sizeof(system_str)); + cs_strncat(system_str, scriptparam, sizeof(system_str)); + } + + fp = popen(system_str, "r"); + if (fp) + { + while (fgets(buf, sizeof(buf), fp) != NULL) + { + tpl_addVar(vars, TPLAPPEND, "SCRIPTRESULTOUT", buf); + } + rc = pclose(fp)/256; + } + + tpl_printf(vars, TPLAPPEND, "CODE", "returncode: %d", rc); + tpl_printf(vars, TPLADD, "SCRIPTNAME", "scriptname: %s", scriptname); + } + else + { + tpl_printf(vars, TPLADD, "SCRIPTRESULTOUT", "[Error]: Script \"%s\" not executable!", scriptname); + } + } + } + else + { + tpl_printf(vars, TPLADD, "SCRIPTRESULTOUT", "[Error]: Script \"%s\" not found!", scriptname); + } + + } + return tpl_getTpl(vars, "SCRIPT"); +} + +static char *send_oscam_scanusb(struct templatevars * vars) +{ + setActiveMenu(vars, MNU_READERS); +#if !defined(__CYGWIN__) + FILE *fp; + char line[100]; + uint8_t i; + char header[11], txt[13], entry[10], bit[8], scan[12], error[120]; + +// key tool package match command + char *elems[] = { "USB", "lsusb", "usbutils", "Bus ", "lsusb -v | egrep '^Bus|^ *iSerial|^ *iProduct'" + , "UDEV", "/dev/serial/by-id", "udev", "<-", "find /dev/serial/by-id -type l -exec readlink -fn {} ';' -exec echo ' <- {}' \\;" +#ifdef CARDREADER_PCSC + , "PCSC", "pcsc_scan", "pcsc-tools", ":", "pcsc_scan -r" +#endif + }; + + for (i = 0; i < (sizeof(elems) / sizeof(elems[0])); i+=5) + { + snprintf(header, sizeof(header), "%sHEADER", elems[i]); //key + snprintf(txt, sizeof(txt), "%s Devices", elems[i]); //key + snprintf(entry, sizeof(entry), "%sENTRY", elems[i]); //key + snprintf(bit, sizeof(bit), "%sBIT", elems[i]); //key + snprintf(scan, sizeof(scan), "SCAN%sBIT", elems[i]); //key + snprintf(error, sizeof(error), "%s: Failed to run or %s package not installed!", elems[i + 1], elems[i + 2]); //tool + package + + tpl_addVar(vars, TPLADD, header, txt); + fp = popen(elems[i + 4], "r"); //command + if(!fp || !fgets(line, sizeof(line) - 1, fp)) + { + tpl_addVar(vars, TPLADD, entry, error); + tpl_addVar(vars, TPLADD, bit, tpl_getTpl(vars, scan)); + } + else + { + do{ + tpl_addVar(vars, TPLADD, "USBENTRYCLASS", ""); + if(strstr(line, elems[i + 3])) //match + { + tpl_addVar(vars, TPLADD, entry, line); + tpl_addVar(vars, TPLADD, "USBENTRYCLASS", "CLASS=\"scanusbsubhead\""); + } + else + { + tpl_printf(vars, TPLADD, entry, "     %s", line); + } + tpl_addVar(vars, TPLAPPEND, bit, tpl_getTpl(vars, scan)); + } + while(fgets(line, sizeof(line) - 1, fp) != NULL); + pclose(fp); + } + } +#else + tpl_addMsg(vars, "Function not supported in CYGWIN environment"); +#endif + return tpl_getTpl(vars, "SCANUSB"); +} + +static void webif_process_logfile(struct templatevars * vars, struct uriparams * params, char *targetfile, size_t targetfile_len) +{ + snprintf(targetfile, targetfile_len, "%s", cfg.logfile); + if(strcmp(getParam(params, "clear"), "logfile") == 0) + { + if(cs_strlen(targetfile) > 0) + { + FILE *file = fopen(targetfile, "w"); + if (file != NULL) + { + fclose(file); + } + } + } +#ifdef WITH_DEBUG + // Debuglevel Selector + int32_t i, lvl; + for(i = 0; i < MAX_DEBUG_LEVELS; i++) + { + lvl = 1 << i; + tpl_printf(vars, TPLADD, "TMPC", "DCLASS%d", lvl); + tpl_printf(vars, TPLADD, "TMPV", "DEBUGVAL%d", lvl); + if(cs_dblevel & lvl) + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugls"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel - lvl); + } + else + { + tpl_addVar(vars, TPLADD, tpl_getVar(vars, "TMPC"), "debugl"); + tpl_printf(vars, TPLADD, tpl_getVar(vars, "TMPV"), "%d", cs_dblevel + lvl); + } + } + if(cs_dblevel == D_ALL_DUMP) + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugls"); } + else + { tpl_addVar(vars, TPLADD, "DCLASS65535", "debugl"); } + tpl_addVar(vars, TPLADD, "CUSTOMPARAM", "&file=logfile"); + tpl_printf(vars, TPLADD, "ACTDEBUG", "%d", cs_dblevel); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECTAIO")); +#else + tpl_addVar(vars, TPLADD, "SDEBUG", tpl_getTpl(vars, "DEBUGSELECT")); +#endif + tpl_addVar(vars, TPLADD, "NEXTPAGE", "files.html"); +#endif + if(!cfg.disablelog) + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 1); + tpl_addVar(vars, TPLADD, "TEXT", "Stop Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUDISABLELOG")); + + } + else + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 0); + tpl_addVar(vars, TPLADD, "TEXT", "Start Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUDISABLELOG")); + } + tpl_addVar(vars, TPLAPPEND, "LOGMENU", tpl_getTpl(vars, "CLEARLOG")); + return; +} + +static void webif_process_userfile(struct templatevars * vars, struct uriparams * params, char *targetfile, size_t targetfile_len) +{ + snprintf(targetfile, targetfile_len, "%s", cfg.usrfile); + if(strcmp(getParam(params, "clear"), "usrfile") == 0) + { + if(cs_strlen(targetfile) > 0) + { + FILE *file = fopen(targetfile, "w"); + if (file) + { + fclose(file); + } + } + } + if(!cfg.disableuserfile) + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 1); + tpl_addVar(vars, TPLADD, "TEXT", "Stop Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUONOFF")); + } + else + { + tpl_printf(vars, TPLADD, "SWITCH", "%d", 0); + tpl_addVar(vars, TPLADD, "TEXT", "Start Log"); + tpl_addVar(vars, TPLADD, "LOGMENU", tpl_getTpl(vars, "LOGMENUONOFF")); + } + tpl_addVar(vars, TPLAPPEND, "LOGMENU", tpl_getTpl(vars, "CLEARUSERLOG")); + tpl_addVar(vars, TPLADD, "FFVAL", "all"); + tpl_addVar(vars, TPLADD, "FILTERFORMOPTIONS", tpl_getTpl(vars, "LOGMENUFILTERFORM")); + struct s_auth *account; + for(account = cfg.account; account; account = account->next) + { + tpl_addVar(vars, TPLADD, "FFVAL", xml_encode(vars, account->usr)); + tpl_addVar(vars, TPLADD, "FFSEL", strcmp(getParam(params, "filter"), account->usr) ? "" : "selected"); + tpl_addVar(vars, TPLAPPEND, "FILTERFORMOPTIONS", tpl_getTpl(vars, "LOGMENUFILTERFORM")); + } + tpl_addVar(vars, TPLADD, "FILTERFORM", tpl_getTpl(vars, "FILTERFORM")); +} + +enum file_types { FTYPE_CONFIG, FTYPE_VERSION, FTYPE_ANTICASC, FTYPE_LOGFILE, FTYPE_USERFILE , FTYPE_GBOX }; + +struct files +{ + char *file; + int menu_id; + enum file_types type; +}; + +static char *send_oscam_files(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + bool writable = false; + const struct files *entry; + static struct files config_files[] = + { + // id are used + // new entry after last entry before first ifdef entry + // ifdef must be add to end + { "oscam.version", MNU_CFG_FVERSION, FTYPE_VERSION }, // id 0 + { "oscam.conf", MNU_CFG_FCONF, FTYPE_CONFIG }, // id 1 + { "oscam.user", MNU_CFG_FUSER, FTYPE_CONFIG }, // id 2 + { "oscam.server", MNU_CFG_FSERVER, FTYPE_CONFIG }, // id 3 + { "oscam.srvid", MNU_CFG_FSRVID, FTYPE_CONFIG }, // id 4 + { "oscam.srvid2", MNU_CFG_FSRVID2, FTYPE_CONFIG }, // id 5 + { "logfile", MNU_CFG_FLOGFILE, FTYPE_LOGFILE }, // id 6 + { "userfile", MNU_CFG_FUSERFILE, FTYPE_USERFILE }, // id 7 + { "css_file_name", MNU_CFG_FCSS, FTYPE_CONFIG }, // id 8 + { "oscam.services", MNU_CFG_FSERVICES, FTYPE_CONFIG }, // id 9 + { "oscam.provid", MNU_CFG_FPROVID, FTYPE_CONFIG }, // id 10 + { "oscam.tiers", MNU_CFG_FTIERS, FTYPE_CONFIG }, // id 11 + { "oscam.ratelimit", MNU_CFG_FRATELIMIT,FTYPE_CONFIG }, // id 12 + { "oscam.whitelist", MNU_CFG_FWHITELIST,FTYPE_CONFIG }, // id 13 +#ifdef HAVE_DVBAPI + { "oscam.dvbapi", MNU_CFG_FDVBAPI, FTYPE_CONFIG }, // id 14 +#endif +#ifdef CS_CACHEEX + { "oscam.fakecws", MNU_CFG_FFAKECWS, FTYPE_CONFIG }, // id 15 +#endif +#ifdef CS_ANTICASC + { "anticasc", MNU_CFG_FACLOG, FTYPE_ANTICASC }, // id 16 +#endif +#ifdef MODULE_SERIAL + { "oscam.twin", MNU_CFG_FTWIN, FTYPE_CONFIG }, // id 17 +#endif +#ifdef MODULE_CONSTCW + { "constant.cw", MNU_CFG_FKEYCW, FTYPE_CONFIG }, // id 18 +#endif +#ifdef MODULE_GBOX + { "sc.info", MNU_GBX_FSCINF, FTYPE_GBOX }, // id 19 + { "share.info", MNU_GBX_FSHRINF, FTYPE_GBOX }, // id 20 + { "share.onl", MNU_GBX_FSHRONL, FTYPE_GBOX }, // id 21 + { "gbox.ver", MNU_GBX_FVERS, FTYPE_GBOX }, // id 22 + { "attack.txt", MNU_GBX_FATTACK, FTYPE_GBOX }, // id 23 + { "gsms.log", MNU_GBX_FSMSLOG, FTYPE_GBOX }, // id 24 + { "gsms.ack", MNU_GBX_FSMSACK, FTYPE_GBOX }, // id 25 + { "gsms.nack", MNU_GBX_FSMSNACK, FTYPE_GBOX }, // id 26 + { "stats.info", MNU_GBX_FSTAINF, FTYPE_GBOX }, // id 27 + { "expired.info", MNU_GBX_FEXPINF, FTYPE_GBOX }, // id 28 + { "info.log", MNU_GBX_INFOLOG, FTYPE_GBOX }, // id 29 +#endif + { NULL, 0, 0 }, + }; + + if(use_srvid2) + { + config_files[4].menu_id = MNU_CFG_FSRVID2; + config_files[5].menu_id = MNU_CFG_FSRVID; + } + + if(cfg.http_css) + { + if(strchr(cfg.http_css,'/')) + config_files[8].file = strrchr(cfg.http_css, '/')+1; + else if(strchr(cfg.http_css,'\\')) + config_files[8].file = strrchr(cfg.http_css, '\\')+1; + else + config_files[8].file = cfg.http_css; + tpl_addVar(vars, TPLADD, "FILE_USER_CSS", xml_encode(vars, config_files[8].file)); + } + + if(!apicall) { setActiveMenu(vars, MNU_FILES); } + + tpl_addVar(vars, TPLADD, "APIFILENAME", "null"); + tpl_addVar(vars, TPLADD, "APIWRITABLE", "0"); + + if(use_srvid2) + { + tpl_printf(vars, TPLADD, "SRVID", "%s", "oscam.srvid2"); + tpl_printf(vars, TPLADD, "SRVIDSUB", "%s", "oscam.srvid"); + } + else + { + tpl_printf(vars, TPLADD, "SRVID", "%s", "oscam.srvid"); + tpl_printf(vars, TPLADD, "SRVIDSUB", "%s", "oscam.srvid2"); + } + + char *stoplog = getParam(params, "stoplog"); + if(cs_strlen(stoplog) > 0) + { cs_disable_log(atoi(stoplog)); } + + char *stopusrlog = getParam(params, "stopusrlog"); + if(cs_strlen(stopusrlog) > 0) + { cfg.disableuserfile = atoi(stopusrlog); } + + char *debuglvl = getParam(params, "debug"); + if(cs_strlen(debuglvl) > 0) + { +#ifndef WITH_DEBUG + cs_log("*** Warning: Debug Support not compiled in ***"); +#else + int32_t dblvl = atoi(debuglvl); + if(dblvl >= 0 && dblvl <= 65535) { cs_dblevel = dblvl; } + cs_log("%s debug_level=%d", "all", cs_dblevel); +#endif + } + // Process config files + char *file = getParam(params, "file"); + char targetfile[256] = { 0 }; + int menu_id = 0; + for(entry = config_files; entry->file; entry++) + { + if(streq(file, entry->file)) + { + if(!apicall) { setActiveSubMenu(vars, entry->menu_id); } + menu_id = entry->menu_id; + tpl_addVar(vars, TPLADD, "APIWRITABLE", writable ? "1" : "0"); + switch(entry->type) + { + case FTYPE_CONFIG: + writable = 1; + get_config_filename(targetfile, sizeof(targetfile), entry->file); + break; + case FTYPE_VERSION: + get_tmp_dir_filename(targetfile, sizeof(targetfile), entry->file); + break; + case FTYPE_ANTICASC: +#ifdef CS_ANTICASC + if(!apicall) { snprintf(targetfile, sizeof(targetfile), "%s", ESTR(cfg.ac_logfile)); } +#endif + break; + case FTYPE_LOGFILE: + if(!apicall) { webif_process_logfile(vars, params, targetfile, sizeof(targetfile)); } + break; + case FTYPE_USERFILE: + if(!apicall) { webif_process_userfile(vars, params, targetfile, sizeof(targetfile)); } + break; + case FTYPE_GBOX: +#ifdef MODULE_GBOX + get_gbox_filename(targetfile, sizeof(targetfile), entry->file); +#endif + break; + } + tpl_addVar(vars, TPLADD, "APIFILENAME", entry->file); + break; + } + } + + if(cfg.http_css) + { + tpl_addVar(vars, TPLADD, "VIEW_FILEMENUCSS", tpl_getTpl(vars, "FILEMENUCSS")); + } + + if(!strstr(targetfile, "/dev/")) + { + if(strcmp(getParam(params, "action"), "Save") == 0) + { + if((cs_strlen(targetfile) > 0) /*&& (file_exists(targetfile) == 1)*/) + { + FILE *fpsave; + char *fcontent = getParam(params, "filecontent"); + if((fpsave = fopen(targetfile, "w"))) + { + int32_t i, lastpos = 0, len = cs_strlen(fcontent) + 1; + //write submitted file line by line to disk and remove windows linebreaks + for(i = 0; i < len; ++i) + { + char tmp = fcontent[i]; + if(tmp == '\r' || tmp == '\n' || tmp == 0) + { + fcontent[i] = 0; + fprintf(fpsave, "%s%s", fcontent + lastpos, tmp == 0 ? "" : "\n"); + if(tmp == '\r' && fcontent[i + 1] == '\n') { ++i; } + lastpos = i + 1; + } + } + fclose(fpsave); + tpl_addVar(vars, TPLAPPEND, "APIFILENAME", " saved!"); + // Reinit on save + switch(menu_id) + { + case MNU_CFG_FSRVID: + case MNU_CFG_FSRVID2: + init_srvid(); + break; + case MNU_CFG_FPROVID: + init_provid(); + break; + case MNU_CFG_FUSER: + cs_accounts_chk(); + break; + case MNU_CFG_FDVBAPI: + dvbapi_read_priority(); + break; + case MNU_CFG_FWHITELIST: + global_whitelist_read(); + break; + case MNU_CFG_FFAKECWS: + init_fakecws(); + break; + default: + break; + } + } + } + } + + if((cs_strlen(targetfile) > 0) && (file_exists(targetfile) == 1)) + { + FILE *fp; + char buffer[256]; + + if((fp = fopen(targetfile, "r")) == NULL) { return "0"; } + while(fgets(buffer, sizeof(buffer), fp) != NULL) + if(!strcmp(getParam(params, "filter"), "all")) + { tpl_addVar(vars, TPLAPPEND, "FILECONTENT", buffer); } + else if(strstr(buffer, getParam(params, "filter"))) + { tpl_addVar(vars, TPLAPPEND, "FILECONTENT", buffer); } + fclose(fp); + } + else + { + tpl_addVar(vars, TPLAPPEND, "FILECONTENT", "File does not exist or no file selected!"); + } + } + else + { + tpl_addVar(vars, TPLAPPEND, "FILECONTENT", "File not valid!"); + } + + tpl_addVar(vars, TPLADD, "PART", file); + + if(!writable) + { + tpl_addVar(vars, TPLADD, "WRITEPROTECTION", tpl_getTpl(vars, "WRITEPROTECTION")); + tpl_addVar(vars, TPLADD, "BTNDISABLED", "DISABLED"); + } + + if(!apicall) + { return tpl_getTpl(vars, "FILE"); } + else + { return tpl_getTpl(vars, "APIFILE"); } +} + +static char *send_oscam_failban(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + IN_ADDR_T ip2delete; + set_null_ip(&ip2delete); + LL_ITER itr = ll_iter_create(cfg.v_list); + V_BAN *v_ban_entry; + //int8_t apicall = 0; //remove before flight + + if(!apicall) { setActiveMenu(vars, MNU_FAILBAN); } + + if(strcmp(getParam(params, "action"), "delete") == 0) + { + if(strcmp(getParam(params, "intip"), "all") == 0) + { + // clear whole list + while(ll_iter_next(&itr)) + { + ll_iter_remove_data(&itr); + } + } + else + { + //we have a single IP + cs_inet_addr(getParam(params, "intip"), &ip2delete); + while((v_ban_entry = ll_iter_next(&itr))) + { + if(IP_EQUAL(v_ban_entry->v_ip, ip2delete)) + { + ll_iter_remove_data(&itr); + break; + } + } + } + } + ll_iter_reset(&itr); + + struct timeb now; + cs_ftime(&now); + + while((v_ban_entry = ll_iter_next(&itr))) + { + tpl_printf(vars, TPLADD, "IPADDRESS", "%s@%d", cs_inet_ntoa(v_ban_entry->v_ip), v_ban_entry->v_port); + tpl_addVar(vars, TPLADD, "VIOLATIONUSER", v_ban_entry->info ? v_ban_entry->info : "unknown"); + struct tm st ; + localtime_r(&v_ban_entry->v_time.time, &st); // fix me, we need walltime! + if(!apicall) + { + tpl_printf(vars, TPLADD, "VIOLATIONDATE", "%02d.%02d.%02d %02d:%02d:%02d", + st.tm_mday, st.tm_mon + 1, + st.tm_year % 100, st.tm_hour, + st.tm_min, st.tm_sec); + } + else + { + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &st); + tpl_addVar(vars, TPLADD, "VIOLATIONDATE", tbuffer); + } + + tpl_printf(vars, TPLADD, "VIOLATIONCOUNT", "%d", v_ban_entry->v_count); + + int64_t gone = comp_timeb(&now, &v_ban_entry->v_time); + if(!apicall) + { + if(!v_ban_entry->acosc_entry) + { tpl_addVar(vars, TPLADD, "LEFTTIME", sec2timeformat(vars, (cfg.failbantime * 60) - (gone / 1000))); } + else + { tpl_addVar(vars, TPLADD, "LEFTTIME", sec2timeformat(vars, v_ban_entry->acosc_penalty_dur - (gone / 1000))); } + } + else + { + if(!v_ban_entry->acosc_entry) + { tpl_printf(vars, TPLADD, "LEFTTIME", "%"PRId64"", (cfg.failbantime * 60) - (gone / 1000)); } + else + { tpl_printf(vars, TPLADD, "LEFTTIME", "%"PRId64"", v_ban_entry->acosc_penalty_dur - (gone / 1000)); } + } + + tpl_addVar(vars, TPLADD, "INTIP", cs_inet_ntoa(v_ban_entry->v_ip)); + + if(!apicall) + { tpl_addVar(vars, TPLAPPEND, "FAILBANROW", tpl_getTpl(vars, "FAILBANBIT")); } + else + { tpl_addVar(vars, TPLAPPEND, "APIFAILBANROW", tpl_getTpl(vars, "APIFAILBANBIT")); } + } + if(!apicall) + { return tpl_getTpl(vars, "FAILBAN"); } + else + { return tpl_getTpl(vars, "APIFAILBAN"); } +} + +static bool send_EMM(struct s_reader * rdr, uint16_t caid, const struct s_cardsystem *csystem, const uint8_t *emmhex, uint32_t len) +{ + if(NULL != rdr && NULL != emmhex && 0 != len) + { + EMM_PACKET *emm_pack = NULL; + + if(cs_malloc(&emm_pack, sizeof(EMM_PACKET))) + { + struct s_client *webif_client = cur_client(); + webif_client->grp = 0xFF; /* to access to all readers */ + + memset(emm_pack, '\0', sizeof(EMM_PACKET)); + emm_pack->client = webif_client; + emm_pack->emmlen = len; + memcpy(emm_pack->emm, emmhex, len); + + emm_pack->caid[0] = (caid >> 8) & 0xFF; + emm_pack->caid[1] = caid & 0xFF; + + if(csystem && csystem->get_emm_type) + { + if(!csystem->get_emm_type(emm_pack, rdr)) + { + rdr_log_dbg(rdr, D_EMM, "get_emm_type() returns error"); + } + } + + cs_log_dbg(D_EMM, "emm is being sent to reader %s.", rdr->label); + add_job(rdr->client, ACTION_READER_EMM, emm_pack, sizeof(EMM_PACKET)); + return true; + } + } + + return false; +} + +static bool process_single_emm(struct templatevars * vars, struct s_reader * rdr, uint16_t caid, const struct s_cardsystem *csystem, const char *ep) +{ + + if(NULL != vars && NULL != rdr && NULL != ep) + { + char emmdata[1025] = {'\0'}; /*1024 + '\0'*/ + uint8_t emmhex[513] = {'\0'}; + char buff[7] = {'\0'}; + uint16_t len = 0; + cs_strncpy(emmdata, ep, sizeof(emmdata)); + remove_white_chars(emmdata); + + if('\0' != emmdata[0]) + { + len = cs_strlen(emmdata); + tpl_addVar(vars, TPLADD, "EP", strtoupper(emmdata)); + if(key_atob_l(emmdata, emmhex, len)) + { + tpl_addMsg(vars, "Single EMM has not been sent due to wrong value!"); + } + else + { + len /= 2; + snprintf(buff, sizeof(buff), "0x%02X", len); + tpl_addVar(vars, TPLADD, "EP", strtoupper(emmdata)); + tpl_addVar(vars, TPLADD, "SIZE", buff); + + if(send_EMM(rdr, caid, csystem, emmhex, len)) + { + tpl_addMsg(vars, "Single EMM has been sent."); + return true; + } + } + } + } + tpl_addVar(vars, TPLADD, "SIZE", "0x00"); + return false; +} + +static bool process_emm_file(struct templatevars * vars, struct s_reader * rdr, uint16_t caid, const struct s_cardsystem *csystem, const char *sFilePath) +{ + + bool bret = false; + uint32_t fsize = 0; + uint32_t rlines = 0; + uint32_t wemms = 0; + uint32_t errsize = 0; + char numerrl[256] = {'\0'}; + char buff[20] = {'\0'}; + + if(NULL != rdr && NULL != sFilePath && '\0' != sFilePath[0]) + { + char sMessage[128] = {0}; + if(true == file_exists(sFilePath)) + { + FILE *fp; + if((fp = fopen(sFilePath, "r"))) + { + char line[2048] = {'\0'}; + uint8_t emmhex[513] = {'\0'}; + uint32_t len = 0; + + tpl_addMsg(vars, "EMM file has been processed."); + while(fgets(line, sizeof(line), fp)) + { + ++rlines; + len = cs_strlen(remove_white_chars(line)); + + // wrong emm + if(len > (sizeof(emmhex) * 2) || + key_atob_l(line, emmhex, len)) + { + errsize += snprintf(numerrl + errsize, sizeof(numerrl) - errsize, "%d, ", rlines); + continue; + } + len /= 2; + if(send_EMM(rdr, caid, csystem, emmhex, len)) + { + ++wemms; + int32_t jcount = ll_count(rdr->client->joblist); + if (jcount > 200) + { + /* Give more time to process EMMs */ + cs_sleepms(1000); + } + rdr_log_dbg(rdr, D_READER, "pending emm jobs: %i, processed emms: %i", jcount, wemms); + } + } + fsize = ftell(fp); + fclose(fp); + } + else + { + snprintf(sMessage, sizeof(sMessage), "Cannot open file '%s' (errno=%d: %s)\n", sFilePath, errno, strerror(errno)); + tpl_addMsg(vars, sMessage); + } + } + else + { + snprintf(sMessage, sizeof(sMessage), "FILE \"%s\" not found!", sFilePath); + tpl_addMsg(vars, sMessage); + } + bret = true; + } + + snprintf(buff, sizeof(buff), "%d bytes", fsize); + tpl_addVar(vars, TPLADD, "FSIZE", buff); + snprintf(buff, sizeof(buff), "%d", rlines); + tpl_addVar(vars, TPLADD, "NUMRLINE", buff); + snprintf(buff, sizeof(buff), "%d", wemms); + tpl_addVar(vars, TPLADD, "NUMWEMM", buff); + tpl_addVar(vars, TPLADD, "ERRLINE", numerrl); + + return bret; +} + +static char *send_oscam_EMM_running(struct templatevars * vars, struct uriparams * params) +{ + + struct s_reader *rdr = NULL; + + setActiveMenu(vars, MNU_READERS); + tpl_addVar(vars, TPLADD, "READER", getParam(params, "label")); + tpl_addVar(vars, TPLADD, "FNAME", getParam(params, "emmfile")); + + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + int32_t tcaid = dyn_word_atob(getParam(params, "emmcaid")); + uint16_t caid = (-1 != tcaid) ? (uint16_t)tcaid : 0; + char buff[7] = ""; + const struct s_cardsystem *csystem = NULL; + int32_t proxy = is_cascading_reader(rdr); + + if((proxy || !rdr->csystem_active) && caid) // network reader (R_CAMD35 R_NEWCAMD R_CS378X R_CCCAM) + { + if(proxy && !rdr->ph.c_send_emm) + { + tpl_addMsg(vars, "The reader does not support EMM's!"); + return tpl_getTpl(vars, "EMM_RUNNING"); + } + + csystem = get_cardsystem_by_caid(caid); + if(!csystem) + { + rdr_log_dbg(rdr, D_EMM, "unable to find cardsystem for caid %04X", caid); + caid = 0; + } + } + else if(!proxy && rdr->csystem_active) // local active reader + { + csystem = rdr->csystem; + caid = rdr->caid; + } + + if(csystem) + { + tpl_addVar(vars, TPLADD, "SYSTEM", csystem->desc); + } + else + { + tpl_addVar(vars, TPLADD, "SYSTEM", "unknown"); + } + if(caid) + { + snprintf(buff, sizeof(buff), "0x%04X", caid); + tpl_addVar(vars, TPLADD, "CAID", buff); + } + else + { + tpl_addVar(vars, TPLADD, "CAID", "unknown"); + } + + process_single_emm(vars, rdr, caid, csystem, getParam(params, "ep")); + process_emm_file(vars, rdr, caid, csystem, getParam(params, "emmfile")); + } + else + { + char sMessage[128] = {0}; + snprintf(sMessage, sizeof(sMessage), "READER \"%s\" not found!", getParam(params, "label")); + tpl_addMsg(vars, sMessage); + tpl_addVar(vars, TPLADD, "READER", "reader not found"); + } + + return tpl_getTpl(vars, "EMM_RUNNING"); +} + +static char *send_oscam_EMM(struct templatevars * vars, struct uriparams * params) +{ + + setActiveMenu(vars, MNU_READERS); + tpl_addVar(vars, TPLADD, "READER", getParam(params, "label")); + + struct s_reader *rdr = NULL; + rdr = get_reader_by_label(getParam(params, "label")); + if(rdr && rdr->caid) + { + char buff[5] = ""; + snprintf(buff, sizeof(buff), "%04X", rdr->caid); + tpl_addVar(vars, TPLADD, "CAID", buff); + if(!is_cascading_reader(rdr)) + { + tpl_addVar(vars, TPLADD, "READONLY", "readonly=\"readonly\""); + } + } + + FILE *fp; + struct stat sb; + char buffer[1024]; + char emm_hex[1024]; + char filename[128]; + char targetfile[256]; + char tmpstr[20]; + char emm_txt[32]; + char emm_title[32]; + char *emm_path; + char *emm_types[] = { "unique_emm", "shared_emm", "global_emm" }; + char *emm_names[] = { "RDREMMUNIQUE", "RDREMMSHARED", "RDREMMGLOBAL" }; + char *emm_cfg_names[] = { "httpemmuclean", "httpemmsclean", "httpemmgclean" }; + int32_t emm_max_size[] = { cfg.http_emmu_clean, cfg.http_emms_clean, cfg.http_emmg_clean }; + int num_emm_types = 3; + int i; + + emm_path = cfg.emmlogdir ? cfg.emmlogdir : cs_confdir; + + for( i = 0 ; i < num_emm_types; i++ ) + { + snprintf(filename, sizeof(filename), "%s%s%s%s", getParam(params, "label"), "_", emm_types[i], ".log"); + snprintf(targetfile, sizeof(targetfile), "%s%s%s", emm_path, emm_path[cs_strlen(emm_path) - 1] == '/' ? "" : "/", filename); + snprintf(emm_txt, sizeof(emm_txt), "%s_TXT", emm_names[i]); + tpl_addVar(vars, TPLADD, emm_txt, filename); + + if((fp = fopen(targetfile, "r")) != NULL) + { + stat(targetfile, &sb); + tpl_printf(vars, TPLAPPEND, emm_txt, " (Size: %'.2f kB)", (double)sb.st_size/1024); + + if(emm_max_size[i]>=0) + { + snprintf(emm_title, sizeof(emm_title), "%s_TITLE", emm_names[i]); + tpl_addVar(vars, TPLADD, emm_title, "title=\"Click Line to Copy EMM in Single EMM Write Field\""); + } + + if(emm_max_size[i]>0) + { + uint32_t emms=0, emm_d, emmrs=0; + char *ptr, *saveptr1 = NULL; + + while(fgets(buffer, sizeof(buffer), fp) != NULL) + { + emms++; + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emms); + tpl_addVar(vars, TPLADD, tmpstr, buffer); + } + + for(emm_d=emms;emm_d>0;--emm_d) + { + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emm_d); + if(sscanf(tpl_getVar(vars, tmpstr), "%*s %*s %*s %s", &emm_hex[0])==1) + { + if(strstr(tpl_getVar(vars, "EMM_TMP"),emm_hex)==0) + { tpl_addVar(vars, TPLAPPEND, "EMM_TMP", tpl_getVar(vars, tmpstr)); } + tpl_addVar(vars, TPLADD, tmpstr, ""); + } + } + + for(ptr = strtok_r(tpl_getVar(vars, "EMM_TMP"),"\n", &saveptr1); ptr; ptr = strtok_r(NULL,"\n", &saveptr1)) + { + emmrs++; + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emmrs); + tpl_addVar(vars, TPLADD, tmpstr, ptr); + } + tpl_addVar(vars, TPLADD, "EMM_TMP", ""); + + tpl_printf(vars, TPLAPPEND, emm_txt, ": %'d different EMM's from a total of %'d Entries", emmrs,emms); + for(emm_d=emmrs;emm_d>0;--emm_d) + { + snprintf(tmpstr, sizeof(tmpstr), "LINE_%d", emm_d); + tpl_printf(vars, TPLAPPEND, emm_names[i], "%s\n", tpl_getVar(vars, tmpstr)); + if(sb.st_size>emm_max_size[i]*1024) { tpl_printf(vars, TPLAPPEND, "EMM_TMP", "%s\n", tpl_getVar(vars, tmpstr)); } + tpl_addVar(vars, TPLADD, tmpstr, ""); + } + + if(sb.st_size>emm_max_size[i]*1024) + { + char orgfile[268]; + int f=0; + do { + snprintf(orgfile, sizeof(orgfile), "%s.%d", targetfile, f); + f++; + } while(access(orgfile, F_OK) != -1); + + if(rename(targetfile, orgfile) == 0) + { + FILE *fs = fopen(targetfile, "w"); + if (fs) + { + fprintf(fs, "%s", tpl_getVar(vars, "EMM_TMP")); + fclose(fs); + } + tpl_printf(vars, TPLAPPEND, emm_txt, "
New reduced File created! Size of Original File is higher as %d kB, saved to %s", emm_max_size[i], orgfile); + } + } + tpl_addVar(vars, TPLADD, "EMM_TMP", ""); + } + else if (emm_max_size[i]==0) + { + while(fgets(buffer, sizeof(buffer), fp) != NULL) + { + tpl_printf(vars, TPLAPPEND, emm_names[i], "%s", buffer); + } + } + else + { + tpl_printf(vars, TPLADD, emm_names[i],"Viewing of EMM File deactivated.
Set %s in Config Webif to 0 or higher for viewing or filtering EMM File.", emm_cfg_names[i]); + } + fclose(fp); + } + if(strcmp(tpl_getVar(vars, emm_names[i]),"")==0) { tpl_addVar(vars, TPLADD, emm_names[i],"no saved EMM's"); } + } + + return tpl_getTpl(vars, "ASKEMM"); +} + +#ifdef CS_CACHEEX +static uint64_t get_cacheex_node(struct s_client * cl) +{ + uint64_t node = 0x00; +#if defined(MODULE_CCCAM) || defined(MODULE_CAMD35) || defined(MODULE_CAMD35_TCP) + struct s_module *module = (cl->reader ? &cl->reader->ph : get_module(cl)); +#endif +#ifdef MODULE_CCCAM + if(module->num == R_CCCAM && cl->cc) + { + struct cc_data *cc = cl->cc; + memcpy(&node, cc->peer_node_id, 8); + } + else +#endif +#ifdef MODULE_CAMD35 + if(module->num == R_CAMD35) + { + memcpy(&node, cl->ncd_skey, 8); + } + else +#endif +#ifdef MODULE_CAMD35_TCP + if(module->num == R_CS378X) + { + memcpy(&node, cl->ncd_skey, 8); + } + else +#endif + {} + return node; +} + + +static char *send_oscam_cacheex(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + + if(!apicall) { setActiveMenu(vars, MNU_CACHEEX); } + + if(strcmp(getParam(params, "x"), "x") == 0) + { + // avoid compilerwarning unused vars + } + char *level[] = {"NONE", "CACHE PULL", "CACHE PUSH", "REVERSE CACHE PUSH"}; + char *getting = "\"Getting\""; + char *pushing = "\"Pushing\""; + char *rowvariable = ""; + + int16_t written = 0; +#ifdef CS_CACHEEX_AIO + int16_t i = 0; +#endif + struct s_client *cl; + time_t now = time((time_t *)0); + int delimiter=0; + + if(!apicall) + { + if(strcmp(getParam(params, "action"), "resetallcacheexstats") == 0) + { + cacheex_clear_all_stats(); + } + } + + tpl_printf(vars, TPLADD, "OWN_CACHEEX_NODEID", "%" PRIu64 "X", cacheex_node_id(cacheex_peer_id)); + + const char *cacheex_name_link_tpl = NULL; + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + + for(cl = first_client; cl ; cl = cl->next) + { +#ifdef CS_CACHEEX_AIO + i++; + char classname[9]; + snprintf(classname, 8, "class%02d", i) < 0 ? abort() : (void)0; + classname[8] = '\0'; + tpl_addVar(vars, TPLADD, "CLASSNAME", classname); +#endif + + if(cl->typ == 'c' && cl->account && cl->account->cacheex.mode) + { + cacheex_name_link_tpl = "SUSER"; + tpl_addVar(vars, TPLADD, "TYPE", "Client"); + if(!apicall) + { + tpl_addVar(vars, TPLADD, "USERNAME", xml_encode(vars, cl->account->usr)); + tpl_addVar(vars, TPLADD, "USERENC", urlencode(vars, cl->account->usr)); + + if(cl->account->description) + { + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->account->description)); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + else + { + tpl_addVar(vars, TPLADD, "NAME", cl->account->usr); + + if(cl->account->description) + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", cl->account->description); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + + + { tpl_addVar(vars, TPLADD, "IP", ""); } + + +// --- NODEID changer detection (persistent, based on cxnodeid_changer_detected) --- +int idx = cl ? cl->idx : 0; +if(idx < 0 || idx >= WEBIF_MAX_NODES) idx = 0; + +uint64_t current_node = get_cacheex_node(cl); + +if(cl && cl->cxnodeid_changer_detected) +{ + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node); +} +else +{ + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node); +} + +// zapamiętaj nodeid w RAM (optional) +webif_last_nodeid[idx] = current_node; + +// --- end detection --- + if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); } + else if(cl->login > cl->logout) + { tpl_addVar(vars, TPLADD, "IP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "IP", ""); } + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", get_cacheex_node(cl)); + tpl_addVar(vars, TPLADD, "LEVEL", level[cl->account->cacheex.mode]); + tpl_printf(vars, TPLADD, "PUSH", "%d", cl->account->cwcacheexpush); + tpl_printf(vars, TPLADD, "GOT", "%d", cl->account->cwcacheexgot); + tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->account->cwc_info); + tpl_printf(vars, TPLADD, "HIT", "%d", cl->account->cwcacheexhit); + tpl_printf(vars, TPLADD, "ERR", "%d", cl->account->cwcacheexerr); + tpl_printf(vars, TPLADD, "ERRCW", "%d", cl->account->cwcacheexerrcw); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->account->cwcacheexgotlg); + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->account->cwcacheexpushlg); + tpl_printf(vars, TPLADD, "REL_CACHEXHITGOT", "%.2f", (double)(cl->account->cwcacheexhit ? (double)cl->account->cwcacheexhit : 0) * 100 / (double)(cl->account->cwcacheexgot ? cl->account->cwcacheexgot : 1)); +#endif + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", (cl->account->cacheex.mode == 3) ? getting : pushing); + rowvariable = "TABLECLIENTROWS"; + written = 1; + } + else if((cl->typ == 'p' || cl->typ == 'r') && (cl->reader && cl->reader->cacheex.mode)) + { + cacheex_name_link_tpl = "SREADER"; + tpl_addVar(vars, TPLADD, "TYPE", "Reader"); + + if(!apicall) + { + tpl_addVar(vars, TPLADD, "READERNAME", xml_encode(vars, cl->reader->label)); + tpl_addVar(vars, TPLADD, "READERNAMEENC", urlencode(vars, cl->reader->label)); + + if(cl->reader->description) + { + tpl_printf(vars, TPLADD, "CLIENTDESCRIPTION","%s(%s)",!apicall?" ":"",xml_encode(vars, cl->reader->description)); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + else + { + tpl_addVar(vars, TPLADD, "NAME", cl->reader->label); + + if(cl->reader->description) + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", cl->reader->description); + } + else + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", ""); + } + } + + if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); } + else if(cl->reader && cl->reader->tcp_connected) + { tpl_addVar(vars, TPLADD, "IP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "IP", ""); } + + uint64_t current_node = get_cacheex_node(cl); + + if(cl && cl->cxnodeid_changer_detected) +{ + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node); +} +else +{ + tpl_printf(vars, TPLADD, "NODE", "%" PRIu64 "X", current_node); +} + tpl_addVar(vars, TPLADD, "LEVEL", level[cl->reader->cacheex.mode]); + tpl_printf(vars, TPLADD, "PUSH", "%d", cl->cwcacheexpush); + tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->cwc_info); + tpl_printf(vars, TPLADD, "GOT", "%d", cl->cwcacheexgot); + tpl_printf(vars, TPLADD, "CWCINFO", "%d", cl->cwc_info); + tpl_printf(vars, TPLADD, "HIT", "%d", cl->cwcacheexhit); + tpl_printf(vars, TPLADD, "ERR", "%d", cl->cwcacheexerr); + tpl_printf(vars, TPLADD, "ERRCW", "%d", cl->cwcacheexerrcw); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->cwcacheexgotlg); + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->cwcacheexpushlg); + tpl_printf(vars, TPLADD, "REL_CACHEXHITGOT", "%.2f", (double)(cl->cwcacheexhit ? (double)cl->cwcacheexhit : 0) * 100 / (double)(cl->cwcacheexgot ? cl->cwcacheexgot : 1)); +#endif + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", (cl->reader->cacheex.mode == 3) ? pushing : getting); + + rowvariable = "TABLEREADERROWS"; + written = 1; + } + else if(get_module(cl)->listenertype == LIS_CSPUDP) + { + cacheex_name_link_tpl = "SREADER"; + tpl_addVar(vars, TPLADD, "TYPE", "csp"); + + if(!apicall) + { + tpl_addVar(vars, TPLADD, "READERNAME", "csp"); + tpl_addVar(vars, TPLADD, "READERNAMEENC", "csp"); + } + else + { + tpl_addVar(vars, TPLADD, "NAME", "csp"); + } + + if(IP_ISSET(cl->ip)) + { tpl_addVar(vars, TPLADD, "IP", cs_inet_ntoa(cl->ip)); } + else if(cl->login > cl->logout) + { tpl_addVar(vars, TPLADD, "IP", "camd.socket"); } + else + { tpl_addVar(vars, TPLADD, "IP", ""); } + tpl_addVar(vars, TPLADD, "NODE", "csp"); + + if(cl->cwcacheexping) + { + tpl_printf(vars, TPLADD, "LEVEL", "csp (%d ms)", cl->cwcacheexping); + } + else + { + tpl_addVar(vars, TPLADD, "LEVEL", "csp"); + } + + tpl_printf(vars, TPLADD, "PUSH", "%d", cl->cwcacheexpush); + tpl_printf(vars, TPLADD, "GOT", "%d", cl->cwcacheexgot); + tpl_printf(vars, TPLADD, "HIT", "%d", cl->cwcacheexhit); + tpl_printf(vars, TPLADD, "ERR", "%d", cl->cwcacheexerr); + tpl_printf(vars, TPLADD, "ERRCW", "%d", cl->cwcacheexerrcw); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cl->cwcacheexgotlg); + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cl->cwcacheexpushlg); + tpl_printf(vars, TPLADD, "REL_CACHEXHITGOT", "%.2f", (double)(cl->cwcacheexhit ? (double)cl->cwcacheexhit : 0) * 100 / (double)(cl->cwcacheexgot ? cl->cwcacheexgot : 1)); +#endif + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", getting); + rowvariable = "TABLECLIENTROWS"; + written = 1; + } + + if(written) + { + if(!apicall) + { + tpl_addVar(vars, TPLADD, "NAME", tpl_getTpl(vars, cacheex_name_link_tpl)); + } +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXAIOTABLEROW")); +#else + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXTABLEROW")); +#endif + + if(cl->ll_cacheex_stats) + { + LL_ITER itr = ll_iter_create(cl->ll_cacheex_stats); + S_CACHEEX_STAT_ENTRY *cacheex_stats_entry; + + while((cacheex_stats_entry = ll_iter_next(&itr))) + { + tpl_addVar(vars, TPLADD, "DIRECTIONIMG", ""); + if(now - cacheex_stats_entry->cache_last < 20) + { tpl_addVar(vars, TPLADD, "TYPE", cacheex_stats_entry->cache_direction == 0 ? pushing : getting); } + else + { tpl_addVar(vars, TPLADD, "TYPE", ""); } + tpl_printf(vars, TPLADD, "NAME", "%04X@%06X:%04X", cacheex_stats_entry->cache_caid, + cacheex_stats_entry->cache_prid, + cacheex_stats_entry->cache_srvid); + if(cacheex_stats_entry->cache_direction == 0) + { + tpl_printf(vars, TPLADD, "PUSH", "%d", cacheex_stats_entry->cache_count); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "PUSHLG", "%d", cacheex_stats_entry->cache_count_lg); +#endif + tpl_addVar(vars, TPLADD, "GOT", ""); + } + else + { + tpl_printf(vars, TPLADD, "GOT", "%d", cacheex_stats_entry->cache_count); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "GOTLG", "%d", cacheex_stats_entry->cache_count_lg); +#endif + tpl_addVar(vars, TPLADD, "PUSH", ""); + } + tpl_addVar(vars, TPLADD, "HIT", ""); + char channame[CS_SERVICENAME_SIZE]; + char *lastchan = xml_encode(vars, get_servicename(cl, cacheex_stats_entry->cache_srvid, cacheex_stats_entry->cache_prid, cacheex_stats_entry->cache_caid, channame, sizeof(channame))); + tpl_addVar(vars, TPLADD, "LEVEL", lastchan); + if (apicall == 2) + { + tpl_printf(vars, TPLADD, "JSONDELIMITER", "%s", (delimiter > 1)?",":""); +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, "JSONCACHEEXBITS", tpl_getTpl(vars, "JSONCACHEEXAIOBIT")); +#else + tpl_addVar(vars, TPLAPPEND, "JSONCACHEEXBITS", tpl_getTpl(vars, "JSONCACHEEXBIT")); +#endif + delimiter++; + } + else + { +#ifdef CS_CACHEEX_AIO + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXAIOTABLEROWSTATS")); +#else + tpl_addVar(vars, TPLAPPEND, rowvariable, tpl_getTpl(vars, "CACHEEXTABLEROW")); +#endif + } + } + } + written = 0; + } + } + + float cachesum = first_client ? first_client->cwcacheexgot : 1; + if(cachesum < 1) + { + cachesum = 1; + } + tpl_printf(vars, TPLADD, "TOTAL_CACHEXPUSH", PRINTF_LOCAL_D, first_client ? first_client->cwcacheexpush : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXPUSH_IMG", pushing); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXGOT", PRINTF_LOCAL_D, first_client ? first_client->cwcacheexgot : 0); + tpl_addVar(vars, TPLADD, "TOTAL_CACHEXGOT_IMG", getting); + tpl_printf(vars, TPLADD, "TOTAL_CACHEXHIT", PRINTF_LOCAL_D, first_client ? first_client->cwcacheexhit : 0); + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE", "%d", cache_size()); +#ifdef CS_CACHEEX_AIO + tpl_printf(vars, TPLADD, "TOTAL_CACHESIZE_LG", "%d", cache_size_lg()); +#endif + + tpl_printf(vars, TPLADD, "REL_CACHEXHIT", "%.2f", (first_client ? first_client->cwcacheexhit : 0) * 100 / cachesum); + + if(!apicall) + { +#ifdef CS_CACHEEX_AIO + return tpl_getTpl(vars, "CACHEEXAIOPAGE"); +#else + return tpl_getTpl(vars, "CACHEEXPAGE"); +#endif + } + else + { + return tpl_getTpl(vars, "JSONCACHEEX"); + } +} +#endif + +#ifdef WEBIF_WIKI +static char *send_oscam_wiki(struct templatevars *vars, struct uriparams *params) +{ + const char *config = getParam(params, "config"); + const char *section = getParam(params, "section"); + const char *param = getParam(params, "param"); + + if(!config || !param || !config[0] || !param[0]) + { + return tpl_getTpl(vars, "WIKIERROR"); + } + + const char *text = wiki_get_help(config, section, param); + + if(text) + { + tpl_addVar(vars, TPLADD, "WIKIPARAM", param); + tpl_addVar(vars, TPLADD, "WIKICONFIG", config); + tpl_addVar(vars, TPLADD, "WIKITEXT", json_encode(vars, text)); + return tpl_getTpl(vars, "WIKIJSON"); + } + else + { + tpl_addVar(vars, TPLADD, "WIKIPARAM", param); + tpl_addVar(vars, TPLADD, "WIKICONFIG", config); + return tpl_getTpl(vars, "WIKINOTFOUND"); + } +} + +static char *send_oscam_wiki_status(struct templatevars *vars, struct uriparams *params) +{ + const char *config = getParam(params, "config"); + + if(!config || !config[0]) + { + return tpl_getTpl(vars, "WIKIERROR"); + } + + /* Build JSON object with status for all params in this config */ + char json_buf[8192]; + int pos = 0; + int32_t i, count = wiki_count(); + bool first = true; + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "{"); + +#ifdef COMPRESSED_WIKI + /* For compressed wiki, we iterate through entries but access via decompressed data */ + const struct wiki_entry *entries = wiki_get_entries(); + char *wiki_data = wiki_get_decompressed_data(); + + if(wiki_data) + { + for(i = 0; i < count && pos < (int)sizeof(json_buf) - 100; i++) + { + const char *e_config = wiki_data + entries[i].config_ofs; + const char *e_param = wiki_data + entries[i].param_ofs; + + if(strcmp(e_config, config) != 0) + { continue; } + + /* Section filter is optional - include all params from this config */ + if(!first) + { pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, ","); } + first = false; + + const char *status_str = "ok"; + if(entries[i].status == 1) status_str = "review"; + else if(entries[i].status == 2) status_str = "missing"; + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "\"%s\":\"%s\"", + e_param, status_str); + } + } +#else + const struct wiki_entry *entries = wiki_get_entries(); + for(i = 0; i < count && pos < (int)sizeof(json_buf) - 100; i++) + { + if(strcmp(entries[i].config, config) != 0) + { continue; } + + /* Section filter is optional - include all params from this config */ + if(!first) + { pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, ","); } + first = false; + + const char *status_str = "ok"; + if(entries[i].status == 1) status_str = "review"; + else if(entries[i].status == 2) status_str = "missing"; + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "\"%s\":\"%s\"", + entries[i].param, status_str); + } +#endif + + pos += snprintf(json_buf + pos, sizeof(json_buf) - pos, "}"); + + tpl_addVar(vars, TPLADD, "WIKISTATUS", json_buf); + return tpl_getTpl(vars, "WIKISTATUSJSON"); +} +#endif + +static char *send_oscam_api(struct templatevars * vars, FILE * f, struct uriparams * params, int8_t *keepalive, int8_t apicall, char *extraheader) +{ + if(strcmp(getParam(params, "part"), "status") == 0) + { + return send_oscam_status(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "userstats") == 0) + { + return send_oscam_user_config(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "failban") == 0) + { + return send_oscam_failban(vars, params, apicall); + } +#ifdef CS_CACHEEX + else if(strcmp(getParam(params, "part"), "cacheex") == 0) + { + return send_oscam_cacheex(vars, params, apicall); + } +#endif + else if(strcmp(getParam(params, "part"), "files") == 0) + { + return send_oscam_files(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "readerlist") == 0) + { + return send_oscam_reader(vars, params, apicall); + } + else if(strcmp(getParam(params, "part"), "serverconfig") == 0) + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "serverconfig not yet avail"); + return tpl_getTpl(vars, "APIERROR"); + } + else if(strcmp(getParam(params, "part"), "userconfig") == 0) + { + if(((strcmp(getParam(params, "action"), "Save") == 0) || + (strcmp(getParam(params, "action"), "Save As") == 0)) && cfg.http_readonly == 1) + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "API is in readonly mode"); + return tpl_getTpl(vars, "APIERROR"); + } + else + { + struct s_auth *account = get_account_by_name(getParam(params, "user")); + if(!account && strcmp(getParam(params, "action"), "Save")) + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "user not exist"); + return tpl_getTpl(vars, "APIERROR"); + } + else + { + return send_oscam_user_config_edit(vars, params, apicall); + } + } + } + else if(strcmp(getParam(params, "part"), "entitlement") == 0) + { + + if(strcmp(getParam(params, "label"), "")) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + if(rdr->enable == 1) + { + return send_oscam_entitlement(vars, params, apicall); + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no cccam reader or disabled"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "reader not exist"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no reader selected"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else if(strcmp(getParam(params, "part"), "ecmhistory") == 0) + { + int32_t isec; + int32_t shown; + time_t now = time((time_t *)0); + const char *usr; + struct s_client *cl; + for(cl = first_client; cl ; cl = cl->next) + { + if(cl->wihidden != 1) + { + isec = now - cl->lastecm; + usr = username(cl); + shown = 0; + if(strcmp(getParam(params, "label"), "") == 0) + { + if(strcmp(getParam(params, "type"), "servers") == 0) + { + if(cl->typ == 'p' || cl->typ == 'r') + { shown = 1; } + } + else if(strcmp(getParam(params, "type"), "users") == 0) + { + if(cl->typ == 'c') + { shown = 1; } + } + else + { + shown = 1; + } + } + else if(strcmp(getParam(params, "label"), usr) == 0) + { + shown = 1; + } + if(shown == 1) + { + tpl_printf(vars, TPLADD, "CLIENTTYPE", "%c", cl->typ); + tpl_addVar(vars, TPLADD, "CLIENTUSER", xml_encode(vars, usr)); + if(cl->typ == 'c' || cl->typ == 'm') + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", xml_encode(vars, cl->account->description ? cl->account->description : "")); + } + else if(cl->typ == 'p' || cl->typ == 'r') + { + tpl_addVar(vars, TPLADD, "CLIENTDESCRIPTION", xml_encode(vars, cl->reader->description ? cl->reader->description : "")); + } + tpl_printf(vars, TPLADD, "CLIENTLASTRESPONSETIME", "%d", cl->cwlastresptime ? cl->cwlastresptime : -1); + tpl_printf(vars, TPLADD, "CLIENTIDLESECS", "%d", isec); + + //load historical values from ringbuffer + char *value = get_ecm_fullhistorystring(cl); + tpl_addVar(vars, TPLADD, "CLIENTLASTRESPONSETIMEHIST", value); + free_mk_t(value); + + tpl_addVar(vars, TPLAPPEND, "APISTATUSBITS", tpl_getTpl(vars, "APISTATUSBIT")); + } + } + } + return tpl_getTpl(vars, "APISTATUS"); + } + else if(strcmp(getParam(params, "part"), "readerstats") == 0) + { + if(strcmp(getParam(params, "label"), "")) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + return send_oscam_reader_stats(vars, params, apicall); + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "reader not exist"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no reader selected"); + return tpl_getTpl(vars, "APIERROR"); + } + } +#ifdef READER_VIDEOGUARD + else if(strcmp(getParam(params, "part"), "sendcmd") == 0) + { + if(strcmp(getParam(params, "label"), "")) + { + struct s_reader *rdr = get_reader_by_label(getParam(params, "label")); + if(rdr) + { + char api_msg[150]; + CMD_PACKET *cmd_pack = NULL; + if(!cs_malloc(&cmd_pack, sizeof(CMD_PACKET))) + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "cs_malloc failed!"); + return tpl_getTpl(vars, "APIERROR"); + } + struct s_client *webif_client = cur_client(); + webif_client->grp = 0xFF; // access all readers + + memset(cmd_pack, '0', sizeof(CMD_PACKET)); + cmd_pack->client = webif_client; + cmd_pack->cmdlen = strlen(getParam(params, "cmd")) / 2; + + if(cmd_pack->cmdlen > 0 && (unsigned long)abs(cmd_pack->cmdlen) <= sizeof(cmd_pack->cmd)) + { + if(key_atob_l(getParam(params, "cmd"), cmd_pack->cmd, cmd_pack->cmdlen*2)) + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "'cmd' has not been sent due to wrong value!"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + if(cmd_pack->cmdlen) + { + snprintf(api_msg, sizeof(api_msg), "Command would exceed %lu bytes!", (long unsigned int)sizeof(cmd_pack->cmd)); + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", api_msg); + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "Missing parameter 'cmd'!"); + } + return tpl_getTpl(vars, "APIERROR"); + } + + struct s_client *cl = rdr->client; + if(rdr->enable == 1 && cl && cl->typ == 'r') + { + add_job(cl, ACTION_READER_SENDCMD, cmd_pack, sizeof(CMD_PACKET)); + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "command sent"); + return tpl_getTpl(vars, "APICONFIRMATION"); + } + else + { + snprintf(api_msg, sizeof(api_msg), "Reader '%s' is not suitable!", xml_encode(vars, rdr->label)); + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", api_msg); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no such reader"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + //Send Errormessage + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "no reader selected"); + return tpl_getTpl(vars, "APIERROR"); + } + } +#endif + else if(strcmp(getParam(params, "part"), "shutdown") == 0) + { + if((strcmp(strtolower(getParam(params, "action")), "restart") == 0) || + (strcmp(strtolower(getParam(params, "action")), "shutdown") == 0)) + { + if(!cfg.http_readonly) + { + return send_oscam_shutdown(vars, f, params, apicall, keepalive, extraheader); + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "webif readonly mode"); + return tpl_getTpl(vars, "APIERROR"); + } + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "missing parameter action"); + return tpl_getTpl(vars, "APIERROR"); + } + + } + else + { + tpl_addVar(vars, TPLADD, "APIERRORMESSAGE", "part not found"); + return tpl_getTpl(vars, "APIERROR"); + } +} + +static char *send_oscam_image(struct templatevars * vars, FILE * f, struct uriparams * params, char *image, time_t modifiedheader, uint32_t etagheader, char *extraheader) +{ + char *wanted; + if(image == NULL) { wanted = getParam(params, "i"); } + else { wanted = image; } + if(cs_strlen(wanted) > 3 && wanted[0] == 'I' && wanted[1] == 'C') + { + if(etagheader == 0) + { + int8_t disktpl = 0; + char *tpl_path; + tpl_path = cfg.http_piconpath? cfg.http_piconpath : cfg.http_tpl; + + if(tpl_path) + { + char path[255]; + if(cs_strlen(tpl_getTplPath(wanted, tpl_path, path, 255)) > 0 && file_exists(path)) + { + struct stat st; + disktpl = 1; + stat(path, &st); + if((time_t)st.st_mtime < modifiedheader) + { + send_header304(f, extraheader); + return "1"; + } + } + } + if(disktpl == 0 && first_client->login < modifiedheader) + { + send_header304(f, extraheader); + return "1"; + } + } + char *header = strstr(tpl_getTpl(vars, wanted), "data:"); + if(header != NULL) + { + char *ptr = header + 5; + while(ptr[0] != ';' && ptr[0] != '\0') { ++ptr; } + if(ptr[0] != '\0' && ptr[1] != '\0') { ptr[0] = '\0'; } + else { return "0"; } + ptr = strstr(ptr + 1, "base64,"); + if(ptr != NULL) + { + int32_t len = b64decode((uint8_t *)ptr + 7); + if(len > 0) + { + if((uint32_t)crc32(0L, (uint8_t *)ptr + 7, len) == etagheader) + { + send_header304(f, extraheader); + } + else + { + send_headers(f, 200, "OK", extraheader, header + 5, 1, len, ptr + 7, 0); + webif_write_raw(ptr + 7, f, len); + } + return "1"; + } + } + } + } + // Return file not found + const char *not_found = "File not found.\n"; + send_headers(f, 404, "Not Found", extraheader, "text/plain", 0, cs_strlen(not_found), (char *)not_found, 0); + webif_write_raw((char *)not_found, f, cs_strlen(not_found)); + return "1"; +} + +static char *send_oscam_robots_txt(FILE * f) +{ + const char *content = "User-agent: *\nDisallow: /\n"; + send_headers(f, 200, "OK", NULL, "text/plain", 0, cs_strlen(content), (char *)content, 0); + webif_write_raw((char *)content, f, cs_strlen(content)); + return "1"; +} + +static char *send_oscam_graph(struct templatevars * vars) +{ + return tpl_getTpl(vars, "GRAPH"); +} + +#ifdef MODULE_GHTTP +static bool ghttp_autoconf(struct templatevars * vars, struct uriparams * params) +{ + int8_t i = 0; + struct s_reader *rdr; + char *name = getParam(params, "gacname"); + if(cs_strlen(name) < 3) + { + tpl_addMsg(vars, "Invalid host name!"); + return false; + } + + LL_ITER itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + if(rdr->ph.num == R_GHTTP) { i++; } // count existing ghttp readers + + while(i < 3) // if less than 3, add more + { + char lbl[128]; + snprintf(lbl, sizeof(lbl), "%s%d", "ghttp", i + 1); + cs_log("GHttp autoconf: adding reader %s", lbl); + struct s_reader *newrdr; + if(!cs_malloc(&newrdr, sizeof(struct s_reader))) + { + tpl_addMsg(vars, "Create reader failed!"); + return false; + }; + newrdr->typ = R_GHTTP; + cs_strncpy(newrdr->label, lbl, sizeof(newrdr->label)); + module_reader_set(newrdr); + reader_set_defaults(newrdr); + newrdr->enable = 0; + newrdr->grp = 1; + ll_append(configured_readers, newrdr); + i++; + } + + uint16_t port = 0; + char *str = strstr(name, ":"); + if(str) + { + port = atoi(str + 1); + str[0] = '\0'; + } + + i = 0; + itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->ph.num == R_GHTTP) + { + if(i > 2) // remove superflous + { + cs_log("GHttp autoconf: removing reader %s", rdr->label); + inactivate_reader(rdr); + ll_iter_remove(&itr); + free_reader(rdr); + } + else // reconfigure the 3 first ghttp readers + { + cs_log("GHttp autoconf: reconfiguring reader %s", rdr->label); + snprintf(rdr->label, sizeof(rdr->label), "%s%d", "ghttp", i + 1); + rdr->r_port = port; + rdr->enable = 1; + rdr->ghttp_use_ssl = 0; +#ifdef WITH_SSL + rdr->ghttp_use_ssl = 1; +#endif + if(rdr->grp < 1) { rdr->grp = 1; } + cs_strncpy(rdr->r_usr, getParam(params, "gacuser"), sizeof(rdr->r_usr)); + cs_strncpy(rdr->r_pwd, getParam(params, "gacpasswd"), sizeof(rdr->r_pwd)); + if(i == 0) { cs_strncpy(rdr->device, name, sizeof(rdr->device)); } + else + { + if(!strstr(name, ".")) + { snprintf(rdr->device, sizeof(rdr->device), "%s%d", name, i); } // name, name1, name2 + else { cs_strncpy(rdr->device, name, sizeof(rdr->device)); } + // . in the name = assume full hostname = use same for all 3 readers + } + if(i == 2) { rdr->fallback = 1; } + else { rdr->fallback = 0; } + i++; + } + } + } + cs_log("GHttp autoconf: Saving %d readers", i); + if(write_server() != 0) { tpl_addMsg(vars, "Write Config failed!"); } + itr = ll_iter_create(configured_readers); + while((rdr = ll_iter_next(&itr))) + { + if(rdr->ph.num == R_GHTTP) + { restart_cardreader(rdr, 1); } + } + return true; +} + +static char *send_oscam_ghttp(struct templatevars * vars, struct uriparams * params, int8_t apicall) +{ + if(strcmp(strtolower(getParam(params, "action")), "autoconf") == 0) + { + if(!apicall) + { + bool missing = false; + if(cs_strlen(getParam(params, "gacuser")) == 0) + { + tpl_addVar(vars, TPLADD, "USERREQ", "(Required)"); + missing = true; + } + else { tpl_addVar(vars, TPLADD, "GACUSER", getParam(params, "gacuser")); } + if(cs_strlen(getParam(params, "gacpasswd")) == 0) + { + tpl_addVar(vars, TPLADD, "PWDREQ", "(Required)"); + missing = true; + } + else { tpl_addVar(vars, TPLADD, "GACPASSWD", getParam(params, "gacpasswd")); } + if(cs_strlen(getParam(params, "gacname")) == 0) + { + tpl_addVar(vars, TPLADD, "NAMEREQ", "(Required)"); + missing = true; + } + else { tpl_addVar(vars, TPLADD, "GACNAME", getParam(params, "gacname")); } + if(missing) { return tpl_getTpl(vars, "PREAUTOCONF"); } + cs_log("GHttp autoconf requested by WebIF from %s", cs_inet_ntoa(GET_IP())); + } + else + { + tpl_addVar(vars, TPLADD, "APICONFIRMMESSAGE", "autoconf"); + cs_log("GHttp autoconf requested by XMLApi from %s", cs_inet_ntoa(GET_IP())); + } + + if(ghttp_autoconf(vars, params)) + { + tpl_printf(vars, TPLADD, "REFRESHTIME", "%d", 3); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + tpl_printf(vars, TPLADD, "SECONDS", "%d", 3); + if(apicall) { return tpl_getTpl(vars, "APICONFIRMATION"); } + else { return tpl_getTpl(vars, "AUTOCONF"); } + } + else { return tpl_getTpl(vars, "PREAUTOCONF"); } // something failed + + } + else + { + if(cs_strlen(getParam(params, "token")) > 0) // parse autoconf token + { + char *token = getParam(params, "token"); + int32_t len = b64decode((uint8_t *)token); + if(len > 0) + { + struct uriparams tokenprms; + tokenprms.paramcount = 0; + parseParams(&tokenprms, token); + if(cs_strlen(getParam(&tokenprms, "u")) > 0) + { + tpl_addVar(vars, TPLADD, "GACUSER", getParam(&tokenprms, "u")); + tpl_addVar(vars, TPLADD, "USERRDONLY", "readonly"); + } + if(cs_strlen(getParam(&tokenprms, "p")) > 0) + { + tpl_addVar(vars, TPLADD, "GACPASSWD", getParam(&tokenprms, "p")); + tpl_addVar(vars, TPLADD, "PWDRDONLY", "readonly"); + } + if(cs_strlen(getParam(&tokenprms, "n")) > 0) + { + tpl_addVar(vars, TPLADD, "GACNAME", getParam(&tokenprms, "n")); + tpl_addVar(vars, TPLADD, "NAMERDONLY", "readonly"); + } + } + } + return tpl_getTpl(vars, "PREAUTOCONF"); + } +} +#endif + +static int8_t check_httpip(IN_ADDR_T addr) +{ + int8_t i = 0; + // check all previously dyndns resolved addresses + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(IP_ISSET(cfg.http_dynip[i]) && IP_EQUAL(cfg.http_dynip[i], addr)) + { return 1; } + } + return 0; +} + +static int8_t check_httpdyndns(IN_ADDR_T addr) +{ + + // check all previously dyndns resolved addresses + if(check_httpip(addr)) + { return 1; } + + // we are not ok, so resolve all dyndns entries into IP's - maybe outdated IP's + + if(cfg.http_dyndns[0][0]) + { + int8_t i = 0; + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(cfg.http_dyndns[i][0]) + { + cs_resolve((const char *)cfg.http_dyndns[i], &cfg.http_dynip[i], NULL, NULL); + cs_log_dbg(D_TRACE, "WebIf: httpdyndns [%d] resolved %s to %s ", i, (char *)cfg.http_dyndns[i], cs_inet_ntoa(cfg.http_dynip[i])); + } + } + } + else + { + cs_log_dbg(D_TRACE, "WebIf: No dyndns addresses found"); + return 0; + } + + // again check all dyndns resolved addresses + if(check_httpip(addr)) + { return 1; } + + return 0; +} + +static int8_t check_valid_origin(IN_ADDR_T addr) +{ + + // check whether requesting IP is in allowed IP ranges + if(check_ip(cfg.http_allowed, addr)) + { return 1; } + + // we havn't found the requesting IP in allowed range. So we check for allowed httpdyndns as last chance + if(cfg.http_dyndns[0][0]) + { + int8_t ok; + ok = check_httpdyndns(addr); + return ok; + } + return 0; +} + +static int8_t check_request(char *result, int32_t readen) +{ + if(readen < 50) { return 0; } + result[readen] = '\0'; + int8_t method; + if(strncmp(result, "POST", 4) == 0) { method = 1; } + else { method = 0; } + char *headerEnd = strstr(result, "\r\n\r\n"); + if(headerEnd == NULL) { return 0; } + else if(method == 0) { return 1; } + else + { + char *ptr = strstr(result, "Content-Length: "); + if(ptr != NULL) + { + ptr += 16; + if(ptr < result + readen) + { + uint32_t length = atoi(ptr); + if(cs_strlen(headerEnd + 4) >= length) { return 1; } + } + } + } + return 0; +} + +static int32_t readRequest(FILE * f, IN_ADDR_T in, char **result, int8_t forcePlain) +{ + int32_t n, bufsize = 0, errcount = 0; + char buf2[1024]; + struct pollfd pfd2[1]; +#ifdef WITH_SSL + int8_t is_ssl = 0; + if(ssl_active && !forcePlain) + { is_ssl = 1; } +#endif + + while(1) + { + errno = 0; + if(forcePlain) + { n = read(fileno(f), buf2, sizeof(buf2)); } + else + { n = webif_read(buf2, sizeof(buf2), f); } + if(n <= 0) + { + if((errno == 0 || errno == EINTR)) + { + if(errcount++ < 10) + { + cs_sleepms(5); + continue; + } + else { return -1; } + } +#ifdef WITH_SSL + if(is_ssl) + { + if(errno != ECONNRESET) + { + int32_t errcode = ERR_peek_error(); + char errstring[128]; + ERR_error_string_n(errcode, errstring, sizeof(errstring) - 1); + cs_log_dbg(D_TRACE, "WebIf: read error ret=%d (%d%s%s)", n, SSL_get_error(cur_ssl(), n), errcode ? " " : "", errcode ? errstring : ""); + } + return -1; + } +#else + if(errno != ECONNRESET) + { cs_log_dbg(D_TRACE, "WebIf: read error ret=%d (errno=%d %s)", n, errno, strerror(errno)); } +#endif + return -1; + } + if(!cs_realloc(result, bufsize + n + 1)) + { + send_error500(f); + NULLFREE(*result); + return -1; + } + + memcpy(*result + bufsize, buf2, n); + bufsize += n; + + if(bufsize > 102400) // max request size 100kb + { + cs_log("error: too much data received from %s", cs_inet_ntoa(in)); + NULLFREE(*result); + *result = NULL; + return -1; + } + +#ifdef WITH_SSL + if(ssl_active && !forcePlain) + { + int32_t len = 0; + len = SSL_pending((SSL *)f); + + if(len > 0) + { continue; } + + pfd2[0].fd = SSL_get_fd((SSL *)f); + + } + else +#endif + pfd2[0].fd = fileno(f); + + pfd2[0].events = (POLLIN | POLLPRI); + + int32_t rc = poll(pfd2, 1, 100); + if(rc > 0 || !check_request(*result, bufsize)) + { continue; } + else + { break; } + } + return bufsize; +} +static int32_t process_request(FILE * f, IN_ADDR_T in) +{ + int32_t ok = 0; + int8_t *keepalive = (int8_t *)pthread_getspecific(getkeepalive); + IN_ADDR_T addr = GET_IP(); + + do + { +#ifdef WITH_SSL + if(!ssl_active && *keepalive) { fflush(f); } +#else + if(*keepalive) { fflush(f); } +#endif + + // at this point we do all checks related origin IP, ranges and dyndns stuff + ok = check_valid_origin(addr); + cs_log_dbg(D_TRACE, "WebIf: Origin checked. Result: access from %s => %s", cs_inet_ntoa(addr), (!ok) ? "forbidden" : "allowed"); + + // based on the failed origin checks we send a 403 to calling browser + if(!ok) + { + send_error(f, 403, "Forbidden", NULL, "Access denied.", 0); + cs_log("unauthorized access from %s - invalid ip or dyndns", cs_inet_ntoa(addr)); + return 0; + } + int32_t authok = 0; + char expectednonce[(MD5_DIGEST_LENGTH * 2) + 1], opaque[(MD5_DIGEST_LENGTH * 2) + 1]; + char authheadertmp[sizeof(AUTHREALM) + sizeof(expectednonce) + sizeof(opaque) + 100]; + + char *method, *path, *protocol, *str1, *saveptr1 = NULL, *authheader = NULL, *extraheader = NULL, *filebuf = NULL; + char *pch, *tmp, *buf, *nameInUrl, subdir[32]; + /* List of possible pages */ + char *pages[] = + { + "/config.html", + "/readers.html", + "/entitlements.html", + "/status.html", + "/userconfig.html", + "/readerconfig.html", + "/services.html", + "/user_edit.html", + "/site.css", + "/services_edit.html", + "/savetemplates.html", + "/shutdown.html", + "/script.html", + "/scanusb.html", + "/files.html", + "/readerstats.html", + "/failban.html", + "/oscam.js", + "/oscamapi.html", + "/image", + "/favicon.ico", + "/graph.svg", + "/oscamapi.xml", + "/cacheex.html", + "/oscamapi.json", + "/emm.html", + "/emm_running.html", + "/robots.txt", + "/ghttp.html", + "/logpoll.html", + "/jquery.js", +#ifdef WEBIF_WIKI + "/wiki.json", + "/wiki_status.json", +#endif + }; + + int32_t pagescnt = sizeof(pages) / sizeof(char *); // Calculate the amount of items in array + int32_t i, bufsize, len, pgidx = -1; + uint32_t etagheader = 0; + struct uriparams params; + params.paramcount = 0; + time_t modifiedheader = 0; + + bufsize = readRequest(f, in, &filebuf, 0); + + if(!filebuf || bufsize < 1) + { + if(!*keepalive) { cs_log_dbg(D_CLIENT, "WebIf: No data received from client %s. Closing connection.", cs_inet_ntoa(addr)); } + return -1; + } + + buf = filebuf; + + if((method = strtok_r(buf, " ", &saveptr1)) != NULL) + { + if((path = strtok_r(NULL, " ", &saveptr1)) != NULL) + { + if((protocol = strtok_r(NULL, "\r", &saveptr1)) == NULL) + { + NULLFREE(filebuf); + return -1; + } + } + else + { + NULLFREE(filebuf); + return -1; + } + } + else + { + NULLFREE(filebuf); + return -1; + } + tmp = protocol + cs_strlen(protocol) + 2; + + pch = path; + /* advance pointer to beginning of query string */ + while(pch[0] != '?' && pch[0] != '\0') { ++pch; } + if(pch[0] == '?') + { + pch[0] = '\0'; + ++pch; + } + + nameInUrl = pch - 1; + while(nameInUrl != path && nameInUrl[0] != '/') { --nameInUrl; } + + /* allow only alphanumeric sub-folders */ + int32_t subdirLen = nameInUrl - path; + subdir[0] = '\0'; + if(subdirLen > 0 && subdirLen < 32) + { + cs_strncpy(subdir, path + 1, subdirLen); + + int32_t invalidSubdir = 0; + for(i = 0; i < subdirLen - 1; i++) + { + if(!((subdir[i] >= '0' && subdir[i] <= '9') + || (subdir[i] >= 'a' && subdir[i] <= 'z') + || (subdir[i] >= 'A' && subdir[i] <= 'Z'))) + { + + invalidSubdir = 1; + subdir[0] = '\0'; + break; + } + } + + if(!invalidSubdir) + { + subdir[subdirLen] = '\0'; +#ifdef WIN32 + subdir[subdirLen - 1] = '\\'; +#else + subdir[subdirLen - 1] = '/'; +#endif + } + } + + /* Map page to our static page definitions */ + for(i = 0; i < pagescnt; i++) + { + if(!strcmp(nameInUrl, pages[i])) { pgidx = i; } + } + + parseParams(¶ms, pch); + + if(!cfg.http_user || !cfg.http_pwd) + { authok = 1; } + + for(str1 = strtok_r(tmp, "\n", &saveptr1); str1; str1 = strtok_r(NULL, "\n", &saveptr1)) + { + len = cs_strlen(str1); + if(str1[len - 1] == '\r') + { + str1[len - 1] = '\0'; + --len; + } + if(len == 0) + { + if(strcmp(method, "POST") == 0) + { + parseParams(¶ms, str1 + 2); + } + break; + } + if(!authok && len > 50 && strncasecmp(str1, "Authorization:", 14) == 0 && strstr(str1, "Digest") != NULL) + { + if(cs_dblevel & D_CLIENT) + { + if(cs_realloc(&authheader, len + 1)) + { cs_strncpy(authheader, str1, len); } + } + authok = check_auth(str1, method, path, addr, expectednonce, opaque); + } + else if(len > 40 && strncasecmp(str1, "If-Modified-Since:", 18) == 0) + { + modifiedheader = parse_modifiedsince(str1); + } + else if(len > 20 && strncasecmp(str1, "If-None-Match:", 14) == 0) + { + for(pch = str1 + 14; pch[0] != '"' && pch[0] != '\0'; ++pch) { ; } + if(cs_strlen(pch) > 5) { etagheader = (uint32_t)strtoul(++pch, NULL, 10); } + } + else if(len > 12 && strncasecmp(str1, "Connection: Keep-Alive", 22) == 0 && strcmp(method, "POST")) + { + *keepalive = 1; + } + } + + if(cfg.http_user && cfg.http_pwd) + { + if (!authok || cs_strlen(opaque) != MD5_DIGEST_LENGTH * 2) + { + calculate_opaque(addr, opaque); + } + + if (authok != 2) + { + if(!authok) + { + if(authheader) + { + cs_log_dbg(D_CLIENT, "WebIf: Received wrong auth header from %s:", cs_inet_ntoa(addr)); + cs_log_dbg(D_CLIENT, "%s", authheader); + } + else + { cs_log_dbg(D_CLIENT, "WebIf: Received no auth header from %s.", cs_inet_ntoa(addr)); } + } + calculate_nonce(NULL, expectednonce, opaque); + } + + if (authok != 1) + { + snprintf(authheadertmp, sizeof(authheadertmp), "WWW-Authenticate: Digest algorithm=\"MD5\", realm=\"%s\", qop=\"auth\", opaque=\"%s\", nonce=\"%s\"", AUTHREALM, opaque, expectednonce); + if (authok == 2) + { + if (!cs_strncat(authheadertmp, ", stale=true", sizeof(authheadertmp))) { + cs_log("WARNING, bug here!"); + } + } + } + else + { + snprintf(authheadertmp, sizeof(authheadertmp), "Authentication-Info: nextnonce=\"%s\"", expectednonce); + } + + extraheader = authheadertmp; + + if (authok != 1) + { + char *msg = "Access denied.\n"; + send_headers(f, 401, "Unauthorized", extraheader, "text/html", 0, cs_strlen(msg), msg, 0); + webif_write(msg, f); + NULLFREE(authheader); + NULLFREE(filebuf); + if (*keepalive) { + continue; + } else { + return 0; + } + } + } + else + { + NULLFREE(authheader); + } + + /*build page*/ + if(pgidx == 8) + { + send_file(f, "CSS", subdir, modifiedheader, etagheader, extraheader); + } + else if(pgidx == 17) + { + send_file(f, "JS", subdir, modifiedheader, etagheader, extraheader); + } + else if(pgidx == 30) + { + send_file(f, "JQ", subdir, modifiedheader, etagheader, extraheader); + } + else + { + time_t t; + struct templatevars *vars = tpl_create(); + if(vars == NULL) + { + send_error500(f); + NULLFREE(filebuf); + return 0; + } + + tpl_addVar(vars, TPLADD, "SUBDIR", subdir); + + struct tm lt, st; + time(&t); + + localtime_r(&t, <); + + tpl_addVar(vars, TPLADD, "SCM_URL", SCM_URL); + tpl_addVar(vars, TPLADD, "WIKI_URL", WIKI_URL); + tpl_addVar(vars, TPLADD, "BOARD_URL", BOARD_URL); +#ifdef WEBIF_WIKI + tpl_addVar(vars, TPLADD, "WIKIINTERNALVAR", "\t\tvar wikiInternal = true;"); +#else + tpl_addVar(vars, TPLADD, "WIKIINTERNALVAR", ""); +#endif + tpl_addVar(vars, TPLADD, "CS_VERSION", CS_VERSION); + tpl_addVar(vars, TPLADD, "CS_GIT_COMMIT", CS_GIT_COMMIT); + tpl_addVar(vars, TPLADD, "CS_TARGET", CS_TARGET); + tpl_addVar(vars, TPLADD, "HTTPOSCAMLABEL", xml_encode(vars,cfg.http_oscam_label)); + if (!boxtype_is("generic")) + { + if (!boxname_is("generic")) + tpl_printf(vars, TPLADD, "DETECTED_BOXTYPE", "%s (%s)", boxtype_get(), boxname_get()); + else + tpl_addVar(vars, TPLADD, "DETECTED_BOXTYPE", boxtype_get()); + } + + if(cfg.http_locale){ + float decimal_point = 0.0; + setlocale(LC_ALL, cfg.http_locale); + tpl_printf(vars, TPLADD, "TMP_DECPOINT", "%.1f", decimal_point); + tpl_addVar(vars, TPLADD, "LOCALE_DECPOINT", strstr(tpl_getVar(vars, "TMP_DECPOINT"), ",") ? ",": "."); + } + + tpl_addVar(vars, TPLADD, "HTTP_CHARSET", "UTF-8"); + if(cfg.http_picon_size > 0) + { + tpl_printf(vars, TPLADD, "HTTPPICONSIZEINS", "img.statususericon, img.protoicon, img.usericon, img.readericon {height:%dpx !important;max-height:%dpx !important;}", cfg.http_picon_size, cfg.http_picon_size); + } + if(cfg.poll_refresh > 0) + { + tpl_printf(vars, TPLADD, "POLLREFRESHTIME", "%d", cfg.poll_refresh); + } + if ( cfg.http_refresh > 0 && + ((( pgidx == 1 || pgidx == 4 ) && !cfg.poll_refresh ) || + ( pgidx == 3 && ( cfg.http_status_log || !cfg.poll_refresh )) || + pgidx == 15 || pgidx == 23 || pgidx == -1 )) // wenn polling bei cachex.html eingeführt wird muss die 23 => 2 zeilen höher + { + tpl_printf(vars, TPLADD, "REFRESHTIME", "%d", cfg.http_refresh); + tpl_addVar(vars, TPLADD, "WITHQUERY", pgidx == 15 ? "1" : "0"); + tpl_addVar(vars, TPLADD, "REFRESH", tpl_getTpl(vars, "REFRESH")); + } +#ifdef WEBIF_JQUERY + tpl_printf(vars, TPLADD, "SRCJQUERY", "jquery.js?v=%s", CS_VERSION); +#else + tpl_addVar(vars, TPLADD, "SRCJQUERY", cfg.http_extern_jquery); +#endif + + if(picon_exists("LOGO")||cs_strlen(tpl_getTpl(vars, "IC_LOGO"))>3) + { + tpl_addVar(vars, TPLADD, "LOGO_INS", tpl_getTpl(vars, "LOGOBITIMG")); + } + else + { + tpl_addVar(vars, TPLADD, "LOGO_INS", tpl_getTpl(vars, "LOGOBITSVG")); + } + tpl_addVar(vars, TPLADD, "LOGO", tpl_getTpl(vars, "LOGOBIT")); + tpl_printf(vars, TPLADD, "CURDATE", "%02d.%02d.%02d", lt.tm_mday, lt.tm_mon + 1, lt.tm_year % 100); + tpl_printf(vars, TPLADD, "CURTIME", "%02d:%02d:%02d", lt.tm_hour, lt.tm_min, lt.tm_sec); + localtime_r(&first_client->login, &st); + tpl_printf(vars, TPLADD, "STARTDATE", "%02d.%02d.%02d", st.tm_mday, st.tm_mon + 1, st.tm_year % 100); + tpl_printf(vars, TPLADD, "STARTTIME", "%02d:%02d:%02d", st.tm_hour, st.tm_min, st.tm_sec); + tpl_printf(vars, TPLADD, "PROCESSID", "%d", getppid()); + tpl_addVar(vars, TPLADD, "RUNAS", urlencode(vars, username(first_client))); + + time_t now = time((time_t *)0); + // XMLAPI + if(pgidx == 18 || pgidx == 22 || pgidx == 24) + { + char tbuffer [30]; + strftime(tbuffer, 30, "%Y-%m-%dT%H:%M:%S%z", &st); + tpl_addVar(vars, TPLADD, "APISTARTTIME", tbuffer); + tpl_printf(vars, TPLADD, "APIRUNTIME", "%" PRId64, (int64_t)now - first_client->login); + tpl_printf(vars, TPLADD, "APIREADONLY", "%d", cfg.http_readonly); + if(strcmp(getParam(¶ms, "callback"), "")) + { + tpl_printf(vars, TPLADD, "CALLBACK", "%s%s", getParam(¶ms, "callback"), "("); + tpl_addVar(vars, TPLADD, "ENDBRACKET", ")"); + } + + } + + if (config_enabled(WITH_LB)) + tpl_addVar(vars, TPLADD, "LBISDEFINED", "1"); + + // language code in helplink + tpl_addVar(vars, TPLADD, "LANGUAGE", cfg.http_help_lang); + tpl_addVar(vars, TPLADD, "RUNTIME", sec2timeformat(vars, (now - first_client->login))); + time_t uptime = oscam_get_uptime(); + if(uptime > 0){ + tpl_printf(vars, TPLADD, "UPTIMETXT", "%s Up Time: ", strcmp(tpl_getVar(vars, "DETECTED_BOXTYPE"),"")==0 ? "System" : tpl_getVar(vars, "DETECTED_BOXTYPE")); + tpl_addVar(vars, TPLADD, "UPTIME", sec2timeformat(vars, uptime)); + } + tpl_addVar(vars, TPLADD, "CURIP", cs_inet_ntoa(addr)); + if(cfg.http_readonly) + { tpl_addVar(vars, TPLAPPEND, "BTNDISABLED", "DISABLED"); } + + i = ll_count(cfg.v_list); + if(i > 0) { tpl_printf(vars, TPLADD, "FAILBANNOTIFIER", "%d", i); } + tpl_printf(vars, TPLADD, "FAILBANNOTIFIERPOLL", "%d", i); + + char *result = NULL; + + // WebIf allows modifying many things. Thus, all pages except images/css/static are expected to be non-threadsafe! + if(pgidx != 19 && pgidx != 20 && pgidx != 21 && pgidx != 27) { cs_writelock(__func__, &http_lock); } + switch(pgidx) + { + case 0: + tpl_addVar(vars, TPLADD, "CONFIG_CONTENT", send_oscam_config(vars, ¶ms)); + result = tpl_getTpl(vars, "CONFIGCONTENT"); + break; + case 1: + result = send_oscam_reader(vars, ¶ms, 0); + break; + case 2: + result = send_oscam_entitlement(vars, ¶ms, 0); + break; + case 3: + result = send_oscam_status(vars, ¶ms, 0); + break; + case 4: + result = send_oscam_user_config(vars, ¶ms, 0); + break; + case 5: + result = send_oscam_reader_config(vars, ¶ms); + break; + case 6: + result = send_oscam_services(vars, ¶ms); + break; + case 7: + result = send_oscam_user_config_edit(vars, ¶ms, 0); + break; + //case 8: css file + case 9: + result = send_oscam_services_edit(vars, ¶ms); + break; + case 10: + result = send_oscam_savetpls(vars); + break; + case 11: + result = send_oscam_shutdown(vars, f, ¶ms, 0, keepalive, extraheader); + break; + case 12: + result = send_oscam_script(vars, ¶ms); + break; + case 13: + result = send_oscam_scanusb(vars); + break; + case 14: + result = send_oscam_files(vars, ¶ms, 0); + break; + case 15: + result = send_oscam_reader_stats(vars, ¶ms, 0); + break; + case 16: + result = send_oscam_failban(vars, ¶ms, 0); + break; + //case 17: js file + case 18: + result = send_oscam_api(vars, f, ¶ms, keepalive, 1, extraheader); + break; //oscamapi.html + case 19: + result = send_oscam_image(vars, f, ¶ms, NULL, modifiedheader, etagheader, extraheader); + break; + case 20: + result = send_oscam_image(vars, f, ¶ms, "ICMAI", modifiedheader, etagheader, extraheader); + break; + case 21: + result = send_oscam_graph(vars); + break; + case 22: + result = send_oscam_api(vars, f, ¶ms, keepalive, 1, extraheader); + break; //oscamapi.xml +#ifdef CS_CACHEEX + case 23: + result = send_oscam_cacheex(vars, ¶ms, 0); + break; +#endif + case 24: + result = send_oscam_api(vars, f, ¶ms, keepalive, 2, extraheader); + break; //oscamapi.json + case 25: + result = send_oscam_EMM(vars, ¶ms); + break; //emm.html + case 26: + result = send_oscam_EMM_running(vars, ¶ms); + break; //emm_running.html + case 27: + result = send_oscam_robots_txt(f); + break; //robots.txt +#ifdef MODULE_GHTTP + case 28: + result = send_oscam_ghttp(vars, ¶ms, 0); + break; +#endif +#ifdef WEBIF_LIVELOG + case 29: + result = send_oscam_logpoll(vars, ¶ms); + break; + //case 30: jquery.js +#endif +#ifdef WEBIF_WIKI + case 31: + result = send_oscam_wiki(vars, ¶ms); + break; + case 32: + result = send_oscam_wiki_status(vars, ¶ms); + break; +#endif + default: + result = send_oscam_status(vars, ¶ms, 0); + break; + } + if(pgidx != 19 && pgidx != 20 && pgidx != 21 && pgidx != 27) { cs_writeunlock(__func__, &http_lock); } + + if(result == NULL || !strcmp(result, "0") || cs_strlen(result) == 0) { send_error500(f); } + else if(strcmp(result, "1")) + { + //it doesn't make sense to check for modified etagheader here as standard template has timestamp in output and so site changes on every request + if(pgidx == 18) + { send_headers(f, 200, "OK", extraheader, "text/xml", 0, cs_strlen(result), NULL, 0); } + else if(pgidx == 21) + { send_headers(f, 200, "OK", extraheader, "image/svg+xml", 0, cs_strlen(result), NULL, 0); } + else if(pgidx == 24) + { send_headers(f, 200, "OK", extraheader, "text/javascript", 0, cs_strlen(result), NULL, 0); } +#ifdef WEBIF_WIKI + else if(pgidx == 31) + { send_headers(f, 200, "OK", extraheader, "application/json", 0, cs_strlen(result), NULL, 0); } +#endif + else + { send_headers(f, 200, "OK", extraheader, "text/html", 0, cs_strlen(result), NULL, 0); } + webif_write(result, f); + } + tpl_clear(vars); + } + NULLFREE(filebuf); + } + while(*keepalive == 1 && !exit_oscam); + return 0; +} + +static void *serve_process(void *conn) +{ + struct s_connection *myconn = (struct s_connection *)conn; + int32_t s = myconn->socket; + struct s_client *cl = myconn->cl; + IN_ADDR_T in; + IP_ASSIGN(in, myconn->remote); + + set_thread_name(__func__); + +#ifdef WITH_SSL + SSL *ssl = myconn->ssl; + SAFE_SETSPECIFIC(getssl, ssl); +#endif + NULLFREE(myconn); + + SAFE_SETSPECIFIC(getip, &in); + SAFE_SETSPECIFIC(getclient, cl); + + int8_t keepalive = 0; + SAFE_SETSPECIFIC(getkeepalive, &keepalive); + +#ifdef WITH_SSL + if(ssl_active) + { + if(SSL_set_fd(ssl, s)) + { + int32_t ok = (SSL_accept(ssl) != -1); + if(!ok) + { + int8_t tries = 100; + while(!ok && tries--) + { + int32_t err = SSL_get_error(ssl, -1); + if(err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE) + { break; } + else + { + struct pollfd pfd; + pfd.fd = s; + pfd.events = POLLIN | POLLPRI; + int32_t rc = poll(&pfd, 1, -1); + if(rc < 0) + { + if(errno == EINTR || errno == EAGAIN) { continue; } + break; + } + if(rc == 1) + { ok = (SSL_accept(ssl) != -1); } + } + } + } + if(ok) + { + process_request((FILE *)ssl, in); + } + else + { + FILE *f; + f = fdopen(s, "r+"); + if(f != NULL) + { + char *ptr, *filebuf = NULL, *host = NULL; + int32_t bufsize = readRequest(f, in, &filebuf, 1); + + if(filebuf) + { + filebuf[bufsize] = '\0'; + host = strstr(filebuf, "Host: "); + if(host) + { + host += 6; + ptr = strchr(host, '\r'); + if(ptr) { ptr[0] = '\0'; } + } + } + if(host) + { + char extra[cs_strlen(host) + 20]; + snprintf(extra, sizeof(extra), "Location: https://%s", host); + send_error(f, 301, "Moved Permanently", extra, "This web server is running in SSL mode.", 1); + } + else + { send_error(f, 200, "Bad Request", NULL, "This web server is running in SSL mode.", 1); } + fflush(f); + fclose(f); + NULLFREE(filebuf); + } + else + { + cs_log_dbg(D_TRACE, "WebIf: fdopen(%d) failed. (errno=%d %s)", s, errno, strerror(errno)); + } + } + } + else { cs_log("WebIf: Error calling SSL_set_fd()."); } + SSL_shutdown(ssl); + close(s); + SSL_free(ssl); + } + else +#endif + { + FILE *f; + f = fdopen(s, "r+"); + if(f != NULL) + { + process_request(f, in); + fflush(f); + fclose(f); + } + else + { + cs_log_dbg(D_TRACE, "WebIf: fdopen(%d) failed. (errno=%d %s)", s, errno, strerror(errno)); + } + shutdown(s, SHUT_WR); + close(s); + } + + return NULL; +} + +/* Creates a random string with specified length. Note that dst must be one larger than size to hold the trailing \0*/ +static void create_rand_str(char *dst, int32_t size) +{ + int32_t i; + for(i = 0; i < size; ++i) + { + dst[i] = (rand() % 94) + 32; + } + dst[i] = '\0'; +} + +static void *http_server(void *UNUSED(d)) +{ + struct s_client *cl = create_client(first_client->ip); + if(cl == NULL) { return NULL; } + SAFE_SETSPECIFIC(getclient, cl); + cl->typ = 'h'; + int32_t s, reuse = 1; + struct s_connection *conn; + + set_thread_name(__func__); + + /* Create random string for nonce value generation */ + create_rand_str(noncekey, 32); + + /* Prepare base64 decoding array */ + b64prepare(); + webif_tpls_prepare(); +#ifdef WEBIF_WIKI + webif_wiki_prepare(); +#endif + + tpl_checkDiskRevisions(); + + cs_lock_create(__func__, &http_lock, "http_lock", 10000); + init_noncelocks(); + + memset(&p_stat_cur, 0x0, sizeof(p_stat_cur)); + + if(pthread_key_create(&getip, NULL)) + { + cs_log("Could not create getip"); + return NULL; + } + if(pthread_key_create(&getkeepalive, NULL)) + { + cs_log("Could not create getkeepalive"); + return NULL; + } + + struct SOCKADDR sin; + socklen_t len = 0; + memset(&sin, 0, sizeof(sin)); + + bool do_ipv6 = config_enabled(IPV6SUPPORT); +#ifdef IPV6SUPPORT + if(do_ipv6) + { + len = sizeof(struct sockaddr_in6); + if((sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + cs_log("HTTP Server: ERROR: Creating IPv6 socket failed! (errno=%d %s)", errno, strerror(errno)); + cs_log("HTTP Server: Falling back to IPv4."); + do_ipv6 = false; + } + else if (IP_ISSET(cfg.http_srvip) && (IN6_IS_ADDR_V4MAPPED(&cfg.http_srvip) || IN6_IS_ADDR_V4COMPAT(&cfg.http_srvip))) // ipv4 set as http_srvip + { + do_ipv6 = false; + } + else + { + struct sockaddr_in6 *ia = (struct sockaddr_in6 *)&sin; + + if (IP_ISSET(cfg.http_srvip)) + { + IP_ASSIGN(SIN_GET_ADDR(sin), cfg.http_srvip); + } + else if (IP_ISSET(cfg.srvip)) + { + IP_ASSIGN(SIN_GET_ADDR(sin), cfg.srvip); + } + else + { + ia->sin6_addr = in6addr_any; + } + + ia->sin6_family = AF_INET6; + ia->sin6_port = htons(cfg.http_port); + } + } +#endif + if(!do_ipv6) + { + len = sizeof(struct sockaddr_in); + if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + { + cs_log("HTTP Server: ERROR: Creating socket failed! (errno=%d %s)", errno, strerror(errno)); + return NULL; + } + SIN_GET_FAMILY(sin) = AF_INET; + if(IP_ISSET(cfg.http_srvip)) + { IP_ASSIGN(SIN_GET_ADDR(sin), cfg.http_srvip); } + else if(IP_ISSET(cfg.srvip)) + { IP_ASSIGN(SIN_GET_ADDR(sin), cfg.srvip); } + // The default is INADDR_ANY (0) + SIN_GET_PORT(sin) = htons(cfg.http_port); + } + + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) + { + cs_log("HTTP Server: Setting SO_REUSEADDR via setsockopt failed! (errno=%d %s)", errno, strerror(errno)); + } + + if(bind(sock, (struct sockaddr *)&sin, len) < 0) + { + cs_log("HTTP Server couldn't bind on port %d (errno=%d %s). Not starting HTTP!", cfg.http_port, errno, strerror(errno)); + close(sock); + return NULL; + } + + if(listen(sock, SOMAXCONN) < 0) + { + cs_log("HTTP Server: Call to listen() failed! (errno=%d %s)", errno, strerror(errno)); + close(sock); + return NULL; + } + +#ifdef WITH_SSL + if(pthread_key_create(&getssl, NULL)) + { + cs_log("Could not create getssl"); + } + + SSL_CTX *ctx = NULL; + if(cfg.http_use_ssl) + { + ctx = SSL_Webif_Init(); + if(ctx == NULL) + { cs_log("SSL could not be initialized. Starting WebIf in plain mode."); } + else { ssl_active = 1; } + } + else { ssl_active = 0; } + cs_log("HTTP%s Server running. ip=%s port=%d (%s)", ssl_active ? "S" : "", cs_inet_ntoa(SIN_GET_ADDR(sin)), cfg.http_port, ssl_active ? OPENSSL_VERSION_TEXT : "no SSL"); +#else + cs_log("HTTP Server running. ip=%s port=%d", cs_inet_ntoa(SIN_GET_ADDR(sin)), cfg.http_port); +#endif + + struct SOCKADDR remote; + memset(&remote, 0, sizeof(remote)); + + while(!exit_oscam) + { + if((s = accept(sock, (struct sockaddr *) &remote, &len)) < 0) + { + if(exit_oscam) + { break; } + if(errno != EAGAIN && errno != EINTR) + { + cs_log("HTTP Server: Error calling accept() (errno=%d %s)", errno, strerror(errno)); + cs_sleepms(100); + } + else { cs_sleepms(5); } + continue; + } + else + { + getpeername(s, (struct sockaddr *) &remote, &len); + if(!cs_malloc(&conn, sizeof(struct s_connection))) + { + close(s); + continue; + } + setTCPTimeouts(s); + cur_client()->last = time((time_t *)0); //reset last busy time + conn->cl = cur_client(); +#ifdef IPV6SUPPORT + if(do_ipv6) + { + struct sockaddr_in6 *ra = (struct sockaddr_in6 *)&remote; + memcpy(&conn->remote, &ra->sin6_addr, sizeof(struct in6_addr)); + } + else + { + struct sockaddr_in *fba = (struct sockaddr_in *)&remote; + struct in6_addr taddr; + memset(&taddr, 0, sizeof(taddr)); + taddr.s6_addr32[3] = fba->sin_addr.s_addr; + memcpy(&conn->remote, &taddr, sizeof(struct in6_addr)); + } +#else + memcpy(&conn->remote, &remote.sin_addr, sizeof(struct in_addr)); +#endif + conn->socket = s; +#ifdef WITH_SSL + conn->ssl = NULL; + if(ssl_active) + { + conn->ssl = SSL_new(ctx); + if(conn->ssl == NULL) + { + close(s); + cs_log("WebIf: Error calling SSL_new()."); + continue; + } + } +#endif + + int32_t ret = start_thread("webif workthread", serve_process, (void *)conn, NULL, 1, 1); + if(ret) + { + NULLFREE(conn); + } + } + } + // Wait a bit so that we don't close ressources while http threads are active + cs_sleepms(300); +#ifdef WITH_SSL + SSL_CTX_free(ctx); + CRYPTO_set_dynlock_create_callback(NULL); + CRYPTO_set_dynlock_lock_callback(NULL); + CRYPTO_set_dynlock_destroy_callback(NULL); + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + OPENSSL_free(lock_cs); + lock_cs = NULL; +#endif + cs_log("HTTP Server stopped"); + free_client(cl); + close(sock); + return NULL; +} + +void webif_client_reset_lastresponsetime(struct s_client * cl) +{ + int32_t i; + for(i = 0; i < CS_ECM_RINGBUFFER_MAX; i++) + { + cl->cwlastresptimes[i].duration = 0; + cl->cwlastresptimes[i].timestamp = time((time_t *)0); + cl->cwlastresptimes[i].rc = 0; + } + cl->cwlastresptimes_last = 0; +} + +void webif_client_add_lastresponsetime(struct s_client * cl, int32_t ltime, time_t timestamp, int32_t rc) +{ + int32_t last = cl->cwlastresptimes_last = (cl->cwlastresptimes_last + 1) & (CS_ECM_RINGBUFFER_MAX - 1); + cl->cwlastresptimes[last].duration = ltime > 9999 ? 9999 : ltime; + cl->cwlastresptimes[last].timestamp = timestamp; + cl->cwlastresptimes[last].rc = rc; +} + +void webif_client_init_lastreader(struct s_client * client, ECM_REQUEST * er, struct s_reader * er_reader, const char *stxt[]) +{ + if(er_reader) + { + if(er->rc == E_FOUND) + { cs_strncpy(client->lastreader, er_reader->label, sizeof(client->lastreader)); } + else if(er->rc == E_CACHEEX) + #ifdef CS_CACHEEX_AIO + { + if (cfg.cacheex_srcname_webif) + { + cs_strncpy(client->lastreader, username(er_reader->client), sizeof(client->lastreader)); + } + else + { + cs_strncpy(client->lastreader, "cache3", sizeof(client->lastreader)); + } + } + #else + { cs_strncpy(client->lastreader, "cache3", sizeof(client->lastreader)); } + #endif + else if(er->rc < E_NOTFOUND) + { snprintf(client->lastreader, sizeof(client->lastreader) - 1, "%.54s (cache)", er_reader->label); } + else + { cs_strncpy(client->lastreader, stxt[er->rc], sizeof(client->lastreader)); } + } + else + { + cs_strncpy(client->lastreader, stxt[er->rc], sizeof(client->lastreader)); + } +} + +void webif_init(void) +{ + char buf[8], fname[256]; + snprintf(buf, 8, "%'d", 7); + if(strcmp(buf, "7")) + { + useLocal = 0; + } + + if(cfg.http_port == 0) + { + cs_log("http disabled"); + return; + } + + get_config_filename(fname, sizeof(fname), "oscam.srvid2"); + use_srvid2 = file_exists(fname); + + if(start_thread("http", http_server, NULL, &httpthread, 0, 1) == 0) + { + httpthread_running = 1; + } +} + +void webif_close(void) +{ + if(!sock) + { return; } + + shutdown(sock, 2); + close(sock); + + if(httpthread_running) + { SAFE_THREAD_JOIN(httpthread, NULL); } +} + +#endif diff --git a/module-webif.h b/module-webif.h old mode 100755 new mode 100644 diff --git a/modules.h b/modules.h old mode 100755 new mode 100644 diff --git a/oscam-aes.c b/oscam-aes.c old mode 100755 new mode 100644 diff --git a/oscam-aes.h b/oscam-aes.h old mode 100755 new mode 100644 diff --git a/oscam-array.c b/oscam-array.c old mode 100755 new mode 100644 diff --git a/oscam-array.h b/oscam-array.h old mode 100755 new mode 100644 diff --git a/oscam-cache.c b/oscam-cache.c old mode 100755 new mode 100644 index f31abda..d50f245 --- a/oscam-cache.c +++ b/oscam-cache.c @@ -549,7 +549,7 @@ CW_CACHE_SETTING get_cw_cache(ECM_REQUEST *er) return cw_cache_setting; } -static bool __attribute__((unused)) cw_cache_check(ECM_REQUEST *er) +static bool cw_cache_check(ECM_REQUEST *er) { if(cw_cache_init_done) { diff --git a/oscam-cache.c.bak b/oscam-cache.c.bak new file mode 100644 index 0000000..ab1b202 --- /dev/null +++ b/oscam-cache.c.bak @@ -0,0 +1,985 @@ +#define MODULE_LOG_PREFIX "cache" + +#include "globals.h" +#include "module-cacheex.h" +#include "module-cw-cycle-check.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-ecm.h" +#include "oscam-garbage.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "oscam-time.h" +#include "oscam-hashtable.h" +#ifdef CS_CACHEEX_AIO +#include "oscam-log.h" +#endif + + +// CACHE functions **************************************************************+ +struct s_pushclient +{ + struct s_client *cl; + struct s_pushclient *next_push; +}; + +typedef struct cw_t +{ + uint8_t cw[16]; + uint8_t odd_even; // odd/even byte (0x80 0x81) + uint8_t cwc_cycletime; + uint8_t cwc_next_cw_cycle; + uint8_t got_bad_cwc; // used by cycle check + uint16_t caid; // first caid received + uint32_t prid; // first prid received + uint16_t srvid; // first srvid received + struct s_reader *selected_reader; // first answering: reader + struct s_client *cacheex_src; // first answering: cacheex client + uint64_t grp; // updated grp + uint8_t csp; // updated if answer from csp + uint8_t cacheex; // updated if answer from cacheex + uint8_t localcards; // updated if answer from local cards (or proxy using localcards option) + uint8_t proxy; // updated if answer from local reader + uint32_t count; // count of same cws receved +#ifdef CS_CACHEEX_AIO + uint8_t localgenerated; // flag for local generated CWs +#endif + // for push out + pthread_rwlock_t pushout_client_lock; + struct s_pushclient *pushout_client; // list of clients that pushing cw + // end push out + node ht_node; // node for hash table + node ll_node; // node for linked list +} CW; + +typedef struct cache_t +{ + hash_table ht_cw; + list ll_cw; + struct timeb upd_time; // updated time. Update time at each cw got + struct timeb first_recv_time; // time of first cw received + uint32_t csp_hash; + node ht_node; // node for hash table + node ll_node; // node for linked list +} ECMHASH; + +#ifdef CS_CACHEEX_AIO +typedef struct cw_cache_t +{ + uint8_t cw[16]; + uint16_t caid; + uint32_t prid; + uint16_t srvid; + struct timeb first_recv_time; // time of first cw received + struct timeb upd_time; // updated time. Update time at each cw got + node ht_node; + node ll_node; +} CW_CACHE; + +typedef struct cw_cache_setting_t +{ + int8_t mode; + uint16_t timediff_old_cw; +} CW_CACHE_SETTING; +#endif + +static pthread_rwlock_t cache_lock; +#ifdef CS_CACHEEX_AIO +static pthread_rwlock_t cw_cache_lock; +#endif +static hash_table ht_cache; +#ifdef CS_CACHEEX_AIO +static hash_table ht_cw_cache; +#endif +static list ll_cache; +#ifdef CS_CACHEEX_AIO +static list ll_cw_cache; +#endif +static int8_t cache_init_done = 0; + +#ifdef CS_CACHEEX_AIO +static int8_t cw_cache_init_done = 0; +static uint32_t lg_cache_size = 0; + +void init_cw_cache(void) +{ +#ifdef CS_CACHEEX + if(cfg.cw_cache_size > 0 || cfg.cw_cache_memory > 0) + { + init_hash_table(&ht_cw_cache, &ll_cw_cache); + if (pthread_rwlock_init(&cw_cache_lock,NULL) != 0) + { cs_log("Error creating lock cw_cache_lock!"); } + else + { cw_cache_init_done = 1; } + } +#endif +} +#endif + +void init_cache(void) +{ + init_hash_table(&ht_cache, &ll_cache); + if (pthread_rwlock_init(&cache_lock,NULL) != 0) + { cs_log("Error creating lock cache_lock!"); } + else + { cache_init_done = 1; } +} + +void free_cache(void) +{ + cleanup_cache(true); +#ifdef CS_CACHEEX_AIO + cw_cache_cleanup(true); + ecm_cache_cleanup(true); + cw_cache_init_done = 0; + deinitialize_hash_table(&ht_cw_cache); + pthread_rwlock_destroy(&cw_cache_lock); +#endif + cache_init_done = 0; + deinitialize_hash_table(&ht_cache); + pthread_rwlock_destroy(&cache_lock); +} + +#ifdef CS_CACHEEX_AIO +uint32_t cache_size_lg(void) +{ + if(!cache_init_done) + { return 0; } + + return lg_cache_size; +} +#endif + +uint32_t cache_size(void) +{ + if(!cache_init_done) + { return 0; } + + return count_hash_table(&ht_cache); +} + +static uint8_t count_sort(CW *a, CW *b) +{ + if (a->count == b->count) return 0; + return (a->count > b->count) ? -1 : 1; // DESC order by count +} + +#ifdef CS_CACHEEX_AIO +static uint8_t time_sort(CW_CACHE *a, CW_CACHE *b) +{ + if (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) == ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) return 0; + return (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) > ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) ? -1 : 1; +} +#endif + +uint8_t check_is_pushed(void *cwp, struct s_client *cl) +{ + struct s_pushclient *cl_tmp; + CW* cw = (CW*)cwp; + bool pushed=false; + + SAFE_RWLOCK_RDLOCK(&cw->pushout_client_lock); + for (cl_tmp = cw->pushout_client; cl_tmp; cl_tmp = cl_tmp->next_push) + { + if(cl_tmp->cl==cl) + { + pushed=true; + break; + } + } + + if(!pushed) + { + SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock); + SAFE_RWLOCK_WRLOCK(&cw->pushout_client_lock); + + struct s_pushclient *new_push_client; + if(cs_malloc(&new_push_client, sizeof(struct s_pushclient))) + { + new_push_client->cl=cl; + + new_push_client->next_push=cw->pushout_client; + cw->pushout_client=new_push_client; + } + + SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock); + return 0; + } + else + { + SAFE_RWLOCK_UNLOCK(&cw->pushout_client_lock); + return 1; + } +} + +uint8_t get_odd_even(ECM_REQUEST *er) +{ + return (er->ecm[0] != 0x80 && er->ecm[0] != 0x81 ? 0 : er->ecm[0]); +} + + +CW *get_first_cw(ECMHASH *ecmhash, ECM_REQUEST *er) +{ + if(!ecmhash) return NULL; + + node *j; + CW *cw; + + j = get_first_node_list(&ecmhash->ll_cw); + while (j) { + cw = get_data_from_node(j); + + if(cw && cw->odd_even == get_odd_even(er) && !cw->got_bad_cwc) + return cw; + + j = j->next; + } + + return NULL; +} + +int compare_csp_hash(const void *arg, const void *obj) +{ + uint32_t h = ((const ECMHASH*)obj)->csp_hash; + return memcmp(arg, &h, 4); +} + +static int compare_cw(const void *arg, const void *obj) +{ + return memcmp(arg, ((const CW*)obj)->cw, 16); +} + +#ifdef CS_CACHEEX_AIO +static int compare_cw_cache(const void *arg, const void *obj) +{ + return memcmp(arg, ((const CW_CACHE*)obj)->cw, 16); +} +#endif + +static bool cwcycle_check_cache(struct s_client *cl, ECM_REQUEST *er, CW *cw) +{ + (void)cl; (void)er; (void)cw; + +#ifdef CW_CYCLE_CHECK + if(cw->got_bad_cwc) + return 0; + + uint8_t cwc_ct = cw->cwc_cycletime > 0 ? cw->cwc_cycletime : 0; + uint8_t cwc_ncwc = cw->cwc_next_cw_cycle < 2 ? cw->cwc_next_cw_cycle : 2; + if(checkcwcycle(cl, er, NULL, cw->cw, 0, cwc_ct, cwc_ncwc) != 0) + { + cs_log_dbg(D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [check_cache] cyclecheck passed ecm in INT. cache.", (cl ? cl->account->usr : "-"), er->caid, er->srvid); + } + else + { +#ifdef CS_CACHEEX_AIO + if(!er->localgenerated) + { +#endif + cs_log_dbg(D_CWC, "cyclecheck [BAD CW Cycle] from Int. Cache detected.. {client %s, caid %04X, srvid %04X} [check_cache] -> skip cache answer", (cl ? cl->account->usr : "-"), er->caid, er->srvid); + cw->got_bad_cwc = 1; // no need to check it again + return 0; +#ifdef CS_CACHEEX_AIO + } + else + { + cs_log_dbg(D_CWC, "cyclecheck [BAD CW Cycle] from Int. Cache detected.. {client %s, caid %04X, srvid %04X} [check_cache] -> lg-flagged CW -> do nothing", (cl ? cl->account->usr : "-"), er->caid, er->srvid); + } +#endif + } +#endif + return 1; +} + +/* + * This function returns cw (mostly received) in cache for er, or NULL if not found. + * IMPORTANT: + * - If found, DON'T forget to free returned ecm, because it is a copy useful to get data + * - If found, and cacheex_src client of returned ecm is not NULL, and we want to access it, + * remember to check for its validity (client structure is still existent) + * E.g.: if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill) + * We don't want make this stuff here to avoid useless cpu time if outside function we would not access to it. + */ +struct ecm_request_t *check_cache(ECM_REQUEST *er, struct s_client *cl) +{ + if(!cache_init_done || !er->csp_hash) return NULL; + + ECM_REQUEST *ecm = NULL; + ECMHASH *result; + CW *cw; + uint64_t grp = cl?cl->grp:0; + + SAFE_RWLOCK_RDLOCK(&cache_lock); + + result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(uint32_t),&compare_csp_hash); + cw = get_first_cw(result, er); + if (!cw) + goto out_err; + + if(cw->csp // csp have no grp! + || !grp // csp client(no grp) searching for cache + || (grp && cw->grp // ecm group --> only when readers/ex-clients answer (e_found) it + && (grp & cw->grp))) + { +#ifdef CS_CACHEEX + //if preferlocalcards=2 for this ecm request, we can server ONLY cw from localcards readers until stage<3 + if(er->preferlocalcards==2 && !cw->localcards && er->stage<3){ + goto out_err; + } + + CWCHECK check_cw = get_cwcheck(er); + + if((!cw->proxy && !cw->localcards) // cw received from ONLY cacheex/csp peers + && check_cw.counter>1 + && cw->count < check_cw.counter + && (check_cw.mode == 1 || !er->cacheex_wait_time_expired)) + { + goto out_err; + } + +#ifdef CS_CACHEEX_AIO + // client + if( cl && !cw->localgenerated + && !(chk_srvid_localgenerated_only_exception(er)) // service-based exception + && (cl->account->cacheex.localgenerated_only + || (chk_lg_only(er, &cl->account->cacheex.lg_only_tab)) + ) // only lg-flagged CWs + ) + { + goto out_err; + } +#endif +#endif + + if (!cwcycle_check_cache(cl, er, cw)) + goto out_err; + + if (cs_malloc(&ecm, sizeof(ECM_REQUEST))) + { + ecm->rc = E_FOUND; + ecm->rcEx = 0; + memcpy(ecm->cw, cw->cw, 16); + ecm->grp = cw->grp; + ecm->selected_reader = cw->selected_reader; + ecm->cwc_cycletime = cw->cwc_cycletime; + ecm->cwc_next_cw_cycle = cw->cwc_next_cw_cycle; + ecm->cacheex_src = cw->cacheex_src; +#ifdef CS_CACHEEX_AIO + ecm->localgenerated = (cw->localgenerated) ? 1:0; +#endif + ecm->cw_count = cw->count; + } + } + +out_err: + SAFE_RWLOCK_UNLOCK(&cache_lock); + return ecm; +} + +#ifdef CS_CACHEEX_AIO +uint16_t get_cacheex_nopushafter(ECM_REQUEST *er) +{ + return caidvaluetab_get_value(&cfg.cacheex_nopushafter_tab, er->caid, 0); +} +#endif + +static void cacheex_cache_add(ECM_REQUEST *er, ECMHASH *result, CW *cw, bool add_new_cw) +{ + (void)er; (void)result; (void)cw; (void)add_new_cw; +#ifdef CS_CACHEEX + er->cw_cache = cw; + cacheex_cache_push(er); + + // cacheex debug log lines and cw diff stuff + if(!check_client(er->cacheex_src)) + return; + +#ifdef CS_CACHEEX_AIO + if (D_CACHEEX & cs_dblevel) + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + + if(!add_new_cw) + { + debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s - hop %i %s, src-nodeid %" PRIu64 "X", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : "", cacheex_node_id(remotenodeid)); + return; + } + + debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s - hop %i %s, src-nodeid %" PRIu64 "X", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : "", cacheex_node_id(remotenodeid)); + } + else + { +#endif + if(!add_new_cw) + { +#ifdef CS_CACHEEX_AIO + debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s - hop %i %s", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : ""); +#else + debug_ecm(D_CACHEEX| D_CSP, "got duplicate pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src)); +#endif + return; + } + +#ifdef CS_CACHEEX_AIO + debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s - hop %i %s", buf, er->from_csp ? "csp" : username(er->cacheex_src), ll_count(er->csp_lastnodes), er->localgenerated ? "(lg)" : ""); +#else + debug_ecm(D_CACHEEX|D_CSP, "got pushed ECM %s from %s", buf, er->from_csp ? "csp" : username(er->cacheex_src)); +#endif + +#ifdef CS_CACHEEX_AIO + } +#endif + + CW *cw_first = get_first_cw(result, er); + if(!cw_first) + return; + + // compare er cw with mostly counted cached cw + if(memcmp(er->cw, cw_first->cw, sizeof(er->cw)) != 0) + { + er->cacheex_src->cwcacheexerrcw++; + if (er->cacheex_src->account) + er->cacheex_src->account->cwcacheexerrcw++; + + if (((0x0200| 0x0800) & cs_dblevel)) // avoid useless operations if debug is not enabled + { + char cw1[16*3+2], cw2[16*3+2]; + cs_hexdump(0, er->cw, 16, cw1, sizeof(cw1)); + cs_hexdump(0, cw_first->cw, 16, cw2, sizeof(cw2)); + + char ip1[20]="", ip2[20]=""; + if (check_client(er->cacheex_src)) + cs_strncpy(ip1, cs_inet_ntoa(er->cacheex_src->ip), sizeof(ip1)); + if (check_client(cw_first->cacheex_src)) + cs_strncpy(ip2, cs_inet_ntoa(cw_first->cacheex_src->ip), sizeof(ip2)); + else if (cw_first->selected_reader && check_client(cw_first->selected_reader->client)) + cs_strncpy(ip2, cs_inet_ntoa(cw_first->selected_reader->client->ip), sizeof(ip2)); + +#ifdef CS_CACHEEX_AIO + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + + uint8_t fakeF0 = 0, offset = 0; + + if(get_odd_even(er) == 0x81) + offset = 8; + + if( + (cw_first->cw[7+offset] != 0x00 && er->cw[7+offset] != 0x00) + && (cw_first->cw[7+offset] ^ 0xF0) == er->cw[7+offset] + ) + { + fakeF0 = 1; + } + + debug_ecm(D_CACHEEX| D_CSP, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s lg: %i<>%i, hop:%02i, src-nodeid: %" PRIu64 "X%s", buf, +#else + debug_ecm(D_CACHEEX| D_CSP, "WARNING: Different CWs %s from %s(%s)<>%s(%s): %s<>%s ", buf, +#endif + er->from_csp ? "csp" : username(er->cacheex_src), ip1, + check_client(cw_first->cacheex_src)?username(cw_first->cacheex_src):(cw_first->selected_reader?cw_first->selected_reader->label:"unknown/csp"), ip2, +#ifdef CS_CACHEEX_AIO + cw1, cw2, er->localgenerated, cw_first->localgenerated, er->csp_lastnodes ? ll_count(er->csp_lastnodes) : 0, er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0, fakeF0 ? " [last byte xor 0xF0]" : ""); +#else + cw1, cw2); +#endif +#ifdef WITH_DEBUG + if(cs_dblevel & D_CACHEEX) + { + LL_LOCKITER *li = ll_li_create(er->csp_lastnodes, 0); + uint8_t *nodeid; + uint8_t hops = 0; + while((nodeid = ll_li_next(li))) + { + cs_log_dbg(D_CACHEEX, "Different CW-nodelist hop%02u: %" PRIu64 "X", ++hops, cacheex_node_id(nodeid)); + } + ll_li_destroy(li); + } +#endif + } + } +#endif +} + +#ifdef CS_CACHEEX_AIO +CW_CACHE_SETTING get_cw_cache(ECM_REQUEST *er) +{ + int32_t i, timediff_old_cw = 0; + int8_t mode = 0; + + for(i = 0; i < cfg.cw_cache_settings.cwchecknum; i++) + { + CWCHECKTAB_DATA *d = &cfg.cw_cache_settings.cwcheckdata[i]; + + if(i == 0 && d->caid <= 0) + { + mode = d->mode; + timediff_old_cw = d->counter; + continue; //check other, only valid for unset + } + + if(d->caid == er->caid || d->caid == er->caid >> 8 || ((d->cmask >= 0 && (er->caid & d->cmask) == d->caid) || d->caid == -1)) + { + if((d->prid >= 0 && d->prid == (int32_t)er->prid) || d->prid == -1) + { + if((d->srvid >= 0 && d->srvid == er->srvid) || d->srvid == -1) + { + mode = d->mode; + timediff_old_cw = d->counter; + break; + } + } + } + } + + //check for correct values + if(mode>3 || mode<0) mode=0; + if(timediff_old_cw<1) timediff_old_cw=0; + + CW_CACHE_SETTING cw_cache_setting; + memset(&cw_cache_setting, 0, sizeof(CW_CACHE_SETTING)); + cw_cache_setting.mode = mode; + cw_cache_setting.timediff_old_cw = timediff_old_cw; + + return cw_cache_setting; +} + +static bool cw_cache_check(ECM_REQUEST *er) +{ + if(cw_cache_init_done) + { + CW_CACHE_SETTING cw_cache_setting = get_cw_cache(er); + if(cw_cache_setting.mode > 0) + { + CW_CACHE *cw_cache = NULL; + SAFE_RWLOCK_WRLOCK(&cw_cache_lock); + cw_cache = find_hash_table(&ht_cw_cache, &er->cw, sizeof(er->cw), &compare_cw_cache); + // add cw to ht_cw_cache if < cw_cache_size + if(!cw_cache) + { + // cw_cache-size(count/memory) pre-check + if( + (cfg.cw_cache_size && (cfg.cw_cache_size > tommy_hashlin_count(&ht_cw_cache))) + || (cfg.cw_cache_memory && (cfg.cw_cache_memory*1024*1024 > (2 * tommy_hashlin_memory_usage(&ht_cw_cache)))) + ) + { + if(cs_malloc(&cw_cache, sizeof(CW_CACHE))) + { + memcpy(cw_cache->cw, er->cw, sizeof(er->cw)); + cw_cache->caid = er->caid; + cw_cache->prid = er->prid; + cw_cache->srvid = er->srvid; + cs_ftime(&cw_cache->first_recv_time); + cs_ftime(&cw_cache->upd_time); + + tommy_hashlin_insert(&ht_cw_cache, &cw_cache->ht_node, cw_cache, tommy_hash_u32(0, &er->cw, sizeof(er->cw))); + tommy_list_insert_tail(&ll_cw_cache, &cw_cache->ll_node, cw_cache); + + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); + return true; + } + else + { + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); + cs_log("[cw_cache] ERROR: NO added HASH to cw_cache!!"); + return false; + } + } + else + { + // clean cache call; + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); + cw_cache_cleanup(false); + return false; + } + } + // cw found + else + { + char cw1[16*3+2]; + char cw2[16*3+2]; + int8_t drop_cw = 0; + int64_t gone_diff = 0; + + gone_diff = comp_timeb(&er->tps, &cw_cache->first_recv_time); + + if(D_CW_CACHE & cs_dblevel) + { + cs_hexdump(0, cw_cache->cw, 16, cw1, sizeof(cw1)); + cs_hexdump(0, er->cw, 16, cw2, sizeof(cw2)); + } + + if(cw_cache_setting.timediff_old_cw > 0 && gone_diff > cw_cache_setting.timediff_old_cw) // late (>cw_cache_setting.timediff_old_cw) cw incoming + { + // log every dupe cw + if(cs_dblevel & D_CW_CACHE) + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + cs_log_dbg(D_CW_CACHE,"[dupe CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): %"PRIi64" ms > %"PRIu16" - %s - hop %i%s, src-nodeid %" PRIu64 "X", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, cw_cache_setting.timediff_old_cw, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : "", er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); + } + + if(cw_cache->srvid == er->srvid && cw_cache->caid == er->caid) // same cw for same caid&srvid + { + cs_ftime(&cw_cache->upd_time); + cs_log_dbg(D_CW_CACHE,"[late CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): %"PRIi64" ms > %"PRIu16" - %s - hop %i%s", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, cw_cache_setting.timediff_old_cw, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : ""); + drop_cw=1; + + } + else if(cw_cache->srvid != er->srvid) // same cw for different srvid & late + { + cs_ftime(&cw_cache->upd_time); + cs_log_dbg(D_CW_CACHE,"[dupe&late CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s| diff(now): %"PRIi64" ms - %s - hop %i%s", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : ""); + drop_cw = 1; + } + else if(gone_diff > 15000) // same cw later as 15 secs + { +#ifdef WITH_DEBUG + if(cs_dblevel & D_CW_CACHE) + { + uint8_t remotenodeid[8]; + cacheex_get_srcnodeid(er, remotenodeid); + cs_log_dbg(D_CW_CACHE,"[late-15sec+ CW] cache: %04X:%06X:%04X:%s | in: %04X:%06X:%04X:%s | diff(now): %"PRIi64" ms > %"PRIu16" - %s - hop %i%s, src-nodeid %" PRIu64 "X", cw_cache->caid, cw_cache->prid, cw_cache->srvid, cw1, er->caid, er->prid, er->srvid, cw2, gone_diff, cw_cache_setting.timediff_old_cw, (er->selected_reader && cs_strlen(er->selected_reader->label)) ? er->selected_reader->label : username(er->cacheex_src), ll_count(er->csp_lastnodes), (er->localgenerated) ? " (lg)" : "", er->csp_lastnodes ? cacheex_node_id(remotenodeid): 0); + } +#endif + drop_cw = 1; + } + + if(cw_cache_setting.mode > 1 && drop_cw) + { + // cw_cache->drop_count++; + cs_log_dbg(D_CW_CACHE,"incoming CW dropped - current cw_cache_size: %i - cw_cache-mem-size: %iMiB", count_hash_table(&ht_cw_cache), 2*(int)tommy_hashlin_memory_usage(&ht_cw_cache)/1024/1024); + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); + return false; + } + } + } + + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); + return true; + } + } + else + { + cs_log_dbg(D_CW_CACHE,"[cw_cache] cw_cache_init_done %i cfg.cw_cache_size: %u cfg.cw_cache_memory %u", cw_cache_init_done, cfg.cw_cache_size, cfg.cw_cache_memory); + return true; + } + return true; +} +#endif + +void add_cache(ECM_REQUEST *er) +{ + if(!cache_init_done || !er->csp_hash) return; +#ifdef CS_CACHEEX_AIO + // cw_cache_check + if(!cw_cache_check(er)) + { + return; + } +#endif + ECMHASH *result = NULL; + CW *cw = NULL; + bool add_new_cw=false; + + SAFE_RWLOCK_WRLOCK(&cache_lock); + + // add csp_hash to cache + result = find_hash_table(&ht_cache, &er->csp_hash, sizeof(uint32_t), &compare_csp_hash); + if(!result) + { + if(cs_malloc(&result, sizeof(ECMHASH))) + { + result->csp_hash = er->csp_hash; + init_hash_table(&result->ht_cw, &result->ll_cw); + cs_ftime(&result->first_recv_time); + add_hash_table(&ht_cache, &result->ht_node, &ll_cache, &result->ll_node, result, &result->csp_hash, sizeof(uint32_t)); + } + else + { + SAFE_RWLOCK_UNLOCK(&cache_lock); + cs_log("ERROR: NO added HASH to cache!!"); + return; + } + } + + cs_ftime(&result->upd_time); // need to be updated at each cw! We use it for deleting this hash when no more cws arrive inside max_cache_time! + + //add cw to this csp hash + cw = find_hash_table(&result->ht_cw, er->cw, sizeof(er->cw), &compare_cw); + + if(!cw) + { + if(count_hash_table(&result->ht_cw) >= 10) // max 10 different cws stored + { + SAFE_RWLOCK_UNLOCK(&cache_lock); + return; + } + + while(1) + { + if(cs_malloc(&cw, sizeof(CW))) + { + memcpy(cw->cw, er->cw, sizeof(er->cw)); + cw->odd_even = get_odd_even(er); + cw->cwc_cycletime = er->cwc_cycletime; + cw->cwc_next_cw_cycle = er->cwc_next_cw_cycle; + cw->count= 0; + cw->csp = 0; + cw->cacheex = 0; + cw->localcards=0; + cw->proxy=0; + cw->grp = 0; + cw->caid = er->caid; + cw->prid = er->prid; + cw->srvid = er->srvid; + cw->selected_reader=er->selected_reader; + cw->cacheex_src=er->cacheex_src; + cw->pushout_client = NULL; + + while(1) + { + if (pthread_rwlock_init(&cw->pushout_client_lock, NULL) == 0) + break; + + cs_log("Error creating lock pushout_client_lock!"); + cs_sleepms(1); + } + + add_hash_table(&result->ht_cw, &cw->ht_node, &result->ll_cw, &cw->ll_node, cw, cw->cw, sizeof(er->cw)); + add_new_cw=true; + break; + } + + cs_log("ERROR: NO added CW to cache!! Re-trying..."); + cs_sleepms(1); + } + } + + // update if answered from csp/cacheex/local_proxy + if(er->from_cacheex) cw->cacheex = 1; + if(er->from_csp) cw->csp = 1; + if(!er->cacheex_src) + { + if(is_localreader(er->selected_reader, er)) cw->localcards=1; + else cw->proxy = 1; + } + +#ifdef CS_CACHEEX_AIO + // copy flag for local generated CW + if(er->localgenerated || (er->selected_reader && !is_network_reader(er->selected_reader))) + { + cw->localgenerated = 1; + er->localgenerated = 1; + // to favorite CWs with this flag while sorting + if(cw->count < 0x0F000000) + { + cw->count |= 0x0F000000; + lg_cache_size++; + } + } + else + { + cw->localgenerated = 0; + } +#endif + + // always update group and counter + cw->grp |= er->grp; + cw->count++; + +#ifdef CS_CACHEEX_AIO + // add count to er for checking @ cacheex_push + er->cw_count += cw->count; +#endif + // sort cw_list by counter (DESC order) + if(cw->count>1) + sort_list(&result->ll_cw, count_sort); + +#ifdef CS_CACHEEX_AIO + // dont push not flagged CWs - global + if(!er->localgenerated && + ( + !chk_srvid_localgenerated_only_exception(er) + && (cfg.cacheex_localgenerated_only || chk_lg_only(er, &cfg.cacheex_lg_only_tab)) + ) ) + { + cs_log_dbg(D_CACHEEX, "cacheex: push denied, cacheex_localgenerated_only->global"); + SAFE_RWLOCK_UNLOCK(&cache_lock); + return; + } + + // dont push CW if time for caid > x && from local reader | proxy + if(er->rc < 3 && er->ecm_time && get_cacheex_nopushafter(er) != 0 &&(get_cacheex_nopushafter(er) < er->ecm_time )) + { + cs_log_dbg(D_CACHEEX, "cacheex: push denied, cacheex_nopushafter %04X:%u < %i, reader: %s", er->caid, get_cacheex_nopushafter(er), er->ecm_time, er->selected_reader->label); + SAFE_RWLOCK_UNLOCK(&cache_lock); + return; + } + + // no cacheex-push on diff-cw's if no localgenerated flag exist + if(cfg.cacheex_dropdiffs && (count_hash_table(&result->ht_cw) > 1) && !er->localgenerated) + { + cs_log_dbg(D_CACHEEX,"cacheex: diff CW - cacheex push denied src: %s", er->selected_reader->label); + SAFE_RWLOCK_UNLOCK(&cache_lock); + return; + } +#endif + + SAFE_RWLOCK_UNLOCK(&cache_lock); + + cacheex_cache_add(er, result, cw, add_new_cw); +} + +#ifdef CS_CACHEEX_AIO +void cw_cache_cleanup(bool force) +{ + if(!cw_cache_init_done) + { return; } + + SAFE_RWLOCK_WRLOCK(&cw_cache_lock); + + CW_CACHE *cw_cache; + node *i, *i_next; + + uint32_t ll_c = 0; + uint32_t ll_ten_percent = (uint)tommy_list_count(&ll_cw_cache)*0.1; // 10 percent of cache + + if(!force) + sort_list(&ll_cw_cache, time_sort); + + i = get_first_node_list(&ll_cw_cache); + while(i) + { + i_next = i->next; + + cw_cache = get_data_from_node(i); + + if(!cw_cache) + { + i = i_next; + continue; + } + if(!force) + { + ++ll_c; + + if(ll_c < ll_ten_percent) + { + remove_elem_list(&ll_cw_cache, &cw_cache->ll_node); + remove_elem_hash_table(&ht_cw_cache, &cw_cache->ht_node); + NULLFREE(cw_cache); + } + else{ + break; + } + } + else + { + remove_elem_list(&ll_cw_cache, &cw_cache->ll_node); + remove_elem_hash_table(&ht_cw_cache, &cw_cache->ht_node); + NULLFREE(cw_cache); + } + + i = i_next; + } + + SAFE_RWLOCK_UNLOCK(&cw_cache_lock); +} +#endif + +void cleanup_cache(bool force) +{ + ECMHASH *ecmhash; + CW *cw; + struct s_pushclient *pc, *nxt; + node *i,*i_next,*j,*j_next; + + struct timeb now; + int64_t gone_first, gone_upd; + + if(!cache_init_done) + { return; } + + SAFE_RWLOCK_WRLOCK(&cache_lock); + + i = get_first_node_list(&ll_cache); + while(i) + { + i_next = i->next; + ecmhash = get_data_from_node(i); + + if(!ecmhash) + { + i = i_next; + continue; + } + + cs_ftime(&now); + gone_first = comp_timeb(&now, &ecmhash->first_recv_time); + gone_upd = comp_timeb(&now, &ecmhash->upd_time); + + if(!force && gone_first<=(cfg.max_cache_time*1000)) // not continue, useless check for nexts one! + { + break; + } + + if(force || gone_upd>(cfg.max_cache_time*1000)) + { + j = get_first_node_list(&ecmhash->ll_cw); + while(j) + { + j_next = j->next; + cw = get_data_from_node(j); + if(cw) + { + pthread_rwlock_destroy(&cw->pushout_client_lock); + pc = cw->pushout_client; + cw->pushout_client=NULL; + while(pc) + { + nxt = pc->next_push; + NULLFREE(pc); + pc = nxt; + } +#ifdef CS_CACHEEX_AIO + if(cw->count >= 0x0F000000) + { + lg_cache_size--; + } +#endif + remove_elem_list(&ecmhash->ll_cw, &cw->ll_node); + remove_elem_hash_table(&ecmhash->ht_cw, &cw->ht_node); + NULLFREE(cw); + } + j = j_next; + } + + deinitialize_hash_table(&ecmhash->ht_cw); + remove_elem_list(&ll_cache, &ecmhash->ll_node); + remove_elem_hash_table(&ht_cache, &ecmhash->ht_node); + NULLFREE(ecmhash); + } + i = i_next; + } + SAFE_RWLOCK_UNLOCK(&cache_lock); +} + +#ifdef CS_CACHEEX_AIO +void cacheex_get_srcnodeid(ECM_REQUEST *er, uint8_t *remotenodeid) +{ + uint8_t *data; + data = ll_last_element(er->csp_lastnodes); + if(data) + { + memcpy(remotenodeid, data, 8); + } + else + { + memset(remotenodeid, 0 , 8); + } +} +#endif diff --git a/oscam-cache.h b/oscam-cache.h old mode 100755 new mode 100644 diff --git a/oscam-chk.c b/oscam-chk.c old mode 100755 new mode 100644 index d2c104f..d9f8522 --- a/oscam-chk.c +++ b/oscam-chk.c @@ -894,7 +894,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr) return 0; } - if(!(rdr->typ == R_EMU) && !is_network_reader(rdr) && ((rdr->caid >> 8) != ((er->caid >> 8) & 0xFF) && (rdr->caid >> 8) != ((er->ocaid >> 8) & 0xFF))) + if( !is_network_reader(rdr) && (rdr->caid >> 8 != ((er->caid >> 8) & 0xFF)) && (rdr->caid >> 8 != ((er->ocaid >> 8) & 0xFF)) ) { if (!rdr->csystem) { return 0; } @@ -932,7 +932,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr) } // Checking ident: - if(!(rdr->typ == R_EMU && caid_is_biss(er->caid)) && !chk_rfilter(er, rdr)) + if(!chk_rfilter(er, rdr)) { cs_log_dbg(D_TRACE, "r-filter reader %s", rdr->label); return (0); @@ -993,7 +993,7 @@ int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr) } // Checking entitlements: - if(ll_count(rdr->ll_entitlements) > 0 && !(rdr->typ == R_EMU)) + if(ll_count(rdr->ll_entitlements) > 0) { LL_ITER itr = ll_iter_create(rdr->ll_entitlements); S_ENTITLEMENT *item; @@ -1174,7 +1174,7 @@ int32_t chk_caid(uint16_t caid, CAIDTAB *ctab) int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid) { - if(is_network_reader(rdr) || rdr->typ == R_EMU) + if(is_network_reader(rdr)) { return 1; // reader caid is not real caid } @@ -1268,7 +1268,7 @@ int8_t chk_fullCW(ECM_REQUEST *er, uint8_t *cw) return -1; } - // jeśli to halfCW system — pomijamy + // jeli to halfCW system pomijamy if(is_halfCW_er(er)) { return -1; @@ -1277,13 +1277,13 @@ int8_t chk_fullCW(ECM_REQUEST *er, uint8_t *cw) int8_t part1 = checkCWpart(cw, 0); int8_t part2 = checkCWpart(cw, 1); - // full CW musi mieć obie części poprawne + // full CW musi mie obie czci poprawne if(part1 && part2) { return 1; // OK } - return 0; // ZŁY FULL CW + return 0; // ZY FULL CW } diff --git a/oscam-chk.c.bak b/oscam-chk.c.bak new file mode 100644 index 0000000..1f44e3b --- /dev/null +++ b/oscam-chk.c.bak @@ -0,0 +1,1316 @@ +#define MODULE_LOG_PREFIX "chk" + +#include "globals.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-ecm.h" +#include "oscam-client.h" +#include "oscam-lock.h" +#include "oscam-net.h" +#include "oscam-string.h" +#include "module-stat.h" +#include "oscam-reader.h" + +#define CS_NANO_CLASS 0xE2 +#define OK 1 +#define ERROR 0 + +uint32_t get_fallbacktimeout(uint16_t caid) +{ + uint32_t ftimeout = caidvaluetab_get_value(&cfg.ftimeouttab, caid, 0); + + if(ftimeout == 0) { ftimeout = cfg.ftimeout; } + + if(ftimeout < 100) { ftimeout = CS_CLIENT_TIMEOUT / 2; } + if(ftimeout >= cfg.ctimeout) { ftimeout = cfg.ctimeout - 100; } + + return ftimeout; +} + +static int32_t find_nano(uint8_t *ecm, int32_t l, uint8_t nano, int32_t s) +{ + uint8_t *snano; + + if(s >= l) { return 0; } + if(!s) { s = (ecm[4] == 0xD2) ? 12 : 9; } // tpsflag -> offset+3 + snano = ecm + s; + + while((*snano != nano) && (s < l)) + { + if(*snano == 0xEA) { return 0; } + snano++; + s++; + } + + return (s < l) ? ++s : 0; +} + +static int32_t chk_class(ECM_REQUEST *er, CLASSTAB *clstab, const char *type, const char *name) +{ + int32_t i, j, an, cl_n, l; + uint8_t ecm_class; + + if(er->caid != 0x0500 && er->caid != 0x4AE1) { return 1; } + if(!clstab->bn && !clstab->an) { return 1; } + + j = an = cl_n = 0; + + if(er->caid == 0x0500) + { + while((j = find_nano(er->ecm, er->ecmlen, CS_NANO_CLASS, j)) > 0) + { + l = er->ecm[j]; + if(l + j > er->ecmlen) { continue; } // skip, this is not a valid class identifier! + + ecm_class = er->ecm[j + l]; + cs_log_dbg(D_CLIENT, "ecm class=%02X", ecm_class); + + for(i = 0; i < clstab->bn; i++) // search in blocked + { + if(ecm_class == clstab->bclass[i]) + { + cs_log_dbg(D_CLIENT, "class %02X rejected by %s '%s' !%02X filter", + ecm_class, type, name, ecm_class); + return 0; + } + } + cl_n++; + + for(i = 0; i < clstab->an; i++) // search in allowed + { + if(ecm_class == clstab->aclass[i]) + { + an++; + break; + } + } + j += l; + } + } + else + { + if(er->prid != 0x11 || er->ecm[0] == 0) { return 1; } + + cl_n++; + ecm_class = er->ecm[5]; + cs_log_dbg(D_CLIENT, "ecm class=%02X", ecm_class); + + for(i = 0; i < clstab->bn; i++) // search in blocked + { + if(ecm_class == clstab->bclass[i]) + { + cs_log_dbg(D_CLIENT, "class %02X rejected by %s '%s' !%02X filter", + ecm_class, type, name, ecm_class); + return 0; + } + } + + for(i = 0; i < clstab->an; i++) // search in allowed + { + if(ecm_class == clstab->aclass[i]) + { + an++; + break; + } + } + } + + if(cl_n && clstab->an) + { + if(an) + { cs_log_dbg(D_CLIENT, "ECM classes allowed by %s '%s' filter", type, name); } + else + { + cs_log_dbg(D_CLIENT, "ECM classes don't match %s '%s' filter, rejecting", type, name); + return 0; + } + } + + return 1; +} + +int32_t chk_srvid_match(ECM_REQUEST *er, SIDTAB *sidtab) +{ + int32_t i, rc = 0; + + if(!sidtab->num_caid) + { rc |= 1; } + else + for(i = 0; (i < sidtab->num_caid) && (!(rc & 1)); i++) + if(er->caid == sidtab->caid[i]) { rc |= 1; } + + if(!er->prid || !sidtab->num_provid) + { rc |= 2; } + else + for(i = 0; (i < sidtab->num_provid) && (!(rc & 2)); i++) + if(er->prid == sidtab->provid[i]) { rc |= 2; } + + if(!sidtab->num_srvid) + { rc |= 4; } + else + for(i = 0; (i < sidtab->num_srvid) && (!(rc & 4)); i++) + if(er->srvid == sidtab->srvid[i]) { rc |= 4; } + + return (rc == 7); +} + +#ifdef CS_CACHEEX_AIO +int32_t chk_srvid_disablecrccws_only_for_exception(ECM_REQUEST *er) +{ + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->disablecrccws_only_for_exception && (sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) && chk_srvid_match(er, sidtab)) + { + return(1); + } + } + return(0); +} + +int32_t chk_srvid_no_wait_time(ECM_REQUEST *er) +{ + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->no_wait_time && (sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) && chk_srvid_match(er, sidtab)) + { + return(1); + } + } + return(0); +} + +int32_t chk_srvid_localgenerated_only_exception(ECM_REQUEST *er) +{ + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->lg_only_exception && (sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) && chk_srvid_match(er, sidtab)) + { + return(1); + } + } + return(0); +} +#endif + +int32_t chk_srvid(struct s_client *cl, ECM_REQUEST *er) +{ + int32_t nr, rc = 0; + SIDTAB *sidtab; + + if(!cl->sidtabs.ok) + { + if(!cl->sidtabs.no) { return (1); } + rc = 1; + } + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_caid | sidtab->num_provid | sidtab->num_srvid) + { + if((cl->sidtabs.no & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { return (0); } + + if((cl->sidtabs.ok & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { rc = 1; } + } + } + return (rc); +} + +int32_t has_srvid(struct s_client *cl, ECM_REQUEST *er) +{ + if(!cl->sidtabs.ok) + { return 0; } + + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_srvid) + { + if((cl->sidtabs.ok & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { return 1; } + } + } + return 0; +} + +int32_t has_lb_srvid(struct s_client *cl, ECM_REQUEST *er) +{ + if(!cl->lb_sidtabs.ok) + { return 0; } + + int32_t nr; + SIDTAB *sidtab; + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if((cl->lb_sidtabs.ok & ((SIDTABBITS)1 << nr)) && (chk_srvid_match(er, sidtab))) + { return 1; } + } + return 0; +} + +int32_t chk_srvid_match_by_caid_prov(uint16_t caid, uint32_t provid, SIDTAB *sidtab) +{ + int32_t i, rc = 0; + + if(!sidtab->num_caid) + { rc |= 1; } + else + for(i = 0; (i < sidtab->num_caid) && (!(rc & 1)); i++) + if(caid == sidtab->caid[i]) { rc |= 1; } + + if(!sidtab->num_provid) + { rc |= 2; } + else + for(i = 0; (i < sidtab->num_provid) && (!(rc & 2)); i++) + if(provid == sidtab->provid[i]) { rc |= 2; } + + return (rc == 3); +} + +int32_t chk_srvid_by_caid_prov(struct s_client *cl, uint16_t caid, uint32_t provid) +{ + int32_t nr, rc = 0; + SIDTAB *sidtab; + + if(!cl->sidtabs.ok) + { + if(!cl->sidtabs.no) { return (1); } + rc = 1; + } + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_caid | sidtab->num_provid) + { + if((cl->sidtabs.no & ((SIDTABBITS)1 << nr)) && !sidtab->num_srvid && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { return (0); } + + if((cl->sidtabs.ok & ((SIDTABBITS)1 << nr)) && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { rc = 1; } + } + } + return (rc); +} + +int32_t chk_srvid_by_caid_prov_rdr(struct s_reader *rdr, uint16_t caid, uint32_t provid) +{ + int32_t nr, rc = 0; + SIDTAB *sidtab; + + if(!rdr->sidtabs.ok) + { + if(!rdr->sidtabs.no) { return (1); } + rc = 1; + } + + for(nr = 0, sidtab = cfg.sidtab; sidtab; sidtab = sidtab->next, nr++) + { + if(sidtab->num_caid | sidtab->num_provid) + { + if((rdr->sidtabs.no & ((SIDTABBITS)1 << nr)) && !sidtab->num_srvid && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { return (0); } + + if((rdr->sidtabs.ok & ((SIDTABBITS)1 << nr)) && + (chk_srvid_match_by_caid_prov(caid, provid, sidtab))) + { rc = 1; } + } + } + return (rc); +} + +int32_t chk_is_betatunnel_caid(uint16_t caid) +{ + if(caid == 0x1702 || caid == 0x1722) { return 1; } + if(caid == 0x1801 || caid == 0x1833 || caid == 0x1834 || caid == 0x1835) { return 2; } + return 0; +} + +uint16_t chk_on_btun(uint8_t chk_sx, struct s_client *cl, ECM_REQUEST *er) +{ + if(chk_is_betatunnel_caid(er->caid)) + { + int32_t i; + TUNTAB *ttab; + ttab = &cl->ttab; + + if(ttab->ttdata) + { + for(i = 0; i < ttab->ttnum; i++) + { + if(er->caid == ttab->ttdata[i].bt_caidfrom) + { + if(er->srvid == ttab->ttdata[i].bt_srvid) { return ttab->ttdata[i].bt_caidto; } + if(chk_sx && ttab->ttdata[i].bt_srvid == 0xFFFF) { return ttab->ttdata[i].bt_caidto; } + if(!chk_sx && !ttab->ttdata[i].bt_srvid) { return ttab->ttdata[i].bt_caidto; } + } + } + } + + if(chk_sx) + return lb_get_betatunnel_caid_to(er); + } + return 0; +} + +// server filter for newcamd +int32_t chk_sfilter(ECM_REQUEST *er, PTAB *ptab) +{ +#ifdef MODULE_NEWCAMD + int32_t i, j, pi, rc = 1; + uint16_t caid, scaid; + uint32_t prid, sprid; + + if(!ptab) { return (1); } + struct s_client *cur_cl = cur_client(); + + caid = er->caid; + prid = er->prid; + pi = cur_cl->port_idx; + + if(cfg.ncd_mgclient) + { return 1; } + + if(ptab->nports && ptab->ports[pi].ncd && ptab->ports[pi].ncd->ncd_ftab.nfilts) + { + for(rc = j = 0; (!rc) && (j < ptab->ports[pi].ncd->ncd_ftab.nfilts); j++) + { + scaid = ptab->ports[pi].ncd->ncd_ftab.filts[j].caid; + if(caid == 0 || (caid != 0 && caid == scaid)) + { + for(i = 0; (!rc) && i < ptab->ports[pi].ncd->ncd_ftab.filts[j].nprids; i++) + { + sprid = ptab->ports[pi].ncd->ncd_ftab.filts[j].prids[i]; + cs_log_dbg(D_CLIENT, "trying server filter %04X@%06X", scaid, sprid); + if(prid == sprid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by server filter %04X@%06X", + caid, prid, scaid, sprid); + } + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by server filters", caid, prid); + snprintf(er->msglog, MSGLOGSIZE, "no server match %04X@%06X", caid, (uint32_t) prid); + + if(!er->rcEx) { er->rcEx = (E1_LSERVER << 4) | E2_IDENT; } + return (rc); + } + } + return (rc); +#else + (void)er; + (void)ptab; + return 1; +#endif +} + +static int32_t chk_chid(ECM_REQUEST *er, FTAB *fchid, char *type, char *name) +{ + int32_t rc = 1, i, j, found_caid = 0; + if(!fchid->nfilts) { return 1; } + if(er->chid == 0 && er->ecm[0] == 0) { return 1; } // skip empty ecm, chid 00 to avoid no matching readers in dvbapi + + for(i = rc = 0; (!rc) && i < fchid->nfilts; i++) + { + if(er->caid == fchid->filts[i].caid) + { + found_caid = 1; + for(j = 0; (!rc) && j < fchid->filts[i].nprids; j++) + { + cs_log_dbg(D_CLIENT, "trying %s '%s' CHID filter %04X:%04X", + type, name, fchid->filts[i].caid, fchid->filts[i].prids[j]); + + if(er->chid == fchid->filts[i].prids[j]) + { + cs_log_dbg(D_CLIENT, "%04X:%04X allowed by %s '%s' CHID filter %04X:%04X", + er->caid, er->chid, type, name, fchid->filts[i].caid, fchid->filts[i].prids[j]); + rc = 1; + } + } + } + } + + if(!rc) + { + if(found_caid) + cs_log_dbg(D_CLIENT, "no match, %04X:%04X rejected by %s '%s' CHID filter(s)", + er->caid, er->chid, type, name); + else + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X:%04X allowed by %s '%s' CHID filter, CAID not spezified", + er->caid, er->chid, type, name); + } + } + return (rc); +} + +int32_t chk_ident_filter(uint16_t rcaid, uint32_t rprid, FTAB *ftab) +{ + int32_t i, j, rc = 1; + uint16_t caid = 0; + uint32_t prid = 0; + + if(ftab->nfilts) + { + for(rc = i = 0; (!rc) && (i < ftab->nfilts); i++) + { + caid = ftab->filts[i].caid; + if((caid != 0 && caid == rcaid) || caid == 0) + { + for(j = 0; (!rc) && (j < ftab->filts[i].nprids); j++) + { + prid = ftab->filts[i].prids[j]; + if(prid == rprid) + { + rc=1; + } + } + } + } + + if(!rc) + { return 0; } + } + + return(rc); +} + +int32_t chk_ufilters(ECM_REQUEST *er) +{ + int32_t i, j, rc = 1; + uint16_t ucaid; + uint32_t uprid; + struct s_client *cur_cl = cur_client(); + + if(cur_cl->ftab.nfilts) + { + FTAB *f = &cur_cl->ftab; + for(i = rc = 0; (!rc) && (i < f->nfilts); i++) + { + ucaid = f->filts[i].caid; + if(er->caid == 0 || ucaid == 0 || (er->caid != 0 && er->caid == ucaid)) + { + if (er->prid == 0) + { + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by user '%s' filter caid %04X prid %06X", + er->caid, er->prid, cur_cl->account->usr, ucaid, 0); + rc = 1; + break; + } + + for(j = rc = 0; (!rc) && (j < f->filts[i].nprids); j++) + { + uprid = f->filts[i].prids[j]; + cs_log_dbg(D_CLIENT, "trying user '%s' filter %04X@%06X", cur_cl->account->usr, ucaid, uprid); + + if(er->prid == uprid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by user '%s' filter %04X@%06X", + er->caid, er->prid, cur_cl->account->usr, ucaid, uprid); + } + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by user '%s' filters", + er->caid, er->prid, cur_cl->account->usr); + + snprintf(er->msglog, MSGLOGSIZE, "no card support %04X@%06X", er->caid, (uint32_t) er->prid); + + if(!er->rcEx) { er->rcEx = (E1_USER << 4) | E2_IDENT; } + return (rc); + } + } + + if(!(rc = chk_class(er, &cur_cl->cltab, "user", cur_cl->account->usr))) + { + if(!er->rcEx) { er->rcEx = (E1_USER << 4) | E2_CLASS; } + } + else if(!(rc = chk_chid(er, &cur_cl->fchid, "user", cur_cl->account->usr))) + { + if(!er->rcEx) { er->rcEx = (E1_USER << 4) | E2_CHID; } + } + + if(rc) { er->rcEx = 0; } + + return (rc); +} + +int32_t chk_rsfilter(struct s_reader *reader, ECM_REQUEST *er) +{ + int32_t i, rc = 1; + uint16_t caid; + uint32_t prid; + + if(reader->ncd_disable_server_filt) + { + cs_log_dbg(D_CLIENT, "%04X@%06X allowed - server filters disabled", er->caid, er->prid); + return 1; + } + + rc = 0; + caid = reader->caid; + if(caid == er->caid) + { + for(i = 0; (!rc) && (i < reader->nprov); i++) + { + prid = (uint32_t)((reader->prid[i][1] << 16) | (reader->prid[i][2] << 8) | (reader->prid[i][3])); + cs_log_dbg(D_CLIENT, "trying server '%s' filter %04X@%06X", reader->device, caid, prid); + + if(prid == er->prid || !er->prid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by server '%s' filter %04X@%06X", + er->caid, er->prid, reader->device, caid, prid); + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by server '%s' filters", + er->caid, er->prid, reader->device); + + if(!er->rcEx) { er->rcEx = (E1_SERVER << 4) | E2_IDENT; } + return 0; + } + + return (rc); +} + +int32_t chk_rfilter2(uint16_t rcaid, uint32_t rprid, struct s_reader *rdr) +{ + int32_t i, j, rc = 1; + uint16_t caid = 0; + uint32_t prid = 0; + + if(rdr->ftab.nfilts) + { + for(rc = i = 0; (!rc) && (i < rdr->ftab.nfilts); i++) + { + caid = rdr->ftab.filts[i].caid; + if((caid != 0 && caid == rcaid) || caid == 0) + { + for(j = 0; (!rc) && (j < rdr->ftab.filts[i].nprids); j++) + { + prid = rdr->ftab.filts[i].prids[j]; + cs_log_dbg(D_CLIENT, "trying reader '%s' filter %04X@%06X", rdr->label, caid, prid); + + if(prid == rprid) + { + rc = 1; + cs_log_dbg(D_CLIENT, "%04X@%06X allowed by reader '%s' filter %04X@%06X", + rcaid, rprid, rdr->label, caid, prid); + } + } + } + } + + if(!rc) + { + cs_log_dbg(D_CLIENT, "no match, %04X@%06X rejected by reader '%s' filters", + rcaid, rprid, rdr->label); + return 0; + } + } + + return (rc); +} + +static int32_t chk_rfilter(ECM_REQUEST *er, struct s_reader *rdr) +{ + return chk_rfilter2(er->caid, er->prid, rdr); +} + +int32_t chk_ctab(uint16_t caid, CAIDTAB *ctab) +{ + if(!caid || !ctab->ctnum) + { return 1; } + + int32_t i; + for(i = 0; i < ctab->ctnum; i++) + { + CAIDTAB_DATA *d = &ctab->ctdata[i]; + if(!d->caid) + { + return 0; + } + if((caid & d->mask) == d->caid) + { return 1; } + } + return 0; +} + +int32_t chk_ctab_ex(uint16_t caid, CAIDTAB *ctab) +{ + if(!caid || !ctab->ctnum) + { return 0; } + + int32_t i; + for(i = 0; i < ctab->ctnum; i++) + { + CAIDTAB_DATA *d = &ctab->ctdata[i]; + if(!d->caid) + { + return 0; + } + + if((caid & d->mask) == d->caid) + { + return 1; + } + } + return 0; +} + +uint8_t is_localreader(struct s_reader *rdr, ECM_REQUEST *er) // to be used for LB/reader selections checks only +{ + if(!rdr) return 0; + + if(!is_network_reader(rdr)) + { + return 1; + } + + if(!rdr->localcards.nfilts) { return 0; } + + int32_t i, k; + for(i = 0; i < rdr->localcards.nfilts; i++) + { + uint16_t tcaid = rdr->localcards.filts[i].caid; + if(tcaid && tcaid == er->caid) // caid match + { + int32_t nprids = rdr->localcards.filts[i].nprids; + if(!nprids) // No Provider -> Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = rdr->localcards.filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} + +uint8_t chk_is_fixed_fallback(struct s_reader *rdr, ECM_REQUEST *er) +{ + if(!rdr->fallback && !rdr->fallback_percaid.nfilts) { return 0; } + + if(!rdr->fallback_percaid.nfilts) + { + if(rdr->fallback) + { return 1; } + } + + int32_t i, k; + for(i = 0; i < rdr->fallback_percaid.nfilts; i++) + { + uint16_t tcaid = rdr->fallback_percaid.filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = rdr->fallback_percaid.filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = rdr->fallback_percaid.filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} + +#ifdef CS_CACHEEX_AIO +uint8_t chk_lg_only(ECM_REQUEST *er, FTAB *lg_only_ftab) +{ + int32_t i, k; + + if(!lg_only_ftab->nfilts) + return 0; + + for(i = 0; i < lg_only_ftab->nfilts; i++) + { + uint16_t tcaid = lg_only_ftab->filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = lg_only_ftab->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = lg_only_ftab->filts[i].prids[k]; + if(prid == NO_PROVID_VALUE || prid == er->prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} + +uint8_t chk_lg_only_cp(uint16_t caid, uint32_t prid, FTAB *lg_only_ftab) +{ + int32_t i, k; + + if(!lg_only_ftab->nfilts) + return 0; + + for(i = 0; i < lg_only_ftab->nfilts; i++) + { + uint16_t tcaid = lg_only_ftab->filts[i].caid; + if(tcaid && (tcaid == caid || (tcaid < 0x0100 && (caid >> 8) == tcaid))) // caid match + { + int32_t nprids = lg_only_ftab->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t fprid = lg_only_ftab->filts[i].prids[k]; + if(fprid == NO_PROVID_VALUE || fprid == prid) // Provider matches + { + return 1; + } + } + } + } + + return 0; +} +#endif + +uint8_t chk_has_fixed_fallback(ECM_REQUEST *er) +{ + struct s_ecm_answer *ea; + struct s_reader *rdr; + int32_t n_falb = 0; + for(ea = er->matching_rdr; ea; ea = ea->next) + { + rdr = ea->reader; + if(chk_is_fixed_fallback(rdr, er)) + { n_falb++; } + } + return n_falb; +} + +uint8_t chk_if_ignore_checksum(ECM_REQUEST *er, FTAB *disablecrc_only_for) +{ + if(!disablecrc_only_for->nfilts) { return 0; } + + int32_t i, k; + for(i = 0; i < disablecrc_only_for->nfilts; i++) + { + uint16_t tcaid = disablecrc_only_for->filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = disablecrc_only_for->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid =disablecrc_only_for->filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { return 1; } + } + } + } + + return 0; +} + +int32_t matching_reader(ECM_REQUEST *er, struct s_reader *rdr) +{ + // simple checks first: + if(!er || !rdr) + { return (0); } + + // reader active? + struct s_client *cl = rdr->client; + if(!cl || !rdr->enable) + { return (0); } + + // if physical reader a card needs to be inserted + if(!is_network_reader(rdr) && rdr->card_status != CARD_INSERTED) + { return (0); } + + // Checking connected & group valid: + struct s_client *cur_cl = er->client; //cur_client(); + +#ifdef CS_CACHEEX + // Cacheex=3 defines a Cacheex-only reader. never match them. + if(rdr->cacheex.mode == 3) + { return (0); } + if(rdr->cacheex.mode == 2 && !rdr->cacheex.allow_request) + { return (0); } +#endif + + if(!(rdr->grp & cur_cl->grp)) + { return (0); } + + // Checking caids: + if((!er->ocaid || !chk_ctab(er->ocaid, &rdr->ctab)) && !chk_ctab(er->caid, &rdr->ctab)) + { + cs_log_dbg(D_TRACE, "caid %04X not found in caidlist reader %s", er->caid, rdr->label); + return 0; + } + + if( !is_network_reader(rdr) && (rdr->caid >> 8 != ((er->caid >> 8) & 0xFF)) && (rdr->caid >> 8 != ((er->ocaid >> 8) & 0xFF)) ) + { + if (!rdr->csystem) + { return 0; } + + int i, caid_found = 0; + for(i = 0; rdr->csystem->caids[i]; i++) + { + uint16_t cs_caid = rdr->csystem->caids[i]; + if(!cs_caid) + { continue; } + + if(cs_caid == er->caid || cs_caid == er->ocaid) + { + caid_found = 1; + break; + } + } + + if(!caid_found) + { return 0; } + } + + // Supports long ecms? + if(er->ecmlen > 255 && is_network_reader(rdr) && !rdr->ph.large_ecm_support) + { + cs_log_dbg(D_TRACE, "no large ecm support (l=%d) for reader %s", er->ecmlen, rdr->label); + return 0; + } + + // Checking services: + if(!chk_srvid(rdr->client, er)) + { + cs_log_dbg(D_TRACE, "service %04X not matching reader %s", er->srvid, rdr->label); + return (0); + } + + // Checking ident: + if(!chk_rfilter(er, rdr)) + { + cs_log_dbg(D_TRACE, "r-filter reader %s", rdr->label); + return (0); + } + + // Check ECM nanos: + if(!chk_class(er, &rdr->cltab, "reader", rdr->label)) + { + cs_log_dbg(D_TRACE, "class filter reader %s", rdr->label); + return (0); + } + + // CDS NL: check for right seca type + if(!is_network_reader(rdr) && er->caid == 0x100 && er->prid == 0x00006a + && !(er->ecm[8] == 0x00 && er->ecm[9] == 0x00)) // no empty ecm + { + if(er->ecm[8] == 0x00 && rdr->secatype == 2) + { + cs_log_dbg(D_TRACE, "Error: this is a nagra/mediaguard3 ECM and readertype is seca2!"); + return 0; // we dont send a nagra/mediaguard3 ecm to a seca2 reader! + } + + if((er->ecm[8] == 0x10) && (er->ecm[9] == 0x01) && rdr->secatype == 3) + { + cs_log_dbg(D_TRACE, "Error: this is a seca2 ECM and readertype is nagra/mediaguard3!"); + return 0; // we dont send a seca2 ecm to a nagra/mediaguard3 reader! + } + } + + // CDS NL: check for right seca type by ECMPID + if(!is_network_reader(rdr) && er->caid == 0x100 && er->prid == 0x00006a) + { + if(rdr->secatype == 2 && er->pid >> 8 == 7) + { + cs_log_dbg(D_TRACE, "Error: this is a nagra/mediaguard3 ECM and readertype is seca2!"); + return 0; // we dont send a nagra/mediaguard3 ecm to a seca2 reader! + } + + if(rdr->secatype == 3 && er->pid >> 8 == 6) + { + cs_log_dbg(D_TRACE, "Error: this is a seca2 ECM and readertype is nagra/mediaguard3!"); + return 0; // we dont send a seca2 ecm to a nagra/mediaguard3 reader! + } + } + + // Checking chid: + if(!chk_chid(er, &rdr->fchid, "reader", rdr->label)) + { + cs_log_dbg(D_TRACE, "chid filter reader %s", rdr->label); + return (0); + } + + // Schlocke reader-defined function, reader-self-check + if(rdr->ph.c_available && !rdr->ph.c_available(rdr, AVAIL_CHECK_CONNECTED, er)) + { + cs_log_dbg(D_TRACE, "reader unavailable %s", rdr->label); + return 0; + } + + // Checking entitlements: + if(ll_count(rdr->ll_entitlements) > 0) + { + LL_ITER itr = ll_iter_create(rdr->ll_entitlements); + S_ENTITLEMENT *item; + int8_t found = 0; + + while((item = ll_iter_next(&itr))) + { + if(item->caid != er->caid) continue; // skip wrong caid! + if(item->type == 7) continue; // skip seca-admin type (provid 000000) since its not used for decoding! + if(er->prid && item->provid && er->prid != item->provid) continue; // skip non matching provid! + if(!er->prid && caid_is_seca(er->caid)) continue; // dont accept requests without provid for seca cas. + if(!er->prid && caid_is_viaccess(er->caid)) continue; // dont accept requests without provid for viaccess cas + if(!er->prid && caid_is_cryptoworks(er->caid)) continue; // dont accept requests without provid for cryptoworks cas + found =1; + break; + } + + if(!found && er->ecm[0]) // ecmrequest can get corrected provid parsed from payload in ecm + { + cs_log_dbg(D_TRACE, "entitlements check failed on reader %s", rdr->label); + return 0; + } + } + + // Checking ecmlength: + if(rdr->ecm_whitelist.ewnum && er->ecmlen) + { + int32_t i; + int8_t ok = 0, foundident = 0; + + for (i = 0; i < rdr->ecm_whitelist.ewnum; i++) + { + ECM_WHITELIST_DATA *d = &rdr->ecm_whitelist.ewdata[i]; + if ((d->caid == 0 || d->caid == er->caid) && (d->ident == 0 || d->ident == er->prid)) + { + foundident = 1; + if (d->len == er->ecmlen) + { + ok = 1; + break; + } + } + } + + if(foundident == 1 && ok == 0) + { + cs_log_dbg(D_TRACE, "ECM is not in ecmwhitelist of reader %s.", rdr->label); + rdr->ecmsfilteredlen += 1; + rdr->webif_ecmsfilteredlen += 1; + return (0); + } + } + + // ECM Header Check + if(rdr->ecm_hdr_whitelist.ehdata && er->ecmlen) + { + int8_t byteok = 0; + int8_t entryok = 0; + int8_t foundcaid = 0; + int8_t foundprovid = 0; + int16_t len = 0; + int32_t i = 0; + int8_t skip = 0; + int32_t r; + + for(r = 0; r < rdr->ecm_hdr_whitelist.ehnum; r++) + { + ECM_HDR_WHITELIST_DATA *tmp = &rdr->ecm_hdr_whitelist.ehdata[r]; + skip = 0; + byteok = 0; + entryok = 0; + + if(tmp->caid == 0 || tmp->caid == er->caid) + { + foundcaid = 1; //-> caid was in list + //rdr_log_dbg(rdr, D_READER, "Headerwhitelist: found matching CAID: %04X in list", tmp->caid); + + if(tmp->provid == 0 || tmp->provid == er->prid) + { + foundprovid = 1; //-> provid was in list + //rdr_log_dbg(rdr, D_READER, "Headerwhitelist: found matching Provid: %06X in list", tmp->provid); + + len = tmp->len; + for(i = 0; i < len / 2; i++) + { + if(tmp->header[i] == er->ecm[i]) + { + byteok = 1; + //rdr_log_dbg(rdr, D_READER, "ECM Byte: %i of ECMHeaderwhitelist is correct. (%02X = %02X Headerlen: %i)", i, er->ecm[i], tmp->header[i], len/2); + } + else + { + byteok = 0; + //rdr_log_dbg(rdr, D_READER, "ECM Byte: %i of ECMHeaderwhitelist is not valid. (%02X != %02X Headerlen: %i)", i, er->ecm[i], tmp->header[i], len/2); + entryok = 0; + break; + } + + if(i == len / 2 - 1 && byteok == 1) + { + entryok = 1; + } + } + } + else + { + //rdr_log_dbg(rdr, D_READER, "ECMHeaderwhitelist: Provid: %06X not found in List-Entry -> skipping check", er->prid); + skip = 1; + continue; + } + } + else + { + //rdr_log_dbg(rdr, D_READER, "ECMHeaderwhitelist: CAID: %04X not found in List-Entry -> skipping check", er->caid); + skip = 1; + continue; + } + + if(entryok == 1) + { + break; + } + } + + if(foundcaid == 1 && foundprovid == 1 && byteok == 1 && entryok == 1) + { + //cs_log("ECM for %04X@%06X:%04X is valid for ECMHeaderwhitelist of reader %s.", er->caid, er->prid, er->srvid, rdr->label); + } + else + { + if(skip == 0 || (foundcaid == 1 && foundprovid == 1 && entryok == 0 && skip == 1)) + { + cs_log_dump_dbg(D_TRACE, er->ecm, er->ecmlen, "following ECM %04X@%06X:%04X was filtered by ECMHeaderwhitelist of Reader %s from User %s because of not matching Header:", er->caid, er->prid, er->srvid, rdr->label, username(er->client)); + rdr->ecmsfilteredhead += 1; + rdr->webif_ecmsfilteredhead += 1; + return (0); + } + } + } + + // Simple ring connection check: + + // Check ip source+dest: + if(cfg.block_same_ip && IP_EQUAL(cur_cl->ip, rdr->client->ip) && get_module(cur_cl)->listenertype != LIS_DVBAPI && is_network_reader(rdr)) + { + rdr_log_dbg(rdr, D_TRACE, "User (%s) has the same ip (%s) as the reader, blocked because block_same_ip=1!", username(cur_cl), cs_inet_ntoa(rdr->client->ip)); + return 0; + } + + if(cfg.block_same_name && strcmp(username(cur_cl), rdr->label) == 0) + { + rdr_log_dbg(rdr, D_TRACE, "User (%s) has the same name as the reader, blocked because block_same_name=1!", username(cur_cl)); + return 0; + } + + if(!reader_slots_available(rdr, er)&& er->ecmlen > 0) // check free slots, er->ecmlen>0 trick to skip this test for matching readers in dvbapi module + { + return 0; + } + + // All checks done, reader is matching! + return (1); +} + +int32_t chk_caid(uint16_t caid, CAIDTAB *ctab) +{ + int32_t i; + + if (!ctab->ctnum) { return caid; } + + for(i = 0; i < ctab->ctnum; i++) + { + CAIDTAB_DATA *d = &ctab->ctdata[i]; + if((caid & d->mask) == d->caid) { return d->cmap ? d->cmap : caid; } + } + return -1; +} + +int32_t chk_caid_rdr(struct s_reader *rdr, uint16_t caid) +{ + if(is_network_reader(rdr)) + { + return 1; // reader caid is not real caid + } + else if(rdr->caid == caid) + { + return 1; + } + return 0; +} + +int32_t chk_bcaid(ECM_REQUEST *er, CAIDTAB *ctab) +{ + int32_t caid; + caid = chk_caid(er->caid, ctab); + if(caid < 0) { return 0; } + er->caid = caid; + return 1; +} + +/** + * Check for NULL CWs + **/ +int32_t chk_is_null_CW(uint8_t cw[]) +{ + int8_t i; + for(i = 0; i < 16; i++) + { + if(cw[i]) + { return 0; } + } + return 1; +} + +/** + * Check for ecm request that expects half cw format + **/ +int8_t is_halfCW_er(ECM_REQUEST *er) +{ + if(caid_is_videoguard(er->caid) && (er->caid != 0x09C7 && er->caid != 0x09EF)) + { return 1; } + return 0; +} + +/** + * Check for wrong half CWs + **/ +int8_t chk_halfCW(ECM_REQUEST *er, uint8_t *cw) +{ + if(is_halfCW_er(er) && cw) + { + uint8_t cw15 = cw[15]; + if(get_odd_even(er) == 0x80 && cw[15] == 0xF0) { cw[15] = 0; } + + int8_t part1 = checkCWpart(cw, 0); + int8_t part2 = checkCWpart(cw, 1); + + // check for correct half cw format + if(part1 && part2){ cw[15] = cw15; return 0; } + + // check for correct cw position + if((get_odd_even(er) == 0x80 && part1 && !part2) // xxxxxxxx00000000 + ||(get_odd_even(er) == 0x81 && !part1 && part2)) // 00000000xxxxxxxx + { + return 1; + } + + cw[15] = cw15; + return 0; // not correct swapped cw + } + else + { + return 1; + } +} + +/** + * Check for NULL nodeid + **/ +int32_t chk_is_null_nodeid(uint8_t node_id[]) +{ + int8_t i; + for(i = 0; i < 8; i++) + { + if(node_id[i]) { return 0; } + } + return 1; +} + +// check if client structure is accessible +bool check_client(struct s_client *cl) +{ + if(cl && !cl->kill) { return true; } + return false; +} + +uint16_t caidvaluetab_get_value(CAIDVALUETAB *cv, uint16_t caid, uint16_t default_value) +{ + int32_t i; + for(i = 0; i < cv->cvnum; i++) + { + CAIDVALUETAB_DATA *cvdata = &cv->cvdata[i]; + if(cvdata->caid == caid || cvdata->caid == caid >> 8) { return cvdata->value; } + } + return default_value; +} + +int32_t chk_is_fakecw(uint8_t *cw) +{ + uint32_t i, is_fakecw = 0; + uint32_t idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF); + + cs_readlock(__func__, &config_lock); + + for(i = 0; i < cfg.fakecws[idx].count; i++) + { + if(memcmp(cw, cfg.fakecws[idx].data[i].cw, 16) == 0) + { + is_fakecw = 1; + break; + } + } + cs_readunlock(__func__, &config_lock); + + return is_fakecw; +} + +#ifdef CS_CACHEEX_AIO +bool chk_nopushafter(uint16_t caid, CAIDVALUETAB *cv, int32_t ecm_time) +{ + uint16_t npa_time = caidvaluetab_get_value(cv, caid, 0); + if(npa_time && (ecm_time > npa_time)) + { + cs_log_dbg(D_CACHEEX, "REJECTED push: nopushafter %u < ecm_time %i", npa_time, ecm_time); + return 0; + } + else + return 1; +} +#endif diff --git a/oscam-chk.h b/oscam-chk.h old mode 100755 new mode 100644 diff --git a/oscam-client.c b/oscam-client.c old mode 100755 new mode 100644 diff --git a/oscam-client.h b/oscam-client.h old mode 100755 new mode 100644 diff --git a/oscam-conf-chk.c b/oscam-conf-chk.c old mode 100755 new mode 100644 index 16636b5..39d86f0 --- a/oscam-conf-chk.c +++ b/oscam-conf-chk.c @@ -622,47 +622,3 @@ void clear_cacheextab(CECSPVALUETAB *ctab) ctab->cevnum = 0; NULLFREE(ctab->cevdata); } - -void cwvote_caidtab_clear(CW_VOTE_CAID_TAB *cwvote_caid_table) -{ - if (cwvote_caid_table->cvcdata) - { - NULLFREE(cwvote_caid_table->cvcdata); - } - cwvote_caid_table->cvcnum = 0; -} - -void chk_cwvote_caidtab(char *value, CW_VOTE_CAID_TAB *cwvote_caid_table) -{ - char *ptr1, *saveptr1 = NULL; - int32_t i; - uint16_t caid; - - cwvote_caidtab_clear(cwvote_caid_table); - - for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); ptr1; ptr1 = strtok_r(NULL, ",", &saveptr1)) - { - if (i >= CS_MAXPROV) - { - cs_log("WARNING: Too many CW Vote CAIDs defined, max %d allowed!", CS_MAXPROV); - break; - } - errno = 0; // Reset errno before calling strtoul - caid = (uint16_t)strtoul(ptr1, NULL, 16); - if (errno != 0) { - cs_log("WARNING: Invalid CAID format '%s' in cwvote_caids, ignoring.", ptr1); - continue; - } - if (caid > 0) - { - if (!cs_realloc(&cwvote_caid_table->cvcdata, sizeof(CW_VOTE_CAID_DATA) * (i + 1))) - { - cwvote_caid_table->cvcnum = 0; - return; - } - cwvote_caid_table->cvcdata[i].caid = caid; - i++; - } - } - cwvote_caid_table->cvcnum = i; -} diff --git a/oscam-conf-chk.h b/oscam-conf-chk.h old mode 100755 new mode 100644 index c1b34e5..6f1b4ae --- a/oscam-conf-chk.h +++ b/oscam-conf-chk.h @@ -15,11 +15,9 @@ void chk_port_tab(char *portasc, PTAB *ptab); void chk_port_camd35_tab(char *portasc, PTAB *ptab); void chk_ecm_whitelist(char *value, ECM_WHITELIST *ecm_whitelist); void chk_ecm_hdr_whitelist(char *value, ECM_HDR_WHITELIST *ecm_hdr_whitelist); -void chk_cwvote_caidtab(char *value, CW_VOTE_CAID_TAB *cwvote_caid_table); -void clear_sip(struct s_ip **base); +void clear_sip(struct s_ip **sip); void clear_ptab(struct s_ptab *ptab); void clear_cacheextab(CECSPVALUETAB *ctab); -void cwvote_caidtab_clear(CW_VOTE_CAID_TAB *cwvote_caid_table); #endif diff --git a/oscam-conf-mk.c b/oscam-conf-mk.c old mode 100755 new mode 100644 index 639301d..79cfbe9 --- a/oscam-conf-mk.c +++ b/oscam-conf-mk.c @@ -1109,26 +1109,6 @@ char *mk_t_allowedtimeframe(struct s_auth *account) * mk_t-functions give back a constant empty string when allocation fails or when the result is an empty string. * This function thus checks the stringlength and only frees if necessary. */ -char *mk_t_cwvote_caidtab(CW_VOTE_CAID_TAB *cwvote_caid_table) -{ - if (!cwvote_caid_table || !cwvote_caid_table->cvcnum) { return ""; } - - // Max entry length is cs_strlen("0000,") == 5 - int32_t i, maxlen = 6 * cwvote_caid_table->cvcnum, pos = 0; - char *ret; - if (!cs_malloc(&ret, maxlen)) - { return ""; } - - const char *comma = ""; - - for (i = 0; i < cwvote_caid_table->cvcnum; i++) - { - pos += snprintf(ret + pos, maxlen - pos, "%s%04X", comma, cwvote_caid_table->cvcdata[i].caid); - comma = ","; - } - return ret; -} - void free_mk_t(char *value) { if(cs_strlen(value) > 0) { NULLFREE(value); } diff --git a/oscam-conf-mk.h b/oscam-conf-mk.h old mode 100755 new mode 100644 index 925c49c..41f7442 --- a/oscam-conf-mk.h +++ b/oscam-conf-mk.h @@ -30,7 +30,6 @@ extern char *mk_t_cltab(CLASSTAB *clstab); extern char *mk_t_emmbylen(struct s_reader *rdr); extern char *mk_t_allowedprotocols(struct s_auth *account); extern char *mk_t_allowedtimeframe(struct s_auth *account); -extern char *mk_t_cwvote_caidtab(CW_VOTE_CAID_TAB *cwvote_caid_table); extern void free_mk_t(char *value); #endif diff --git a/oscam-conf.c b/oscam-conf.c old mode 100755 new mode 100644 index 3cf55e3..5453c74 --- a/oscam-conf.c +++ b/oscam-conf.c @@ -70,8 +70,7 @@ int config_list_parse(const struct config_list *clist, const char *token, char * { case OPT_INT8: { - int8_t tmp = (int8_t)strToIntVal(value, c->def.d_int8); - *(int8_t *)var = tmp; + *(int8_t *)var = (int8_t)strToIntVal(value, c->def.d_int8); return 1; } case OPT_UINT8: @@ -92,12 +91,6 @@ int config_list_parse(const struct config_list *clist, const char *token, char * memcpy(var, &tmp, sizeof(uint32_t)); return 1; } - case OPT_FLOAT: - { - float tmp = (float)atof(value); // Użyj atof do konwersji na float - memcpy(var, &tmp, sizeof(float)); - return 1; - } case OPT_STRING: { char **scfg = var; @@ -180,35 +173,24 @@ void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_ { int8_t val = *(int8_t *)var; - // always save pmt_mode, and all cwvote settings, because external tools parse it - if(save_all || val != c->def.d_int8 || !strcmp(c->config_name, "pmt_mode") || - !strcmp(c->config_name, "cwvote_enabled") || !strcmp(c->config_name, "cwvote_log_enabled")) - { - fprintf_conf(f, c->config_name, "%d\n", val); - } + // always save pmt_mode, because external tools parse it + if(save_all || val != c->def.d_int8 || !strcmp(c->config_name, "pmt_mode")) + { fprintf_conf(f, c->config_name, "%d\n", val); } continue; } case OPT_UINT8: { uint8_t val = *(uint8_t *)var; if(save_all || val != c->def.d_uint8) - { - fprintf_conf(f, c->config_name, "%u\n", val); - } + { fprintf_conf(f, c->config_name, "%u\n", val); } continue; } case OPT_INT32: { int32_t val; memcpy(&val, var, sizeof(int32_t)); - // always save all cwvote settings - if(save_all || val != c->def.d_int32 || - !strcmp(c->config_name, "cwvote_timeout") || !strcmp(c->config_name, "cwvote_min_votes") || - !strcmp(c->config_name, "cwvote_max_candidates") || !strcmp(c->config_name, "cwvote_compare_len") || - !strcmp(c->config_name, "cwvote_fallback")) - { - fprintf_conf(f, c->config_name, "%d\n", val); - } + if(save_all || val != c->def.d_int32) + { fprintf_conf(f, c->config_name, "%d\n", val); } continue; } case OPT_UINT32: @@ -216,20 +198,7 @@ void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_ uint32_t val; memcpy(&val, var, sizeof(uint32_t)); if(save_all || val != c->def.d_uint32) - { - fprintf_conf(f, c->config_name, "%u\n", val); - } - continue; - } - case OPT_FLOAT: - { - float val; - memcpy(&val, var, sizeof(float)); - // always save cwvote_local_weight - if(save_all || val != c->def.d_float || !strcmp(c->config_name, "cwvote_local_weight")) - { - fprintf_conf(f, c->config_name, "%g\n", val); - } + { fprintf_conf(f, c->config_name, "%u\n", val); } continue; } case OPT_STRING: @@ -270,17 +239,7 @@ void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_ } case OPT_FUNC: { - // always save cwvote_caids - if(save_all || !strcmp(c->config_name, "cwvote_caids")) - { - c->ops.process_fn((const char *)c->config_name, NULL, var, f); - } - else - { - // Call the function only if it's not a cwvote_caids and not save_all - // or if it's a cwvote_caids but save_all is false (handled above) - c->ops.process_fn((const char *)c->config_name, NULL, var, f); - } + c->ops.process_fn((const char *)c->config_name, NULL, var, f); continue; } case OPT_FUNC_EXTRA: @@ -351,11 +310,6 @@ void config_list_set_defaults(const struct config_list *clist, void *config_data memcpy(var, &c->def.d_uint32, sizeof(uint32_t)); break; } - case OPT_FLOAT: - { - memcpy(var, &c->def.d_float, sizeof(float)); - break; - } case OPT_STRING: { char **scfg = var; @@ -566,7 +520,7 @@ FILE *create_config_file(const char *conf_filename) bool flush_config_file(FILE *f, const char *conf_filename) { - char dst_file[220], tmp_file[220], bak_file[220]; // Zmniejszono rozmiar bufora, aby uniknąć potencjalnych problemów z przepełnieniem + char dst_file[256], tmp_file[256], bak_file[256]; get_config_filename(dst_file, sizeof(dst_file), conf_filename); memcpy(tmp_file, dst_file, sizeof(tmp_file)); memcpy(bak_file, dst_file, sizeof(bak_file)); @@ -576,6 +530,5 @@ bool flush_config_file(FILE *f, const char *conf_filename) { fclose(f); } - int32_t result = safe_overwrite_with_bak(dst_file, tmp_file, bak_file, cfg.http_overwrite_bak_file); - return result; + return safe_overwrite_with_bak(dst_file, tmp_file, bak_file, cfg.http_overwrite_bak_file); } diff --git a/oscam-conf.h b/oscam-conf.h old mode 100755 new mode 100644 index 780d0c5..e5b894f --- a/oscam-conf.h +++ b/oscam-conf.h @@ -10,7 +10,6 @@ enum opt_types OPT_UINT8, OPT_INT32, OPT_UINT32, - OPT_FLOAT, OPT_STRING, OPT_SSTRING, OPT_HEX_ARRAY, @@ -32,7 +31,6 @@ struct config_list uint8_t d_uint8; int32_t d_int32; uint32_t d_uint32; - float d_float; char *d_char; long d_extra; uint32_t array_size; @@ -53,10 +51,7 @@ struct config_list .opt_type = OPT_INT8, \ .config_name = __name, \ .var_offset = __var_ofs, \ - .str_size = 0, \ - .def.d_int8 = __default, \ - .ops.process_fn = NULL, \ - .free_value = NULL \ + .def.d_int8 = __default \ } #define DEF_OPT_UINT8(__name, __var_ofs, __default) \ @@ -64,10 +59,7 @@ struct config_list .opt_type = OPT_UINT8, \ .config_name = __name, \ .var_offset = __var_ofs, \ - .str_size = 0, \ - .def.d_uint8 = __default, \ - .ops.process_fn = NULL, \ - .free_value = NULL \ + .def.d_uint8 = __default \ } #define DEF_OPT_INT32(__name, __var_ofs, __default) \ @@ -75,10 +67,7 @@ struct config_list .opt_type = OPT_INT32, \ .config_name = __name, \ .var_offset = __var_ofs, \ - .str_size = 0, \ - .def.d_int32 = __default, \ - .ops.process_fn = NULL, \ - .free_value = NULL \ + .def.d_int32 = __default \ } #define DEF_OPT_UINT32(__name, __var_ofs, __default) \ @@ -86,21 +75,7 @@ struct config_list .opt_type = OPT_UINT32, \ .config_name = __name, \ .var_offset = __var_ofs, \ - .str_size = 0, \ - .def.d_uint32 = __default, \ - .ops.process_fn = NULL, \ - .free_value = NULL \ - } - -#define DEF_OPT_FLOAT(__name, __var_ofs, __default) \ - { \ - .opt_type = OPT_FLOAT, \ - .config_name = __name, \ - .var_offset = __var_ofs, \ - .str_size = 0, \ - .def.d_float = __default, \ - .ops.process_fn = NULL, \ - .free_value = NULL \ + .def.d_uint32 = __default \ } #define DEF_OPT_STR(__name, __var_ofs, __default) \ @@ -108,10 +83,7 @@ struct config_list .opt_type = OPT_STRING, \ .config_name = __name, \ .var_offset = __var_ofs, \ - .str_size = 0, \ - .def.d_char = __default, \ - .ops.process_fn = NULL, \ - .free_value = NULL \ + .def.d_char = __default \ } #define DEF_OPT_SSTR(__name, __var_ofs, __default, __str_size) \ @@ -120,9 +92,7 @@ struct config_list .config_name = __name, \ .var_offset = __var_ofs, \ .str_size = __str_size, \ - .def.d_char = __default, \ - .ops.process_fn = NULL, \ - .free_value = NULL \ + .def.d_char = __default \ } #define DEF_OPT_HEX(__name, __var_ofs, __array_size) \ @@ -130,10 +100,7 @@ struct config_list .opt_type = OPT_HEX_ARRAY, \ .config_name = __name, \ .var_offset = __var_ofs, \ - .str_size = 0, \ - .def.array_size = __array_size, \ - .ops.process_fn = NULL, \ - .free_value = NULL \ + .def.array_size = __array_size \ } #define DEF_OPT_FUNC(__name, __var_ofs, __process_fn, ...) \ @@ -141,8 +108,6 @@ struct config_list .opt_type = OPT_FUNC, \ .config_name = __name, \ .var_offset = __var_ofs, \ - .str_size = 0, \ - .def.d_int32 = 0, /* Inicjalizacja pola unii, aby uniknąć niezainicjalizowanych wartości */ \ .ops.process_fn = __process_fn, \ ##__VA_ARGS__ \ } @@ -152,43 +117,26 @@ struct config_list .opt_type = OPT_FUNC_EXTRA, \ .config_name = __name, \ .var_offset = __var_ofs, \ - .str_size = 0, \ - .def.d_extra = __extra, \ .ops.process_fn_extra = __process_fn_extra, \ + .def.d_extra = __extra, \ ##__VA_ARGS__ \ } #define DEF_OPT_SAVE_FUNC(__fn) \ { \ .opt_type = OPT_SAVE_FUNC, \ - .config_name = NULL, \ - .var_offset = 0, \ - .str_size = 0, \ - .def.d_int32 = 0, \ - .ops.should_save_fn = __fn, \ - .free_value = NULL \ + .ops.should_save_fn = __fn \ } #define DEF_OPT_FIXUP_FUNC(__fn) \ { \ .opt_type = OPT_FIXUP_FUNC, \ - .config_name = NULL, \ - .var_offset = 0, \ - .str_size = 0, \ - .def.d_int32 = 0, \ - .ops.fixup_fn = __fn, \ - .free_value = NULL \ + .ops.fixup_fn = __fn \ } #define DEF_LAST_OPT \ { \ - .opt_type = OPT_UNKNOWN, \ - .config_name = NULL, \ - .var_offset = 0, \ - .str_size = 0, \ - .def.d_int32 = 0, \ - .ops.process_fn = NULL, \ - .free_value = NULL \ + .opt_type = OPT_UNKNOWN \ } struct config_sections diff --git a/oscam-config-account.c b/oscam-config-account.c old mode 100755 new mode 100644 index 3340fd3..bfdf1c5 --- a/oscam-config-account.c +++ b/oscam-config-account.c @@ -478,10 +478,6 @@ static const struct config_list account_opts[] = 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_maxhop_percaid" , OFS(cacheex.maxhop_percaid), caidvaluetab_fn), -#ifdef CS_CACHEEX_AIO - DEF_OPT_FUNC("cacheex_maxhop_lg_percaid" , OFS(cacheex.maxhop_lg_percaid),caidvaluetab_fn), #endif DEF_OPT_FUNC("cacheex_ecm_filter" , OFS(cacheex.filter_caidtab), cacheex_hitvaluetab_fn), DEF_OPT_UINT8("cacheex_drop_csp" , OFS(cacheex.drop_csp), 0), diff --git a/oscam-config-global.c b/oscam-config-global.c old mode 100755 new mode 100644 index 73b3d14..01054bb --- a/oscam-config-global.c +++ b/oscam-config-global.c @@ -199,40 +199,6 @@ void caidvaluetab_fn(const char *token, char *value, void *setting, FILE *f) } } -#ifdef __CYGWIN__ -#include -#else -#include // for setpriority -#endif - -void cwvote_caidtab_fn(const char *token, char *value, void *setting, FILE *f) -{ - CW_VOTE_CAID_TAB *cwvote_caid_table = setting; - if(value) - { - if(cs_strlen(value)) { - chk_cwvote_caidtab(value, cwvote_caid_table); - } else { - cwvote_caidtab_clear(cwvote_caid_table); - } - return; - } - if(cwvote_caid_table->cvcnum || cfg.http_full_cfg) - { - value = mk_t_cwvote_caidtab(cwvote_caid_table); - fprintf_conf(f, token, "%s\n", value); - free_mk_t(value); - } -} - -static void cwvote_local_weight_fixups_fn(void *UNUSED(var)) -{ - if(cfg.cwvote_local_weight < 0.0f || cfg.cwvote_local_weight > 10.0f) { - cfg.cwvote_local_weight = 1.0f; - cs_log("CW Vote: local_weight out of range (0.0-10.0), using default value 1.0"); - } -} - #ifdef CS_CACHEEX void cacheex_valuetab_fn(const char *token, char *value, void *setting, FILE *f) { @@ -298,6 +264,12 @@ void cacheex_hitvaluetab_fn(const char *token, char *value, void *setting, FILE } #endif +#ifdef __CYGWIN__ +#include +#else +#include // for setpriority +#endif + void global_fixups_fn(void *UNUSED(var)) { if(!cfg.usrfile) { cfg.disableuserfile = 1; } @@ -345,7 +317,6 @@ void global_fixups_fn(void *UNUSED(var)) static const struct config_list global_opts[] = { DEF_OPT_FIXUP_FUNC(global_fixups_fn), - DEF_OPT_FIXUP_FUNC(cwvote_local_weight_fixups_fn), #ifdef LEDSUPPORT DEF_OPT_INT8("enableled" , OFS(enableled) , 0), #endif @@ -427,15 +398,6 @@ static const struct config_list global_opts[] = DEF_OPT_INT8("double_check" , OFS(double_check) , 0), DEF_OPT_INT8("disablecrccws" , OFS(disablecrccws) , 0), DEF_OPT_FUNC("disablecrccws_only_for" , OFS(disablecrccws_only_for) , chk_ftab_fn), - DEF_OPT_INT8("cwvote_enabled" , OFS(cwvote_enabled) , 0), - DEF_OPT_INT8("cwvote_log_enabled" , OFS(cwvote_log_enabled) , 0), - DEF_OPT_INT32("cwvote_timeout" , OFS(cwvote_timeout) , 0), - DEF_OPT_INT32("cwvote_min_votes" , OFS(cwvote_min_votes) , 2), - DEF_OPT_FLOAT("cwvote_local_weight" , OFS(cwvote_local_weight) , 2.0f), - DEF_OPT_INT32("cwvote_max_candidates" , OFS(cwvote_max_candidates) , 8), - DEF_OPT_INT32("cwvote_compare_len" , OFS(cwvote_compare_len) , 8), - DEF_OPT_INT32("cwvote_fallback" , OFS(cwvote_fallback) , 0), // 0 = czekaj, 1 = weź najlepszy, 2 = weź pierwszy - DEF_OPT_FUNC("cwvote_caids" , OFS(cwvote_caids) , cwvote_caidtab_fn), #ifdef CS_CACHEEX_AIO DEF_OPT_INT8("cacheex_srcname_webif" , OFS(cacheex_srcname_webif) , 0), #endif @@ -987,10 +949,6 @@ static const struct config_list streamrelay_opts[] = DEF_OPT_STR("stream_source_auth_password" , OFS(stream_source_auth_password), NULL), DEF_OPT_UINT32("stream_relay_buffer_time" , OFS(stream_relay_buffer_time), 0), DEF_OPT_UINT8("stream_relay_reconnect_count" , OFS(stream_relay_reconnect_count), 0), -#ifdef WITH_EMU - DEF_OPT_INT8("stream_emm_enabled" , OFS(emu_stream_emm_enabled), 0), - DEF_OPT_UINT32("stream_ecm_delay" , OFS(emu_stream_ecm_delay), 600), -#endif DEF_OPT_INT8("stream_display_client" , OFS(stream_display_client), 0), DEF_OPT_INT8("stream_reuse_client" , OFS(stream_reuse_client), 0), #ifdef WEBIF @@ -1441,7 +1399,6 @@ void config_free(void) caidvaluetab_clear(&cfg.ftimeouttab); ftab_clear(&cfg.double_check_caid); ftab_clear(&cfg.disablecrccws_only_for); - cwvote_caidtab_clear(&cfg.cwvote_caids); #ifdef WITH_LB caidvaluetab_clear(&cfg.lb_retrylimittab); caidvaluetab_clear(&cfg.lb_nbest_readers_tab); @@ -1503,7 +1460,6 @@ int32_t init_config(void) #ifdef HAVE_DVBAPI cfg.dvbapi_enabled = 1; #endif - cs_log("DEBUG: init_config - No oscam.conf found, using default settings."); return 0; } @@ -1573,73 +1529,3 @@ int32_t write_config(void) config_sections_save(oscam_conf, f, &cfg); return flush_config_file(f, cs_conf); } - -int32_t reload_global_config(void) -{ - FILE *fp = open_config_file(cs_conf); - if(!fp) - { - cs_log("WARNING: Cannot open oscam.conf for reloading global settings."); - return 1; - } - - char *token; - if(!cs_malloc(&token, MAXLINESIZE)) - { - fclose(fp); - return 1; - } - - const struct config_sections *global_section = config_find_section(oscam_conf, "global"); - if(!global_section || !config_section_is_active(global_section)) - { - cs_log("ERROR: Global section not found or not active during reload."); - NULLFREE(token); - fclose(fp); - return 1; - } - - int in_global_section = 0; - while(fgets(token, MAXLINESIZE, fp)) - { - int len = cs_strlen(trim(token)); - if(len < 3) { continue; } - if(token[0] == '#') { continue; } - - if(token[0] == '[' && token[len - 1] == ']') - { - token[len - 1] = '\0'; - if(streq(token + 1, "global")) - { - in_global_section = 1; - } - else - { - in_global_section = 0; - } - continue; - } - - if(in_global_section) - { - char *value = strchr(token, '='); - if(!value) { continue; } - *value++ = '\0'; - char *tvalue = trim(value); - char *ttoken = trim(strtolower(token)); - - if(!config_list_parse(global_section->config, ttoken, tvalue, &cfg)) - { - cs_log("WARNING: During global config reload, unknown setting '%s=%s' in section [global].", ttoken, tvalue); - } - } - } - - NULLFREE(token); - fclose(fp); - - // Apply fixups after reloading global settings - config_list_apply_fixups(global_section->config, &cfg); - - return 0; -} diff --git a/oscam-config-global.c.bak b/oscam-config-global.c.bak new file mode 100644 index 0000000..d55517e --- /dev/null +++ b/oscam-config-global.c.bak @@ -0,0 +1,1522 @@ +#define MODULE_LOG_PREFIX "config" + +#include "globals.h" +#include "module-dvbapi.h" +#include "module-gbox.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-net.h" +#include "oscam-string.h" +#ifdef CS_CACHEEX_AIO +#include "module-cacheex.h" +#endif +#define cs_conf "oscam.conf" + +#define DEFAULT_HTTP_PORT 8888 +#define DEFAULT_HTTP_ALLOW "127.0.0.1,192.168.0.0-192.168.255.255,10.0.0.0-10.255.255.255,172.16.0.0-172.31.255.255,::1" + +static void disablelog_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + cs_disable_log(strToIntVal(value, 0)); + return; + } + if(cfg.disablelog || cfg.http_full_cfg) + { fprintf_conf(f, token, "%d\n", cfg.disablelog); } +} + +#if defined(WEBIF) || defined(MODULE_MONITOR) +static void loghistorylines_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + uint32_t newsize = strToUIntVal(value, 256); + if(newsize < 64 && newsize != 0) + { + fprintf(stderr, "WARNING: loghistorylines is too small, adjusted to 64\n"); + newsize = 64; + } + cs_reinit_loghist(newsize); + return; + } + if(cfg.loghistorylines != 256 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%u\n", cfg.loghistorylines); } +} +#endif + +static void serverip_fn(const char *token, char *value, void *setting, FILE *f) +{ + IN_ADDR_T srvip = *(IN_ADDR_T *)setting; + if(value) + { + if(cs_strlen(value) == 0) + { + set_null_ip((IN_ADDR_T *)setting); + } + else + { + cs_inet_addr(value, (IN_ADDR_T *)setting); + } + return; + } + if(IP_ISSET(srvip) || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", cs_inet_ntoa(srvip)); } +} + +void iprange_fn(const char *token, char *value, void *setting, FILE *f) +{ + struct s_ip **ip = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + clear_sip(ip); + } + else + { + chk_iprange(value, ip); + } + return; + } + value = mk_t_iprange(*ip); + if(cs_strlen(value) > 0 || cfg.http_full_cfg) + { fprintf_conf(f, token, "%s\n", value); } + free_mk_t(value); +} + +void iprange_free_fn(void *setting) +{ + clear_sip(setting); +} + +static void logfile_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + char *saveptr1 = NULL; + cfg.logtostdout = 0; + cfg.logtosyslog = 0; + NULLFREE(cfg.logfile); + if(cs_strlen(value) > 0) + { + char *pch; + for(pch = strtok_r(value, ";", &saveptr1); pch != NULL; pch = strtok_r(NULL, ";", &saveptr1)) + { + pch = trim(pch); + if(!strcmp(pch, "stdout")) { cfg.logtostdout = 1; } + else if(!strcmp(pch, "syslog")) { cfg.logtosyslog = 1; } + else + { + NULLFREE(cfg.logfile); + if(!(cfg.logfile = cs_strdup(pch))) + { continue; } + } + } + } + else + { + if(!(cfg.logfile = cs_strdup(CS_LOGFILE))) + { cfg.logtostdout = 1; } + } + return; + } + if(cfg.logfile || cfg.logtostdout == 1 || cfg.logtosyslog == 1 || cfg.http_full_cfg) + { + value = mk_t_logfile(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +void check_caidtab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CAIDTAB *caid_table = setting; + if(value) + { + if(cs_strlen(value)) { + chk_caidtab(value, caid_table); + } else { + caidtab_clear(caid_table); + } + return; + } + if(caid_table->ctnum || cfg.http_full_cfg) + { + value = mk_t_caidtab(caid_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +void chk_ftab_fn(const char *token, char *value, void *setting, FILE *f) +{ + FTAB *ftab = setting; + if(value) + { + if(cs_strlen(value)) + chk_ftab(value, ftab); + else + ftab_clear(ftab); + return; + } + 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); +} + + +void caidvaluetab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CAIDVALUETAB *caid_value_table = setting; + if(value) + { + if (cs_strlen(value)) { + chk_caidvaluetab(value, caid_value_table); + if (streq(token, "lb_retrylimits")) + { + int32_t i; + for (i = 0; i < caid_value_table->cvnum; i++) + { + if (caid_value_table->cvdata[i].value < 50) + caid_value_table->cvdata[i].value = 50; + } + } + } else { + caidvaluetab_clear(caid_value_table); + } + return; + } + if(caid_value_table->cvnum || cfg.http_full_cfg) + { + value = mk_t_caidvaluetab(caid_value_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +#ifdef CS_CACHEEX +void cacheex_valuetab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CECSPVALUETAB *cacheex_value_table = setting; + if(value) + { + if(cs_strlen(value) == 0) + { clear_cacheextab(cacheex_value_table); } + else + { chk_cacheex_valuetab(value, cacheex_value_table); } + return; + } + if(cacheex_value_table->cevnum || cfg.http_full_cfg) + { + value = mk_t_cacheex_valuetab(cacheex_value_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +void cacheex_cwcheck_tab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CWCHECKTAB *cacheex_value_table = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + cacheex_value_table->cwchecknum = 0; + NULLFREE(cacheex_value_table->cwcheckdata); + } + else + { + chk_cacheex_cwcheck_valuetab(value, cacheex_value_table); + } + return; + } + + if(cacheex_value_table->cwchecknum || cfg.http_full_cfg) + { + value = mk_t_cacheex_cwcheck_valuetab(cacheex_value_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +void cacheex_hitvaluetab_fn(const char *token, char *value, void *setting, FILE *f) +{ + CECSPVALUETAB *cacheex_value_table = setting; + if(value) + { + if(cs_strlen(value) == 0) + { clear_cacheextab(cacheex_value_table); } + else + { chk_cacheex_hitvaluetab(value, cacheex_value_table); } + return; + } + if(cacheex_value_table->cevnum || cfg.http_full_cfg) + { + value = mk_t_cacheex_hitvaluetab(cacheex_value_table); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} +#endif + +#ifdef __CYGWIN__ +#include +#else +#include // for setpriority +#endif + +void global_fixups_fn(void *UNUSED(var)) +{ + if(!cfg.usrfile) { cfg.disableuserfile = 1; } + if(!cfg.mailfile) { cfg.disablemail = 1; } + if(cfg.ctimeout < 10) { cfg.ctimeout = cfg.ctimeout * 1000; } // save always in ms + + if(cfg.nice < -20 || cfg.nice > 20) { cfg.nice = 99; } + if(cfg.nice != 99) + { +#ifndef __CYGWIN__ + setpriority(PRIO_PROCESS, 0, cfg.nice); +#else + HANDLE WinId; + uint32_t wprio; + switch((cfg.nice + 20) / 10) + { + case 0: + wprio = REALTIME_PRIORITY_CLASS; + break; + case 1: + wprio = HIGH_PRIORITY_CLASS; + break; + case 2: + wprio = NORMAL_PRIORITY_CLASS; + break; + default: + wprio = IDLE_PRIORITY_CLASS; + break; + } + WinId = GetCurrentProcess(); + SetPriorityClass(WinId, wprio); +#endif + } + if(cfg.netprio <= 0 || cfg.netprio > 20) { cfg.netprio = 0; } + if(cfg.max_log_size != 0 && cfg.max_log_size <= 10) { cfg.max_log_size = 10; } +#ifdef WITH_LB + if(cfg.lb_save > 0 && cfg.lb_save < 100) { cfg.lb_save = 100; } + if(cfg.lb_nbest_readers < 2) { cfg.lb_nbest_readers = DEFAULT_NBEST; } +#endif +} + +#define OFS(X) offsetof(struct s_config, X) +#define SIZEOF(X) sizeof(((struct s_config *)0)->X) + +static const struct config_list global_opts[] = +{ + DEF_OPT_FIXUP_FUNC(global_fixups_fn), +#ifdef LEDSUPPORT + DEF_OPT_INT8("enableled" , OFS(enableled) , 0), +#endif + DEF_OPT_FUNC("disablelog" , OFS(disablelog) , disablelog_fn), +#if defined(WEBIF) || defined(MODULE_MONITOR) + DEF_OPT_FUNC("loghistorylines" , OFS(loghistorylines) , loghistorylines_fn), +#endif + DEF_OPT_FUNC("serverip" , OFS(srvip) , serverip_fn), + DEF_OPT_FUNC("logfile" , OFS(logfile) , logfile_fn), + DEF_OPT_INT32("initial_debuglevel" , OFS(initial_debuglevel) , 0), + DEF_OPT_STR("sysloghost" , OFS(sysloghost) , NULL), + DEF_OPT_INT32("syslogport" , OFS(syslogport) , 514), + DEF_OPT_INT8("logduplicatelines" , OFS(logduplicatelines) , 0), + DEF_OPT_STR("pidfile" , OFS(pidfile) , NULL), + DEF_OPT_INT8("disableuserfile" , OFS(disableuserfile) , 1), + DEF_OPT_INT8("disablemail" , OFS(disablemail) , 1), + DEF_OPT_INT8("usrfileflag" , OFS(usrfileflag) , 0), + DEF_OPT_UINT32("clienttimeout" , OFS(ctimeout) , CS_CLIENT_TIMEOUT), + DEF_OPT_UINT32("fallbacktimeout" , OFS(ftimeout) , CS_CLIENT_TIMEOUT / 2), + DEF_OPT_FUNC("fallbacktimeout_percaid" , OFS(ftimeouttab) , caidvaluetab_fn), + DEF_OPT_UINT32("clientmaxidle" , OFS(cmaxidle) , CS_CLIENT_MAXIDLE), + DEF_OPT_INT32("bindwait" , OFS(bindwait) , CS_BIND_TIMEOUT), + DEF_OPT_UINT32("netprio" , OFS(netprio) , 0), + DEF_OPT_INT32("sleep" , OFS(tosleep) , 0), + DEF_OPT_INT32("unlockparental" , OFS(ulparent) , 0), + DEF_OPT_INT32("nice" , OFS(nice) , 99), + DEF_OPT_INT32("maxlogsize" , OFS(max_log_size) , 10), + DEF_OPT_INT8("waitforcards" , OFS(waitforcards) , 1), + DEF_OPT_INT32("waitforcards_extra_delay" , OFS(waitforcards_extra_delay) , 500), + DEF_OPT_INT8("preferlocalcards" , OFS(preferlocalcards) , 0), + DEF_OPT_INT32("readerrestartseconds" , OFS(reader_restart_seconds) , 5), + DEF_OPT_INT8("dropdups" , OFS(dropdups) , 0), + DEF_OPT_INT8("reload_useraccounts" , OFS(reload_useraccounts) , 0), + DEF_OPT_INT8("reload_readers" , OFS(reload_readers) , 0), + DEF_OPT_INT8("reload_provid" , OFS(reload_provid) , 0), + DEF_OPT_INT8("reload_services_ids" , OFS(reload_services_ids) , 0), + DEF_OPT_INT8("reload_tier_ids" , OFS(reload_tier_ids) , 0), + DEF_OPT_INT8("reload_fakecws" , OFS(reload_fakecws) , 0), + DEF_OPT_INT8("reload_ac_stat" , OFS(reload_ac_stat) , 0), + DEF_OPT_INT8("reload_log" , OFS(reload_log) , 0), + DEF_OPT_INT8("block_same_ip" , OFS(block_same_ip) , 1), + DEF_OPT_INT8("block_same_name" , OFS(block_same_name) , 1), + DEF_OPT_STR("usrfile" , OFS(usrfile) , NULL), + DEF_OPT_STR("mailfile" , OFS(mailfile) , NULL), + DEF_OPT_STR("cwlogdir" , OFS(cwlogdir) , NULL), + DEF_OPT_STR("emmlogdir" , OFS(emmlogdir) , NULL), +#ifdef WITH_LB + DEF_OPT_INT32("lb_mode" , OFS(lb_mode) , DEFAULT_LB_MODE), + DEF_OPT_INT32("lb_save" , OFS(lb_save) , 0), + DEF_OPT_INT32("lb_nbest_readers" , OFS(lb_nbest_readers) , DEFAULT_NBEST), + DEF_OPT_INT32("lb_nfb_readers" , OFS(lb_nfb_readers) , DEFAULT_NFB), + DEF_OPT_INT32("lb_min_ecmcount" , OFS(lb_min_ecmcount) , DEFAULT_MIN_ECM_COUNT), + DEF_OPT_INT32("lb_max_ecmcount" , OFS(lb_max_ecmcount) , DEFAULT_MAX_ECM_COUNT), + DEF_OPT_INT32("lb_reopen_seconds" , OFS(lb_reopen_seconds) , DEFAULT_REOPEN_SECONDS), + DEF_OPT_INT8("lb_reopen_invalid" , OFS(lb_reopen_invalid) , 1), + DEF_OPT_INT8("lb_force_reopen_always" , OFS(lb_force_reopen_always) , 0), + DEF_OPT_INT32("lb_retrylimit" , OFS(lb_retrylimit) , DEFAULT_RETRYLIMIT), + DEF_OPT_INT32("lb_stat_cleanup" , OFS(lb_stat_cleanup) , DEFAULT_LB_STAT_CLEANUP), + DEF_OPT_INT32("lb_max_readers" , OFS(lb_max_readers) , 0), + DEF_OPT_INT32("lb_auto_betatunnel" , OFS(lb_auto_betatunnel) , DEFAULT_LB_AUTO_BETATUNNEL), + DEF_OPT_INT32("lb_auto_betatunnel_mode" , OFS(lb_auto_betatunnel_mode) , DEFAULT_LB_AUTO_BETATUNNEL_MODE), + DEF_OPT_INT32("lb_auto_betatunnel_prefer_beta" , OFS(lb_auto_betatunnel_prefer_beta), DEFAULT_LB_AUTO_BETATUNNEL_PREFER_BETA), + DEF_OPT_STR("lb_savepath" , OFS(lb_savepath) , NULL), + DEF_OPT_FUNC("lb_retrylimits" , OFS(lb_retrylimittab) , caidvaluetab_fn), + DEF_OPT_FUNC("lb_nbest_percaid" , OFS(lb_nbest_readers_tab) , caidvaluetab_fn), + DEF_OPT_FUNC("lb_noproviderforcaid" , OFS(lb_noproviderforcaid) , check_caidtab_fn), + DEF_OPT_INT32("lb_auto_timeout" , OFS(lb_auto_timeout) , DEFAULT_LB_AUTO_TIMEOUT), + DEF_OPT_INT32("lb_auto_timeout_p" , OFS(lb_auto_timeout_p) , DEFAULT_LB_AUTO_TIMEOUT_P), + DEF_OPT_INT32("lb_auto_timeout_t" , OFS(lb_auto_timeout_t) , DEFAULT_LB_AUTO_TIMEOUT_T), +#endif + DEF_OPT_FUNC("double_check_caid" , OFS(double_check_caid) , chk_ftab_fn), + DEF_OPT_STR("ecmfmt" , OFS(ecmfmt) , NULL), + DEF_OPT_INT32("resolvegethostbyname" , OFS(resolve_gethostbyname) , 0), + DEF_OPT_INT32("failbantime" , OFS(failbantime) , 0), + DEF_OPT_INT32("failbancount" , OFS(failbancount) , 0), + DEF_OPT_INT8("suppresscmd08" , OFS(c35_suppresscmd08) , 0), + DEF_OPT_INT8("getblockemmauprovid" , OFS(getblockemmauprovid) , 0), + DEF_OPT_INT8("double_check" , OFS(double_check) , 0), + DEF_OPT_INT8("disablecrccws" , OFS(disablecrccws) , 0), + DEF_OPT_FUNC("disablecrccws_only_for" , OFS(disablecrccws_only_for) , chk_ftab_fn), +#ifdef CS_CACHEEX_AIO + DEF_OPT_INT8("cacheex_srcname_webif" , OFS(cacheex_srcname_webif) , 0), +#endif + DEF_LAST_OPT +}; + +#ifdef CS_ANTICASC +static void anticasc_fixups_fn(void *UNUSED(var)) +{ + if(cfg.ac_users < 0) { cfg.ac_users = 0; } + if(cfg.ac_stime < 0) { cfg.ac_stime = 2; } + if(cfg.ac_samples < 2 || cfg.ac_samples > 10) { cfg.ac_samples = 10; } + if(cfg.ac_penalty < 0 || cfg.ac_penalty > 3) { cfg.ac_penalty = 0; } + if(cfg.ac_fakedelay < 100 || cfg.ac_fakedelay > 3000) { cfg.ac_fakedelay = 1000; } + if(cfg.ac_denysamples < 2 || cfg.ac_denysamples > cfg.ac_samples - 1) { cfg.ac_denysamples = cfg.ac_samples - 1; } + if(cfg.ac_denysamples + 1 > cfg.ac_samples) { cfg.ac_denysamples = cfg.ac_samples - 1; } + if(cfg.acosc_max_ecms_per_minute < 0) { cfg.acosc_max_ecms_per_minute = 0; } + if(cfg.acosc_penalty == 4) + { + cfg.acosc_max_active_sids = 0; // set default + cfg.acosc_zap_limit = 0; // set default + //cfg.acosc_penalty_duration = 0; // set default + + if(cfg.acosc_max_ecms_per_minute != 0) + { + if(cfg.acosc_max_ecms_per_minute < 6) { cfg.acosc_max_ecms_per_minute = 6; } + if(cfg.acosc_max_ecms_per_minute > 20) { cfg.acosc_max_ecms_per_minute = 20; } + cfg.acosc_penalty_duration = (60 / cfg.acosc_max_ecms_per_minute); + } + } + if(cfg.acosc_max_active_sids < 0) { cfg.acosc_max_active_sids = 0; } + if(cfg.acosc_zap_limit < 0) { cfg.acosc_zap_limit = 0; } + if(cfg.acosc_penalty < 0 || cfg.acosc_penalty > 4) { cfg.acosc_penalty = 0; } + if(cfg.acosc_penalty_duration < 0) { cfg.acosc_penalty_duration = 0; } + if(cfg.acosc_delay < 0 || cfg.acosc_delay > 4000) { cfg.acosc_delay = 0; } +} + +static bool anticasc_should_save_fn(void *UNUSED(var)) +{ + return cfg.ac_enabled || cfg.acosc_enabled; +} + +static const struct config_list anticasc_opts[] = +{ + DEF_OPT_SAVE_FUNC(anticasc_should_save_fn), + DEF_OPT_FIXUP_FUNC(anticasc_fixups_fn), + DEF_OPT_INT8("enabled" , OFS(ac_enabled) , 0), + DEF_OPT_INT32("numusers" , OFS(ac_users) , 0), + DEF_OPT_INT32("sampletime" , OFS(ac_stime) , 2), + DEF_OPT_INT32("samples" , OFS(ac_samples) , 10), + DEF_OPT_INT8("penalty" , OFS(ac_penalty) , 0), + DEF_OPT_STR("aclogfile" , OFS(ac_logfile) , NULL), + DEF_OPT_INT32("fakedelay" , OFS(ac_fakedelay) , 3000), + DEF_OPT_INT32("denysamples" , OFS(ac_denysamples) , 8), + DEF_OPT_INT8("acosc_enabled" , OFS(acosc_enabled) , 0 ), + DEF_OPT_INT8("acosc_max_ecms_per_minute" , OFS(acosc_max_ecms_per_minute), 0 ), + DEF_OPT_INT8("acosc_max_active_sids" , OFS(acosc_max_active_sids) , 0 ), + DEF_OPT_INT8("acosc_zap_limit" , OFS(acosc_zap_limit) , 0 ), + DEF_OPT_INT8("acosc_penalty" , OFS(acosc_penalty) , 0 ), + DEF_OPT_INT32("acosc_penalty_duration" , OFS(acosc_penalty_duration), 0 ), + DEF_OPT_INT32("acosc_delay" , OFS(acosc_delay) , 0 ), + DEF_LAST_OPT +}; +#else +static const struct config_list anticasc_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_MONITOR +static bool monitor_should_save_fn(void *UNUSED(var)) +{ + return cfg.mon_port; +} + +static const struct config_list monitor_opts[] = +{ + DEF_OPT_SAVE_FUNC(monitor_should_save_fn), + DEF_OPT_INT32("port" , OFS(mon_port) , 0), + DEF_OPT_FUNC("serverip" , OFS(mon_srvip) , serverip_fn), + DEF_OPT_FUNC("nocrypt" , OFS(mon_allowed) , iprange_fn, .free_value = iprange_free_fn), + DEF_OPT_INT32("aulow" , OFS(aulow) , 30), + DEF_OPT_UINT8("monlevel" , OFS(mon_level) , 2), + DEF_OPT_INT32("hideclient_to" , OFS(hideclient_to), 25), + DEF_LAST_OPT +}; +#else +static const struct config_list monitor_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef WEBIF +static void http_port_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + cfg.http_port = 0; + if(value[0]) + { + if(value[0] == '+') + { + if(config_enabled(WITH_SSL)) + { + cfg.http_use_ssl = 1; + } + else + { + fprintf(stderr, "Warning: OSCam compiled without SSL support.\n"); + } + cfg.http_port = strtoul(value + 1, NULL, 10); + } + else + { + cfg.http_port = strtoul(value, NULL, 10); + } + } + return; + } + fprintf_conf(f, token, "%s%d\n", cfg.http_use_ssl ? "+" : "", cfg.http_port); +} + +static void http_dyndns_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + int i; + if(value) + { + char *ptr, *saveptr1 = NULL; + memset(cfg.http_dyndns, 0, sizeof(cfg.http_dyndns)); + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); (i < MAX_HTTP_DYNDNS) && (ptr); ptr = strtok_r(NULL, ",", &saveptr1), i++) + { + trim(ptr); + cs_strncpy((char *)cfg.http_dyndns[i], ptr, sizeof(cfg.http_dyndns[i])); + } + return; + } + if(cs_strlen((const char *)(cfg.http_dyndns[0])) > 0 || cfg.http_full_cfg) + { + fprintf_conf(f, token, "%s", ""); // it should not have \n at the end + for(i = 0; i < MAX_HTTP_DYNDNS; i++) + { + if(cfg.http_dyndns[i][0]) + { + fprintf(f, "%s%s", i > 0 ? "," : "", cfg.http_dyndns[i]); + } + } + fprintf(f, "\n"); + } +} + +static bool webif_should_save_fn(void *UNUSED(var)) +{ + return cfg.http_port; +} + +static const struct config_list webif_opts[] = +{ + DEF_OPT_SAVE_FUNC(webif_should_save_fn), + DEF_OPT_FUNC("httpport" , OFS(http_port) , http_port_fn), + DEF_OPT_FUNC("serverip" , OFS(http_srvip) , serverip_fn), + DEF_OPT_STR("httpuser" , OFS(http_user) , NULL), + DEF_OPT_STR("httppwd" , OFS(http_pwd) , NULL), + DEF_OPT_STR("httpcss" , OFS(http_css) , NULL), + DEF_OPT_STR("httpjscript" , OFS(http_jscript) , NULL), + DEF_OPT_STR("httpscript" , OFS(http_script) , NULL), + DEF_OPT_STR("httptpl" , OFS(http_tpl) , NULL), + DEF_OPT_STR("httppiconpath" , OFS(http_piconpath) , NULL), + DEF_OPT_STR("httphelplang" , OFS(http_help_lang) , "en"), + DEF_OPT_STR("httplocale" , OFS(http_locale) , NULL), + DEF_OPT_INT8("http_prepend_embedded_css", OFS(http_prepend_embedded_css), 0), + DEF_OPT_INT32("httprefresh" , OFS(http_refresh) , 0), + DEF_OPT_INT32("httppollrefresh" , OFS(poll_refresh) , 60), + DEF_OPT_INT8("httphideidleclients" , OFS(http_hide_idle_clients) , 1), + DEF_OPT_STR("httphidetype" , OFS(http_hide_type) , NULL), + DEF_OPT_INT8("httpshowpicons" , OFS(http_showpicons) , 0), + DEF_OPT_INT8("httppiconsize" , OFS(http_picon_size) , 0), + DEF_OPT_INT8("httpshowmeminfo" , OFS(http_showmeminfo) , 0), + DEF_OPT_INT8("httpshowuserinfo" , OFS(http_showuserinfo) , 0), + DEF_OPT_INT8("httpshowreaderinfo" , OFS(http_showreaderinfo) , 0), + DEF_OPT_INT8("httpshowcacheexinfo" , OFS(http_showcacheexinfo) , 0), + DEF_OPT_INT8("httpshowecminfo" , OFS(http_showecminfo) , 0), + DEF_OPT_INT8("httpshowloadinfo" , OFS(http_showloadinfo) , 0), + DEF_OPT_FUNC("httpallowed" , OFS(http_allowed) , iprange_fn, .free_value = iprange_free_fn), + DEF_OPT_INT8("httpreadonly" , OFS(http_readonly) , 0), + DEF_OPT_INT8("httpsavefullcfg" , OFS(http_full_cfg) , 0), + DEF_OPT_INT8("httpoverwritebakfile" , OFS(http_overwrite_bak_file) , 0), + DEF_OPT_STR("httpcert" , OFS(http_cert) , NULL), + DEF_OPT_INT8("https_force_secure_mode" , OFS(https_force_secure_mode) , 1), + DEF_OPT_INT8("https_auto_create_cert" , OFS(https_auto_create_cert) , 1), + DEF_OPT_FUNC("httpdyndns" , OFS(http_dyndns) , http_dyndns_fn), + DEF_OPT_INT32("aulow" , OFS(aulow) , 30), + DEF_OPT_INT32("hideclient_to" , OFS(hideclient_to) , 25), + DEF_OPT_STR("httposcamlabel" , OFS(http_oscam_label) , "OSCam"), + DEF_OPT_INT32("httpemmuclean" , OFS(http_emmu_clean) , 256), + DEF_OPT_INT32("httpemmsclean" , OFS(http_emms_clean) , -1), + DEF_OPT_INT32("httpemmgclean" , OFS(http_emmg_clean) , -1), +#ifdef WEBIF_LIVELOG + DEF_OPT_INT8("http_status_log" , OFS(http_status_log) , 0), +#else + DEF_OPT_INT8("http_status_log" , OFS(http_status_log) , 1), +#endif +#ifndef WEBIF_JQUERY + DEF_OPT_STR("http_extern_jquery" , OFS(http_extern_jquery) , "//code.jquery.com/jquery-3.7.1.min.js"), +#endif + DEF_LAST_OPT +}; +#else +static const struct config_list webif_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_CAMD33 +static bool camd33_should_save_fn(void *UNUSED(var)) +{ + return cfg.c33_port; +} + +static const struct config_list camd33_opts[] = +{ + DEF_OPT_SAVE_FUNC(camd33_should_save_fn), + DEF_OPT_INT32("port" , OFS(c33_port) , 0), + DEF_OPT_FUNC("serverip", OFS(c33_srvip) , serverip_fn), + DEF_OPT_FUNC("nocrypt" , OFS(c33_plain) , iprange_fn, .free_value = iprange_free_fn), + DEF_OPT_INT32("passive", OFS(c33_passive), 0), + DEF_OPT_HEX("key" , OFS(c33_key) , SIZEOF(c33_key)), + DEF_LAST_OPT +}; +#else +static const struct config_list camd33_opts[] = { DEF_LAST_OPT }; +#endif + + +void cache_fixups_fn(void *UNUSED(var)) +{ + if(cfg.max_cache_time < ((int32_t)(cfg.ctimeout + 500) / 1000 + 3)) { cfg.max_cache_time = ((cfg.ctimeout + 500) / 1000 + 3); } +#ifdef CW_CYCLE_CHECK + if(cfg.maxcyclelist > 4000) { cfg.maxcyclelist = 4000; } + if(cfg.keepcycletime > 240) { cfg.keepcycletime = 240; } + if(cfg.cwcycle_sensitive > 4) { cfg.cwcycle_sensitive = 4; } + if(cfg.cwcycle_sensitive == 1) { cfg.cwcycle_sensitive = 2; } +#endif +#ifdef CS_CACHEEX_AIO + // lgo-ctab -> lgo-ftab port + caidtab2ftab_add(&cfg.cacheex_localgenerated_only_in_caidtab, &cfg.cacheex_lg_only_in_tab); + caidtab_clear(&cfg.cacheex_localgenerated_only_in_caidtab); + caidtab2ftab_add(&cfg.cacheex_localgenerated_only_caidtab, &cfg.cacheex_lg_only_tab); + caidtab_clear(&cfg.cacheex_localgenerated_only_caidtab); +#endif +} + +static bool cache_should_save_fn(void *UNUSED(var)) +{ + return cfg.delay > CS_DELAY || cfg.max_cache_time != DEFAULT_MAX_CACHE_TIME +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO + || cfg.cw_cache_size > 0 || cfg.cw_cache_memory > 0 || cfg.cw_cache_settings.cwchecknum || cfg.ecm_cache_size > 0 + || cfg.ecm_cache_memory > 0 || cfg.ecm_cache_droptime > 0 || cfg.cacheex_dropdiffs > 0 || cfg.cacheex_push_lg_groups + || cfg.cacheex_lg_only_remote_settings != 1 || cfg.cacheex_localgenerated_only > 0 || cfg.cacheex_localgenerated_only_caidtab.ctnum + || cfg.cacheex_lg_only_tab.nfilts || cfg.cacheex_lg_only_in_aio_only > 0 || cfg.cacheex_localgenerated_only_in > 0 + || cfg.cacheex_localgenerated_only_in_caidtab.ctnum || cfg.cacheex_lg_only_in_tab.nfilts || cfg.cacheex_filter_caidtab.cevnum + || cfg.cacheex_filter_caidtab_aio.cevnum || cfg.cacheex_nopushafter_tab.cvnum || cfg.waittime_block_start > 0 || cfg.waittime_block_time > 0 +#endif + || cfg.max_hitcache_time != DEFAULT_MAX_HITCACHE_TIME || cfg.cacheex_wait_timetab.cevnum || cfg.cacheex_mode1_delay_tab.cvnum + || cfg.cacheex_enable_stats > 0 || cfg.csp_port > 0 || IP_ISSET(cfg.csp_srvip) || cfg.csp.filter_caidtab.cevnum || cfg.csp.allow_request != 1 + || cfg.csp.allow_reforward > 0 || cfg.cacheex_cwcheck_tab.cwchecknum || cfg.wait_until_ctimeout > 0 || cfg.csp.block_fakecws > 0 +#endif +#ifdef CW_CYCLE_CHECK + || cfg.cwcycle_check_enable || cfg.cwcycle_check_caidtab.ctnum || cfg.maxcyclelist != 500 || cfg.keepcycletime != 15 || cfg.onbadcycle != 1 + || cfg.cwcycle_dropold != 1 || cfg.cwcycle_sensitive != 4 || cfg.cwcycle_allowbadfromffb > 0 || cfg.cwcycle_usecwcfromce > 0 +#endif + ; +} + +static const struct config_list cache_opts[] = +{ + DEF_OPT_SAVE_FUNC(cache_should_save_fn), + DEF_OPT_FIXUP_FUNC(cache_fixups_fn), + DEF_OPT_UINT32("delay" , OFS(delay) , CS_DELAY), + DEF_OPT_INT32("max_time" , OFS(max_cache_time) , DEFAULT_MAX_CACHE_TIME), +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO + DEF_OPT_UINT32("cw_cache_size" , OFS(cw_cache_size) , 0), + DEF_OPT_UINT32("cw_cache_memory" , OFS(cw_cache_memory) , 0), + DEF_OPT_FUNC("cw_cache_settings" , OFS(cw_cache_settings) , cacheex_cwcheck_tab_fn), + DEF_OPT_UINT32("ecm_cache_size" , OFS(ecm_cache_size) , 0), + DEF_OPT_UINT32("ecm_cache_memory" , OFS(ecm_cache_memory) , 0), + DEF_OPT_INT32("ecm_cache_droptime" , OFS(ecm_cache_droptime) , 0), +#endif + DEF_OPT_INT32("max_hit_time" , OFS(max_hitcache_time) , DEFAULT_MAX_HITCACHE_TIME), + DEF_OPT_FUNC("wait_time" , OFS(cacheex_wait_timetab) , cacheex_valuetab_fn), + DEF_OPT_FUNC("cacheex_mode1_delay" , OFS(cacheex_mode1_delay_tab) , caidvaluetab_fn), + DEF_OPT_UINT8("cacheexenablestats" , OFS(cacheex_enable_stats) , 0), +#ifdef CS_CACHEEX_AIO + DEF_OPT_UINT8("cacheex_dropdiffs" , OFS(cacheex_dropdiffs) , 0), + DEF_OPT_FUNC("cacheex_push_lg_groups" , OFS(cacheex_push_lg_groups) , group_fn), + 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_ecm_filter" , OFS(cacheex_filter_caidtab) , cacheex_hitvaluetab_fn), + DEF_OPT_FUNC("cacheex_ecm_filter_aio" , OFS(cacheex_filter_caidtab_aio) , cacheex_hitvaluetab_fn), +#endif + DEF_OPT_INT32("csp_port" , OFS(csp_port) , 0), + DEF_OPT_FUNC("csp_serverip" , OFS(csp_srvip) , serverip_fn), + DEF_OPT_FUNC("csp_ecm_filter" , OFS(csp.filter_caidtab) , cacheex_hitvaluetab_fn), + DEF_OPT_UINT8("csp_allow_request" , OFS(csp.allow_request) , 1), + DEF_OPT_UINT8("csp_allow_reforward" , OFS(csp.allow_reforward) , 0), + DEF_OPT_FUNC("cacheex_cw_check" , OFS(cacheex_cwcheck_tab) , cacheex_cwcheck_tab_fn), + DEF_OPT_UINT8("wait_until_ctimeout" , OFS(wait_until_ctimeout) , 0), + DEF_OPT_UINT8("csp_block_fakecws" , OFS(csp.block_fakecws) , 0), +#ifdef CS_CACHEEX_AIO + DEF_OPT_FUNC("cacheex_nopushafter" , OFS(cacheex_nopushafter_tab) , caidvaluetab_fn), + DEF_OPT_UINT8("waittime_block_start" , OFS(waittime_block_start) , 0), + DEF_OPT_INT32("waittime_block_time" , OFS(waittime_block_time) , 0), +#endif +#endif +#ifdef CW_CYCLE_CHECK + DEF_OPT_INT8("cwcycle_check_enable" , OFS(cwcycle_check_enable) , 0), + DEF_OPT_FUNC("cwcycle_check_caid" , OFS(cwcycle_check_caidtab) , check_caidtab_fn), + DEF_OPT_INT32("cwcycle_maxlist" , OFS(maxcyclelist) , 500), + DEF_OPT_INT32("cwcycle_keeptime" , OFS(keepcycletime) , 15), + DEF_OPT_INT8("cwcycle_onbad" , OFS(onbadcycle) , 1), + DEF_OPT_INT8("cwcycle_dropold" , OFS(cwcycle_dropold) , 1), + DEF_OPT_INT8("cwcycle_sensitive" , OFS(cwcycle_sensitive) , 4), + DEF_OPT_INT8("cwcycle_allowbadfromffb" , OFS(cwcycle_allowbadfromffb) , 0), + DEF_OPT_INT8("cwcycle_usecwcfromce" , OFS(cwcycle_usecwcfromce) , 0), +#endif + DEF_LAST_OPT +}; + +#ifdef MODULE_CAMD35 +static bool camd35_should_save_fn(void *UNUSED(var)) +{ + return cfg.c35_port; +} + +static const struct config_list camd35_opts[] = +{ + DEF_OPT_SAVE_FUNC(camd35_should_save_fn), + DEF_OPT_INT32("port" , OFS(c35_port) , 0), + DEF_OPT_FUNC("serverip" , OFS(c35_srvip) , serverip_fn), + DEF_OPT_INT8("suppresscmd08", OFS(c35_udp_suppresscmd08), 0), + DEF_LAST_OPT +}; +#else +static const struct config_list camd35_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_NEWCAMD +static void porttab_fn(const char *token, char *value, void *setting, FILE *f) +{ + PTAB *ptab = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + clear_ptab(ptab); + } + else + { + chk_port_tab(value, ptab); + } + return; + } + value = mk_t_newcamd_port(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); +} +#endif + +#ifdef MODULE_CAMD35_TCP +static void porttab_camd35_fn(const char *token, char *value, void *setting, FILE *f) +{ + PTAB *ptab = setting; + if(value) + { + if(cs_strlen(value) == 0) + { + clear_ptab(ptab); + } + else + { + chk_port_camd35_tab(value, ptab); + } + return; + } + value = mk_t_camd35tcp_port(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); +} +#endif + +#if defined(MODULE_NEWCAMD) || defined(MODULE_CAMD35_TCP) +static void porttab_free_fn(void *setting) +{ + clear_ptab(setting); +} +#endif + +#ifdef MODULE_CAMD35_TCP +static bool cs378x_should_save_fn(void *UNUSED(var)) +{ + return cfg.c35_tcp_ptab.nports && cfg.c35_tcp_ptab.ports[0].s_port; +} + +static const struct config_list cs378x_opts[] = +{ + DEF_OPT_SAVE_FUNC(cs378x_should_save_fn), + DEF_OPT_FUNC("port" , OFS(c35_tcp_ptab) , porttab_camd35_fn, .free_value = porttab_free_fn), + DEF_OPT_FUNC("serverip" , OFS(c35_tcp_srvip) , serverip_fn), + DEF_OPT_INT8("suppresscmd08", OFS(c35_tcp_suppresscmd08), 0), + DEF_LAST_OPT +}; +#else +static const struct config_list cs378x_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_NEWCAMD +static bool newcamd_should_save_fn(void *UNUSED(var)) +{ + return cfg.ncd_ptab.nports && cfg.ncd_ptab.ports[0].s_port; +} + +static const struct config_list newcamd_opts[] = +{ + DEF_OPT_SAVE_FUNC(newcamd_should_save_fn), + DEF_OPT_FUNC("port" , OFS(ncd_ptab) , porttab_fn, .free_value = porttab_free_fn), + DEF_OPT_FUNC("serverip" , OFS(ncd_srvip) , serverip_fn), + DEF_OPT_FUNC("allowed" , OFS(ncd_allowed) , iprange_fn, .free_value = iprange_free_fn), + DEF_OPT_HEX("key" , OFS(ncd_key) , SIZEOF(ncd_key)), + DEF_OPT_INT8("keepalive" , OFS(ncd_keepalive), DEFAULT_NCD_KEEPALIVE), + DEF_OPT_INT8("mgclient" , OFS(ncd_mgclient) , 0), + DEF_LAST_OPT +}; +#else +static const struct config_list newcamd_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_CCCAM +static void cccam_port_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + int i; + char *ptr, *saveptr1 = NULL; + memset(cfg.cc_port, 0, sizeof(cfg.cc_port)); + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); ptr && i < CS_MAXPORTS; ptr = strtok_r(NULL, ",", &saveptr1)) + { + cfg.cc_port[i] = strtoul(ptr, NULL, 10); + if(cfg.cc_port[i]) + { i++; } + } + return; + } + value = mk_t_cccam_port(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); +} + +static bool cccam_should_save_fn(void *UNUSED(var)) +{ + return cfg.cc_port[0]; +} + +static const struct config_list cccam_opts[] = +{ + DEF_OPT_SAVE_FUNC(cccam_should_save_fn), + DEF_OPT_FUNC("port" , OFS(cc_port) , cccam_port_fn), + DEF_OPT_FUNC("serverip" , OFS(cc_srvip) , serverip_fn), + DEF_OPT_HEX("nodeid" , OFS(cc_fixed_nodeid) , SIZEOF(cc_fixed_nodeid)), + DEF_OPT_SSTR("version" , OFS(cc_version) , "", SIZEOF(cc_version)), + DEF_OPT_INT8("reshare" , OFS(cc_reshare) , 10), + DEF_OPT_INT8("reshare_mode" , OFS(cc_reshare_services) , 4), + DEF_OPT_INT8("ignorereshare" , OFS(cc_ignore_reshare) , 0), + DEF_OPT_INT8("forward_origin_card", OFS(cc_forward_origin_card), 0), + DEF_OPT_INT8("stealth" , OFS(cc_stealth) , 0), + DEF_OPT_INT32("updateinterval" , OFS(cc_update_interval) , DEFAULT_UPDATEINTERVAL), + DEF_OPT_INT8("minimizecards" , OFS(cc_minimize_cards) , 0), + DEF_OPT_INT8("keepconnected" , OFS(cc_keep_connected) , 1), + DEF_OPT_UINT32("recv_timeout" , OFS(cc_recv_timeout) , DEFAULT_CC_RECV_TIMEOUT), + DEF_LAST_OPT +}; +#else +static const struct config_list cccam_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_PANDORA +static bool pandora_should_save_fn(void *UNUSED(var)) +{ + return cfg.pand_port; +} + +static const struct config_list pandora_opts[] = +{ + DEF_OPT_SAVE_FUNC(pandora_should_save_fn), + DEF_OPT_INT32("pand_port" , OFS(pand_port) , 0), + DEF_OPT_FUNC("pand_srvid" , OFS(pand_srvip) , serverip_fn), + DEF_OPT_STR("pand_usr" , OFS(pand_usr) , NULL), + DEF_OPT_STR("pand_pass" , OFS(pand_pass) , NULL), + DEF_OPT_INT8("pand_ecm" , OFS(pand_ecm) , 0), + DEF_OPT_INT8("pand_skip_send_dw", OFS(pand_skip_send_dw), 0), + DEF_OPT_FUNC("pand_allowed" , OFS(pand_allowed) , iprange_fn, .free_value = iprange_free_fn), + DEF_LAST_OPT +}; +#else +static const struct config_list pandora_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_SCAM +static bool scam_should_save_fn(void *UNUSED(var)) +{ + return cfg.scam_port; +} +static const struct config_list scam_opts[] = +{ + DEF_OPT_SAVE_FUNC(scam_should_save_fn), + DEF_OPT_INT32("port" , OFS(scam_port) , 0), + DEF_OPT_FUNC("serverip" , OFS(scam_srvip) , serverip_fn), + DEF_OPT_FUNC("allowed" , OFS(scam_allowed), iprange_fn, .free_value = iprange_free_fn), + DEF_LAST_OPT +}; +#else +static const struct config_list scam_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_STREAMRELAY +static bool streamrelay_should_save_fn(void *UNUSED(var)) +{ + return cfg.stream_relay_enabled; +} +static const struct config_list streamrelay_opts[] = +{ + DEF_OPT_SAVE_FUNC(streamrelay_should_save_fn), + DEF_OPT_INT8("stream_relay_enabled" , OFS(stream_relay_enabled), 0), + DEF_OPT_INT32("stream_relay_port" , OFS(stream_relay_port), 17999), + DEF_OPT_STR("stream_relay_user" , OFS(stream_relay_user), NULL), + DEF_OPT_FUNC("stream_relay_ctab" , OFS(stream_relay_ctab), check_caidtab_fn), + DEF_OPT_STR("stream_source_host" , OFS(stream_source_host), "127.0.0.1"), + DEF_OPT_INT8("stream_client_source_host" , OFS(stream_client_source_host), 1), + DEF_OPT_INT32("stream_source_port" , OFS(stream_source_port), DEFAULT_STREAM_SOURCE_PORT), + DEF_OPT_STR("stream_source_auth_user" , OFS(stream_source_auth_user), NULL), + DEF_OPT_STR("stream_source_auth_password" , OFS(stream_source_auth_password), NULL), + DEF_OPT_UINT32("stream_relay_buffer_time" , OFS(stream_relay_buffer_time), 0), + DEF_OPT_UINT8("stream_relay_reconnect_count" , OFS(stream_relay_reconnect_count), 0), + DEF_OPT_INT8("stream_display_client" , OFS(stream_display_client), 0), + DEF_OPT_INT8("stream_reuse_client" , OFS(stream_reuse_client), 0), +#ifdef WEBIF + DEF_OPT_INT8("stream_hide_client" , OFS(stream_hide_client), 0), +#endif + DEF_LAST_OPT +}; +#else +static const struct config_list streamrelay_opts[] = { DEF_LAST_OPT }; +#endif + + +#ifdef MODULE_RADEGAST +static bool radegast_should_save_fn(void *UNUSED(var)) +{ + return cfg.rad_port; +} + +static const struct config_list radegast_opts[] = +{ + DEF_OPT_SAVE_FUNC(radegast_should_save_fn), + DEF_OPT_INT32("port" , OFS(rad_port) , 0), + DEF_OPT_FUNC("serverip", OFS(rad_srvip) , serverip_fn), + DEF_OPT_FUNC("allowed" , OFS(rad_allowed), iprange_fn, .free_value = iprange_free_fn), + DEF_OPT_STR("user" , OFS(rad_usr) , NULL), + DEF_LAST_OPT +}; +#else +static const struct config_list radegast_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_SERIAL +static bool serial_should_save_fn(void *UNUSED(var)) +{ + return cfg.ser_device != NULL; +} + +static const struct config_list serial_opts[] = +{ + DEF_OPT_SAVE_FUNC(serial_should_save_fn), + DEF_OPT_STR("device", OFS(ser_device), NULL), + DEF_LAST_OPT +}; +#else +static const struct config_list serial_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef MODULE_GBOX + +static void gbox_password_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + const char *s; + s = value; + if (s[strspn(s, "0123456789abcdefABCDEF")] == 0) + { + /* valid Hexa symbol */ + cfg.gbox_password = a2i(value, 8); + return; + } + else + { + cfg.gbox_password = 0; + } + } + if (cfg.gbox_password != 0) + { + fprintf_conf(f, token, "%08X\n", cfg.gbox_password); + } +} + +static void gbox_block_ecm_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + char *ptr1, *saveptr1 = NULL; + const char *s; + memset(cfg.gbox_block_ecm, 0, sizeof(cfg.gbox_block_ecm)); + int n = 0, i; + for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); (i < 4) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s=ptr1; + if ((n < GBOX_MAX_BLOCKED_ECM) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_block_ecm[n++] = a2i(ptr1, 4); } + } + cfg.gbox_block_ecm_num = n; + return; + } + if (cfg.gbox_block_ecm_num > 0) + { + value = mk_t_gbox_block_ecm(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void accept_remm_peer_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + char *ptr1, *saveptr1 = NULL; + const char *s; + memset(cfg.accept_remm_peer, 0, sizeof(cfg.accept_remm_peer)); + int n = 0, i; + for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); (i < 4) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s=ptr1; + if ((n < GBOX_MAX_REMM_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.accept_remm_peer[n++] = a2i(ptr1, 4); } + } + cfg.accept_remm_peer_num = n; + return; + } + + if (cfg.accept_remm_peer_num > 0) + { + value = mk_t_accept_remm_peer(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void gbox_ignored_peer_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + char *ptr1, *saveptr1 = NULL; + const char *s; + memset(cfg.gbox_ignored_peer, 0, sizeof(cfg.gbox_ignored_peer)); + int n = 0, i; + + for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); (i < 4) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s = ptr1; + if ((n < GBOX_MAX_IGNORED_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_ignored_peer[n++] = a2i(ptr1, 4); } + } + cfg.gbox_ignored_peer_num = n; + return; + } + + if (cfg.gbox_ignored_peer_num > 0) + { + value = mk_t_gbox_ignored_peer(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void gbox_proxy_card_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + char *ptr1, *saveptr1 = NULL; + const char *s; + memset(cfg.gbox_proxy_card, 0, sizeof(cfg.gbox_proxy_card)); + int n = 0, i; + for (i = 0, ptr1 = strtok_r(value, ",", &saveptr1); (i < 8) && (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s = ptr1; + if ((n < GBOX_MAX_PROXY_CARDS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_proxy_card[n++] = a2i(ptr1, 8); } + } + cfg.gbox_proxy_cards_num = n; + return; + } + + if (cfg.gbox_proxy_cards_num > 0) + { + value = mk_t_gbox_proxy_card(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void gbox_port_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + int i; + char *ptr, *saveptr1 = NULL; + memset(cfg.gbox_port, 0, sizeof(cfg.gbox_port)); + for(i = 0, ptr = strtok_r(value, ",", &saveptr1); ptr && i < CS_MAXPORTS; ptr = strtok_r(NULL, ",", &saveptr1)) + { + cfg.gbox_port[i] = strtoul(ptr, NULL, 10); + if(cfg.gbox_port[i]) + { i++; } + } + return; + } + value = mk_t_gbox_port(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); +} + +static void gbox_my_vers_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + const char *s; + s = value; + int32_t len = cs_strlen(value); + + if ((s[strspn(s, "0123456789abcdefABCDEF")] != 0) || (len == 0) || (len > 2)) + { + cfg.gbox_my_vers = GBOX_MY_VERS_DEF; + } + else + { + cfg.gbox_my_vers = a2i(value, 1); + return; + } + } + + if(cfg.gbox_my_vers != GBOX_MY_VERS_DEF) + { + fprintf_conf(f, token, "%02X\n", cfg.gbox_my_vers); + } + else if(cfg.http_full_cfg) + { + fprintf_conf(f, token, "%02X\n", GBOX_MY_VERS_DEF); + } +} + +static void gbox_my_cpu_api_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + const char *s; + s = value; + int32_t len = cs_strlen(value); + + if ((s[strspn(s, "0123456789abcdefABCDEF")] != 0) || (len == 0) || (len > 2)) + { + cfg.gbox_my_cpu_api = GBOX_MY_CPU_API_DEF; + } + else + { + cfg.gbox_my_cpu_api = a2i(value,1); + return; + } + } + + if(cfg.gbox_my_cpu_api != GBOX_MY_CPU_API_DEF) + { + fprintf_conf(f, token, "%02X\n", cfg.gbox_my_cpu_api); + } + else if(cfg.http_full_cfg) + { fprintf_conf(f, token, "%02X\n", GBOX_MY_CPU_API_DEF); } +} + +static void gbox_dest_peers_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + char *ptr1, *saveptr1 = NULL; + const char *s; + memset(cfg.gbox_dest_peers, 0, sizeof(cfg.gbox_dest_peers)); + int n = 0; + + for (ptr1 = strtok_r(value, ",", &saveptr1); (ptr1); ptr1 = strtok_r(NULL, ",", &saveptr1)) + { + s = trim(ptr1); + if ((n < GBOX_MAX_DEST_PEERS) && (s[strspn(s, "0123456789abcdefABCDEF")] == 0)) + { cfg.gbox_dest_peers[n++] = a2i(trim(ptr1), cs_strlen(trim(ptr1))); } + } + cfg.gbox_dest_peers_num = n; + return; + } + + if ((cfg.gbox_dest_peers_num > 0) && cfg.gbox_save_gsms) + { + value = mk_t_gbox_dest_peers(); + fprintf_conf(f, token, "%s\n", value); + free_mk_t(value); + } +} + +static void gbox_msg_txt_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if (value) + { + cs_strncpy(cfg.gbox_msg_txt, value, sizeof(cfg.gbox_msg_txt)); + return; + } + + if ((cfg.gbox_msg_txt[0] != '\0') && cfg.gbox_save_gsms) + { + fprintf_conf(f, token, "%s\n", cfg.gbox_msg_txt); + } +} + +static bool gbox_should_save_fn(void *UNUSED(var)) +{ + return cfg.gbox_port[0]; +} + +static const struct config_list gbox_opts[] = +{ + DEF_OPT_SAVE_FUNC(gbox_should_save_fn), + DEF_OPT_FUNC("port" , OFS(gbox_port) , gbox_port_fn), + DEF_OPT_STR("hostname" , OFS(gbox_hostname) , NULL), + DEF_OPT_FUNC("my_password" , OFS(gbox_password) , gbox_password_fn ), + DEF_OPT_UINT32("gbox_reconnect", OFS(gbox_reconnect) , DEFAULT_GBOX_RECONNECT), + DEF_OPT_FUNC("my_vers" , OFS(gbox_my_vers) , gbox_my_vers_fn), + DEF_OPT_FUNC("my_cpu_api" , OFS(gbox_my_cpu_api) , gbox_my_cpu_api_fn), + DEF_OPT_UINT8("gsms_disable" , OFS(gsms_dis) , 1), + DEF_OPT_UINT8("dis_attack_txt" , OFS(dis_attack_txt) , 0), + DEF_OPT_UINT8("log_hello" , OFS(log_hello) , 1), + DEF_OPT_STR("tmp_dir" , OFS(gbox_tmp_dir) , NULL ), + DEF_OPT_FUNC("ignore_peer" , OFS(gbox_ignored_peer), gbox_ignored_peer_fn ), + DEF_OPT_FUNC("accept_remm_peer", OFS(accept_remm_peer) , accept_remm_peer_fn ), + DEF_OPT_FUNC("block_ecm" , OFS(gbox_block_ecm) , gbox_block_ecm_fn ), + DEF_OPT_FUNC("proxy_card" , OFS(gbox_proxy_card) , gbox_proxy_card_fn ), + DEF_OPT_UINT8("gbox_save_gsms" , OFS(gbox_save_gsms) , 0), + DEF_OPT_UINT8("gbox_msg_type" , OFS(gbox_msg_type) , 0), + DEF_OPT_FUNC("gbox_dest_peers" , OFS(gbox_dest_peers) , gbox_dest_peers_fn ), + DEF_OPT_FUNC("gbox_msg_txt" , OFS(gbox_msg_txt) , gbox_msg_txt_fn ), + DEF_OPT_UINT8("ccc_reshare" , OFS(cc_gbx_reshare_en), 0), + DEF_LAST_OPT +}; +#else +static const struct config_list gbox_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef HAVE_DVBAPI +extern const char *boxdesc[]; + +static void dvbapi_boxtype_fn(const char *token, char *value, void *UNUSED(setting), FILE *f) +{ + if(value) + { + int i; + cfg.dvbapi_boxtype = 0; + for(i = 1; i <= BOXTYPES; i++) + { + if(streq(value, boxdesc[i])) + { + cfg.dvbapi_boxtype = i; + break; + } + } + return; + } + + if(cfg.dvbapi_boxtype) + { fprintf_conf(f, token, "%s\n", boxdesc[cfg.dvbapi_boxtype]); } +} + +static void dvbapi_services_fn(const char *UNUSED(token), char *value, void *UNUSED(setting), FILE *UNUSED(f)) +{ + if(value) + { chk_services(value, &cfg.dvbapi_sidtabs); } + // THIS OPTION IS NOT SAVED +} + +static bool dvbapi_should_save_fn(void *UNUSED(var)) +{ + return cfg.dvbapi_enabled; +} + +static const struct config_list dvbapi_opts[] = +{ + DEF_OPT_SAVE_FUNC(dvbapi_should_save_fn), + DEF_OPT_INT8("enabled" , OFS(dvbapi_enabled) , 0), + DEF_OPT_INT8("au" , OFS(dvbapi_au) , 0), + DEF_OPT_INT8("pmt_mode" , OFS(dvbapi_pmtmode) , 0), + DEF_OPT_INT8("request_mode" , OFS(dvbapi_requestmode) , 0), + DEF_OPT_INT32("listen_port" , OFS(dvbapi_listenport) , 0), + DEF_OPT_FUNC("serverip" , OFS(dvbapi_srvip) , serverip_fn), + DEF_OPT_INT32("delayer" , OFS(dvbapi_delayer) , 0), + DEF_OPT_INT8("ecminfo_file" , OFS(dvbapi_ecminfo_file) , 1), + DEF_OPT_INT8("ecminfo_type" , OFS(dvbapi_ecminfo_type) , 0), + DEF_OPT_STR("user" , OFS(dvbapi_usr) , NULL), + DEF_OPT_INT8("read_sdt" , OFS(dvbapi_read_sdt) , 0), + DEF_OPT_INT8("write_sdt_prov" , OFS(dvbapi_write_sdt_prov) , 0), +#ifdef WITH_EXTENDED_CW + DEF_OPT_INT8("extended_cw_api", OFS(dvbapi_extended_cw_api), 0), +#endif +#ifdef MODULE_STREAMRELAY + DEF_OPT_INT8("demuxer_fix" , OFS(dvbapi_demuxer_fix) , 0), +#endif + DEF_OPT_FUNC("boxtype" , OFS(dvbapi_boxtype) , dvbapi_boxtype_fn), + DEF_OPT_FUNC("services" , OFS(dvbapi_sidtabs.ok) , dvbapi_services_fn), + DEF_LAST_OPT +}; +#else +static const struct config_list dvbapi_opts[] = { DEF_LAST_OPT }; +#endif + +#ifdef LCDSUPPORT +static void lcd_fixups_fn(void *UNUSED(var)) +{ + if(cfg.lcd_write_intervall < 5) { cfg.lcd_write_intervall = 5; } +} + +static bool lcd_should_save_fn(void *UNUSED(var)) +{ + return cfg.enablelcd; +} + +static const struct config_list lcd_opts[] = +{ + DEF_OPT_SAVE_FUNC(lcd_should_save_fn), + DEF_OPT_FIXUP_FUNC(lcd_fixups_fn), + DEF_OPT_INT8("enablelcd" , OFS(enablelcd) , 0), + DEF_OPT_STR("lcd_outputpath" , OFS(lcd_output_path) , NULL), + DEF_OPT_INT32("lcd_hideidle" , OFS(lcd_hide_idle) , 0), + DEF_OPT_INT32("lcd_writeintervall", OFS(lcd_write_intervall), 10), + DEF_LAST_OPT +}; +#else +static const struct config_list lcd_opts[] = { DEF_LAST_OPT }; +#endif + +static const struct config_sections oscam_conf[] = +{ + { "global", global_opts }, // *** MUST BE FIRST *** + { "anticasc", anticasc_opts }, + { "cache", cache_opts }, + { "lcd", lcd_opts }, + { "camd33", camd33_opts }, + { "cs357x", camd35_opts }, + { "cs378x", cs378x_opts }, + { "newcamd", newcamd_opts }, + { "radegast", radegast_opts }, + { "serial", serial_opts }, + { "gbox", gbox_opts }, + { "cccam", cccam_opts }, + { "pandora", pandora_opts }, + { "scam", scam_opts }, + { "streamrelay", streamrelay_opts }, + { "dvbapi", dvbapi_opts }, + { "monitor", monitor_opts }, + { "webif", webif_opts }, + { NULL, NULL } +}; + +void config_set(char *section, const char *token, char *value) +{ + config_set_value(oscam_conf, section, token, value, &cfg); +} + +void config_free(void) +{ + config_sections_free(oscam_conf, &cfg); + caidvaluetab_clear(&cfg.ftimeouttab); + ftab_clear(&cfg.double_check_caid); + ftab_clear(&cfg.disablecrccws_only_for); +#ifdef WITH_LB + caidvaluetab_clear(&cfg.lb_retrylimittab); + caidvaluetab_clear(&cfg.lb_nbest_readers_tab); + caidtab_clear(&cfg.lb_noproviderforcaid); +#endif +#ifdef CS_CACHEEX +#ifdef CS_CACHEEX_AIO + cwcheckvaluetab_clear(&cfg.cw_cache_settings); +#endif + caidvaluetab_clear(&cfg.cacheex_mode1_delay_tab); +#ifdef CS_CACHEEX_AIO + caidvaluetab_clear(&cfg.cacheex_nopushafter_tab); + caidtab_clear(&cfg.cacheex_localgenerated_only_caidtab); + caidtab_clear(&cfg.cacheex_localgenerated_only_in_caidtab); + ftab_clear(&cfg.cacheex_lg_only_tab); + ftab_clear(&cfg.cacheex_lg_only_in_tab); + cecspvaluetab_clear(&cfg.cacheex_filter_caidtab); + cecspvaluetab_clear(&cfg.cacheex_filter_caidtab_aio); +#endif + cecspvaluetab_clear(&cfg.cacheex_wait_timetab); +#endif +#ifdef CW_CYCLE_CHECK + caidtab_clear(&cfg.cwcycle_check_caidtab); +#endif +} + +int32_t init_config(void) +{ + FILE *fp; + + if(config_enabled(WEBIF)) + { + fp = open_config_file(cs_conf); + } + else + { + fp = open_config_file_or_die(cs_conf); + } + + const struct config_sections *cur_section = oscam_conf; // Global + char *token; + + config_sections_set_defaults(oscam_conf, &cfg); + + if(!fp) + { + // no oscam.conf but webif is included in build, set it up for lan access and tweak defaults +#ifdef WEBIF + cfg.http_port = DEFAULT_HTTP_PORT; + char *default_allowed; + if ((default_allowed = cs_strdup(DEFAULT_HTTP_ALLOW))) + { + chk_iprange(default_allowed, &cfg.http_allowed); + free(default_allowed); + } +#endif + NULLFREE(cfg.logfile); + cfg.logtostdout = 1; +#ifdef HAVE_DVBAPI + cfg.dvbapi_enabled = 1; +#endif + return 0; + } + + if(!cs_malloc(&token, MAXLINESIZE)) + { return 1; } + + int line = 0; + int valid_section = 1; + while(fgets(token, MAXLINESIZE, fp)) + { + ++line; + int len = cs_strlen(trim(token)); + if(len < 3) // a=b or [a] are at least 3 chars + { continue; } + if(token[0] == '#') // Skip comments + { continue; } + if(token[0] == '[' && token[len - 1] == ']') + { + token[len - 1] = '\0'; + valid_section = 0; + const struct config_sections *newconf = config_find_section(oscam_conf, token + 1); + if(config_section_is_active(newconf) && cur_section) + { + config_list_apply_fixups(cur_section->config, &cfg); + cur_section = newconf; + valid_section = 1; + } + if(!newconf) + { + fprintf(stderr, "WARNING: %s line %d unknown section [%s].\n", + cs_conf, line, token + 1); + continue; + } + if(!config_section_is_active(newconf)) + { + fprintf(stderr, "WARNING: %s line %d section [%s] is ignored (support not compiled in).\n", + cs_conf, line, newconf->section); + } + continue; + } + + if(!valid_section) + { continue; } + char *value = strchr(token, '='); + if(!value) // No = found, well go on + { continue; } + *value++ = '\0'; + char *tvalue = trim(value); + char *ttoken = trim(strtolower(token)); + if(cur_section && !config_list_parse(cur_section->config, ttoken, tvalue, &cfg)) + { + fprintf(stderr, "WARNING: %s line %d section [%s] contains unknown setting '%s=%s'\n", + cs_conf, line, cur_section->section, ttoken, tvalue); + } + } + NULLFREE(token); + fclose(fp); + if(cur_section) { config_list_apply_fixups(cur_section->config, &cfg); } + return 0; +} + +int32_t write_config(void) +{ + FILE *f = create_config_file(cs_conf); + if(!f) + { return 1; } + config_sections_save(oscam_conf, f, &cfg); + return flush_config_file(f, cs_conf); +} diff --git a/oscam-config-reader.c b/oscam-config-reader.c old mode 100755 new mode 100644 index f249a92..02880e1 --- a/oscam-config-reader.c +++ b/oscam-config-reader.c @@ -114,7 +114,6 @@ static void protocol_fn(const char *token, char *value, void *setting, FILE *f) { "newcamd525", R_NEWCAMD }, { "newcamd524", R_NEWCAMD }, { "drecas", R_DRECAS }, - { "emu", R_EMU }, { NULL, 0 } }, *p; int i; @@ -691,9 +690,6 @@ void ftab_fn(const char *token, char *value, void *setting, long ftab_type, FILE 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 @@ -1200,10 +1196,6 @@ static const struct config_list reader_opts[] = 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_maxhop_percaid" , OFS(cacheex.maxhop_percaid), caidvaluetab_fn), -#ifdef CS_CACHEEX_AIO - DEF_OPT_FUNC("cacheex_maxhop_lg_percaid" , OFS(cacheex.maxhop_lg_percaid), caidvaluetab_fn), #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), @@ -1358,7 +1350,6 @@ static const struct config_list reader_opts[] = 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), @@ -1373,10 +1364,6 @@ static const struct config_list reader_opts[] = #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 @@ -1479,7 +1466,7 @@ static bool reader_check_setting(const struct config_list *UNUSED(clist), void * // These are written only when the reader is CCCAM static const char *cccam_settings[] = { - "cccversion", "cccmaxhops", "cccmindown", "cccwantemu", "ccckeepalive", + "cccversion", "cccmaxhops", "cccmindown", "ccckeepalive", "cccreconnect", 0 }; diff --git a/oscam-config.c b/oscam-config.c old mode 100755 new mode 100644 index 2211e8a..bd96be4 --- a/oscam-config.c +++ b/oscam-config.c @@ -796,7 +796,7 @@ int32_t init_fakecws(void) int32_t nr = 0, i, j, idx; uint32_t alloccount[0x100], count[0x100], tmp, max_compares = 0, average_compares = 0; char *token, cw_string[64]; - uint8_t cw[16], wrong_checksum, __attribute__((unused)) c, have_fakecw = 0; + uint8_t cw[16], wrong_checksum, c, have_fakecw = 0; FILE *fp; memset(alloccount, 0, sizeof(count)); @@ -827,6 +827,15 @@ int32_t init_fakecws(void) { wrong_checksum = 0; + for(i = 0; i < 16; i += 4) + { + c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff); + if(cw[i + 3] != c) + { + wrong_checksum = 1; + } + } + if(wrong_checksum) { cs_log("skipping fake cw %s because of wrong checksum!", cw_string); @@ -877,6 +886,15 @@ int32_t init_fakecws(void) { wrong_checksum = 0; + for(i = 0; i < 16; i += 4) + { + c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff); + if(cw[i + 3] != c) + { + wrong_checksum = 1; + } + } + if(!wrong_checksum) { idx = ((cw[0] & 0xF) << 4) | (cw[8] & 0xF); diff --git a/oscam-config.h b/oscam-config.h old mode 100755 new mode 100644 index a308790..b0179dd --- a/oscam-config.h +++ b/oscam-config.h @@ -7,7 +7,6 @@ int32_t init_config(void); void config_set(char *section, const char *token, char *value); void config_free(void); int32_t write_config(void); -int32_t reload_global_config(void); // Dodano deklarację funkcji do ponownego wczytywania globalnej konfiguracji void chk_account(const char *token, char *value, struct s_auth *account); void account_set_defaults(struct s_auth *auth); @@ -65,7 +64,6 @@ enum ftab_fn FTAB_LOCALCARDS = 0x20, FTAB_IGNCHKSMCAID = 0x40, FTAB_IGNCRCCEX4USERONLYFOR = 0x80, - FTAB_EMUAU = 0x100, FTAB_CCCGBXRESHARE = 0x200 }; diff --git a/oscam-ecm.c b/oscam-ecm.c old mode 100755 new mode 100644 index 305d68a..ed13cd3 --- a/oscam-ecm.c +++ b/oscam-ecm.c @@ -1,8 +1,55 @@ #define MODULE_LOG_PREFIX "ecm" #include "globals.h" -#include // Dodano dla definicji size_t #include +// ---------- ENTROPY FUNCTIONS START ---------- + +static double entropy_calc(const uint8_t *data, int len) +{ + if(!data || len <= 0) + return 0.0; + + int freq[256] = {0}; + + for(int i = 0; i < len; i++) + freq[data[i]]++; + + double entropy = 0.0; + + for(int i = 0; i < 256; i++) + { + if(freq[i] == 0) + continue; + + double p = (double)freq[i] / len; + entropy -= p * (log(p) / log(2)); + } + + return entropy; +} + +static void entropy_log_ecm(ECM_REQUEST *er, struct s_reader *reader) +{ + if(!er) + return; + + double full = entropy_calc(er->cw, 16); + double half1 = entropy_calc(er->cw, 8); + double half2 = entropy_calc(er->cw + 8, 8); + + cs_log_dbg(D_TRACE, + "ENTROPY reader=%s caid=%04X FULL=%.4f HALF1=%.4f HALF2=%.4f", + reader ? reader->label : "?", + er->caid, + full, + half1, + half2); +} + +// ---------- ENTROPY FUNCTIONS END ---------- +#define ENTROPY_THRESHOLD_DEFAULT 3.0 +#define ENTROPY_THRESHOLD_NDS 2.3 + #include "cscrypt/md5.h" #include "module-anticasc.h" #include "module-cacheex.h" @@ -29,6 +76,140 @@ #include "oscam-hashtable.h" #endif +// ADD THIS LINE: +static int cw_checksum_ok(uint8_t *cw); + +///////////////////////////////////////////////////////////// +// AUTODETECT STRUCT AND TABLE (ADD THIS HERE) +///////////////////////////////////////////////////////////// + +#define CW_DETECT_MAX 8192 +#define CW_LEARN_THRESHOLD 5 +#define CW_CACHEEX_NODEID_MAX 5 + +typedef struct +{ + uint16_t caid; + uint32_t prid; + uint16_t srvid; + + uint8_t is64; + uint8_t is48; + + uint8_t learn64_count; + uint8_t learn48_count; + + uint8_t strict_logged; + + /* CacheEX learning: track unique nodeid (64-bit) */ + uint64_t cacheex_nodeid_48[CW_CACHEEX_NODEID_MAX]; + uint8_t cacheex_nodeid_48_count; + + uint64_t cacheex_nodeid_64[CW_CACHEEX_NODEID_MAX]; + uint8_t cacheex_nodeid_64_count; + + /* finalized flags (no more learning) */ + uint8_t cacheex_48_finalized; + uint8_t cacheex_64_finalized; + +} cw_detect_entry; + + +static cw_detect_entry cw_detect_table[CW_DETECT_MAX]; + + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// GET OR CREATE ENTRY +////////////////////////////////////////////////////////////////////////////////////////////////////// + +static cw_detect_entry *cw_detect_get(ECM_REQUEST *er) +{ + int i; + + // search existing entry + for(i = 0; i < CW_DETECT_MAX; i++) + { + if(cw_detect_table[i].caid == er->caid && + cw_detect_table[i].prid == er->prid && + cw_detect_table[i].srvid == er->srvid) + { + return &cw_detect_table[i]; + } + } + + // create new entry + for(i = 0; i < CW_DETECT_MAX; i++) + { + if(cw_detect_table[i].caid == 0) + { + cw_detect_table[i].caid = er->caid; + cw_detect_table[i].prid = er->prid; + cw_detect_table[i].srvid = er->srvid; + + cw_detect_table[i].is64 = 0; + cw_detect_table[i].is48 = 0; + + cw_detect_table[i].learn64_count = 0; + cw_detect_table[i].learn48_count = 0; + + cw_detect_table[i].strict_logged = 0; + + /* initialize CacheEX learning fields */ + cw_detect_table[i].cacheex_nodeid_48_count = 0; + cw_detect_table[i].cacheex_nodeid_64_count = 0; + cw_detect_table[i].cacheex_48_finalized = 0; + cw_detect_table[i].cacheex_64_finalized = 0; + + return &cw_detect_table[i]; + } + } + + return &cw_detect_table[0]; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CACHEEX LEARNING HELPERS +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* Check if nodeid is already in list and add if not, return 1 if finalized (5+ unique nodeid) */ +static int cacheex_learn_add_nodeid(uint64_t *nodeid_list, uint8_t *count, uint64_t new_nodeid) +{ + int i; + + /* check if already present */ + for (i = 0; i < *count; i++) + { + if (nodeid_list[i] == new_nodeid) + return 0; /* already exists, no new entry */ + } + + /* add new nodeid if space available */ + if (*count < CW_CACHEEX_NODEID_MAX) + { + nodeid_list[*count] = new_nodeid; + (*count)++; + } + + /* finalized when we have CW_CACHEEX_NODEID_MAX unique sources */ + if (*count >= CW_CACHEEX_NODEID_MAX) + return 1; + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CW CHECKSUM FUNCTION (ADD THIS BELOW cw_detect_get) +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int cw_checksum_ok(uint8_t *cw) +{ + if(((cw[0]+cw[1]+cw[2])&0xFF)!=cw[3]) return 0; + if(((cw[4]+cw[5]+cw[6])&0xFF)!=cw[7]) return 0; + if(((cw[8]+cw[9]+cw[10])&0xFF)!=cw[11]) return 0; + if(((cw[12]+cw[13]+cw[14])&0xFF)!=cw[15]) return 0; + return 1; +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////// extern CS_MUTEX_LOCK ecmcache_lock; extern struct ecm_request_t *ecmcwcache; extern uint32_t ecmcwcache_size; @@ -42,10 +223,6 @@ static pthread_cond_t cw_process_sleep_cond; static int cw_process_wakeups; int64_t ecmc_next, cache_next, msec_wait = 3000; -// Deklaracja wirtualnych czytników -static struct s_reader *virtual_cacheex_reader = NULL; -static struct s_reader *virtual_csp_reader = NULL; - #ifdef CS_CACHEEX_AIO // ecm-cache typedef struct ecm_cache @@ -755,24 +932,6 @@ void ecm_timeout(ECM_REQUEST *er) if(check_client(er->client) && er->rc >= E_UNHANDLED) { - // Wymuś decyzję o głosowaniu, jeśli limit czasu upłynął, a ECM jest nadal nieobsługiwany - if (cfg.cwvote_enabled && is_cwvote_caid(er) && er->rc == E_UNHANDLED) - { - if (cfg.cwvote_log_enabled) - { - cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [ecm_timeout] CW Vote enabled and CAID matches, attempting to decide.", - (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); - } - int vote_result = cw_vote_decide(er); - if (vote_result == 1) // Znaleziono zwycięzcę - { - er->rc = E_FOUND; - er->rcEx = 0; - send_dcw(er->client, er); - return; // ECM obsłużony przez głosowanie - } - } - debug_ecm(D_TRACE, "timeout for %s %s", username(er->client), buf); // set timeout for readers not answering @@ -811,7 +970,127 @@ uint8_t checkCWpart(uint8_t *cw, int8_t part) if(cw[i + eo]) { return 1; } return 0; } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ===== CW AUTODETECT PATCH WITH LEARN THRESHOLD (48 + 64 LEARNING) ===== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define CW_LEARN_THRESHOLD 5 + +static int cw_autodetect_and_block(struct s_reader *reader, ECM_REQUEST *er) +{ + if(!er || !er->cw) + return 1; + + cw_detect_entry *e = cw_detect_get(er); + + const char *reader_name = + (reader && reader->label) ? reader->label : "?"; + + int checksum_ok = cw_checksum_ok(er->cw); + + int whitelist = + chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) || + (reader && chk_if_ignore_checksum(er, &reader->disablecrccws_only_for)); + + ///////////////////////////////////////////////////////////// + // STRICT MODE (NOT WHITELIST) → ONLY 48bit ALLOWED + ///////////////////////////////////////////////////////////// + + if(!whitelist) + { + if(!e->strict_logged) + { + cs_log("AUTODETECT STRICT 48bit MODE reader=%s (%04X@%06X/%04X)", + reader_name, er->caid, er->prid, er->srvid); + + e->strict_logged = 1; + } + + // block 64bit + if(!checksum_ok) + { + cs_log("AUTODETECT BLOCK 64bit reader=%s (%04X@%06X/%04X)", + reader_name, er->caid, er->prid, er->srvid); + + return 0; + } + + return 1; + } + + ///////////////////////////////////////////////////////////// + // WHITELIST MODE → LEARN 48bit AND 64bit SEPARATELY + ///////////////////////////////////////////////////////////// + + // ---------- LEARN 64bit ---------- + if(!checksum_ok) + { + if(!e->is64) + { + if(e->learn64_count < CW_LEARN_THRESHOLD) + { + e->learn64_count++; + + cs_log("AUTODETECT LEARN 64bit STEP %d/%d reader=%s (%04X@%06X/%04X)", + e->learn64_count, + CW_LEARN_THRESHOLD, + reader_name, + er->caid, + er->prid, + er->srvid); + } + + if(e->learn64_count >= CW_LEARN_THRESHOLD) + { + e->is64 = 1; + + cs_log("AUTODETECT LOCKED 64bit reader=%s (%04X@%06X/%04X)", + reader_name, + er->caid, + er->prid, + er->srvid); + } + } + + return 1; + } + + // ---------- LEARN 48bit ---------- + if(checksum_ok) + { + if(!e->is48) + { + if(e->learn48_count < CW_LEARN_THRESHOLD) + { + e->learn48_count++; + + cs_log("AUTODETECT LEARN 48bit STEP %d/%d reader=%s (%04X@%06X/%04X)", + e->learn48_count, + CW_LEARN_THRESHOLD, + reader_name, + er->caid, + er->prid, + er->srvid); + } + + if(e->learn48_count >= CW_LEARN_THRESHOLD) + { + e->is48 = 1; + + cs_log("AUTODETECT LOCKED 48bit reader=%s (%04X@%06X/%04X)", + reader_name, + er->caid, + er->prid, + er->srvid); + } + } + + return 1; + } + + return 1; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void update_n_request(void) { struct s_client *cl; @@ -841,35 +1120,6 @@ void update_n_request(void) cs_readunlock(__func__, &clientlist_lock); } -// Funkcja inicjalizująca wirtualne czytniki -static void init_virtual_readers(void) -{ - if (!virtual_cacheex_reader) { - if (cs_malloc(&virtual_cacheex_reader, sizeof(struct s_reader))) { - memset(virtual_cacheex_reader, 0, sizeof(struct s_reader)); - cs_strncpy(virtual_cacheex_reader->label, "CACHEEX_VIRTUAL", sizeof(virtual_cacheex_reader->label)); - virtual_cacheex_reader->grp = 0; - virtual_cacheex_reader->client = NULL; - cs_log_dbg(D_TRACE, "Virtual CACHEEX reader initialized."); - } else { - cs_log("ERROR: Failed to allocate virtual_cacheex_reader!"); - } - } - - if (!virtual_csp_reader) { - if (cs_malloc(&virtual_csp_reader, sizeof(struct s_reader))) { - memset(virtual_csp_reader, 0, sizeof(struct s_reader)); - cs_strncpy(virtual_csp_reader->label, "CSP_VIRTUAL", sizeof(virtual_csp_reader->label)); - virtual_csp_reader->grp = 0; - virtual_csp_reader->client = NULL; - cs_log_dbg(D_TRACE, "Virtual CSP reader initialized."); - } else { - cs_log("ERROR: Failed to allocate virtual_csp_reader!"); - } - } -} - - static void *cw_process(void) { set_thread_name(__func__); @@ -937,8 +1187,7 @@ static void *cw_process(void) { tbc = er->tps; time_to_check_cacheex_mode1_delay = 0; - time_to_check_ctimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, er->client_timeout)); - + time_to_check_cacheex_wait_time = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, er->cacheex_wait_time)); if(comp_timeb(&t_now, &tbc) >= 0) { @@ -966,43 +1215,40 @@ static void *cw_process(void) { next_check = time_to_check_cacheex_mode1_delay; } } #endif - if(er->stage < 4) - { - // fbtimeout - tbc = er->tps; - time_to_check_fbtimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, get_fallbacktimeout(er->caid))); + if(er->stage < 4) + { + // fbtimeout + tbc = er->tps; + time_to_check_fbtimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, get_fallbacktimeout(er->caid))); - if(comp_timeb(&t_now, &tbc) >= 0) - { - add_job(er->client, ACTION_FALLBACK_TIMEOUT, (void *)er, 0); - time_to_check_fbtimeout = 0; - } + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_FALLBACK_TIMEOUT, (void *)er, 0); + time_to_check_fbtimeout = 0; + } - if(!next_check || (time_to_check_fbtimeout > 0 && time_to_check_fbtimeout < next_check)) - { next_check = time_to_check_fbtimeout; } - } + if(!next_check || (time_to_check_fbtimeout > 0 && time_to_check_fbtimeout < next_check)) + { next_check = time_to_check_fbtimeout; } + } + } - // clienttimeout - if(!er->readers_timeout_check) // ecm stays in cache at least ctimeout+2seconds! - { - tbc = er->tps; - time_to_check_ctimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, er->client_timeout)); - - - - if(comp_timeb(&t_now, &tbc) >= 0) - { - add_job(er->client, ACTION_CLIENT_TIMEOUT, (void *)er, 0); - time_to_check_ctimeout = 0; - } - - if(!next_check || (time_to_check_ctimeout > 0 && time_to_check_ctimeout < next_check)) - { next_check = time_to_check_ctimeout; } - } - } - } + // clienttimeout + if(!er->readers_timeout_check) // ecm stays in cache at least ctimeout+2seconds! + { + tbc = er->tps; + uint32_t ct = er->client_timeout ? er->client_timeout : cfg.ctimeout; + time_to_check_ctimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, ct)); + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_CLIENT_TIMEOUT, (void *)er, 0); + time_to_check_ctimeout = 0; + } + if(!next_check || (time_to_check_ctimeout > 0 && time_to_check_ctimeout < next_check)) + { next_check = time_to_check_ctimeout; } + } + } cs_readunlock(__func__, &ecmcache_lock); #ifdef CS_ANTICASC if(cfg.ac_enabled && (ac_next = comp_timeb(&ac_time, &t_now)) <= 10) @@ -1020,9 +1266,8 @@ static void *cw_process(void) cs_readlock(__func__, &ecmcache_lock); for(ecm = ecmcwcache, prv = NULL; ecm; prv = ecm, ecm = ecm->next, count++) { - ecm_maxcachetime = t_now.time - ((ecm->client_timeout + 500) / 1000 + 3); - - + uint32_t ct2 = (ecm->client_timeout ? ecm->client_timeout : cfg.ctimeout); + ecm_maxcachetime = t_now.time - ((ct2 + 500) / 1000 + 3); if(ecm->tps.time < ecm_maxcachetime) { @@ -1053,10 +1298,7 @@ static void *cw_process(void) cs_readlock(__func__, &ecm_pushed_deleted_lock); for(ecm = ecm_pushed_deleted, prv = NULL; ecm; prv = ecm, ecm = ecm->next) { - ecm_maxcachetime = t_now.time - ((ecm->client_timeout + 500) / 1000 + 3); - - - + ecm_maxcachetime = t_now.time - ((cfg.ctimeout + 500) / 1000 + 3); if(ecm->tps.time < ecm_maxcachetime) { cs_readunlock(__func__, &ecm_pushed_deleted_lock); @@ -1126,7 +1368,6 @@ static void *cw_process(void) void cw_process_thread_start(void) { - init_virtual_readers(); // Inicjalizacja wirtualnych czytników start_thread("cw_process", (void *) &cw_process, NULL, NULL, 1, 1); } @@ -1297,8 +1538,6 @@ ECM_REQUEST *get_ecmtask(void) { return NULL; } if(!cs_malloc(&er, sizeof(ECM_REQUEST))) { return NULL; } - memset(er->vote_pool, 0, sizeof(er->vote_pool)); // Initialize vote_pool to zero - er->vote_pool_session = 0; // Initialize vote session to 0 cs_ftime(&er->tps); er->rc = E_UNHANDLED; er->client = cl; @@ -2020,7 +2259,7 @@ void request_cw_from_readers(ECM_REQUEST *er, uint8_t stop_stage) { // Cache-Exchange if((ea->status & REQUEST_SENT) || - (ea->status & (READER_ACTIVE | READER_FALLBACK | READER_LOCAL)) != (READER_ACTIVE | READER_LOCAL)) + (ea->status & (READER_CACHEEX | READER_ACTIVE)) != (READER_CACHEEX | READER_ACTIVE)) { continue; } break; } @@ -2106,7 +2345,6 @@ void add_cache_from_reader(ECM_REQUEST *er, struct s_reader *rdr, uint32_t csp_h ECM_REQUEST *ecm; if (cs_malloc(&ecm, sizeof(ECM_REQUEST))) { - memset(ecm, 0, sizeof(ECM_REQUEST)); cs_ftime(&ecm->tps); ecm->cwc_cycletime = er->cwc_cycletime; @@ -2121,7 +2359,6 @@ void add_cache_from_reader(ECM_REQUEST *er, struct s_reader *rdr, uint32_t csp_h memcpy(ecm->cw, cw, sizeof(ecm->cw)); ecm->grp = rdr->grp; ecm->selected_reader = rdr; - ecm->from_csp = er->from_csp; // Propagacja flagi from_csp #ifdef CS_CACHEEX_AIO ecm->ecm_time = ecm_time; ecm->localgenerated = er->localgenerated; @@ -2185,30 +2422,10 @@ void chk_dcw(struct s_ecm_answer *ea) return; } - // Logika głosowania CW - if (cfg.cwvote_enabled && is_cwvote_caid(ert) && ert->rc == E_UNHANDLED) - { - if (cfg.cwvote_log_enabled) - { - cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [chk_dcw] CW Vote enabled and CAID matches, attempting to decide.", - (check_client(ert->client) ? ert->client->account->usr : "-"), ert->caid, ert->prid, ert->srvid); - } - int vote_result = cw_vote_decide(ert); - if (vote_result == 1) // Znaleziono zwycięzcę - { - ert->rc = E_FOUND; - ert->rcEx = 0; - send_dcw(ert->client, ert); - return; // ECM obsłużony przez głosowanie - } - // Jeśli vote_result wynosi 0 (brak wyraźnego zwycięzcy) lub -1 (za mało głosów), - // kontynuuj z istniejącą logiką, aby umożliwić więcej czytników/przekroczenie limitu czasu. - } - #ifdef CS_CACHEEX - /* jeśli odpowiedź od czytnika cacheex-1, nie wysyłaj odpowiedzi do klienta! - * wątek check_cache sprawdzi licznik i wyśle odpowiedź do klienta! - * W każdym razie, powinniśmy sprawdzić, czy musimy przejść do innego etapu (>1) + /* if answer from cacheex-1 reader, not send answer to client! + * thread check_cache will check counter and send answer to client! + * Anyway, we should check if we have to go to oher stage (>1) */ if(eardr && cacheex_reader(eardr)) @@ -2379,7 +2596,76 @@ void update_chid(ECM_REQUEST *er) er->chid = get_subid(er); } +/* + * This function writes the current CW from ECM struct to a cwl file. + * The filename is re-calculated and file re-opened every time. + * This will consume a bit cpu time, but nothing has to be stored between + * each call. If not file exists, a header is prepended + */ +static void logCWtoFile(ECM_REQUEST *er, uint8_t *cw) +{ + FILE *pfCWL; + char srvname[CS_SERVICENAME_SIZE]; + /* %s / %s _I %04X _ %s .cwl */ + char buf[256 + sizeof(srvname)]; + char date[9]; + uint8_t i, parity, writeheader = 0; + struct tm timeinfo; + /* + * search service name for that id and change characters + * causing problems in file name + */ + + get_servicename(cur_client(), er->srvid, er->prid, er->caid, srvname, sizeof(srvname)); + + for(i = 0; srvname[i]; i++) + if(srvname[i] == ' ') { srvname[i] = '_'; } + + /* calc log file name */ + time_t walltime = cs_time(); + localtime_r(&walltime, &timeinfo); + strftime(date, sizeof(date), "%Y%m%d", &timeinfo); + snprintf(buf, sizeof(buf), "%s/%s_I%04X_%s.cwl", cfg.cwlogdir, date, er->srvid, srvname); + + /* open failed, assuming file does not exist, yet */ + if((pfCWL = fopen(buf, "r")) == NULL) + { + writeheader = 1; + } + else + { + /* we need to close the file if it was opened correctly */ + fclose(pfCWL); + } + + if((pfCWL = fopen(buf, "a+")) == NULL) + { + /* maybe this fails because the subdir does not exist. Is there a common function to create it? + for the moment do not print32_t to log on every ecm + cs_log(""error opening cw logfile for writing: %s (errno=%d %s)", buf, errno, strerror(errno)); */ + return; + } + if(writeheader) + { + /* no global macro for cardserver name :( */ + fprintf(pfCWL, "# OSCam cardserver v%s - %s\n", CS_VERSION, SCM_URL); + fprintf(pfCWL, "# control word log file for use with tsdec offline decrypter\n"); + strftime(buf, sizeof(buf), "DATE %Y-%m-%d, TIME %H:%M:%S, TZ %Z\n", &timeinfo); + fprintf(pfCWL, "# %s", buf); + fprintf(pfCWL, "# CAID 0x%04X, SID 0x%04X, SERVICE \"%s\"\n", er->caid, er->srvid, srvname); + } + + parity = er->ecm[0] & 1; + fprintf(pfCWL, "%d ", parity); + for(i = parity * 8; i < 8 + parity * 8; i++) + { fprintf(pfCWL, "%02X ", cw[i]); } + /* better use incoming time er->tps rather than current time? */ + strftime(buf, sizeof(buf), "%H:%M:%S\n", &timeinfo); + fprintf(pfCWL, "# %s", buf); + fflush(pfCWL); + fclose(pfCWL); +} int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, uint8_t rcEx, uint8_t *cw, char *msglog, uint16_t used_cardtier, EXTENDED_CW* cw_ex) { @@ -2387,8 +2673,6 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui // drop too late answers, to avoid seg fault --> only answer until tps.time+((cfg.ctimeout+500)/1000+1) is accepted time_t timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1); - - if(er->tps.time < timeout) // < and NOT <= { return 0; } @@ -2412,7 +2696,7 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui #endif timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1); - if(er->tps.time < timeout) + if(er->tps.time < timeout) { return 0; } } @@ -2438,14 +2722,10 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui return 0; } - // Special checks for rc - // Skip check for BISS1 - cw could be zero but still catch cw=0 by anticascading - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero - // bad/wrong chksum/ecm if(rc == E_NOTFOUND && rcEx == E2_WRONG_CHKSUM) { - cs_log_dbg(D_READER, "Ai_vote: FAKE DCW DETECTED from reader %s (bad/wrong checksum)", reader ? reader->label : "-"); + cs_log_dbg(D_READER, "ECM for reader %s was bad/has a wrong chksum!", reader ? reader->label : "-"); rc = E_INVALID; rcEx = E2_WRONG_CHKSUM; er->stage = 5; @@ -2461,16 +2741,16 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui } } - if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw) && !caid_is_biss(er->caid)) + if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw)) { rc = E_NOTFOUND; - cs_log_dbg(D_TRACE | D_LB, "Ai_vote: FAKE DCW DETECTED from reader %s (null CW)", reader ? reader->label : "-"); + cs_log_dbg(D_TRACE | D_LB, "WARNING: reader %s send fake cw, set rc=E_NOTFOUND!", reader ? reader->label : "-"); } if(rc < E_NOTFOUND && cw && !chk_halfCW(er,cw)) { rc = E_NOTFOUND; - cs_log_dbg(D_TRACE | D_LB, "Ai_vote: FAKE DCW DETECTED from reader %s (wrong swapped NDS CW)", reader ? reader->label : "-"); + cs_log_dbg(D_TRACE | D_LB, "WARNING: reader %s send wrong swapped NDS cw, set rc=E_NOTFOUND!", reader ? reader->label : "-"); } if(reader && cw && rc < E_NOTFOUND) @@ -2579,34 +2859,140 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui } #ifdef CW_CYCLE_CHECK - uint8_t cwc_ct = er->cwc_cycletime > 0 ? er->cwc_cycletime : 0; - uint8_t cwc_ncwc = er->cwc_next_cw_cycle < 2 ? er->cwc_next_cw_cycle : 2; + uint8_t cwc_ct = er->cwc_cycletime > 0 ? er->cwc_cycletime : 0; + uint8_t cwc_ncwc = er->cwc_next_cw_cycle < 2 ? er->cwc_next_cw_cycle : 2; + if(!checkcwcycle(er->client, er, reader, cw, rc, cwc_ct, cwc_ncwc)) + { +#ifdef CS_CACHEEX_AIO + if(!er->localgenerated) + { +#endif + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + cs_log_dbg(D_CACHEEX | D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck failed! Reader: %s set rc: %i", (er->client ? er->client->account->usr : "-"), er->caid, er->srvid, reader ? reader->label : "-", rc); - if(!checkcwcycle(er->client, er, reader, cw, rc, cwc_ct, cwc_ncwc)) +#ifdef CS_CACHEEX_AIO + } + else + { + cs_log_dbg(D_CACHEEX | D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck failed! Reader: %s set rc: %i -> lg-flagged CW -> do nothing", (er->client ? er->client->account->usr : "-"), er->caid, er->srvid, reader ? reader->label : "-", rc); + } +#endif + } + else { cs_log_dbg(D_CACHEEX | D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck passed! Reader: %s rc: %i", (er->client ? er->client->account->usr : "-"), er->caid, er->srvid, reader ? reader->label : "-", rc); } +#endif + //END -- SPECIAL CHECKs for rc + + +//////////////// TRUST / ENTROPY / AUTODETECT BLOCK //////////////////////////////////////////////////////////////// +if(rc == E_FOUND && cw && !chk_is_null_CW(cw)) +{ + /* SKIP CACHEEX readers */ + if(reader && reader->cacheex.mode) + goto TRUST_DONE; + + /* Używamy lokalnej kopii CW do testów */ + uint8_t testcw[16]; + memcpy(testcw, cw, 16); + + //////////////////////////////////////////////////////////// + // ENTROPY CALC + //////////////////////////////////////////////////////////// + + double full = entropy_calc(testcw, 16); + double half1 = entropy_calc(testcw, 8); + double half2 = entropy_calc(testcw + 8, 8); + + double threshold; + double entropy_value; + + // NDS HALF entropy (always 64bit CAID) + if(er->caid == 0x098D || er->caid == 0x098C) { - rc = E_NOTFOUND; - rcEx = E2_WRONG_CHKSUM; - - cs_log_dbg(D_CACHEEX | D_CWC | D_LB, - "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck FAILED → DROP CW from reader: %s", - (er->client ? er->client->account->usr : "-"), - er->caid, - er->srvid, - reader ? reader->label : "-"); + entropy_value = (half1 > half2) ? half1 : half2; + threshold = 1.35; } else { - cs_log_dbg(D_CACHEEX | D_CWC | D_LB, - "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck PASSED → CW allowed from reader: %s", - (er->client ? er->client->account->usr : "-"), - er->caid, - er->srvid, - reader ? reader->label : "-"); + entropy_value = full; + threshold = 2.5778; } -#endif - //END -- SPECIAL CHECKs for rc + //////////////////////////////////////////////////////////// + // ENTROPY BLOCK (LEVEL 1) + //////////////////////////////////////////////////////////// + if(entropy_value < threshold) + { + cs_log("TRUST BLOCK ENTROPY reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f", + reader ? reader->label : "?", + er->caid, + er->prid, + er->srvid, + entropy_value, + threshold); + + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + + cw = NULL; + + goto TRUST_DONE; + } + + //////////////////////////////////////////////////////////// + // AUTODETECT BLOCK (LEVEL 2) + //////////////////////////////////////////////////////////// + + /* ustaw testcw tymczasowo do er->cw dla autodetect */ + uint8_t original_cw[16]; + memcpy(original_cw, er->cw, 16); + + memcpy(er->cw, testcw, 16); + + if(!cw_autodetect_and_block(reader, er)) + { + cs_log("TRUST BLOCK AUTODETECT reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f", + reader ? reader->label : "?", + er->caid, + er->prid, + er->srvid, + entropy_value, + threshold); + + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + + memset(er->cw, 0, 16); + cw = NULL; + + goto TRUST_DONE; + } + + //////////////////////////////////////////////////////////// + // TRUST OK → FINAL COPY + //////////////////////////////////////////////////////////// + + memcpy(er->cw, testcw, 16); + + cs_log("TRUST OK reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f", + reader ? reader->label : "?", + er->caid, + er->prid, + er->srvid, + entropy_value, + threshold); + +TRUST_DONE: + ; +} + +// global protection +if(rc != E_FOUND) +{ + cw = NULL; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #ifdef CS_CACHEEX_AIO if(!dontsetAnswered) { @@ -2615,104 +3001,11 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui #ifdef CS_CACHEEX_AIO } #endif - - // Update reader stats BEFORE vote logic (with original rc), because after vote ea->rc is set to E_UNHANDLED - if(!dontwriteStats && rc < E_NOTFOUND) - { - ea->ecm_time = comp_timeb(&now, &ea->time_request_sent); - if(ea->ecm_time < 1) { ea->ecm_time = 1; } - send_reader_stat(reader, er, ea, rc, ea->ecm_time); - } - else - { - ea->ecm_time = comp_timeb(&now, &ea->time_request_sent); - if(ea->ecm_time < 1) { ea->ecm_time = 1; } - } - - // Update reader ECM counters BEFORE vote logic (with original rc) - // These counters (ecmsok, ecmsnok, etc.) are used for webif statistics - if(!ea->is_pending && rc < E_NOTFOUND) - { - reader->ecmsok++; - reader->webif_ecmsok++; -#ifdef CS_CACHEEX_AIO - if(er->localgenerated) - reader->ecmsoklg++; -#endif -#ifdef CS_CACHEEX - struct s_client *eacl = reader->client; - if(cacheex_reader(reader) && check_client(eacl)) - { - eacl->cwcacheexgot++; - cacheex_add_stats(eacl, ea->er->caid, ea->er->srvid, ea->er->prid, 1 -#ifdef CS_CACHEEX_AIO - , er->localgenerated); -#else - ); -#endif - first_client->cwcacheexgot++; -#ifdef CS_CACHEEX_AIO - if(er->localgenerated) - { - eacl->cwcacheexgotlg++; - first_client->cwcacheexgotlg++; - } -#endif - } -#endif - } - - // Update reader ECM counters for E_NOTFOUND BEFORE vote logic - if(!ea->is_pending && rc == E_NOTFOUND) - { - reader->ecmsnok++; - reader->webif_ecmsnok++; - if(reader->ecmnotfoundlimit && reader->ecmsnok >= reader->ecmnotfoundlimit) - { - rdr_log(reader, "ECM not found limit reached %u. Restarting the reader.", - reader->ecmsnok); - reader->ecmsnok = 0; // Reset the variable - reader->ecmshealthnok = 0; // Reset the variable - add_job(reader->client, ACTION_READER_RESTART, NULL, 0); - } - } - - // Update reader ECM counters for E_TIMEOUT BEFORE vote logic - if(!ea->is_pending && rc == E_TIMEOUT) - { -#ifdef WITH_LB - STAT_QUERY q; - readerinfofix_get_stat_query(er, &q); - READER_STAT *s; - s = readerinfofix_get_add_stat(reader, &q); - if (s) - { - cs_log_dbg(D_LB, "inc fail {client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer] reader %s rc %d, ecm time %d ms", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader ? reader->label : "-", rc, ea->ecm_time); - readerinfofix_inc_fail(s); // now increase fail factor for unhandled timeouts - } -#endif - reader->ecmstout++; // now append timeouts to the readerinfo timeout count - reader->webif_ecmstout++; - } - - // If a valid CW is found, and CW Vote is enabled for this CAID, add it to the vote pool and mark this answer as unhandled for now - if(cfg.cwvote_enabled && is_cwvote_caid(er) && rc < E_NOTFOUND && cw && !chk_is_null_CW(cw) && !caid_is_biss(er->caid)) - { - if (cfg.cwvote_log_enabled) - { - cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer] CW Vote enabled, adding CW to vote pool from reader %s.", - (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader->label); - } - cw_vote_add(er, cw, reader); // Add to vote pool - ea->rc = E_UNHANDLED; // Mark as unhandled, waiting for vote decision - } - else - { - ea->rc = rc; // For non-CW answers or invalid CWs, set the actual RC - } - + ea->rc = rc; + ea->ecm_time = comp_timeb(&now, &ea->time_request_sent); + if(ea->ecm_time < 1) { ea->ecm_time = 1; } // set ecm_time 1 if answer immediately ea->rcEx = rcEx; - if(cw) { memcpy(ea->cw, cw, 16); } // Store the CW in ea->cw for voting + if(cw) { memcpy(ea->cw, cw, 16); } if(msglog) { memcpy(ea->msglog, msglog, MSGLOGSIZE); } ea->tier = used_cardtier; if(cw_ex) @@ -2745,9 +3038,7 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui if(!ea->is_pending) // not for pending ea - only once for ea { // cache update - // Skip check for BISS1 - cw could be indeed zero - // Skip check for BISS2 - we use the extended cw, so the "simple" cw is always zero - if(ea && (ea->rc < E_NOTFOUND) && (!chk_is_null_CW(ea->cw) && !caid_is_biss(er->caid))) + if(ea && (ea->rc < E_NOTFOUND) && !chk_is_null_CW(ea->cw)) { #ifdef CS_CACHEEX_AIO int32_t ecmtime = ea->ecm_time; @@ -2763,6 +3054,12 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui #endif } + if(!dontwriteStats) + { + // readers stats for LB + send_reader_stat(reader, er, ea, ea->rc); + } + // reader checks #ifdef WITH_DEBUG if(cs_dblevel & D_TRACE) @@ -2773,9 +3070,74 @@ int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, ui } #endif // Update reader stats: - // Old reader ECM counter updates removed - now handled before vote logic + if(ea->rc == E_FOUND) + { + if(cfg.cwlogdir != NULL) + { logCWtoFile(er, ea->cw); } // CWL logging only if cwlogdir is set in config - // reader checks + reader->ecmsok++; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + reader->ecmsoklg++; +#endif + reader->webif_ecmsok++; +#ifdef CS_CACHEEX + struct s_client *eacl = reader->client; + if(cacheex_reader(reader) && check_client(eacl)) + { + eacl->cwcacheexgot++; + cacheex_add_stats(eacl, ea->er->caid, ea->er->srvid, ea->er->prid, 1 +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + ); +#endif + first_client->cwcacheexgot++; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + eacl->cwcacheexgotlg++; + first_client->cwcacheexgotlg++; + } +#endif + } +#endif + } + else if(ea->rc == E_NOTFOUND) + { + reader->ecmsnok++; + reader->webif_ecmsnok++; + if(reader->ecmnotfoundlimit && reader->ecmsnok >= reader->ecmnotfoundlimit) + { + rdr_log(reader, "ECM not found limit reached %u. Restarting the reader.", + reader->ecmsnok); + reader->ecmsnok = 0; // Reset the variable + reader->ecmshealthnok = 0; // Reset the variable + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } + + // this fixes big oscam mistake + // wrong reader status on web info aka not counted timeouts which dispalyed + // reader info 100 percent OK but reader had a ton of unhandled timeouts! + else if(ea->rc == E_TIMEOUT) + { +#ifdef WITH_LB + STAT_QUERY q; + readerinfofix_get_stat_query(er, &q); + READER_STAT *s; + s = readerinfofix_get_add_stat(reader, &q); + if (s) + { + cs_log_dbg(D_LB, "inc fail {client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer] reader %s rc %d, ecm time %d ms (%d ms)", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader ? reader->label : "-", rc, ea->ecm_time, ntime); + readerinfofix_inc_fail(s); // now increase fail factor for unhandled timeouts + } +#endif + reader->ecmstout++; // now append timeouts to the readerinfo timeout count + reader->webif_ecmstout++; + } + + // Reader ECMs Health Try (by Pickser) if(reader->ecmsok != 0 || reader->ecmsnok != 0 || reader->ecmstout != 0) { reader->ecmshealthok = ((double) reader->ecmsok / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; @@ -2884,123 +3246,87 @@ void write_ecm_answer_fromcache(struct s_write_from_cache *wfc) if(ecm->from_csp) { er->csp_answered = 1; } // update er as answered by csp (csp have no group) #endif - if(er->rc >= E_NOTFOUND || er->rc == E_UNHANDLED) + if(er->rc >= E_NOTFOUND) { - // CW Vote: If enabled and CAID matches, add to vote pool instead of sending DCW directly - if (cfg.cwvote_enabled && is_cwvote_caid(er) && !chk_is_null_CW(ecm->cw) && !caid_is_biss(er->caid)) - { - // Use cacheex_src or from_csp directly if selected_reader is NULL - // This fixes the issue where virtual readers are NULL - struct s_reader *source_rdr = NULL; - if (ecm->selected_reader) { - source_rdr = ecm->selected_reader; +#ifdef CS_CACHEEX + if(ecm->cacheex_src) // from cacheex or csp + { + er->rc = E_CACHEEX; } + else +#endif + { er->rc=E_CACHE1; } // from normal readers - // Pass the actual cacheex source or csp info for voting - // Use ecm->cacheex_src or ecm->from_csp when virtual readers are not available - if (source_rdr || ecm->cacheex_src || ecm->from_csp) { - if (cfg.cwvote_log_enabled) { - const char *source_label = "unknown"; - if (source_rdr) { - source_label = source_rdr->label; - } else if (ecm->cacheex_src && check_client(ecm->cacheex_src) && ecm->cacheex_src->account) { - source_label = ecm->cacheex_src->account->usr; - } else if (ecm->from_csp) { - source_label = "CSP"; - } - cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer_fromcache] CW Vote enabled, adding CW from cache (source: %s) to vote pool.", - (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, source_label); - } - // Always copy cacheex_src and from_csp so cw_vote_decide can use it - if (ecm->cacheex_src) { - er->cacheex_src = ecm->cacheex_src; - } - if (ecm->from_csp) { - er->from_csp = ecm->from_csp; - } - cw_vote_add(er, ecm->cw, source_rdr); - // Keep er->rc as E_UNHANDLED to allow voting to proceed - // Do NOT call send_dcw here. It will be called by cw_vote_decide or ecm_timeout. - } else { - // Fallback if no source could be determined for voting - if (cfg.cwvote_log_enabled) { - cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer_fromcache] CW Vote enabled, but source for cache entry could not be determined. Sending DCW directly.", - (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); - } - send_dcw(er->client, er); - } + memcpy(er->cw, ecm->cw, 16); + +er->selected_reader = ecm->selected_reader; + +entropy_log_ecm(er, er->selected_reader); + +//////////////// AUTODETECT INVALID CW BLOCK //////////////////////////////////////////////// +if(!cw_autodetect_and_block(er->selected_reader, er)) +{ + return; +} +//////////////////////////////////////////////////////////////////////////////////////////// + +er->cw_count = ecm->cw_count; + +#ifdef CS_CACHEEX + // here we should be sure cex client has not been freed! + if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill) + { + er->cacheex_src = ecm->cacheex_src; + er->cwc_cycletime = ecm->cwc_cycletime; + er->cwc_next_cw_cycle = ecm->cwc_next_cw_cycle; } - else if (rc_orig == E_UNHANDLED) // Original condition for sending DCW if not voting + else { -#ifdef CS_CACHEEX - if(ecm->cacheex_src) // from cacheex or csp - { - er->rc = E_CACHEEX; - } - else -#endif - { er->rc=E_CACHE1; } // from normal readers + er->cacheex_src = NULL; + } - memcpy(er->cw, ecm->cw, 16); - er->selected_reader = ecm->selected_reader; - er->cw_count = ecm->cw_count; - -#ifdef CS_CACHEEX - // here we should be sure cex client has not been freed! - if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill) - { - er->cacheex_src = ecm->cacheex_src; - er->cwc_cycletime = ecm->cwc_cycletime; - er->cwc_next_cw_cycle = ecm->cwc_next_cw_cycle; - } - else - { - er->cacheex_src = NULL; - } - - int8_t cacheex = check_client(er->client) && er->client->account ? er->client->account->cacheex.mode : 0; - if(cacheex == 1 && check_client(er->client)) - { - cacheex_add_stats(er->client, er->caid, er->srvid, er->prid, 0 + int8_t cacheex = check_client(er->client) && er->client->account ? er->client->account->cacheex.mode : 0; + if(cacheex == 1 && check_client(er->client)) + { + cacheex_add_stats(er->client, er->caid, er->srvid, er->prid, 0 #ifdef CS_CACHEEX_AIO - , er->localgenerated); + , er->localgenerated); #else - ); + ); #endif - er->client->cwcacheexpush++; - if(er->client->account) - { er->client->account->cwcacheexpush++; } - first_client->cwcacheexpush++; + er->client->cwcacheexpush++; + if(er->client->account) + { er->client->account->cwcacheexpush++; } + first_client->cwcacheexpush++; #ifdef CS_CACHEEX_AIO - if(er->localgenerated) - { - er->client->cwcacheexpushlg++; - first_client->cwcacheexpushlg++; - } + if(er->localgenerated) + { + er->client->cwcacheexpushlg++; + first_client->cwcacheexpushlg++; + } #endif - } + } #endif #ifdef CS_CACHEEX - if(cfg.delay && cacheex!=1) // No delay on cacheexchange mode 1 client! - { cs_sleepms(cfg.delay); } + if(cfg.delay && cacheex!=1) // No delay on cacheexchange mode 1 client! + { cs_sleepms(cfg.delay); } #else - if(cfg.delay) - { cs_sleepms(cfg.delay); } + if(cfg.delay) + { cs_sleepms(cfg.delay); } #endif - if(rc_orig == E_UNHANDLED) - { - cs_log_dbg(D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer_fromcache] found cw in CACHE (count %d)!", (check_client(er->client)?er->client->account->usr:"-"),er->caid, er->prid, er->srvid, + if(rc_orig == E_UNHANDLED) + { + cs_log_dbg(D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer_fromcache] found cw in CACHE (count %d)!", (check_client(er->client)?er->client->account->usr:"-"),er->caid, er->prid, er->srvid, #ifdef CS_CACHEEX_AIO - (er->cw_count > 0x0F000000) ? er->cw_count ^= 0x0F000000 : er->cw_count); + (er->cw_count > 0x0F000000) ? er->cw_count ^= 0x0F000000 : er->cw_count); #else - er->cw_count); + er->cw_count); #endif - send_dcw(er->client, er); - } + send_dcw(er->client, er); } } } @@ -3379,9 +3705,7 @@ void get_cw(struct s_client *client, ECM_REQUEST *er) } // Check for odd/even byte - // Don't check for BISS1 and BISS2 mode 1/E or fake caid (ECM is fake for them) - // Don't check for BISS2 mode CA (ECM table is always 0x80) - if(!caid_is_biss(er->caid) && !caid_is_fake(er->caid) && get_odd_even(er) == 0) + if(get_odd_even(er) == 0) { cs_log_dbg(D_TRACE, "warning: ecm with null odd/even byte from %s", (check_client(er->client) ? er->client->account->usr : "-")); er->rc = E_INVALID; @@ -3395,28 +3719,6 @@ void get_cw(struct s_client *client, ECM_REQUEST *er) return; } - // CW Vote: If enabled and CAID matches, keep as E_UNHANDLED to allow voting - if (cfg.cwvote_enabled && is_cwvote_caid(er)) - { - if (cfg.cwvote_log_enabled) - { - cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] CW Vote enabled for this CAID, keeping ECM as E_UNHANDLED for voting. Logging enabled.", - (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); - } - else - { - cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] CW Vote enabled for this CAID, keeping ECM as E_UNHANDLED for voting. Logging disabled.", - (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); - } - er->rc = E_UNHANDLED; // Ensure it's unhandled for voting - } - else if (er->rc < E_UNHANDLED) // If not for voting, and already an error, send it - { - send_dcw(client, er); - free_ecm(er); - return; - } - #ifdef CS_CACHEEX int8_t cacheex = client->account ? client->account->cacheex.mode : 0; @@ -3914,345 +4216,3 @@ int32_t format_ecm(ECM_REQUEST *ecm, char *result, size_t size) return ecmfmt(result, size, ecm->caid, ecm->onid, ecm->prid, ecm->chid, ecm->pid, ecm->srvid, ecm->ecmlen, ecmd5hex, csphash, cwhex, 0, ((ecm->selected_reader && ecm->selected_reader->currenthops) ? ecm->selected_reader->currenthops : 0), payload, tier); } - -/* ========================= - * Ai Fake Dcw Detector - * ========================= */ - -int cw_vote_add(struct ecm_request_t *er, uint8_t *cw, struct s_reader *rdr) -{ - int i, free_idx = -1; - int is_local = 0; - int is_cacheex = 0; - char cw_hex[33]; - const char *source_label = "unknown"; - - // Flaga czy sprawdzać duplikaty źródła - // Dla cacheex/CSP nie sprawdzamy duplikatów - każde źródło cacheex/csp jest traktowane jako unikalne - int check_source_duplicates = 1; - - if (rdr) { - source_label = rdr->label; - // Wirtualne czytniki nie są lokalne - to jest cacheex lub CSP - if (rdr == virtual_cacheex_reader || rdr == virtual_csp_reader) { - is_local = 0; - is_cacheex = 1; - // Dla wirtualnych readerów nie sprawdzamy duplikatów - każdy cache jest unikalny - check_source_duplicates = 0; - } else { - is_local = is_localreader(rdr, er); - is_cacheex = cacheex_reader(rdr) ? 1 : 0; - } - // Dodatkowe sprawdzenie: jeśli er->cacheex_src lub er->from_csp jest ustawione, - // to CW pochodzi z CacheEX, nawet jeśli selected_reader nie jest CacheEX readerem - if (er->cacheex_src || er->from_csp) { - is_cacheex = 1; - check_source_duplicates = 0; - } - } else if (er->cacheex_src) { - is_cacheex = 1; - if (check_client(er->cacheex_src) && er->cacheex_src->account) { - source_label = er->cacheex_src->account->usr; - } else { - source_label = "CACHEEX_CLIENT"; - } - // Dla cacheex_src nie sprawdzamy duplikatów - check_source_duplicates = 0; - } else if (er->from_csp) { - is_cacheex = 1; - source_label = "CSP"; - // Dla CSP nie sprawdzamy duplikatów - check_source_duplicates = 0; - } - - - cs_hexdump(0, cw, 16, cw_hex, sizeof(cw_hex)); - - -// Zarządzanie sesjami głosowania - używamy er->tps.time jako identyfikatora sesji ECM -// Zamiast wyczyścić całą pulę, sprawdzamy czy sesja się zmieniła i wtedy resetujemy tylko wtedy -if (er->vote_pool_session != er->tps.time) { - // Sesja się zmieniła - resetujemy pulę głosowania - memset(er->vote_pool, 0, sizeof(er->vote_pool)); - er->vote_pool_session = er->tps.time; -} - - // Logowanie wyłączone - odkomentuj jeśli potrzebne - // if (cfg.cwvote_log_enabled) { - // cs_log("[Ai_vote_add] Adding CW from client %s, source %s (CAID: %04X, PRID: %06X, SRVID: %04X). CW: %s", - // (check_client(er->client) ? er->client->account->usr : "-"), - // source_label, er->caid, er->prid, er->srvid, cw_hex); - // } - - int max_cand = cfg.cwvote_max_candidates; - // Używamy konsekwentnie MIN(max_cand, MAX_VOTE_CANDIDATES) aby uniknąć overflow - int max_iter = (max_cand < MAX_VOTE_CANDIDATES) ? max_cand : MAX_VOTE_CANDIDATES; - - // Sprawdź czy wpis jest poprawnie zainicjalizowany (votes > 0) i w granicach tablicy - // Porównujemy cały CW (16 bajtów), nie tylko compare_len - for (i = 0; i < max_iter; i++) { - if (er->vote_pool[i].votes == 0 && free_idx < 0) - free_idx = i; - - if (er->vote_pool[i].votes > 0) { - int cmp = memcmp(er->vote_pool[i].cw, cw, 16); - - if (cmp == 0) { - // Sprawdź unikalność źródła przed agregacją głosów - // Zapobiega wielokrotnemu głosowaniu z tego samego readera dla tego samego CW - // Dla cacheex/CSP (check_source_duplicates = 0) pomijamy to sprawdzenie - int duplicate_source = 0; - - if (check_source_duplicates) { - int j; - for (j = 0; j < er->vote_pool[i].votes; j++) { - if (er->vote_pool[i].voters[j] == rdr) { - duplicate_source = 1; - break; - } - } - } - - if (duplicate_source) { - // Źródło już głosowało dla tego CW - ignoruj - if (cfg.cwvote_log_enabled) { - cs_log("[Ai_vote_add] Duplicate source %s for same CW - ignoring", source_label); - } - return 0; - } - - // CW już istnieje w puli - agreguj głosy niezależnie od źródła - // To pozwala na agregację głosów z różnych cacheex peerów dla tego samego CW - if (er->vote_pool[i].votes < MAX_VOTE_CANDIDATES) { - er->vote_pool[i].voters[er->vote_pool[i].votes] = rdr; - er->vote_pool[i].votes++; - if (is_local) er->vote_pool[i].local_votes++; - // FIX #2: Zapisz flagę czy głos pochodzi z cacheex - if (is_cacheex) er->vote_pool[i].has_cacheex_vote = 1; - } - // cs_log("[Ai_vote_add] Aggregated vote for existing CW → Votes: %d (local: %d) from %s", - // er->vote_pool[i].votes, er->vote_pool[i].local_votes, source_label); - return 0; - } - } - } - - if (free_idx < 0) { - // cs_log("[Ai_vote_add] Voting pool full!"); - return -1; - } - - // Nowy CW - memcpy(er->vote_pool[free_idx].cw, cw, 16); - er->vote_pool[free_idx].votes = 1; - er->vote_pool[free_idx].local_votes = is_local ? 1 : 0; - er->vote_pool[free_idx].has_cacheex_vote = is_cacheex ? 1 : 0; - // Store the actual reader if available, otherwise NULL - er->vote_pool[free_idx].voters[0] = rdr; - - if (cfg.cwvote_log_enabled) { - cs_log("[Ai_vote_add] New CW in slot %d → Votes: %d (local: %d) from %s", - free_idx, 1, er->vote_pool[free_idx].local_votes, source_label); - } - - return 0; -} - -int cw_vote_decide(struct ecm_request_t *er) -{ - if (!cfg.cwvote_enabled) - return 0; - - int i, total_votes = 0, best = -1; - int best_score = -1; - char cw_hex[33]; - struct timeb now_tb; - cs_ftime(&now_tb); // aktualny czas - - // Logowanie wyłączone - odkomentuj jeśli potrzebne - // if (cfg.cwvote_log_enabled) { - // cs_log("[Ai_vote_decide] Starting vote for client %s (CAID: %04X, SRVID: %04X)", - // (check_client(er->client) ? er->client->account->usr : "-"), - // er->caid, er->srvid); - // } - - int max_cand = cfg.cwvote_max_candidates; - float local_weight = cfg.cwvote_local_weight; - int min_votes = cfg.cwvote_min_votes; - int timeout = cfg.cwvote_timeout; - int fallback = cfg.cwvote_fallback; - - // Liczymy total_votes i logujemy kandydatów - for (i = 0; i < max_cand; i++) { - if (er->vote_pool[i].votes > 0) { - total_votes += er->vote_pool[i].votes; - - if (cfg.cwvote_log_enabled) { - cs_hexdump(0, er->vote_pool[i].cw, 16, cw_hex, sizeof(cw_hex)); - cs_log("[Ai_vote_decide] Candidate %d: CW: %s | Votes: %d (local: %d)", - i, cw_hex, er->vote_pool[i].votes, er->vote_pool[i].local_votes); - } - } - } - - // Logowanie podsumowania - tylko gdy są jakieś głosy - if (cfg.cwvote_log_enabled && total_votes > 0) { - cs_log("[Ai_vote_decide] Total votes: %d | Min required: %d | Timeout: %d ms", - total_votes, min_votes, timeout); - } - - // Szukamy najlepszego (z effective score) - MUSI być przed sprawdzeniem pojedynczego kandydata - for (i = 0; i < max_cand; i++) { - if (er->vote_pool[i].votes == 0) continue; - - int effective_score = er->vote_pool[i].votes + - (int)(er->vote_pool[i].local_votes * local_weight); - - if (effective_score > best_score) { - best_score = effective_score; - best = i; - } - } - - // Sprawdź czy jest tylko jeden kandydat (jeden unikalny CW) - // Jeśli timeout == 0, przyjmij od razu - nie ma sensu czekać - // Jeśli timeout > 0, czekaj - może przyjdą inne klucze - if (total_votes == 1 && best >= 0 && timeout == 0) { - memcpy(er->cw, er->vote_pool[best].cw, 16); - - // Update cacheex hit stats if the winning CW came from cacheex - // FIX #2: Używamy flagi has_cacheex_vote zamiast er->cacheex_src - if (cfg.cwvote_enabled && er->vote_pool[best].has_cacheex_vote) { - struct s_client *src_cl = er->cacheex_src; - if (src_cl && src_cl->cwcacheexhit >= 0) { - src_cl->cwcacheexhit++; - if (src_cl->account) { - src_cl->account->cwcacheexhit++; - } - first_client->cwcacheexhit++; - } - } - - if (cfg.cwvote_log_enabled) { - cs_hexdump(0, er->cw, 16, cw_hex, sizeof(cw_hex)); - cs_log("[Ai_vote_decide] SINGLE CANDIDATE → Accepting CW: %s | Votes: %d (local: %d)", - cw_hex, er->vote_pool[best].votes, er->vote_pool[best].local_votes); - } - return 1; - } - - // Za mało głosów - if (total_votes < min_votes) { - // Jeśli timeout minął, zaakceptuj pojedynczy głos jako fallback - if (timeout > 0 && comp_timeb(&now_tb, &er->tps) >= timeout) { - if (cfg.cwvote_log_enabled) - cs_log("[Ai_vote_decide] Timeout reached with too few votes (%d/%d) → accepting single candidate as fallback", total_votes, min_votes); - - if (best >= 0) { - memcpy(er->cw, er->vote_pool[best].cw, 16); - - // FIX #2: Używamy flagi has_cacheex_vote zamiast er->cacheex_src - if (cfg.cwvote_enabled && er->vote_pool[best].has_cacheex_vote) { - struct s_client *src_cl = er->cacheex_src; - if (src_cl && src_cl->cwcacheexhit >= 0) { - src_cl->cwcacheexhit++; - if (src_cl->account) { - src_cl->account->cwcacheexhit++; - } - first_client->cwcacheexhit++; - } - } - - if (cfg.cwvote_log_enabled) { - cs_hexdump(0, er->cw, 16, cw_hex, sizeof(cw_hex)); - cs_log("[Ai_vote_decide] SINGLE CANDIDATE (fallback) → Accepting CW: %s | Votes: %d (local: %d)", - cw_hex, er->vote_pool[best].votes, er->vote_pool[best].local_votes); - } - return 1; - } - } - return 0; - } - - if (best < 0) - return 0; - - int majority_needed = total_votes / 2 + 1; // > 50% - - bool has_majority = er->vote_pool[best].votes >= majority_needed; - bool timeout_reached = (timeout > 0 && comp_timeb(&now_tb, &er->tps) >= timeout); - - if (has_majority || (timeout_reached && fallback == 1)) { - memcpy(er->cw, er->vote_pool[best].cw, 16); - - // Update cacheex hit stats if the winning CW came from cacheex - // FIX #2: Używamy flagi has_cacheex_vote zamiast er->cacheex_src - if (cfg.cwvote_enabled && er->vote_pool[best].has_cacheex_vote) { - struct s_client *src_cl = er->cacheex_src; - if (src_cl && src_cl->cwcacheexhit >= 0) { - src_cl->cwcacheexhit++; - if (src_cl->account) { - src_cl->account->cwcacheexhit++; - } - first_client->cwcacheexhit++; - } - } - - if (cfg.cwvote_log_enabled) { - cs_hexdump(0, er->cw, 16, cw_hex, sizeof(cw_hex)); - cs_log("[Ai_vote_decide] WINNER → CW: %s | Votes: %d (local: %d) | Effective: %d", - cw_hex, er->vote_pool[best].votes, er->vote_pool[best].local_votes, best_score); - } - return 1; // decyzja podjęta - } - - if (timeout_reached && fallback == 2) { - // fallback = weź pierwszy CW (slot 0) - if (er->vote_pool[0].votes > 0) { - memcpy(er->cw, er->vote_pool[0].cw, 16); - - // Update cacheex hit stats if the winning CW came from cacheex - if (cfg.cwvote_enabled && er->cacheex_src) { - struct s_client *src_cl = er->cacheex_src; - if (src_cl && src_cl->cwcacheexhit >= 0) { - src_cl->cwcacheexhit++; - if (src_cl->account) { - src_cl->account->cwcacheexhit++; - } - first_client->cwcacheexhit++; - } - } - - if (cfg.cwvote_log_enabled) cs_log("[Ai_vote_decide] Fallback: taking first CW"); - return 1; - } - } - - if (cfg.cwvote_log_enabled) - cs_log("[Ai_vote_decide] No clear winner yet... waiting"); - - return 0; -} - -/* ========================= - * Ai VOTING HELPER FUNCTIONS - * ========================= */ - -int32_t is_cwvote_caid(ECM_REQUEST *er) -{ - if (!cfg.cwvote_caids.cvcnum) // If no CAIDs are specified, it applies to all CAIDs - { - return 1; - } - - for (int32_t i = 0; i < cfg.cwvote_caids.cvcnum; i++) - { - if (cfg.cwvote_caids.cvcdata[i].caid == er->caid) - { - return 1; - } - } - return 0; -} - -/* ===== END CW VOTING ===== */ diff --git a/oscam-ecm.c.bak b/oscam-ecm.c.bak new file mode 100644 index 0000000..b542d76 --- /dev/null +++ b/oscam-ecm.c.bak @@ -0,0 +1,4167 @@ +#define MODULE_LOG_PREFIX "ecm" + +#include "globals.h" +#include +// ---------- ENTROPY FUNCTIONS START ---------- + +static double entropy_calc(const uint8_t *data, int len) +{ + if(!data || len <= 0) + return 0.0; + + int freq[256] = {0}; + + for(int i = 0; i < len; i++) + freq[data[i]]++; + + double entropy = 0.0; + + for(int i = 0; i < 256; i++) + { + if(freq[i] == 0) + continue; + + double p = (double)freq[i] / len; + entropy -= p * (log(p) / log(2)); + } + + return entropy; +} + +static void entropy_log_ecm(ECM_REQUEST *er, struct s_reader *reader) +{ + if(!er) + return; + + double full = entropy_calc(er->cw, 16); + double half1 = entropy_calc(er->cw, 8); + double half2 = entropy_calc(er->cw + 8, 8); + + cs_log_dbg(D_TRACE, + "ENTROPY reader=%s caid=%04X FULL=%.4f HALF1=%.4f HALF2=%.4f", + reader ? reader->label : "?", + er->caid, + full, + half1, + half2); +} + +// ---------- ENTROPY FUNCTIONS END ---------- +#define ENTROPY_THRESHOLD_DEFAULT 3.0 +#define ENTROPY_THRESHOLD_NDS 2.3 + +#include "cscrypt/md5.h" +#include "module-anticasc.h" +#include "module-cacheex.h" +#include "module-led.h" +#include "module-stat.h" +#include "module-webif.h" +#include "module-cw-cycle-check.h" +#include "module-gbox.h" +#include "oscam-cache.h" +#include "oscam-chk.h" +#include "oscam-client.h" +#include "oscam-config.h" +#include "oscam-ecm.h" +#include "oscam-garbage.h" +#include "oscam-failban.h" +#include "oscam-net.h" +#include "oscam-time.h" +#include "oscam-lock.h" +#include "oscam-string.h" +#include "oscam-work.h" +#include "reader-common.h" +#include "module-cccam-data.h" +#ifdef CS_CACHEEX_AIO +#include "oscam-hashtable.h" +#endif + +// ADD THIS LINE: +static int cw_checksum_ok(uint8_t *cw); + +///////////////////////////////////////////////////////////// +// AUTODETECT STRUCT AND TABLE (ADD THIS HERE) +///////////////////////////////////////////////////////////// + +#define CW_DETECT_MAX 8192 +#define CW_LEARN_THRESHOLD 5 + +typedef struct +{ + uint16_t caid; + uint32_t prid; + uint16_t srvid; + + uint8_t is64; + uint8_t is48; + + uint8_t learn64_count; + uint8_t learn48_count; + + uint8_t strict_logged; + +} cw_detect_entry; + + +static cw_detect_entry cw_detect_table[CW_DETECT_MAX]; + + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// GET OR CREATE ENTRY +////////////////////////////////////////////////////////////////////////////////////////////////////// + +static cw_detect_entry *cw_detect_get(ECM_REQUEST *er) +{ + int i; + + // search existing entry + for(i = 0; i < CW_DETECT_MAX; i++) + { + if(cw_detect_table[i].caid == er->caid && + cw_detect_table[i].prid == er->prid && + cw_detect_table[i].srvid == er->srvid) + { + return &cw_detect_table[i]; + } + } + + // create new entry + for(i = 0; i < CW_DETECT_MAX; i++) + { + if(cw_detect_table[i].caid == 0) + { + cw_detect_table[i].caid = er->caid; + cw_detect_table[i].prid = er->prid; + cw_detect_table[i].srvid = er->srvid; + + cw_detect_table[i].is64 = 0; + cw_detect_table[i].is48 = 0; + + cw_detect_table[i].learn64_count = 0; + cw_detect_table[i].learn48_count = 0; + + cw_detect_table[i].strict_logged = 0; + + return &cw_detect_table[i]; + } + } + + return &cw_detect_table[0]; +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +// CW CHECKSUM FUNCTION (ADD THIS BELOW cw_detect_get) +/////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static int cw_checksum_ok(uint8_t *cw) +{ + if(((cw[0]+cw[1]+cw[2])&0xFF)!=cw[3]) return 0; + if(((cw[4]+cw[5]+cw[6])&0xFF)!=cw[7]) return 0; + if(((cw[8]+cw[9]+cw[10])&0xFF)!=cw[11]) return 0; + if(((cw[12]+cw[13]+cw[14])&0xFF)!=cw[15]) return 0; + return 1; +} +/////////////////////////////////////////////////////////////////////////////////////////////////////////// +extern CS_MUTEX_LOCK ecmcache_lock; +extern struct ecm_request_t *ecmcwcache; +extern uint32_t ecmcwcache_size; +extern int32_t exit_oscam; + +extern CS_MUTEX_LOCK ecm_pushed_deleted_lock; +extern struct ecm_request_t *ecm_pushed_deleted; + +static pthread_mutex_t cw_process_sleep_cond_mutex; +static pthread_cond_t cw_process_sleep_cond; +static int cw_process_wakeups; +int64_t ecmc_next, cache_next, msec_wait = 3000; + +#ifdef CS_CACHEEX_AIO +// ecm-cache +typedef struct ecm_cache +{ + struct timeb first_recv_time;// time of first cw received + struct timeb upd_time; // updated time. Update time at each cw got + uint32_t csp_hash; + node ht_node; + node ll_node; +} ECM_CACHE; + +static pthread_rwlock_t ecm_cache_lock; +static hash_table ht_ecm_cache; +static list ll_ecm_cache; +static int8_t ecm_cache_init_done = 0; + +void free_ecm_cache(void) +{ + deinitialize_hash_table(&ht_ecm_cache); + pthread_rwlock_destroy(&ecm_cache_lock); +} + +void init_ecm_cache(void) +{ +#ifdef CS_CACHEEX + if(cfg.cw_cache_size > 0 || cfg.cw_cache_memory > 0) + { + init_hash_table(&ht_ecm_cache, &ll_ecm_cache); + if (pthread_rwlock_init(&ecm_cache_lock,NULL) != 0) + { cs_log("Error creating lock ecm_cache_lock!"); } + else + { ecm_cache_init_done = 1; } + } +#endif +} + +static uint8_t time_sort(ECM_CACHE *a, ECM_CACHE *b) +{ + if (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) == ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) return 0; + return (((int64_t)(a->upd_time.time) * 1000ull + (int64_t) a->upd_time.millitm) > ((int64_t)(b->upd_time.time) * 1000ull + (int64_t) b->upd_time.millitm)) ? -1 : 1; +} + +static int compare_csp_hash_ecmcache(const void *arg, const void *obj) +{ + uint32_t h = ((const ECM_CACHE*)obj)->csp_hash; + return memcmp(arg, &h, 4); +} + +void ecm_cache_cleanup(bool force) +{ + if(!ecm_cache_init_done) + { return; } + + SAFE_RWLOCK_WRLOCK(&ecm_cache_lock); + + ECM_CACHE *ecm_cache; + node *i, *i_next; + uint32_t ll_c = 0; + uint32_t ll_ten_percent = (uint)tommy_list_count(&ll_ecm_cache)*0.1; // 10 percent of cache + + if(!force) + sort_list(&ll_ecm_cache, time_sort); + + i = get_first_node_list(&ll_ecm_cache); + while(i) + { + i_next = i->next; + + ecm_cache = get_data_from_node(i); + + if(!ecm_cache) + { + i = i_next; + continue; + } + if(!force) + { + ++ll_c; + + if(ll_c < ll_ten_percent) + { + remove_elem_list(&ll_ecm_cache, &ecm_cache->ll_node); + remove_elem_hash_table(&ht_ecm_cache, &ecm_cache->ht_node); + NULLFREE(ecm_cache); + } + else{ + break; + } + } + else{ + remove_elem_list(&ll_ecm_cache, &ecm_cache->ll_node); + remove_elem_hash_table(&ht_ecm_cache, &ecm_cache->ht_node); + NULLFREE(ecm_cache); + } + i = i_next; + } + + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); +} +#endif + +/** + * maxparallel - Helper: Calculate pending slots array size + * Formula: round(maxparallel * parallelfactor) + * parallelfactor <= 0 means no pending slots (zapping disabled) + */ +static inline int32_t get_pending_size(struct s_reader *rdr) +{ + // parallelfactor <= 0 means no pending slots (disabled or not configured) + if(rdr->parallelfactor <= 0.0f) + return 0; + int32_t size = (int32_t)(rdr->maxparallel * rdr->parallelfactor + 0.5f); + return (size < 1) ? 1 : size; +} + +/** + * maxparallel - Helper: Check if slot is expired + * Returns timeout threshold in ms, 0 if no timeout check needed + */ +static int32_t get_slot_timeout(struct s_parallel_slot *slot, int32_t paralleltimeout) +{ + if(slot->ecm_interval > 0) + return slot->ecm_interval + paralleltimeout; + else + return 10000 + paralleltimeout; // Default 10s if no interval measured +} + +/** + * maxparallel - Helper: Clear a slot + */ +static void clear_slot(struct s_parallel_slot *slot) +{ + slot->srvid = 0; + slot->ecm_interval = 0; + slot->client = NULL; + memset(&slot->last_ecm, 0, sizeof(struct timeb)); +} + +/** + * maxparallel - Helper: Add client+service to blocked list + * Replaces any existing entry for the same client + */ +static void block_client_service(struct s_reader *rdr, struct s_client *client, uint16_t srvid) +{ + if(!rdr || !client || !rdr->blocked_services) + return; + + // Check if client already has a blocked entry - if so, update it + LL_ITER it = ll_iter_create(rdr->blocked_services); + struct s_blocked_client *bc; + while((bc = ll_iter_next(&it))) + { + if(bc->client == client) + { + bc->srvid = srvid; + return; + } + } + + // Add new entry + if(!cs_malloc(&bc, sizeof(struct s_blocked_client))) + return; + bc->client = client; + bc->srvid = srvid; + ll_append(rdr->blocked_services, bc); +} + +/** + * maxparallel - Helper: Check if client+service is blocked + * Returns: 1 = blocked, 0 = not blocked + */ +static int8_t is_client_blocked(struct s_reader *rdr, struct s_client *client, uint16_t srvid) +{ + if(!rdr || !client || !rdr->blocked_services) + return 0; + + LL_ITER it = ll_iter_create(rdr->blocked_services); + struct s_blocked_client *bc; + while((bc = ll_iter_next(&it))) + { + if(bc->client == client && bc->srvid == srvid) + return 1; + } + return 0; +} + +/** + * maxparallel - Helper: Remove block for client if zapping to different service + * Only unblocks if the new service is DIFFERENT from the blocked one + */ +static void unblock_client_if_different(struct s_reader *rdr, struct s_client *client, uint16_t new_srvid) +{ + if(!rdr || !client || !rdr->blocked_services) + return; + + LL_ITER it = ll_iter_create(rdr->blocked_services); + struct s_blocked_client *bc; + while((bc = ll_iter_next(&it))) + { + if(bc->client == client) + { + // Only unblock if zapping to a DIFFERENT service + if(bc->srvid != new_srvid) + { + ll_iter_remove_data(&it); + } + return; + } + } +} + +/** + * maxparallel - Helper: Clear all blocked entries (when active slot becomes free) + */ +static void clear_blocked_services(struct s_reader *rdr) +{ + if(!rdr || !rdr->blocked_services) + return; + + ll_clear_data(rdr->blocked_services); +} + +/** + * maxparallel - Cleanup expired slots in both arrays and promote pending + * - Removes expired slots from parallel_slots (active) and parallel_slots_pending (pending) + * - Upgrades pending to active when space becomes available (FIFO) + * - Does NOT drop pending (that's done separately when active services send ECMs) + * MUST be called with parallel_lock held + * Returns: count of active slots after cleanup + */ +static int32_t reader_cleanup_slots_nolock(struct s_reader *rdr, struct timeb *now) +{ + if(!rdr || rdr->maxparallel <= 0) + return 0; + + int32_t i; + int32_t active_count = 0; + int32_t pending_count = 0; + int32_t free_active = -1; // First empty slot in active array + + // Pass 1: Clean expired slots in active array, count active + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == 0) + { + if(free_active < 0) + free_active = i; + continue; + } + + int64_t gone = comp_timeb(now, &rdr->parallel_slots[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots[i], rdr->paralleltimeout); + + if(gone > timeout) + { + cs_log_dbg(D_READER, "reader %s: service %04X expired (no ECM for %"PRId64" ms, timeout %d ms)", + rdr->label, rdr->parallel_slots[i].srvid, gone, timeout); + clear_slot(&rdr->parallel_slots[i]); + if(free_active < 0) + free_active = i; + } + else + { + active_count++; + } + } + + // Pass 2: Clean expired slots in pending array, count active + int32_t pending_size = get_pending_size(rdr); + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid == 0) + continue; + + int64_t gone = comp_timeb(now, &rdr->parallel_slots_prov[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots_prov[i], rdr->paralleltimeout); + + if(gone > timeout) + { + cs_log_dbg(D_READER, "reader %s: pending service %04X expired (no ECM for %"PRId64" ms, timeout %d ms)", + rdr->label, rdr->parallel_slots_prov[i].srvid, gone, timeout); + clear_slot(&rdr->parallel_slots_prov[i]); + } + else + { + pending_count++; + } + } + + // Pass 3: Upgrade pending to active if space available (FIFO) + // This handles the zapping case: old service expired, new one can be promoted + while(active_count < rdr->maxparallel && pending_count > 0) + { + // Find oldest pending (largest age = first to arrive = FIFO upgrade) + int32_t oldest_idx = -1; + int64_t oldest_time = -1; + + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + { + int64_t age = comp_timeb(now, &rdr->parallel_slots_prov[i].last_ecm); + if(oldest_idx < 0 || age > oldest_time) + { + oldest_idx = i; + oldest_time = age; + } + } + } + + if(oldest_idx < 0) + break; + + // Find empty slot in active array + if(free_active < 0) + { + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == 0) + { + free_active = i; + break; + } + } + } + + if(free_active < 0) + break; // Should not happen, but safety check + + // Move pending to active + rdr->parallel_slots[free_active] = rdr->parallel_slots_prov[oldest_idx]; + clear_slot(&rdr->parallel_slots_prov[oldest_idx]); + + cs_log_dbg(D_READER, "reader %s: service %04X promoted from pending to active (slot %d)", + rdr->label, rdr->parallel_slots[free_active].srvid, free_active); + + active_count++; + pending_count--; + free_active = -1; // Need to find next empty slot + } + + // If active slots are now available, clear blocked list + // This allows previously blocked clients to try again + if(active_count < rdr->maxparallel) + { + clear_blocked_services(rdr); + } + + return active_count; +} + +/** + * maxparallel - Drop pending services when active array is full (FIFO) + * Called only when an ACTIVE service receives an ECM, proving it's still active. + * Drops the OLDEST pending first (first in, first out) + * This is fair: first to request overload is first to be denied. + * Dropped clients are added to the blocked list so they fall over to other readers. + * MUST be called with parallel_lock held + */ +static void reader_drop_pending_nolock(struct s_reader *rdr, struct timeb *now) +{ + if(!rdr || rdr->maxparallel <= 0) + return; + + // Count active and pending + int32_t i; + int32_t active_count = 0; + int32_t pending_count = 0; + int32_t pending_size = get_pending_size(rdr); + + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid != 0) + active_count++; + } + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + pending_count++; + } + + // Drop pending if active array is full (FIFO - oldest dropped first) + while(active_count >= rdr->maxparallel && pending_count > 0) + { + // Find oldest pending (largest age = first to arrive = drop first) + int32_t oldest_idx = -1; + int64_t oldest_time = -1; + + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + { + int64_t age = comp_timeb(now, &rdr->parallel_slots_prov[i].last_ecm); + if(oldest_idx < 0 || age > oldest_time) + { + oldest_idx = i; + oldest_time = age; + } + } + } + + if(oldest_idx < 0) + break; + + // Add client to blocked list before clearing slot + struct s_parallel_slot *slot = &rdr->parallel_slots_prov[oldest_idx]; + if(slot->client) + { + block_client_service(rdr, slot->client, slot->srvid); + } + + cs_log("reader %s: dropped pending service %04X (%.3f sec old, active services still running)", + rdr->label, slot->srvid, (float)oldest_time / 1000); + clear_slot(slot); + pending_count--; + } +} + +/** + * maxparallel - Check if reader has capacity for a service (does NOT reserve slot) + * Used during ECM request to decide if reader should be tried. + * Also checks if client+service is blocked (was dropped earlier). + * Returns: 1 = has capacity (or service already registered), 0 = full or blocked (skip reader) + */ +static int8_t reader_has_capacity(struct s_reader *rdr, uint16_t srvid, struct s_client *client) +{ + if(!rdr || rdr->maxparallel <= 0) + return 1; // unlimited + + cs_readlock(__func__, &rdr->parallel_lock); + + struct timeb now; + cs_ftime(&now); + + int32_t i; + int32_t pending_size = get_pending_size(rdr); + int8_t result = 0; + + // Check if client+service is blocked (was dropped earlier) + if(is_client_blocked(rdr, client, srvid)) + { + result = 0; + goto done; + } + + // Check if service already registered in active array + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == srvid) + { + result = 1; + goto done; + } + } + + // Check if service already registered in pending array + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid == srvid) + { + result = 1; + goto done; + } + } + + // Count active slots (with expiry check) + int32_t active_count = 0; + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid != 0) + { + int64_t gone = comp_timeb(&now, &rdr->parallel_slots[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots[i], rdr->paralleltimeout); + if(gone <= timeout) + active_count++; + } + } + + // Space in active array? + if(active_count < rdr->maxparallel) + { + result = 1; + goto done; + } + + // Space in pending array? + if(pending_size > 0) + { + int32_t pending_count = 0; + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + { + int64_t gone = comp_timeb(&now, &rdr->parallel_slots_prov[i].last_ecm); + int32_t timeout = get_slot_timeout(&rdr->parallel_slots_prov[i], rdr->paralleltimeout); + if(gone <= timeout) + pending_count++; + } + } + if(pending_count < pending_size) + { + result = 1; + goto done; + } + } + + // No capacity available + result = 0; + +done: + cs_readunlock(__func__, &rdr->parallel_lock); + return result; +} + +/** + * maxparallel - Register a service on a reader (called when CW is found) + * Uses dual-array architecture: + * - parallel_slots: active services (the limit) + * - parallel_slots_pending: pending services during zapping + * New services go to active if space available, otherwise pending. + * Returns: 1 = registered, 0 = failed + */ +static int8_t reader_register_service(struct s_reader *rdr, uint16_t srvid, struct s_client *client) +{ + if(!rdr || rdr->maxparallel <= 0) + return 1; // unlimited, always OK + + cs_writelock(__func__, &rdr->parallel_lock); + + // If client is registering a DIFFERENT service, unblock them + // (they zapped away from the blocked service) + unblock_client_if_different(rdr, client, srvid); + + struct timeb now; + cs_ftime(&now); + + // Cleanup expired slots and handle promotions/drops + int32_t active_count = reader_cleanup_slots_nolock(rdr, &now); + + int32_t i; + int32_t pending_size = get_pending_size(rdr); + + // Search for existing slot in both arrays + int32_t existing_active = -1; + int32_t existing_pending = -1; + int32_t free_active = -1; + int32_t free_pending = -1; + + // Search active array + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid == srvid) + existing_active = i; + else if(free_active < 0 && rdr->parallel_slots[i].srvid == 0) + free_active = i; + } + + // Search pending array + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid == srvid) + existing_pending = i; + else if(free_pending < 0 && rdr->parallel_slots_prov[i].srvid == 0) + free_pending = i; + } + + struct s_parallel_slot *slot = NULL; + int8_t is_new = 0; + int8_t is_pending = 0; + int32_t slot_idx = -1; + const char *array_name = "active"; + + if(existing_active >= 0) + { + // Already in active array - just update + slot = &rdr->parallel_slots[existing_active]; + slot_idx = existing_active; + is_new = 0; + } + else if(existing_pending >= 0) + { + // Already in pending array - update there + slot = &rdr->parallel_slots_prov[existing_pending]; + slot_idx = existing_pending; + is_new = 0; + is_pending = 1; + array_name = "pending"; + } + else if(active_count < rdr->maxparallel && free_active >= 0) + { + // New service, space in active array + slot = &rdr->parallel_slots[free_active]; + slot_idx = free_active; + slot->srvid = srvid; + slot->ecm_interval = 0; + slot->client = client; + is_new = 1; + } + else if(free_pending >= 0) + { + // New service, active full -> put in pending + slot = &rdr->parallel_slots_prov[free_pending]; + slot_idx = free_pending; + slot->srvid = srvid; + slot->ecm_interval = 0; + slot->client = client; + is_new = 1; + is_pending = 1; + array_name = "pending"; + } + else + { + // No slot available in either array + cs_writeunlock(__func__, &rdr->parallel_lock); + cs_log("reader %s: no slot available for service %04X (all %d+%d slots used)", + rdr->label, srvid, rdr->maxparallel, pending_size); + return 0; + } + + // Update interval for existing slots + if(!is_new && slot->last_ecm.time > 0) + { + int64_t interval = comp_timeb(&now, &slot->last_ecm); + if(interval > 0 && interval < 30000) // Sanity: < 30 seconds + { + if(slot->ecm_interval == 0) + slot->ecm_interval = (int32_t)interval; + else + slot->ecm_interval = (slot->ecm_interval + (int32_t)interval) / 2; + } + } + + slot->last_ecm = now; + + // If this is an ACTIVE service (not pending), drop pending if needed + // This ensures pending are only dropped when active services prove they're still active + if(!is_pending) + { + reader_drop_pending_nolock(rdr, &now); + } + + // Recount for logging (drop may have changed counts) + int32_t final_active = 0, final_pending = 0; + for(i = 0; i < rdr->maxparallel; i++) + { + if(rdr->parallel_slots[i].srvid != 0) + final_active++; + } + for(i = 0; i < pending_size; i++) + { + if(rdr->parallel_slots_prov[i].srvid != 0) + final_pending++; + } + + if(is_new) + { + if(final_pending > 0) + { + cs_log_dbg(D_READER, "reader %s: registered service %04X in %s slot %d (%d/%d active, +%d pending)", + rdr->label, srvid, array_name, slot_idx, final_active, rdr->maxparallel, final_pending); + } + else + { + cs_log_dbg(D_READER, "reader %s: registered service %04X in %s slot %d (%d/%d active)", + rdr->label, srvid, array_name, slot_idx, final_active, rdr->maxparallel); + } + + // Log "now full" only once per state change, and only for active services + if(!is_pending && final_active >= rdr->maxparallel && !rdr->parallel_full) + { + rdr->parallel_full = 1; + cs_log("reader %s: now full (%d/%d active)", + rdr->label, final_active, rdr->maxparallel); + } + } + + // Reset full flag when capacity becomes available + if(final_active < rdr->maxparallel && rdr->parallel_full) + { + rdr->parallel_full = 0; + cs_log_dbg(D_READER, "reader %s: capacity available (%d/%d active)", + rdr->label, final_active, rdr->maxparallel); + } + + cs_writeunlock(__func__, &rdr->parallel_lock); + return 1; +} + +void fallback_timeout(ECM_REQUEST *er) +{ + if(er->rc >= E_UNHANDLED && er->stage < 4) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} fallback timeout! (stage: %d)", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, er->stage); + debug_ecm(D_TRACE, "fallback for %s %s", username(er->client), buf); + while(er->stage < 4) // if preferlocalcards=1 and no answer from locals, initial stage will be 2! We need to reach stage=4 to call fallback's. + { + request_cw_from_readers(er, 0); + } + } +} + +void ecm_timeout(ECM_REQUEST *er) +{ + if(!er->readers_timeout_check) + { + er->readers_timeout_check = 1; + + if(check_client(er->client) && er->rc >= E_UNHANDLED) + { + debug_ecm(D_TRACE, "timeout for %s %s", username(er->client), buf); + + // set timeout for readers not answering + struct s_ecm_answer *ea_list; + for(ea_list = er->matching_rdr; ea_list; ea_list = ea_list->next) + { + if((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED)) == REQUEST_SENT) // Request sent, but no answer! + { + write_ecm_answer(ea_list->reader, er, E_TIMEOUT, 0, NULL, NULL, 0, NULL); // set timeout for readers not answered! + } + } + + // send timeout to client! + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} client timeout! ", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + er->rc = E_TIMEOUT; + er->rcEx = 0; + send_dcw(er->client, er); + } + } +} + +void increment_n_request(struct s_client *cl) +{ + if(check_client(cl)) + { + cl->n_request[1]++; + first_client->n_request[1]++; + } +} + +uint8_t checkCWpart(uint8_t *cw, int8_t part) +{ + uint8_t eo = part ? 8 : 0; + int8_t i; + for(i = 0; i < 8; i++) + if(cw[i + eo]) { return 1; } + return 0; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ===== CW AUTODETECT PATCH WITH LEARN THRESHOLD (48 + 64 LEARNING) ===== +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#define CW_LEARN_THRESHOLD 5 + +static int cw_autodetect_and_block(struct s_reader *reader, ECM_REQUEST *er) +{ + if(!er || !er->cw) + return 1; + + cw_detect_entry *e = cw_detect_get(er); + + const char *reader_name = + (reader && reader->label) ? reader->label : "?"; + + int checksum_ok = cw_checksum_ok(er->cw); + + int whitelist = + chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) || + (reader && chk_if_ignore_checksum(er, &reader->disablecrccws_only_for)); + + ///////////////////////////////////////////////////////////// + // STRICT MODE (NOT WHITELIST) → ONLY 48bit ALLOWED + ///////////////////////////////////////////////////////////// + + if(!whitelist) + { + if(!e->strict_logged) + { + cs_log("AUTODETECT STRICT 48bit MODE reader=%s (%04X@%06X/%04X)", + reader_name, er->caid, er->prid, er->srvid); + + e->strict_logged = 1; + } + + // block 64bit + if(!checksum_ok) + { + cs_log("AUTODETECT BLOCK 64bit reader=%s (%04X@%06X/%04X)", + reader_name, er->caid, er->prid, er->srvid); + + return 0; + } + + return 1; + } + + ///////////////////////////////////////////////////////////// + // WHITELIST MODE → LEARN 48bit AND 64bit SEPARATELY + ///////////////////////////////////////////////////////////// + + // ---------- LEARN 64bit ---------- + if(!checksum_ok) + { + if(!e->is64) + { + if(e->learn64_count < CW_LEARN_THRESHOLD) + { + e->learn64_count++; + + cs_log("AUTODETECT LEARN 64bit STEP %d/%d reader=%s (%04X@%06X/%04X)", + e->learn64_count, + CW_LEARN_THRESHOLD, + reader_name, + er->caid, + er->prid, + er->srvid); + } + + if(e->learn64_count >= CW_LEARN_THRESHOLD) + { + e->is64 = 1; + + cs_log("AUTODETECT LOCKED 64bit reader=%s (%04X@%06X/%04X)", + reader_name, + er->caid, + er->prid, + er->srvid); + } + } + + return 1; + } + + // ---------- LEARN 48bit ---------- + if(checksum_ok) + { + if(!e->is48) + { + if(e->learn48_count < CW_LEARN_THRESHOLD) + { + e->learn48_count++; + + cs_log("AUTODETECT LEARN 48bit STEP %d/%d reader=%s (%04X@%06X/%04X)", + e->learn48_count, + CW_LEARN_THRESHOLD, + reader_name, + er->caid, + er->prid, + er->srvid); + } + + if(e->learn48_count >= CW_LEARN_THRESHOLD) + { + e->is48 = 1; + + cs_log("AUTODETECT LOCKED 48bit reader=%s (%04X@%06X/%04X)", + reader_name, + er->caid, + er->prid, + er->srvid); + } + } + + return 1; + } + + return 1; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void update_n_request(void) +{ + struct s_client *cl; + + cs_readlock(__func__, &clientlist_lock); + for(cl = first_client->next; cl; cl = cl->next) + { +#ifdef CS_CACHEEX + if(check_client(cl) && get_module(cl)->num != R_CSP && cl->typ == 'c' && !cl->dup && cl->account && cl->account->cacheex.mode<=1) //no cacheex 2/3 client +#else + if(check_client(cl) && get_module(cl)->num != R_CSP && cl->typ == 'c' && !cl->dup) +#endif + { + cl->n_request[0] = cl->n_request[1]; + cl->n_request[1] = 0; + } + else + { + cl->n_request[0] = 0; + cl->n_request[1] = 0; + } + } + + first_client->n_request[0] = first_client->n_request[1]; + first_client->n_request[1] = 0; + + cs_readunlock(__func__, &clientlist_lock); +} + +static void *cw_process(void) +{ + set_thread_name(__func__); + int64_t time_to_check_fbtimeout, time_to_check_ctimeout, next_check, n_request_next; + struct timeb t_now, tbc, ecmc_time, cache_time, n_request_time; + ECM_REQUEST *er = NULL; + time_t ecm_maxcachetime; + +#ifdef CS_CACHEEX + int64_t time_to_check_cacheex_wait_time; + int64_t time_to_check_cacheex_mode1_delay; +#endif + + cs_pthread_cond_init(__func__, &cw_process_sleep_cond_mutex, &cw_process_sleep_cond); + +#ifdef CS_ANTICASC + int32_t ac_next; + struct timeb ac_time; + cs_ftime(&ac_time); + add_ms_to_timeb(&ac_time, cfg.ac_stime * 60 * 1000); +#endif + + cs_ftime(&ecmc_time); + add_ms_to_timeb(&ecmc_time, 1000); + cs_ftime(&cache_time); + add_ms_to_timeb(&cache_time, 3000); + cs_ftime(&n_request_time); + add_ms_to_timeb(&n_request_time, 60 * 1000); + + while(!exit_oscam) + { + if(cw_process_wakeups == 0) // No waiting wakeups, proceed to sleep + { + sleepms_on_cond(__func__, &cw_process_sleep_cond_mutex, &cw_process_sleep_cond, msec_wait); + } + cw_process_wakeups = 0; // We've been woken up, reset the counter + if(exit_oscam) + { break; } + + next_check = 0; +#ifdef CS_ANTICASC + ac_next = 0; +#endif + ecmc_next = 0; + cache_next = 0; + msec_wait = 0; + + cs_ftime(&t_now); + cs_readlock(__func__, &ecmcache_lock); + for(er = ecmcwcache; er; er = er->next) + { + + if((er->from_cacheex || er->from_csp) // ignore ecms from cacheex/csp + || er->readers_timeout_check // ignore already checked + || !check_client(er->client)) // ignore ecm of killed clients + { + continue; + } + + if(er->rc >= E_UNHANDLED) + { +#ifdef CS_CACHEEX + // cacheex_wait_time + if(er->cacheex_wait_time && !er->cacheex_wait_time_expired) + { + tbc = er->tps; + time_to_check_cacheex_mode1_delay = 0; + time_to_check_cacheex_wait_time = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, er->cacheex_wait_time)); + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_CACHEEX_TIMEOUT, (void *)er, 0); + time_to_check_cacheex_wait_time = 0; + + } + else if(er->cacheex_mode1_delay && !er->stage && er->cacheex_reader_count>0) + { + // check for cacheex_mode1_delay + tbc = er->tps; + time_to_check_cacheex_mode1_delay = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, er->cacheex_mode1_delay)); + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_CACHEEX1_DELAY, (void *)er, 0); + time_to_check_cacheex_mode1_delay = 0; + } + } + + if(!next_check || (time_to_check_cacheex_wait_time > 0 && time_to_check_cacheex_wait_time < next_check)) + { next_check = time_to_check_cacheex_wait_time; } + + if(!next_check || (time_to_check_cacheex_mode1_delay > 0 && time_to_check_cacheex_mode1_delay < next_check)) + { next_check = time_to_check_cacheex_mode1_delay; } + } +#endif + if(er->stage < 4) + { + // fbtimeout + tbc = er->tps; + time_to_check_fbtimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, get_fallbacktimeout(er->caid))); + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_FALLBACK_TIMEOUT, (void *)er, 0); + time_to_check_fbtimeout = 0; + } + + if(!next_check || (time_to_check_fbtimeout > 0 && time_to_check_fbtimeout < next_check)) + { next_check = time_to_check_fbtimeout; } + } + } + + // clienttimeout + if(!er->readers_timeout_check) // ecm stays in cache at least ctimeout+2seconds! + { + tbc = er->tps; + time_to_check_ctimeout = add_ms_to_timeb_diff(&tbc, lb_auto_timeout(er, cfg.ctimeout)); + + if(comp_timeb(&t_now, &tbc) >= 0) + { + add_job(er->client, ACTION_CLIENT_TIMEOUT, (void *)er, 0); + time_to_check_ctimeout = 0; + } + + if(!next_check || (time_to_check_ctimeout > 0 && time_to_check_ctimeout < next_check)) + { next_check = time_to_check_ctimeout; } + } + } + cs_readunlock(__func__, &ecmcache_lock); +#ifdef CS_ANTICASC + if(cfg.ac_enabled && (ac_next = comp_timeb(&ac_time, &t_now)) <= 10) + { + ac_do_stat(); + cs_ftime(&ac_time); + ac_next = add_ms_to_timeb_diff(&ac_time, cfg.ac_stime * 60 * 1000); + } +#endif + if((ecmc_next = comp_timeb(&ecmc_time, &t_now)) <= 10) + { + uint32_t count = 0; + struct ecm_request_t *ecm, *ecmt = NULL, *prv; + + cs_readlock(__func__, &ecmcache_lock); + for(ecm = ecmcwcache, prv = NULL; ecm; prv = ecm, ecm = ecm->next, count++) + { + ecm_maxcachetime = t_now.time - ((cfg.ctimeout + 500) / 1000 + 3); // to be sure no more access er! + + if(ecm->tps.time < ecm_maxcachetime) + { + cs_readunlock(__func__, &ecmcache_lock); + cs_writelock(__func__, &ecmcache_lock); + ecmt = ecm; + if(prv) + { prv->next = NULL; } + else + { ecmcwcache = NULL; } + cs_writeunlock(__func__, &ecmcache_lock); + break; + } + } + if(!ecmt) + { cs_readunlock(__func__, &ecmcache_lock); } + ecmcwcache_size = count; + + while(ecmt) + { + ecm = ecmt->next; + free_ecm(ecmt); + ecmt = ecm; + } + +#ifdef CS_CACHEEX + ecmt=NULL; + cs_readlock(__func__, &ecm_pushed_deleted_lock); + for(ecm = ecm_pushed_deleted, prv = NULL; ecm; prv = ecm, ecm = ecm->next) + { + ecm_maxcachetime = t_now.time - ((cfg.ctimeout + 500) / 1000 + 3); + if(ecm->tps.time < ecm_maxcachetime) + { + cs_readunlock(__func__, &ecm_pushed_deleted_lock); + cs_writelock(__func__, &ecm_pushed_deleted_lock); + ecmt = ecm; + if(prv) + { prv->next = NULL; } + else + { ecm_pushed_deleted = NULL; } + cs_writeunlock(__func__, &ecm_pushed_deleted_lock); + break; + } + } + if(!ecmt) + { cs_readunlock(__func__, &ecm_pushed_deleted_lock); } + + while(ecmt) + { + ecm = ecmt->next; + free_push_in_ecm(ecmt); + ecmt = ecm; + } +#endif + + cs_ftime(&ecmc_time); + ecmc_next = add_ms_to_timeb_diff(&ecmc_time, 1000); + } + + if((cache_next = comp_timeb(&cache_time, &t_now)) <= 10) + { + cleanup_cache(false); + cacheex_cleanup_hitcache(false); + + cs_ftime(&cache_time); + cache_next = add_ms_to_timeb_diff(&cache_time, 3000); + } + + if((n_request_next = comp_timeb(&n_request_time, &t_now)) <= 10) + { + update_n_request(); + cs_ftime(&n_request_time); + n_request_next = add_ms_to_timeb_diff(&n_request_time, 60 * 1000); + } + + msec_wait = next_check; +#ifdef CS_ANTICASC + if(!msec_wait || (ac_next > 0 && ac_next < msec_wait)) + { msec_wait = ac_next; } +#endif + if(!msec_wait || (ecmc_next > 0 && ecmc_next < msec_wait)) + { msec_wait = ecmc_next; } + + if(!msec_wait || (cache_next > 0 && cache_next < msec_wait)) + { msec_wait = cache_next; } + + if(!msec_wait || (n_request_next > 0 && n_request_next < msec_wait)) + { msec_wait = n_request_next; } + + if(!msec_wait) + { msec_wait = 3000; } + + cleanupcwcycle(); + } + + return NULL; +} + +void cw_process_thread_start(void) +{ + start_thread("cw_process", (void *) &cw_process, NULL, NULL, 1, 1); +} + +void cw_process_thread_wakeup(void) +{ + cw_process_wakeups++; // Do not sleep... + SAFE_COND_SIGNAL(&cw_process_sleep_cond); +} + +void convert_to_beta(struct s_client *cl, ECM_REQUEST *er, uint16_t caidto) +{ + static uint8_t headerN3[10] = { 0xc7, 0x00, 0x00, 0x00, 0x01, 0x10, 0x10, 0x00, 0x87, 0x12 }; + static uint8_t headerN2[10] = { 0xc9, 0x00, 0x00, 0x00, 0x01, 0x10, 0x10, 0x00, 0x48, 0x12 }; + + er->ocaid = er->caid; + er->caid = caidto; + er->prid = 0; + er->ecmlen = er->ecm[2] + 3; + + memmove(er->ecm + 13, er->ecm + 3, er->ecmlen - 3); + + if(er->ecmlen > 0x88) + { + memcpy(er->ecm + 3, headerN3, 10); + if(er->ecm[0] == 0x81) + { er->ecm[12] += 1; } + er->ecm[1] = 0x70; + } + else + { + memcpy(er->ecm + 3, headerN2, 10); + } + + er->ecmlen += 10; + er->ecm[2] = er->ecmlen - 3; + er->btun = 1; + + cl->cwtun++; + cl->account->cwtun++; + first_client->cwtun++; + + cs_log_dbg(D_TRACE, "ECM converted ocaid from 0x%04X to BetaCrypt caid 0x%04X for service id 0x%04X", + er->ocaid, caidto, er->srvid); +} + +void convert_to_nagra(struct s_client *cl, ECM_REQUEST *er, uint16_t caidto) +{ + cs_log_dbg(D_TRACE, "convert_to_nagra"); + er->ocaid = er->caid; + er->caid = caidto; + er->prid = 0; + er->ecmlen = er->ecm[2] + 3; + + // not sure + if(er->ecmlen < 0x52) + { er->ecm[1] = 0x30; } + + memmove(er->ecm + 3, er->ecm + 13, er->ecmlen - 3); + + er->ecmlen -= 10; + er->ecm[2] = er->ecmlen - 3; + er->btun = 1; + + cl->cwtun++; + cl->account->cwtun++; + first_client->cwtun++; + + cs_log_dbg(D_TRACE, "ECM converted ocaid from: 0x%04X to Nagra: 0x04%X for service id:0x04%X", + er->ocaid, caidto, er->srvid); +} + +void cs_betatunnel(ECM_REQUEST *er) +{ + int32_t i; + struct s_client *cl = cur_client(); + uint32_t mask_all = 0xFFFF; + TUNTAB *ttab = &cl->ttab; + + for(i = 0; i < ttab->ttnum; i++) + { + if((er->caid == ttab->ttdata[i].bt_caidfrom) && ((er->srvid == ttab->ttdata[i].bt_srvid) || (ttab->ttdata[i].bt_srvid) == mask_all)) + { + if(chk_is_betatunnel_caid(er->caid) == 1 && er->ocaid == 0x0000) + { + convert_to_nagra(cl, er, ttab->ttdata[i].bt_caidto); + } + else if(er->ocaid == 0x0000) + { + convert_to_beta(cl, er, ttab->ttdata[i].bt_caidto); + } + return; + } + } +} + +static void remove_ecm_from_reader(ECM_REQUEST *ecm) +{ + int32_t i; + struct s_ecm_answer *ea = ecm->matching_rdr; + while(ea) + { + if((ea->status & REQUEST_SENT) && !(ea->status & REQUEST_ANSWERED)) + { + // we found a outstanding reader, clean it: + struct s_reader *rdr = ea->reader; + if(rdr) + { + struct s_client *cl = rdr->client; + if(check_client(cl)) + { + ECM_REQUEST *ecmtask = cl->ecmtask; + if(ecmtask) + { + for(i = 0; i < cfg.max_pending; ++i) + { + if(ecmtask[i].parent == ecm) + { + ecmtask[i].client = NULL; + cacheex_set_csp_lastnode(&ecmtask[i]); + } + } + } + } + } + } + ea = ea->next; + } +} + +void free_ecm(ECM_REQUEST *ecm) +{ + struct s_ecm_answer *ea, *nxt; + cacheex_free_csp_lastnodes(ecm); + gbox_free_cards_pending(ecm); + // remove this ecm from reader queue to avoid segfault on very late answers (when ecm is already disposed) + // first check for outstanding answers: + remove_ecm_from_reader(ecm); + // free matching_rdr list: + ea = ecm->matching_rdr; + ecm->matching_rdr = NULL; + while(ea) + { + nxt = ea->next; + cs_lock_destroy(__func__, &ea->ecmanswer_lock); + add_garbage(ea); + ea = nxt; + } + if(ecm->src_data) + { add_garbage(ecm->src_data); } + add_garbage(ecm); +} + + +void free_push_in_ecm(ECM_REQUEST *ecm) +{ + cacheex_free_csp_lastnodes(ecm); + gbox_free_cards_pending(ecm); + if(ecm->src_data) + { NULLFREE(ecm->src_data); } + NULLFREE(ecm); +} + +ECM_REQUEST *get_ecmtask(void) +{ + ECM_REQUEST *er = NULL; + struct s_client *cl = cur_client(); + if(!cl) + { return NULL; } + if(!cs_malloc(&er, sizeof(ECM_REQUEST))) + { return NULL; } + cs_ftime(&er->tps); + er->rc = E_UNHANDLED; + er->client = cl; + er->grp = 0; // no readers/cacheex-clients answers yet + //cs_log("client %s ECMTASK %d module %s", username(cl), n, get_module(cl)->desc); + return er; +} + +void cleanup_ecmtasks(struct s_client *cl) +{ + if(!cl) { return; } + + ECM_REQUEST *ecm; + + // remove this clients ecm from queue. because of cache, just null the client: + cs_readlock(__func__, &ecmcache_lock); + for(ecm = ecmcwcache; ecm && cl; ecm = ecm->next) + { + if(ecm->client == cl) + { + ecm->client = NULL; + } + } + cs_readunlock(__func__, &ecmcache_lock); + + // remove client from rdr ecm-queue: + cs_readlock(__func__, &readerlist_lock); + struct s_reader *rdr = first_active_reader; + while(rdr) + { + if(check_client(rdr->client) && rdr->client->ecmtask) + { + int i; + for(i = 0; (i < cfg.max_pending) && cl; i++) + { + ecm = &rdr->client->ecmtask[i]; + if(ecm->client == cl) + { + ecm->client = NULL; + } + } + } + rdr = rdr->next; + } + cs_readunlock(__func__, &readerlist_lock); + +} + +static void add_cascade_data(struct s_client *client, ECM_REQUEST *er) +{ + if(!client->cascadeusers) + { client->cascadeusers = ll_create("cascade_data"); } + LLIST *l = client->cascadeusers; + LL_ITER it = ll_iter_create(l); + time_t now = time(NULL); + struct s_cascadeuser *cu; + int8_t found = 0; + while((cu = ll_iter_next(&it))) + { + if(er->caid == cu->caid && er->prid == cu->prid && er->srvid == cu->srvid) // found it + { + if(cu->time < now) + { cu->cwrate = now - cu->time; } + cu->time = now; + found = 1; + } + else if(cu->time + 60 < now) // old + { ll_iter_remove_data(&it); } + } + + if(!found) // add it if not found + { + if(!cs_malloc(&cu, sizeof(struct s_cascadeuser))) + { return; } + cu->caid = er->caid; + cu->prid = er->prid; + cu->srvid = er->srvid; + cu->time = now; + ll_append(l, cu); + } +} + +int32_t is_double_check_caid(ECM_REQUEST *er, FTAB *double_check_caid) +{ + if(!double_check_caid->nfilts) { return 1; } + + int32_t i, k; + for(i = 0; i < double_check_caid->nfilts; i++) + { + uint16_t tcaid = double_check_caid->filts[i].caid; + if(tcaid && (tcaid == er->caid || (tcaid < 0x0100 && (er->caid >> 8) == tcaid))) // caid match + { + int32_t nprids = double_check_caid->filts[i].nprids; + if(!nprids) // No Provider ->Ok + { return 1; } + + for(k = 0; k < nprids; k++) + { + uint32_t prid = double_check_caid->filts[i].prids[k]; + if(prid == er->prid) // Provider matches + { return 1; } + } + } + } + + return 0; +} + +struct s_ecm_answer *get_ecm_answer(struct s_reader *reader, ECM_REQUEST *er) +{ + if(!er || !reader) { return NULL; } + + struct s_ecm_answer *ea; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if(ea->reader == reader) + { + return ea; + } + } + return NULL; +} + +void distribute_ea(struct s_ecm_answer *ea) +{ + struct s_ecm_answer *ea_temp; + + for(ea_temp = ea->pending; ea_temp; ea_temp = ea_temp->pending_next) + { + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [distribute_ea] send ea (%s) by reader %s answering for client %s", (check_client(ea_temp->er->client) ? ea_temp->er->client->account->usr : "-"), ea_temp->er->caid, ea_temp->er->prid, ea_temp->er->srvid, ea->rc==E_FOUND?"OK":"NOK", ea_temp->reader->label, (check_client(ea->er->client) ? ea->er->client->account->usr : "-")); +#ifdef CS_CACHEEX_AIO + if(ea->rc==E_FOUND && ea->er->localgenerated) + ea_temp->er->localgenerated = 1; +#endif + // e.g. we cannot send timeout, because "ea_temp->er->client" could wait/ask other readers! Simply set not_found if different from E_FOUND! + write_ecm_answer(ea_temp->reader, ea_temp->er, (ea->rc==E_FOUND? E_FOUND : E_NOTFOUND), ea->rcEx, ea->cw, NULL, ea->tier, &ea->cw_ex); + } +} + +int32_t send_dcw(struct s_client *client, ECM_REQUEST *er) +{ + if(!check_client(client) || client->typ != 'c') + { return 0; } + + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [send_dcw] rc %d from reader %s", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, er->rc, er->selected_reader ? er->selected_reader->label : "-"); + + static const char stageTxt[] = { '0', 'C', 'L', 'P', 'F', 'X' }; + static const char *stxt[] = { "found", "cache1", "cache2", "cache3", "not found", "timeout", "sleeping", + "fake", "invalid", "corrupt", "no card", "expdate", "disabled", "stopped" }; + + static const char *stxtEx[16] = {"", "group", "caid", "ident", "class", "chid", "queue", "peer", "sid", "", "", "", "", "", "", ""}; + static const char *stxtWh[16] = {"", "user ", "reader ", "server ", "lserver ", "", "", "", "", "", "", "", "" , "" , "", ""}; +#ifdef CS_CACHEEX_AIO + char sby[100] = "", sreason[100] = "", scwcinfo[32] = "", schaninfo[CS_SERVICENAME_SIZE] = "", srealecmtime[50]=""; +#else + char sby[100] = "", sreason[70] = "", scwcinfo[32] = "", schaninfo[CS_SERVICENAME_SIZE] = "", srealecmtime[50]=""; +#endif + char erEx[32] = ""; + char usrname[38] = ""; + char channame[28]; + struct timeb tpe; + + snprintf(usrname, sizeof(usrname) - 1, "%s", username(client)); + +#ifdef WITH_DEBUG + if(cs_dblevel & D_CLIENTECM) + { + char buf[ECM_FMT_LEN]; + char ecmd5[17 * 3]; + char cwstr[17 * 3]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + cs_hexdump(0, er->cw, 16, cwstr, sizeof(cwstr)); +#ifdef CS_CACHEEX + char csphash[5 * 3]; + cs_hexdump(0, (void *)&er->csp_hash, 4, csphash, sizeof(csphash)); + cs_log_dbg(D_CLIENTECM, "Client %s csphash %s cw %s rc %d %s", username(client), csphash, cwstr, er->rc, buf); +#else + cs_log_dbg(D_CLIENTECM, "Client %s cw %s rc %d %s", username(client), cwstr, er->rc, buf); +#endif + } +#endif + + struct s_reader *er_reader = er->selected_reader; // responding reader + struct s_ecm_answer *ea_orig = get_ecm_answer(er_reader, er); + + + // check if ecm_answer from pending's + if(ea_orig && ea_orig->is_pending && er->rc == E_FOUND) + { er->rc = E_CACHE2; } + + + // check if answer from cacheex-1 reader + if(er->rc == E_FOUND && er_reader && cacheex_reader(er_reader)) // so add hit to cacheex mode 1 readers + { + er->rc = E_CACHEEX; + } + + // real ecm time + if(ea_orig && !ea_orig->is_pending && er->rc == E_FOUND + && ( +#ifdef CS_CACHEEX + er->cacheex_wait_time || +#endif + (ea_orig->status & READER_FALLBACK))) + { + snprintf(srealecmtime, sizeof(srealecmtime) - 1, " (real %d ms)", ea_orig->ecm_time); + } + + + if(er->rc == E_TIMEOUT) + { +#ifdef CS_CACHEEX + if(!er->from_cacheex1_client) // cosmetic: show "by" readers only for "normal" clients + { +#endif + struct s_ecm_answer *ea_list; + int32_t ofs = 0; + + for(ea_list = er->matching_rdr; ea_list; ea_list = ea_list->next) + { + if(ea_list->reader && ofs < (int32_t)sizeof(sby) && ((ea_list->status & REQUEST_SENT) && (ea_list->rc == E_TIMEOUT || ea_list->rc >= E_99))) //Request send, but no cw answered! + { + ofs += snprintf(sby + ofs, sizeof(sby) - ofs - 1, "%s%s", ofs ? "," : " by ", ea_list->reader->label); + } + } + + if(er->ocaid && ofs < (int32_t)sizeof(sby)) + { snprintf(sby + ofs, sizeof(sby) - ofs - 1, "(btun %04X)", er->ocaid); } + +#ifdef CS_CACHEEX + } +#endif + } + else if(er_reader) + { + // add marker to reader if ECM_REQUEST was betatunneled + if(er->ocaid) + { snprintf(sby, sizeof(sby) - 1, " by %s(btun %04X)", er_reader->label, er->ocaid); } + else + { snprintf(sby, sizeof(sby) - 1, " by %s", er_reader->label); } + } +#ifdef CS_CACHEEX + else if(er->cacheex_src) // only for cacheex mode-3 clients (no mode-1 or mode-2 because reader is set!) and csp + { + char *cex_name = "-"; + if(check_client(er->cacheex_src) && er->cacheex_src->account) + { + if(er->cacheex_src->account->usr[0] != '\0') + cex_name = er->cacheex_src->account->usr; + else + cex_name = "csp"; + } + + if(er->ocaid) + { + snprintf(sby, sizeof(sby) - 1, " by %s(btun %04X)", cex_name, er->ocaid); + } + else + { + snprintf(sby, sizeof(sby) - 1, " by %s", cex_name); + } + } +#endif + + if(er->rc < E_NOTFOUND) + { + er->rcEx = 0; + // memset(er->msglog, 0, MSGLOGSIZE); // remove reader msglog from previous requests that failed, founds never give back msglog! + } + + if(er->rcEx) + { snprintf(erEx, sizeof(erEx) - 1, "rejected %s%s", stxtWh[er->rcEx >> 4], stxtEx[er->rcEx & 0xf]); } + + get_servicename_or_null(client, er->srvid, er->prid, er->caid, channame, sizeof(channame)); + if(!channame[0]) + { + schaninfo[0] = '\0'; + } + else + { + snprintf(schaninfo, sizeof(schaninfo) - 1, " - %s", channame); + } + +#ifdef CS_CACHEEX + int cx = 0; + if(er->msglog[0]) + { + cx = snprintf(sreason, sizeof(sreason) - 1, " (%s)", er->msglog); + } +#else + if(er->msglog[0]) + { + snprintf(sreason, sizeof(sreason) - 1, " (%s)", er->msglog); + } +#endif +#ifdef CW_CYCLE_CHECK + if(er->cwc_msg_log[0]) + { snprintf(scwcinfo, sizeof(scwcinfo) - 1, " (%.26s)", er->cwc_msg_log); } +#endif + + cs_ftime(&tpe); + +#ifdef CS_CACHEEX + int cx2 = 0; + if(er->rc >= E_CACHEEX && er->cacheex_wait_time && er->cacheex_wait_time_expired) + { + cx2 = snprintf(sreason+cx, (sizeof sreason)-cx, " (wait_time over)"); + } + else + { + cx2 = cx; + } + + if(er->cw_count>1) + { +#ifdef CS_CACHEEX_AIO + if(er->cw_count > 0x0F000000 || er->localgenerated) + { + uint32_t cw_count_cleaned = er->cw_count ^ 0x0F000000; + if(cw_count_cleaned > 1) + snprintf(sreason+cx2, (sizeof sreason)-cx2, " (cw count %d) (lg)", cw_count_cleaned); + else + snprintf(sreason+cx2, (sizeof sreason)-cx2, " (lg)"); + } + else + { +#endif + + snprintf (sreason+cx2, (sizeof sreason)-cx2, " (cw count %d)", er->cw_count); + +#ifdef CS_CACHEEX_AIO + } + + } + else + { + if(er->localgenerated) + snprintf(sreason+cx2, (sizeof sreason)-cx2, " (lg)"); +#endif + } + +#endif + + client->cwlastresptime = comp_timeb(&tpe, &er->tps); + + time_t now = time(NULL); + webif_client_add_lastresponsetime(client, client->cwlastresptime, now, er->rc); // add to ringbuffer + + if(er_reader) + { + struct s_client *er_cl = er_reader->client; + if(check_client(er_cl)) + { + er_cl->cwlastresptime = client->cwlastresptime; + webif_client_add_lastresponsetime(er_cl, client->cwlastresptime, now, er->rc); + er_cl->last_providptr = client->last_providptr; + er_cl->last_srvidptr = client->last_srvidptr; + } + } + + webif_client_init_lastreader(client, er, er_reader, stxt); + + client->last = now; + + //cs_log_dbg(D_TRACE, "CHECK rc=%d er->cacheex_src=%s", er->rc, username(er->cacheex_src)); + switch(er->rc) + { + case E_FOUND: + { + client->cwfound++; + client->account->cwfound++; + first_client->cwfound++; + break; + } + case E_CACHE1: + case E_CACHE2: + case E_CACHEEX: + { + client->cwcache++; + client->account->cwcache++; + first_client->cwcache++; +#ifdef CS_CACHEEX + if(check_client(er->cacheex_src)) + { + first_client->cwcacheexhit++; + er->cacheex_src->cwcacheexhit++; + if(er->cacheex_src->account) + { er->cacheex_src->account->cwcacheexhit++; } + } +#endif + break; + } + case E_NOTFOUND: + case E_CORRUPT: + case E_NOCARD: + { + if(er->rcEx) + { + client->cwignored++; + client->account->cwignored++; + first_client->cwignored++; + } + else + { + client->cwnot++; + client->account->cwnot++; + first_client->cwnot++; + } + break; + } + case E_TIMEOUT: + { + client->cwtout++; + client->account->cwtout++; + first_client->cwtout++; + break; + } + default: + { + client->cwignored++; + client->account->cwignored++; + first_client->cwignored++; + } + } + +#ifdef CS_ANTICASC +// [zaplist] ACoSC anticascading + if(cfg.acosc_enabled) + { + int8_t max_active_sids = 0; + int8_t zap_limit = 0; + int8_t penalty = 0; + int32_t penalty_duration = 0; + int32_t delay = 0; + int8_t max_ecms_per_minute = 0; + char *info1 = NULL; + char *info2 = NULL; + char *info3 = NULL; + char *info4 = NULL; + char *info5 = NULL; + char *info6 = NULL; + + // **global or user value? + cs_writelock(__func__, &clientlist_lock); + + max_active_sids = client->account->acosc_max_active_sids == -1 ? cfg.acosc_max_active_sids : client->account->acosc_max_active_sids; + info1 = client->account->acosc_max_active_sids == -1 ? "Globalvalue" : "Uservalue"; + + zap_limit = client->account->acosc_zap_limit == -1 ? cfg.acosc_zap_limit : client->account->acosc_zap_limit; + info5 = client->account->acosc_zap_limit == -1 ? "Globalvalue" : "Uservalue"; + + penalty = client->account->acosc_penalty == -1 ? cfg.acosc_penalty : client->account->acosc_penalty; + info2 = client->account->acosc_penalty == -1 ? "Globalvalue" : "Uservalue"; + + penalty_duration = client->account->acosc_penalty_duration == -1 ? cfg.acosc_penalty_duration : client->account->acosc_penalty_duration; + info3 = client->account->acosc_penalty_duration == -1 ? "Globalvalue" : "Uservalue"; + + delay = client->account->acosc_delay == -1 ? cfg.acosc_delay : client->account->acosc_delay; + info4 = client->account->acosc_delay == -1 ? "Globalvalue" : "Uservalue"; + + max_ecms_per_minute = client->account->acosc_max_ecms_per_minute == -1 ? cfg.acosc_max_ecms_per_minute : client->account->acosc_max_ecms_per_minute; + info6 = client->account->acosc_max_ecms_per_minute == -1 ? "Globalvalue" : "Uservalue"; + + //** + + if((er->rc < E_NOTFOUND && max_active_sids > 0) || zap_limit > 0 || max_ecms_per_minute > 0) + { + int8_t k = 0; + int8_t active_sid_count = 0; + time_t zaptime = time(NULL); + + if(client->account->acosc_penalty_active == 4 && client->account->acosc_penalty_until <= zaptime) // reset penalty_active + { + client->account->acosc_penalty_active = 0; + client->account->acosc_penalty_until = 0; + } + + if(client->account->acosc_penalty_active == 0 && max_active_sids > 0) + { + for(k=0; k<15 ; k++) + { + if(zaptime-30 < client->client_zap_list[k].lasttime && client->client_zap_list[k].request_stage == 10) + { + cs_log_dbg(D_TRACE, "[zaplist] ACoSC for Client: %s more then 10 ECM's for %04X@%06X/%04X/%04X", username(client), client->client_zap_list[k].caid, client->client_zap_list[k].provid, client->client_zap_list[k].chid, client->client_zap_list[k].sid); + active_sid_count ++; + } + } + cs_log_dbg(D_TRACE, "[zaplist] ACoSC for Client: %s active_sid_count= %i with more than 10 followed ECM's (mas:%i (%s))", username(client), active_sid_count, max_active_sids, info1); + } + + if(client->account->acosc_penalty_active == 0 && max_active_sids > 0 && active_sid_count > max_active_sids) //max_active_sids reached + { + client->account->acosc_penalty_active = 1; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active == 0 && zap_limit > 0 && client->account->acosc_user_zap_count > zap_limit) // zap_limit reached + { + client->account->acosc_penalty_active = 2; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active == 0 && max_ecms_per_minute > 0 && client->n_request[1] >= max_ecms_per_minute && penalty != 4) // max ecms per minute reached + { + client->account->acosc_penalty_active = 3; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active == 0 && max_ecms_per_minute > 0 && client->n_request[1] > 0 && penalty == 4) // max ecms per minute with hidecards penalty + { + client->account->acosc_penalty_active = 3; + client->account->acosc_penalty_until = zaptime + penalty_duration; + } + + if(client->account->acosc_penalty_active > 0) + { + if(client->account->acosc_penalty_active == 4) + { cs_log_dbg(D_TRACE, "[zaplist] ACoSC for Client: %s penalty_duration: %" PRId64 " seconds left(%s)", username(client), (int64_t)(client->account->acosc_penalty_until - zaptime), info3); } + + int16_t lt = get_module(client)->listenertype; + switch(penalty) + { + case 1: // NULL CW + er->rc = E_FAKE; // E_FAKE give only a status fake not a NULL cw + er->rcEx = E2_WRONG_CHKSUM; + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 1(%s) send null CW", username(client), active_sid_count, max_active_sids, info1, info2); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 1(%s) send null CW", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 1(%s) send null CW", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2); } + break; + + case 2: // ban + if(lt != LIS_DVBAPI) + { + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 2(%s) BAN Client - Kill and set Client to failban list for %i sec.", username(client), active_sid_count, max_active_sids, info1, info2, penalty_duration); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 2(%s) BAN Client - Kill and set Client to failban list for %i sec.", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2, penalty_duration); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 2(%s) BAN Client - Kill and set Client to failban list for %i sec.", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2, penalty_duration); } + cs_add_violation_acosc(client, client->account->usr, penalty_duration); + add_job(client, ACTION_CLIENT_KILL, NULL, 0); + } + else + { + cs_log("[zaplist] ACoSC for Client: %s %i:%i(%s) penalty: 2(%s) BAN Client - don't Ban dvbapi user only stop decoding", username(client), active_sid_count, max_active_sids, info1, info2); + } + er->rc = E_DISABLED; + break; + + case 3: // delay + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 3(%s) delay CW: %ims(%s)", username(client), active_sid_count, max_active_sids, info1, info2, delay, info4); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 3(%s) delay CW: %ims(%s)", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2, delay, info4); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 3(%s) delay CW: %ims(%s)", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2, delay, info4); } + cs_writeunlock(__func__, &clientlist_lock); + cs_sleepms(delay); + cs_writelock(__func__, &clientlist_lock); + client->cwlastresptime += delay; + snprintf(sreason, sizeof(sreason)-1, " (%d ms penalty delay)", delay); + break; + case 4: // hidecards + if(client->account->acosc_penalty_active == 3) + { + cs_log("[maxecms] ACoSC for Client: %s ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 4(%s) hidecards - hidecards to the client for %i sec", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2, penalty_duration); + client->start_hidecards = 1; + } + break; + default: // logging + if(client->account->acosc_penalty_active == 1) + { cs_log("[zaplist] ACoSC for Client: %s max_activ_sids reached: %i:%i(%s) penalty: 0(%s) only logging", username(client), active_sid_count, max_active_sids, info1, info2); } + if(client->account->acosc_penalty_active == 2) + { cs_log("[zaplist] ACoSC for Client: %s zap_limit reached: %i:%i(%s) penalty: 0(%s) only logging", username(client), client->account->acosc_user_zap_count, zap_limit, info5, info2); } + if(client->account->acosc_penalty_active == 3) + { cs_log("[maxecms] ACoSC for Client: %s max_ecms_per_minute reached: ecms_last_minute=%i ecms_now=%i max=%i(%s) penalty: 0(%s) only logging", username(client), client->n_request[0], client->n_request[1], max_ecms_per_minute, info6, info2); } + break; + } + client->account->acosc_user_zap_count = 0; // we got already a penalty + client->account->acosc_penalty_active = 3; + client->account->acosc_penalty_active = 4; + } + } + cs_writeunlock(__func__, &clientlist_lock); + } +#endif + + if(cfg.double_check && er->rc <= E_CACHE2 && er->selected_reader && is_double_check_caid(er, &cfg.double_check_caid)) + { + if(er->checked == 0) // First CW, save it and wait for next one + { + er->checked = 1; + er->origin_reader = er->selected_reader; + memcpy(er->cw_checked, er->cw, sizeof(er->cw)); + cs_log("DOUBLE CHECK FIRST CW by %s idx %d cpti %d", er->origin_reader->label, er->idx, er->msgid); + } + else if(er->origin_reader != er->selected_reader) // Second (or third and so on) cw. We have to compare + { + if(memcmp(er->cw_checked, er->cw, sizeof(er->cw)) == 0) + { + er->checked++; + cs_log("DOUBLE CHECKED! %d. CW by %s idx %d cpti %d", er->checked, er->selected_reader->label, er->idx, er->msgid); + } + else + { + cs_log("DOUBLE CHECKED NONMATCHING! %d. CW by %s idx %d cpti %d", er->checked, er->selected_reader->label, er->idx, er->msgid); + } + } + if(er->checked < 2) // less as two same cw? mark as pending! + { + er->rc = E_UNHANDLED; + goto ESC; + } + } + + ac_chk(client, er, 1); + int32_t is_fake = 0; + if(er->rc == E_FAKE) + { + is_fake = 1; + er->rc = E_FOUND; + } + + get_module(client)->send_dcw(client, er); + + add_cascade_data(client, er); + + if(is_fake) + { er->rc = E_FAKE; } + +#ifdef CS_ANTICASC + cs_writelock(__func__, &clientlist_lock); + if(client->start_hidecards) + { + client->start_hidecards = 0; + add_job(client, ACTION_CLIENT_HIDECARDS, NULL, 0); + } + cs_writeunlock(__func__, &clientlist_lock); +#endif + + if(!(er->rc == E_SLEEPING && client->cwlastresptime == 0)) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + if(er->reader_avail == 1 || er->stage == 0) + { + cs_log("%s (%s): %s (%d ms)%s%s%s%s", usrname, buf, er->rcEx ? erEx : stxt[er->rc], + client->cwlastresptime, sby, schaninfo, sreason, scwcinfo); + } + else + { + cs_log("%s (%s): %s (%d ms)%s (%c/%d/%d/%d)%s%s%s%s", usrname, buf, er->rcEx ? erEx : stxt[er->rc], + client->cwlastresptime, sby, stageTxt[er->stage], er->reader_requested, + (er->reader_count + er->fallback_reader_count), er->reader_avail, schaninfo, + srealecmtime, sreason, scwcinfo); + } + } + + cs_log_dump_dbg(D_ATR, er->cw, 16, "cw:"); + led_status_cw_not_found(er); + +ESC: + + return 0; +} + +/* + * write_ecm_request(): + */ +static int32_t write_ecm_request(struct s_reader *rdr, ECM_REQUEST *er) +{ + add_job(rdr->client, ACTION_READER_ECM_REQUEST, (void *)er, 0); + return 1; +} + +/** + * sends the ecm request to the readers + * ECM_REQUEST er : the ecm + * er->stage: 0 = no reader asked yet + * 2 = ask only local reader (skipped without preferlocalcards) + * 3 = ask any non fallback reader + * 4 = ask fallback reader + **/ +void request_cw_from_readers(ECM_REQUEST *er, uint8_t stop_stage) +{ + struct s_ecm_answer *ea; + int8_t sent = 0; + + if(er->stage >= 4) { return; } + + while(1) + { + if(stop_stage && er->stage >= stop_stage) { return; } + + er->stage++; + +#ifdef CS_CACHEEX + if(er->stage == 1 && er->preferlocalcards==2) + { er->stage++; } +#else + if(er->stage == 1) + { er->stage++; } +#endif + + if(er->stage == 2 && !er->preferlocalcards) + { er->stage++; } + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + switch(er->stage) + { +#ifdef CS_CACHEEX + case 1: + { + // Cache-Exchange + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_CACHEEX | READER_ACTIVE)) != (READER_CACHEEX | READER_ACTIVE)) + { continue; } + break; + } +#endif + case 2: + { + // only local reader + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_ACTIVE | READER_FALLBACK | READER_LOCAL)) != (READER_ACTIVE | READER_LOCAL)) + { continue; } + break; + } + case 3: + { + // any non fallback reader not asked yet + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_ACTIVE | READER_FALLBACK)) != READER_ACTIVE) + { continue; } + break; + } + default: + { + // only fallbacks + if((ea->status & REQUEST_SENT) || + (ea->status & (READER_ACTIVE | READER_FALLBACK)) != (READER_ACTIVE | READER_FALLBACK)) + { continue; } + break; + } + } + + struct s_reader *rdr = ea->reader; +#ifdef WITH_DEBUG + if (cs_dblevel & (D_TRACE | D_CSP)) + { + char ecmd5[17 * 3]; + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + cs_log_dbg(D_TRACE | D_CSP, "request_cw stage=%d to reader %s ecm hash=%s", er->stage, rdr ? rdr->label : "", ecmd5); + } +#endif + // maxparallel: check if reader has capacity (slot is reserved later when CW found) + if(rdr->maxparallel > 0 && !reader_has_capacity(rdr, er->srvid, er->client)) + { + cs_log_dbg(D_LB, "reader %s skipped for %s srvid %04X (maxparallel %d reached)", + rdr->label, check_client(er->client) ? er->client->account->usr : "-", + er->srvid, rdr->maxparallel); + continue; + } + + ea->status |= REQUEST_SENT; + cs_ftime(&ea->time_request_sent); + + er->reader_requested++; + + write_ecm_request(ea->reader, er); + + // set sent=1 only if reader is active/connected. If not, switch to next stage! + if(!sent && rdr) + { + struct s_client *rcl = rdr->client; + if(check_client(rcl)) + { + if(rcl->typ == 'r' && rdr->card_status == CARD_INSERTED) + { sent = 1; } + else if(rcl->typ == 'p' && (rdr->card_status == CARD_INSERTED || rdr->tcp_connected)) + { sent = 1; } + } + } + + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_request] reader %s --> SENT %d", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, rdr ? ea->reader->label : "-", sent); + } + + if(sent || er->stage >= 4) + { break; } + } +} + +void add_cache_from_reader(ECM_REQUEST *er, struct s_reader *rdr, uint32_t csp_hash, uint8_t *ecmd5, uint8_t *cw, int16_t caid, int32_t prid, int16_t srvid +#ifdef CS_CACHEEX_AIO + , int32_t ecm_time +#endif +) +{ + ECM_REQUEST *ecm; + if (cs_malloc(&ecm, sizeof(ECM_REQUEST))) + { + cs_ftime(&ecm->tps); + + ecm->cwc_cycletime = er->cwc_cycletime; + ecm->cwc_next_cw_cycle = er->cwc_next_cw_cycle; + memcpy(ecm->ecm, er->ecm, sizeof(ecm->ecm)); // ecm[0] is pushed to cacheexclients so we need a copy from it + ecm->caid = caid; + ecm->prid = prid; + ecm->srvid = srvid; + memcpy(ecm->ecmd5, ecmd5, CS_ECMSTORESIZE); + ecm->csp_hash = csp_hash; + ecm->rc = E_FOUND; + memcpy(ecm->cw, cw, sizeof(ecm->cw)); + ecm->grp = rdr->grp; + ecm->selected_reader = rdr; +#ifdef CS_CACHEEX_AIO + ecm->ecm_time = ecm_time; + ecm->localgenerated = er->localgenerated; +#endif +#ifdef CS_CACHEEX + if(rdr && cacheex_reader(rdr)) + { ecm->cacheex_src = rdr->client; } //so adds hits to reader +#endif + + add_cache(ecm); //add cw to cache + +#ifdef CS_CACHEEX + cs_writelock(__func__, &ecm_pushed_deleted_lock); + ecm->next = ecm_pushed_deleted; + ecm_pushed_deleted = ecm; + cs_writeunlock(__func__, &ecm_pushed_deleted_lock); +#else + NULLFREE(ecm); +#endif + } +} + +void chk_dcw(struct s_ecm_answer *ea) +{ + if(!ea || !ea->er) + { return; } + + ECM_REQUEST *ert = ea->er; + struct s_ecm_answer *ea_list; + struct s_reader *eardr = ea->reader; + if(!ert || !eardr) + { return; } + + // ecm request already answered! + if(ert->rc < E_99) + { +#ifdef CS_CACHEEX + if(ea && ert->rc < E_NOTFOUND && ea->rc < E_NOTFOUND && memcmp(ea->cw, ert->cw, sizeof(ert->cw)) != 0) + { + char cw1[16 * 3 + 2], cw2[16 * 3 + 2]; +#ifdef WITH_DEBUG + if(cs_dblevel & D_TRACE) + { + cs_hexdump(0, ea->cw, 16, cw1, sizeof(cw1)); + cs_hexdump(0, ert->cw, 16, cw2, sizeof(cw2)); + } +#endif + char ip1[20] = "", ip2[20] = ""; + if(ea->reader && check_client(ea->reader->client)) { cs_strncpy(ip1, cs_inet_ntoa(ea->reader->client->ip), sizeof(ip1)); } + if(ert->cacheex_src) { cs_strncpy(ip2, cs_inet_ntoa(ert->cacheex_src->ip), sizeof(ip2)); } + else if(ert->selected_reader && check_client(ert->selected_reader->client)) { cs_strncpy(ip2, cs_inet_ntoa(ert->selected_reader->client->ip), sizeof(ip2)); } + + ECM_REQUEST *er = ert; + debug_ecm(D_TRACE, "WARNING2: Different CWs %s from %s(%s)<>%s(%s): %s<>%s", buf, + username(ea->reader ? ea->reader->client : ert->client), ip1, + er->cacheex_src ? username(er->cacheex_src) : (ert->selected_reader ? ert->selected_reader->label : "unknown/csp"), ip2, + cw1, cw2); + } +#endif + + return; + } + +#ifdef CS_CACHEEX + /* if answer from cacheex-1 reader, not send answer to client! + * thread check_cache will check counter and send answer to client! + * Anyway, we should check if we have to go to oher stage (>1) + */ + + if(eardr && cacheex_reader(eardr)) + { + // if wait_time, and not wait_time expired and wait_time due to hitcache(or awtime>0), + // we have to wait cacheex timeout before call other readers (stage>1) + if(cacheex_reader(eardr) && !ert->cacheex_wait_time_expired && ert->cacheex_hitcache) + { return; } + + int8_t cacheex_left = 0; + uint8_t has_cacheex = 0; + if(ert->stage == 1) + { + for(ea_list = ert->matching_rdr; ea_list; ea_list = ea_list->next) + { + cs_readlock(__func__, &ea_list->ecmanswer_lock); + if(((ea_list->status & (READER_CACHEEX | READER_FALLBACK | READER_ACTIVE))) == (READER_CACHEEX | READER_ACTIVE)) + { has_cacheex = 1; } + if((!(ea_list->status & READER_FALLBACK) && ((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED | READER_CACHEEX | READER_ACTIVE)) == (REQUEST_SENT | READER_CACHEEX | READER_ACTIVE))) || ea_list->rc < E_NOTFOUND) + { cacheex_left++; } + cs_readunlock(__func__, &ea_list->ecmanswer_lock); + } + + if(has_cacheex && !cacheex_left) { request_cw_from_readers(ert, 0); } + } + + return; + } +#endif + + int32_t reader_left = 0, local_left = 0, reader_not_flb_left = 0, has_not_fallback = 0, has_local = 0; + ert->selected_reader = eardr; + + switch(ea->rc) + { + case E_FOUND: + // maxparallel: register this service on the winning reader + if(eardr->maxparallel > 0) + { reader_register_service(eardr, ert->srvid, ert->client); } + + memcpy(ert->cw, ea->cw, 16); + ert->cw_ex = ea->cw_ex; + ert->rcEx = 0; + ert->rc = ea->rc; + ert->grp |= eardr->grp; + cs_strncpy(ert->msglog, ea->msglog, sizeof(ert->msglog)); +#ifdef HAVE_DVBAPI + ert->adapter_index = ea->er->adapter_index; +#endif + break; + + case E_INVALID: + case E_NOTFOUND: + { + // check if there are other readers to ask, and if not send NOT_FOUND to client + ert->rcEx = ea->rcEx; + cs_strncpy(ert->msglog, ea->msglog, sizeof(ert->msglog)); + + for(ea_list = ert->matching_rdr; ea_list; ea_list = ea_list->next) + { + cs_readlock(__func__, &ea_list->ecmanswer_lock); + + if((!(ea_list->status & READER_FALLBACK) && ((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED | READER_LOCAL | READER_ACTIVE)) == (REQUEST_SENT | READER_LOCAL | READER_ACTIVE))) || ea_list->rc < E_NOTFOUND) + { local_left++; } + + if((!(ea_list->status & READER_FALLBACK) && ((ea_list->status & (REQUEST_SENT | REQUEST_ANSWERED | READER_ACTIVE)) == (REQUEST_SENT | READER_ACTIVE))) || ea_list->rc < E_NOTFOUND) + { reader_not_flb_left++; } + + if(((ea_list->status & (REQUEST_ANSWERED | READER_ACTIVE)) == (READER_ACTIVE)) || ea_list->rc < E_NOTFOUND) + { reader_left++; } + + if(((ea_list->status & (READER_FALLBACK | READER_ACTIVE))) == (READER_ACTIVE)) + { has_not_fallback = 1; } + if(((ea_list->status & (READER_LOCAL | READER_FALLBACK | READER_ACTIVE))) == (READER_LOCAL | READER_ACTIVE)) + { has_local = 1; } + + cs_readunlock(__func__, &ea_list->ecmanswer_lock); + } + + switch(ert->stage) + { + case 2: // only local reader (used only if preferlocalcards=1) + { + if(has_local && !local_left) { request_cw_from_readers(ert, 0); } + break; + } + case 3: + { + // any fallback reader not asked yet + if(has_not_fallback && !reader_not_flb_left) { request_cw_from_readers(ert, 0); } + break; + } + } + + if(!reader_left // no more matching reader +#ifdef CS_CACHEEX + && !cfg.wait_until_ctimeout +#endif + ) + { ert->rc = E_NOTFOUND; } // so we set the return code + + break; + } + + case E_TIMEOUT: // if timeout, we have to send timeout to client: this is done by ecm_timeout callback + return; + break; + + case E_UNHANDLED: + return; + break; + + default: + cs_log("unexpected ecm answer rc=%d.", ea->rc); + return; + break; + } + + if(ert->rc < E_99) + send_dcw(ert->client, ert); +} + +uint32_t chk_provid(uint8_t *ecm, uint16_t caid) +{ + int32_t i, len, descriptor_length = 0; + uint32_t provid = 0; + + switch(caid >> 8) + { + case 0x01: // seca + provid = b2i(2, ecm + 3); + break; + + case 0x05: // viaccess + i = (ecm[4] == 0xD2) ? ecm[5] + 2 : 0; // skip d2 nano + if((ecm[5 + i] == 3) && ((ecm[4 + i] == 0x90) || (ecm[4 + i] == 0x40))) + { provid = (b2i(3, ecm + 6 + i) & 0xFFFFF0); } + + i = (ecm[6] == 0xD2) ? ecm[7] + 2 : 0; // skip d2 nano long ecm + if((ecm[7 + i] == 7) && ((ecm[6 + i] == 0x90) || (ecm[6 + i] == 0x40))) + { provid = (b2i(3, ecm + 8 + i) & 0xFFFFF0); } + break; + + case 0x0D: // cryptoworks + len = (((ecm[1] & 0xf) << 8) | ecm[2]) + 3; + for(i = 8; i < len; i += descriptor_length + 2) + { + descriptor_length = ecm[i + 1]; + if(ecm[i] == 0x83) + { + provid = (uint32_t)ecm[i + 2] & 0xFE; + break; + } + } + break; + + case 0x18: // nagra2 + if (caid == 0x1801) // more safety + provid = b2i(2, ecm + 5); + break; + } + + return provid; +} + +void update_chid(ECM_REQUEST *er) +{ + er->chid = get_subid(er); +} + +/* + * This function writes the current CW from ECM struct to a cwl file. + * The filename is re-calculated and file re-opened every time. + * This will consume a bit cpu time, but nothing has to be stored between + * each call. If not file exists, a header is prepended + */ +static void logCWtoFile(ECM_REQUEST *er, uint8_t *cw) +{ + FILE *pfCWL; + char srvname[CS_SERVICENAME_SIZE]; + /* %s / %s _I %04X _ %s .cwl */ + char buf[256 + sizeof(srvname)]; + char date[9]; + uint8_t i, parity, writeheader = 0; + struct tm timeinfo; + + /* + * search service name for that id and change characters + * causing problems in file name + */ + + get_servicename(cur_client(), er->srvid, er->prid, er->caid, srvname, sizeof(srvname)); + + for(i = 0; srvname[i]; i++) + if(srvname[i] == ' ') { srvname[i] = '_'; } + + /* calc log file name */ + time_t walltime = cs_time(); + localtime_r(&walltime, &timeinfo); + strftime(date, sizeof(date), "%Y%m%d", &timeinfo); + snprintf(buf, sizeof(buf), "%s/%s_I%04X_%s.cwl", cfg.cwlogdir, date, er->srvid, srvname); + + /* open failed, assuming file does not exist, yet */ + if((pfCWL = fopen(buf, "r")) == NULL) + { + writeheader = 1; + } + else + { + /* we need to close the file if it was opened correctly */ + fclose(pfCWL); + } + + if((pfCWL = fopen(buf, "a+")) == NULL) + { + /* maybe this fails because the subdir does not exist. Is there a common function to create it? + for the moment do not print32_t to log on every ecm + cs_log(""error opening cw logfile for writing: %s (errno=%d %s)", buf, errno, strerror(errno)); */ + return; + } + if(writeheader) + { + /* no global macro for cardserver name :( */ + fprintf(pfCWL, "# OSCam cardserver v%s - %s\n", CS_VERSION, SCM_URL); + fprintf(pfCWL, "# control word log file for use with tsdec offline decrypter\n"); + strftime(buf, sizeof(buf), "DATE %Y-%m-%d, TIME %H:%M:%S, TZ %Z\n", &timeinfo); + fprintf(pfCWL, "# %s", buf); + fprintf(pfCWL, "# CAID 0x%04X, SID 0x%04X, SERVICE \"%s\"\n", er->caid, er->srvid, srvname); + } + + parity = er->ecm[0] & 1; + fprintf(pfCWL, "%d ", parity); + for(i = parity * 8; i < 8 + parity * 8; i++) + { fprintf(pfCWL, "%02X ", cw[i]); } + /* better use incoming time er->tps rather than current time? */ + strftime(buf, sizeof(buf), "%H:%M:%S\n", &timeinfo); + fprintf(pfCWL, "# %s", buf); + fflush(pfCWL); + fclose(pfCWL); +} + +int32_t write_ecm_answer(struct s_reader *reader, ECM_REQUEST *er, int8_t rc, uint8_t rcEx, uint8_t *cw, char *msglog, uint16_t used_cardtier, EXTENDED_CW* cw_ex) +{ + if(!reader || !er || !er->tps.time) { return 0; } + + // drop too late answers, to avoid seg fault --> only answer until tps.time+((cfg.ctimeout+500)/1000+1) is accepted + time_t timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1); + if(er->tps.time < timeout) // < and NOT <= + { return 0; } + + struct timeb now; + cs_ftime(&now); + +#ifdef CS_CACHEEX_AIO + uint8_t dontsetAnswered = 0; +#endif + uint8_t dontwriteStats = 0; + + if(er && er->parent) + { + // parent is only set on reader->client->ecmtask[], but we want original er + ECM_REQUEST *er_reader_cp = er; + er = er->parent; // Now er is "original" ecm, before it was the reader-copy + er_reader_cp->rc = rc; + er_reader_cp->idx = 0; +#ifdef CS_CACHEEX_AIO + er->localgenerated = er_reader_cp->localgenerated; +#endif + + timeout = time(NULL) - ((cfg.ctimeout + 500) / 1000 + 1); + if(er->tps.time < timeout) + { return 0; } + } + +#ifdef CS_CACHEEX_AIO + if(rc < E_NOTFOUND && !er->localgenerated && (reader->cacheex.localgenerated_only_in || chk_lg_only(er, &reader->cacheex.lg_only_in_tab)) && !chk_srvid_localgenerated_only_exception(er)) + { + cs_log_dbg(D_CACHEEX, "reader: %s !er->localgenerated - rc: E_NOTFOUND set, no stats written for reader", reader ? reader->label : "-"); + rc = E_NOTFOUND; + dontsetAnswered = 1; + dontwriteStats = 1; + } +#endif + + struct s_ecm_answer *ea = get_ecm_answer(reader, er); + if(!ea) { return 0; } + + cs_writelock(__func__, &ea->ecmanswer_lock); + + if((ea->status & REQUEST_ANSWERED)) + { + cs_log_dbg(D_READER, "Reader %s already answer, skip this ecm answer!", reader ? reader->label : "-"); + cs_writeunlock(__func__, &ea->ecmanswer_lock); + return 0; + } + + // bad/wrong chksum/ecm + if(rc == E_NOTFOUND && rcEx == E2_WRONG_CHKSUM) + { + cs_log_dbg(D_READER, "ECM for reader %s was bad/has a wrong chksum!", reader ? reader->label : "-"); + rc = E_INVALID; + rcEx = E2_WRONG_CHKSUM; + er->stage = 5; + + // dont write stats for bad/wrong chksum/ecm + dontwriteStats = 1; + + // set all other matching_readers => inactive to skip them and dont spread the bad ecm + struct s_ecm_answer *ea_list; + for(ea_list = er->matching_rdr; ea_list; ea_list = ea_list->next) + { + ea_list->status &= ~(READER_ACTIVE | READER_FALLBACK); + } + } + + if(rc < E_NOTFOUND && cw && chk_is_null_CW(cw)) + { + rc = E_NOTFOUND; + cs_log_dbg(D_TRACE | D_LB, "WARNING: reader %s send fake cw, set rc=E_NOTFOUND!", reader ? reader->label : "-"); + } + + if(rc < E_NOTFOUND && cw && !chk_halfCW(er,cw)) + { + rc = E_NOTFOUND; + cs_log_dbg(D_TRACE | D_LB, "WARNING: reader %s send wrong swapped NDS cw, set rc=E_NOTFOUND!", reader ? reader->label : "-"); + } + + if(reader && cw && rc < E_NOTFOUND) + { + if(!cfg.disablecrccws && !reader->disablecrccws) + { + if(!chk_if_ignore_checksum(er, &cfg.disablecrccws_only_for) && !chk_if_ignore_checksum(er, &reader->disablecrccws_only_for)) + { + uint8_t i, c; + for(i = 0; i < 16; i += 4) + { + c = ((cw[i] + cw[i + 1] + cw[i + 2]) & 0xff); + + if(cw[i + 3] != c) + { + uint8_t nano = 0x00; + if(er->caid == 0x100 && er->ecm[5] > 0x00) + { + nano = er->ecm[5]; // seca nano protection + } + + if(reader->dropbadcws && !nano) // only drop controlword if no cw encryption is applied + { + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + break; + } + else + { + if(!nano) // only fix checksum if no cw encryption is applied (nano = 0) + { + cs_log_dbg(D_TRACE, "notice: changed dcw checksum byte cw[%i] from %02x to %02x", i + 3, cw[i + 3], c); + cw[i + 3] = c; + } + else + { + if(i == 12) // there are servers delivering correct controlwords but with failing last cw checksum (on purpose?!) + { + cs_log_dbg(D_TRACE,"NANO%02d: BAD PEER DETECTED, oscam has fixed the last cw crc that wasn't matching!", nano); + cw[i + 3] = c; // fix the last controlword + } + else + { + cs_log_dbg(D_TRACE,"NANO%02d: not fixing the crc of this cw since its still encrypted!", nano); + break; // crc failed so stop! + } + } + } + } + } + } + else + { + cs_log_dbg(D_TRACE, "notice: CW checksum check disabled for %04X:%06X", er->caid, er->prid); + } + } + else + { + cs_log_dbg(D_TRACE, "notice: CW checksum check disabled"); + } + + if(chk_if_ignore_checksum(er, &reader->disablecrccws_only_for) && caid_is_videoguard(er->caid) +#ifdef CS_CACHEEX_AIO + && !chk_srvid_disablecrccws_only_for_exception(er) +#endif + ) + { + uint8_t k, csum; + uint8_t hit = 0; + uint8_t oe = checkCWpart(cw, 0) ? 0 : 8; + for(k = 0; k < 8; k += 4) + { + csum = ((cw[k + oe] + cw[k + oe + 1] + cw[k + oe + 2]) & 0xff); + if(cw[k + oe + 3] == csum) + { + hit++; + } + } + if(hit > 1) + { + char ecmd5s[17 * 3]; + cs_hexdump(0, er->ecmd5, 16, ecmd5s, sizeof(ecmd5s)); + if(reader->dropbadcws) + { + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + cs_log("Probably got bad CW from reader: %s, caid %04X, srvid %04X (%s) - dropping CW, lg: %i", reader->label, er->caid, er->srvid, ecmd5s +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + , 0); +#endif + } + else + { + cs_log("Probably got bad CW from reader: %s, caid %04X, srvid %04X (%s), lg: %i", reader->label, er->caid, er->srvid, ecmd5s +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + , 0); +#endif + } + } + } + + } + +#ifdef CW_CYCLE_CHECK + uint8_t cwc_ct = er->cwc_cycletime > 0 ? er->cwc_cycletime : 0; + uint8_t cwc_ncwc = er->cwc_next_cw_cycle < 2 ? er->cwc_next_cw_cycle : 2; + if(!checkcwcycle(er->client, er, reader, cw, rc, cwc_ct, cwc_ncwc)) + { +#ifdef CS_CACHEEX_AIO + if(!er->localgenerated) + { +#endif + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + cs_log_dbg(D_CACHEEX | D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck failed! Reader: %s set rc: %i", (er->client ? er->client->account->usr : "-"), er->caid, er->srvid, reader ? reader->label : "-", rc); + +#ifdef CS_CACHEEX_AIO + } + else + { + cs_log_dbg(D_CACHEEX | D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck failed! Reader: %s set rc: %i -> lg-flagged CW -> do nothing", (er->client ? er->client->account->usr : "-"), er->caid, er->srvid, reader ? reader->label : "-", rc); + } +#endif + } + else { cs_log_dbg(D_CACHEEX | D_CWC | D_LB, "{client %s, caid %04X, srvid %04X} [write_ecm_answer] cyclecheck passed! Reader: %s rc: %i", (er->client ? er->client->account->usr : "-"), er->caid, er->srvid, reader ? reader->label : "-", rc); } +#endif + //END -- SPECIAL CHECKs for rc + + +//////////////// TRUST / ENTROPY / AUTODETECT BLOCK //////////////////////////////////////////////////////////////// +if(rc == E_FOUND && cw && !chk_is_null_CW(cw)) +{ + /* SKIP CACHEEX readers */ + if(reader && reader->cacheex.mode) + goto TRUST_DONE; + + /* Używamy lokalnej kopii CW do testów */ + uint8_t testcw[16]; + memcpy(testcw, cw, 16); + + //////////////////////////////////////////////////////////// + // ENTROPY CALC + //////////////////////////////////////////////////////////// + + double full = entropy_calc(testcw, 16); + double half1 = entropy_calc(testcw, 8); + double half2 = entropy_calc(testcw + 8, 8); + + double threshold; + double entropy_value; + + // NDS HALF entropy (always 64bit CAID) + if(er->caid == 0x098D || er->caid == 0x098C) + { + entropy_value = (half1 > half2) ? half1 : half2; + threshold = 1.35; + } + else + { + entropy_value = full; + threshold = 2.5778; + } + + //////////////////////////////////////////////////////////// + // ENTROPY BLOCK (LEVEL 1) + //////////////////////////////////////////////////////////// + + if(entropy_value < threshold) + { + cs_log("TRUST BLOCK ENTROPY reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f", + reader ? reader->label : "?", + er->caid, + er->prid, + er->srvid, + entropy_value, + threshold); + + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + + cw = NULL; + + goto TRUST_DONE; + } + + //////////////////////////////////////////////////////////// + // AUTODETECT BLOCK (LEVEL 2) + //////////////////////////////////////////////////////////// + + /* ustaw testcw tymczasowo do er->cw dla autodetect */ + uint8_t original_cw[16]; + memcpy(original_cw, er->cw, 16); + + memcpy(er->cw, testcw, 16); + + if(!cw_autodetect_and_block(reader, er)) + { + cs_log("TRUST BLOCK AUTODETECT reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f", + reader ? reader->label : "?", + er->caid, + er->prid, + er->srvid, + entropy_value, + threshold); + + rc = E_NOTFOUND; + rcEx = E2_WRONG_CHKSUM; + + memset(er->cw, 0, 16); + cw = NULL; + + goto TRUST_DONE; + } + + //////////////////////////////////////////////////////////// + // TRUST OK → FINAL COPY + //////////////////////////////////////////////////////////// + + memcpy(er->cw, testcw, 16); + + cs_log("TRUST OK reader=%s (%04X@%06X/%04X) entropy=%.4f threshold=%.4f", + reader ? reader->label : "?", + er->caid, + er->prid, + er->srvid, + entropy_value, + threshold); + +TRUST_DONE: + ; +} + +// global protection +if(rc != E_FOUND) +{ + cw = NULL; +} +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef CS_CACHEEX_AIO + if(!dontsetAnswered) + { +#endif + ea->status |= REQUEST_ANSWERED; +#ifdef CS_CACHEEX_AIO + } +#endif + ea->rc = rc; + ea->ecm_time = comp_timeb(&now, &ea->time_request_sent); + if(ea->ecm_time < 1) { ea->ecm_time = 1; } // set ecm_time 1 if answer immediately + ea->rcEx = rcEx; + if(cw) { memcpy(ea->cw, cw, 16); } + if(msglog) { memcpy(ea->msglog, msglog, MSGLOGSIZE); } + ea->tier = used_cardtier; + if(cw_ex) + { + ea->cw_ex = *cw_ex; + } + + cs_writeunlock(__func__, &ea->ecmanswer_lock); + + struct timeb tpe; + cs_ftime(&tpe); + int32_t ntime = comp_timeb(&tpe, &er->tps); + if(ntime < 1) { ntime = 1; } + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer] reader %s rc %d, ecm time %d ms (%d ms)", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader ? reader->label : "-", rc, ea->ecm_time, ntime); + + // send ea for ecm request + int32_t res = 0; + struct s_client *cl = er->client; + if(check_client(cl)) + { + res = 1; + add_job(er->client, ACTION_ECM_ANSWER_READER, ea, 0); // chk_dcw + } + + // distribute ea for pendings + if(ea->pending) // has pending ea + { distribute_ea(ea); } + + + if(!ea->is_pending) // not for pending ea - only once for ea + { + // cache update + if(ea && (ea->rc < E_NOTFOUND) && !chk_is_null_CW(ea->cw)) + { +#ifdef CS_CACHEEX_AIO + int32_t ecmtime = ea->ecm_time; + + if(er->cacheex_wait_time_expired && er->cacheex_wait_time) + ecmtime = ea->ecm_time + er->cacheex_wait_time; +#endif + add_cache_from_reader(er, reader, er->csp_hash, er->ecmd5, ea->cw, er->caid, er->prid, er->srvid +#ifdef CS_CACHEEX_AIO + , ecmtime); +#else + ); +#endif + } + + if(!dontwriteStats) + { + // readers stats for LB + send_reader_stat(reader, er, ea, ea->rc); + } + + // reader checks +#ifdef WITH_DEBUG + if(cs_dblevel & D_TRACE) + { + char ecmd5[17 * 3]; + cs_hexdump(0, er->ecmd5, 16, ecmd5, sizeof(ecmd5)); + rdr_log_dbg(reader, D_TRACE, "ecm answer for ecm hash %s rc=%d", ecmd5, ea->rc); + } +#endif + // Update reader stats: + if(ea->rc == E_FOUND) + { + if(cfg.cwlogdir != NULL) + { logCWtoFile(er, ea->cw); } // CWL logging only if cwlogdir is set in config + + reader->ecmsok++; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + reader->ecmsoklg++; +#endif + reader->webif_ecmsok++; +#ifdef CS_CACHEEX + struct s_client *eacl = reader->client; + if(cacheex_reader(reader) && check_client(eacl)) + { + eacl->cwcacheexgot++; + cacheex_add_stats(eacl, ea->er->caid, ea->er->srvid, ea->er->prid, 1 +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + ); +#endif + first_client->cwcacheexgot++; +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + eacl->cwcacheexgotlg++; + first_client->cwcacheexgotlg++; + } +#endif + } +#endif + } + else if(ea->rc == E_NOTFOUND) + { + reader->ecmsnok++; + reader->webif_ecmsnok++; + if(reader->ecmnotfoundlimit && reader->ecmsnok >= reader->ecmnotfoundlimit) + { + rdr_log(reader, "ECM not found limit reached %u. Restarting the reader.", + reader->ecmsnok); + reader->ecmsnok = 0; // Reset the variable + reader->ecmshealthnok = 0; // Reset the variable + add_job(reader->client, ACTION_READER_RESTART, NULL, 0); + } + } + + // this fixes big oscam mistake + // wrong reader status on web info aka not counted timeouts which dispalyed + // reader info 100 percent OK but reader had a ton of unhandled timeouts! + else if(ea->rc == E_TIMEOUT) + { +#ifdef WITH_LB + STAT_QUERY q; + readerinfofix_get_stat_query(er, &q); + READER_STAT *s; + s = readerinfofix_get_add_stat(reader, &q); + if (s) + { + cs_log_dbg(D_LB, "inc fail {client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer] reader %s rc %d, ecm time %d ms (%d ms)", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, reader ? reader->label : "-", rc, ea->ecm_time, ntime); + readerinfofix_inc_fail(s); // now increase fail factor for unhandled timeouts + } +#endif + reader->ecmstout++; // now append timeouts to the readerinfo timeout count + reader->webif_ecmstout++; + } + + // Reader ECMs Health Try (by Pickser) + if(reader->ecmsok != 0 || reader->ecmsnok != 0 || reader->ecmstout != 0) + { + reader->ecmshealthok = ((double) reader->ecmsok / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; +#ifdef CS_CACHEEX_AIO + reader->ecmshealthoklg = ((double) reader->ecmsoklg / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; +#endif + reader->ecmshealthnok = ((double) reader->ecmsnok / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; + reader->ecmshealthtout = ((double) reader->ecmstout / (reader->ecmsok + reader->ecmsnok + reader->ecmstout)) * 100; + } + + if(rc == E_FOUND && reader->resetcycle > 0) + { + reader->resetcounter++; + if(reader->resetcounter > reader->resetcycle) + { + reader->resetcounter = 0; + rdr_log(reader, "Resetting reader, resetcyle of %d ecms reached", reader->resetcycle); + reader->card_status = CARD_NEED_INIT; + cardreader_reset(cl); + } + } + } + + return res; +} + +// chid calculation from module stat to here +// to improve the quickfix concerning ecm chid info and extend it +// to all client requests wereby the chid is known in module stat + +uint32_t get_subid(ECM_REQUEST *er) +{ + if(!er->ecmlen) + { return 0; } + + uint32_t id = 0; + switch(er->caid >> 8) + { + case 0x01: // seca + id = b2i(2, er->ecm + 7); + break; + + case 0x05: // viaccess + id = b2i(2, er->ecm + 8); + break; + + case 0x06: // irdeto + id = b2i(2, er->ecm + 6); + break; + + case 0x09: // videoguard + id = b2i(2, er->ecm + 11); + break; + + case 0x4A: // DRE-Crypt, Bulcrypt, Tongfang and others? + if(!caid_is_bulcrypt(er->caid) && !caid_is_dre(er->caid)) + { id = b2i(2, er->ecm + 6); } + break; + } + return id; +} + +static void set_readers_counter(ECM_REQUEST *er) +{ + struct s_ecm_answer *ea; + + er->reader_count = 0; + er->fallback_reader_count = 0; + er->localreader_count = 0; + er->cacheex_reader_count = 0; + + for(ea = er->matching_rdr; ea; ea = ea->next) + { + if(ea->status & READER_ACTIVE) + { + if(!(ea->status & READER_FALLBACK)) + { er->reader_count++; } + else + { er->fallback_reader_count++; } + + if(cacheex_reader(ea->reader)) + { er->cacheex_reader_count++; } + else if(is_localreader(ea->reader, er)) + { er->localreader_count++; } + } + } +} + +void write_ecm_answer_fromcache(struct s_write_from_cache *wfc) +{ + ECM_REQUEST *er = NULL; + ECM_REQUEST *ecm = NULL; + + er = wfc->er_new; + ecm = wfc->er_cache; + +#ifdef CS_CACHEEX_AIO + if(ecm->localgenerated || (ecm->cw_count > 0x0F000000)) + er->localgenerated = 1; +#endif + + int8_t rc_orig = er->rc; + + er->grp |= ecm->grp; // update group +#ifdef CS_CACHEEX + if(ecm->from_csp) { er->csp_answered = 1; } // update er as answered by csp (csp have no group) +#endif + + if(er->rc >= E_NOTFOUND) + { +#ifdef CS_CACHEEX + if(ecm->cacheex_src) // from cacheex or csp + { + er->rc = E_CACHEEX; + } + else +#endif + { er->rc=E_CACHE1; } // from normal readers + + memcpy(er->cw, ecm->cw, 16); + +er->selected_reader = ecm->selected_reader; + +entropy_log_ecm(er, er->selected_reader); + +//////////////// AUTODETECT INVALID CW BLOCK //////////////////////////////////////////////// +if(!cw_autodetect_and_block(er->selected_reader, er)) +{ + return; +} +//////////////////////////////////////////////////////////////////////////////////////////// + +er->cw_count = ecm->cw_count; + +#ifdef CS_CACHEEX + // here we should be sure cex client has not been freed! + if(ecm->cacheex_src && is_valid_client(ecm->cacheex_src) && !ecm->cacheex_src->kill) + { + er->cacheex_src = ecm->cacheex_src; + er->cwc_cycletime = ecm->cwc_cycletime; + er->cwc_next_cw_cycle = ecm->cwc_next_cw_cycle; + } + else + { + er->cacheex_src = NULL; + } + + int8_t cacheex = check_client(er->client) && er->client->account ? er->client->account->cacheex.mode : 0; + if(cacheex == 1 && check_client(er->client)) + { + cacheex_add_stats(er->client, er->caid, er->srvid, er->prid, 0 +#ifdef CS_CACHEEX_AIO + , er->localgenerated); +#else + ); +#endif + er->client->cwcacheexpush++; + if(er->client->account) + { er->client->account->cwcacheexpush++; } + first_client->cwcacheexpush++; + +#ifdef CS_CACHEEX_AIO + if(er->localgenerated) + { + er->client->cwcacheexpushlg++; + first_client->cwcacheexpushlg++; + } +#endif + + } +#endif + +#ifdef CS_CACHEEX + if(cfg.delay && cacheex!=1) // No delay on cacheexchange mode 1 client! + { cs_sleepms(cfg.delay); } +#else + if(cfg.delay) + { cs_sleepms(cfg.delay); } +#endif + + if(rc_orig == E_UNHANDLED) + { + cs_log_dbg(D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [write_ecm_answer_fromcache] found cw in CACHE (count %d)!", (check_client(er->client)?er->client->account->usr:"-"),er->caid, er->prid, er->srvid, +#ifdef CS_CACHEEX_AIO + (er->cw_count > 0x0F000000) ? er->cw_count ^= 0x0F000000 : er->cw_count); +#else + er->cw_count); +#endif + send_dcw(er->client, er); + } + } +} + +#ifdef CS_CACHEEX_AIO +static bool ecm_cache_check(ECM_REQUEST *er) +{ + if(ecm_cache_init_done && cfg.ecm_cache_droptime > 0) + { + ECM_CACHE *ecm_cache = NULL; + SAFE_RWLOCK_WRLOCK(&ecm_cache_lock); + ecm_cache = find_hash_table(&ht_ecm_cache, &er->csp_hash, sizeof(uint32_t), &compare_csp_hash_ecmcache); + if(!ecm_cache) + { + // ecm_cache-size(count/memory) pre-check + if( + (cfg.ecm_cache_size && (cfg.ecm_cache_size > tommy_hashlin_count(&ht_ecm_cache))) + || (cfg.ecm_cache_memory && (cfg.ecm_cache_memory*1024*1024 > tommy_hashlin_memory_usage(&ht_ecm_cache))) + ) + { + if(cs_malloc(&ecm_cache, sizeof(ECM_CACHE))) + { + ecm_cache->csp_hash = er->csp_hash; + cs_ftime(&ecm_cache->first_recv_time); + cs_ftime(&ecm_cache->upd_time); + + tommy_hashlin_insert(&ht_ecm_cache, &ecm_cache->ht_node, ecm_cache, tommy_hash_u32(0, &er->csp_hash, sizeof(er->csp_hash))); + tommy_list_insert_tail(&ll_ecm_cache, &ecm_cache->ll_node, ecm_cache); + + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + return true; + } + else{ + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + cs_log("[ecm_cache] ERROR: NO added HASH to ecm_cache!!"); + return false; + } + } + else{ + // clean cache call; + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + ecm_cache_cleanup(true); + return false; + } + } + // ecm found + else{ + int64_t gone_diff = 0; + gone_diff = comp_timeb(&er->tps, &ecm_cache->first_recv_time); + cs_ftime(&ecm_cache->upd_time); + + if(gone_diff >= cfg.ecm_cache_droptime * 1000) + { + cs_log_dbg(D_CW_CACHE, "[ecm_cache] ECM drop, current ecm_cache_size: %i - ecm_cache-mem-size: %i MiB", count_hash_table(&ht_ecm_cache), (int)(tommy_hashlin_memory_usage(&ht_ecm_cache)/1024/1024)); + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + return false; + } + } + + SAFE_RWLOCK_UNLOCK(&ecm_cache_lock); + return true; + } + else{ + cs_log_dbg(D_CW_CACHE,"[ecm_cache] ecm_cache_init_done %i cfg.ecm_cache_size: %"PRIu32" cfg.ecm_cache_memory %"PRIu32" MiB", ecm_cache_init_done, cfg.ecm_cache_size, cfg.ecm_cache_memory); + return true; + } +} +#endif + +void get_cw(struct s_client *client, ECM_REQUEST *er) +{ + // clienttimeout per CAID override + uint32_t client_timeout = cfg.ctimeout; + int32_t percaid_timeout = caidvaluetab_get_value(&cfg.ctimeouttab, er->caid, 0); + + if(percaid_timeout > 0) + { + client_timeout = percaid_timeout; + } + + er->client_timeout = client_timeout; + +#ifdef CS_CACHEEX_AIO + + + cacheex_update_hash(er); + if(!ecm_cache_check(er)) + { + er->rc = E_INVALID; + send_dcw(client, er); + free_ecm(er); + return; + } +#endif + cs_log_dump_dbg(D_ATR, er->ecm, er->ecmlen, "get cw for ecm:"); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] NEW REQUEST!", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + increment_n_request(client); + + int32_t i, j, m; + time_t now = time((time_t *)0); + uint32_t line = 0; + uint16_t sct_len; + + er->client = client; + er->rc = E_UNHANDLED; // set default rc status to unhandled + er->cwc_next_cw_cycle = 2; // set it to: we dont know + + // user was on freetv or didn't request for some time + // so we reset lastswitch to get correct stats/webif display + if(now - client->lastecm > cfg.hideclient_to) { client->lastswitch = 0; } + client->lastecm = now; + + if(client == first_client || !client ->account || client->account == first_client->account) + { + // DVBApi+serial is allowed to request anonymous accounts: + int16_t listenertype = get_module(client)->listenertype; + if(listenertype != LIS_DVBAPI && listenertype != LIS_SERIAL) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "invalid user account %s", username(client)); + } + } + + // ecmlen must be 0 (no ecm) or >2 (because SCT_LEN() needs at least 3 bytes) + if(er->ecmlen < 0 || er->ecmlen == 1 || er->ecmlen == 2) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "ECM size %d invalid, ignored! client %s", er->ecmlen, username(client)); + } + + if(er->ecmlen > MAX_ECM_SIZE) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "ECM size %d > Max ECM size %d, ignored! client %s", er->ecmlen, MAX_ECM_SIZE, username(client)); + } + + if(er->ecmlen > 2) + { + sct_len = SCT_LEN(er->ecm); + if(sct_len > er->ecmlen || sct_len < 4) + { + er->rc = E_INVALID; + er->rcEx = E2_GLOBAL; + snprintf(er->msglog, sizeof(er->msglog), "Real ECM size %d > ECM size %d, ignored! client %s", sct_len, er->ecmlen, username(client)); + } + er->ecmlen = sct_len; + } + + if(!client->grp) + { + er->rc = E_INVALID; + er->rcEx = E2_GROUP; + snprintf(er->msglog, sizeof(er->msglog), "invalid user group %s", username(client)); + } + + // add chid for all client requests as in module stat + update_chid(er); + + // betacrypt ecm with nagra header + if(chk_is_betatunnel_caid(er->caid) == 1 && (er->ecmlen == 0x89 || er->ecmlen == 0x4A) && er->ecm[3] == 0x07 && (er->ecm[4] == 0x84 || er->ecm[4] == 0x45)) + { + if(er->caid == 0x1702) + { + er->caid = 0x1833; + } + else + { + check_lb_auto_betatunnel_mode(er); + } + cs_log_dbg(D_TRACE, "Quickfix remap beta->nagra: 0x%X, 0x%X, 0x%X, 0x%X", er->caid, er->ecmlen, er->ecm[3], er->ecm[4]); + } + + // nagra ecm with betacrypt header 1801, 1833, 1834, 1835 + if(chk_is_betatunnel_caid(er->caid) == 2 && (er->ecmlen == 0x93 || er->ecmlen == 0x54) && er->ecm[13] == 0x07 && (er->ecm[14] == 0x84 || er->ecm[14] == 0x45)) + { + if(er->caid == 0x1833) + { + er->caid = 0x1702; + } + else + { + er->caid = 0x1722; + } + cs_log_dbg(D_TRACE, "Quickfix remap nagra->beta: 0x%X, 0x%X, 0x%X, 0x%X", er->caid, er->ecmlen, er->ecm[13], er->ecm[44]); + } + + // Ariva quickfix (invalid nagra provider) + if(((er->caid & 0xFF00) == 0x1800) && er->prid > 0x00FFFF) + { er->prid = 0; } + + // Check for invalid provider, extract provider out of ecm: + uint32_t prid = chk_provid(er->ecm, er->caid); + if(!er->prid) + { + er->prid = prid; + } + else + { + if(prid && prid != er->prid) + { + cs_log_dbg(D_TRACE, "provider fixed: %04X@%06X to %04X@%06X", er->caid, er->prid, er->caid, prid); + er->prid = prid; + } + } + +#ifdef MODULE_NEWCAMD + // Set providerid for newcamd clients if none is given + if(!er->prid && client->ncd_server) + { + int32_t pi = client->port_idx; + if(pi >= 0 && cfg.ncd_ptab.nports && cfg.ncd_ptab.nports >= pi && cfg.ncd_ptab.ports[pi].ncd) + { er->prid = cfg.ncd_ptab.ports[pi].ncd->ncd_ftab.filts[0].prids[0]; } + } +#endif + + // CAID not supported or found + if(!er->caid) + { + er->rc = E_INVALID; + er->rcEx = E2_CAID; + snprintf(er->msglog, MSGLOGSIZE, "CAID not supported or found"); + } + + // user expired + if(client->expirationdate && client->expirationdate < client->lastecm) + { er->rc = E_EXPDATE; } + + // out of timeframe + if(client->allowedtimeframe_set) + { + struct tm acttm; + localtime_r(&now, &acttm); + int32_t curday = acttm.tm_wday; + char *dest = strstr(weekdstr,"ALL"); + int32_t all_idx = (dest - weekdstr) / 3; + uint8_t allowed = 0; + + // checkout if current time is allowed in the current day + allowed = CHECK_BIT(client->allowedtimeframe[curday][acttm.tm_hour][acttm.tm_min / 30], (acttm.tm_min % 30)); + + // or checkout if current time is allowed for all days + allowed |= CHECK_BIT(client->allowedtimeframe[all_idx][acttm.tm_hour][acttm.tm_min / 30], (acttm.tm_min % 30)); + + if(!(allowed)) + { + er->rc = E_EXPDATE; + } + cs_log_dbg(D_TRACE, "Check Timeframe - result: %d, day:%s time: %02dH%02d, allowed: %s\n", er->rc, shortDay[curday], acttm.tm_hour, acttm.tm_min, allowed ? "true" : "false"); + } + + // user disabled + if(client->disabled != 0) + { + if(client->failban & BAN_DISABLED) + { + cs_add_violation(client, client->account->usr); + cs_disconnect_client(client); + } + er->rc = E_DISABLED; + } + + if(!chk_global_whitelist(er, &line)) + { + debug_ecm(D_TRACE, "whitelist filtered: %s (%s) line %d", username(client), buf, line); + er->rc = E_INVALID; + } + +#ifdef CS_CACHEEX + if(client->account && client->account->cacheex.mode == 2 && !client->account->cacheex.allow_request) + { + er->rc = E_INVALID; + snprintf(er->msglog, MSGLOGSIZE, "invalid request from cacheex-2 client"); + } +#endif + + // rc < 100 -> ecm error + if(er->rc >= E_UNHANDLED) + { + m = er->caid; + i = er->srvid; + + if(i != client->last_srvid || !client->lastswitch) + { + if(cfg.usrfileflag) + { cs_statistics(client); } + client->lastswitch = now; + } + + // user sleeping + if(client->tosleep && (now - client->lastswitch > client->tosleep)) + { + if(client->failban & BAN_SLEEPING) + { + cs_add_violation(client, client->account->usr); + cs_disconnect_client(client); + } + if(client->c35_sleepsend != 0) + { + er->rc = E_STOPPED; // send sleep command CMD08 {00 255} + } + else + { + er->rc = E_SLEEPING; + } + } + + client->last_srvid = i; + client->last_caid = m; + client->last_provid = er->prid; + + int32_t ecm_len = (((er->ecm[1] & 0x0F) << 8) | er->ecm[2]) + 3; + + for(j = 0; (j < 6) && (er->rc >= E_UNHANDLED); j++) + { + switch(j) + { + case 0: + // fake (uniq) + if(client->dup) + { er->rc = E_FAKE; } + break; + + case 1: + // invalid (caid) + if(!chk_bcaid(er, &client->ctab)) + { + er->rc = E_INVALID; + er->rcEx = E2_CAID; + snprintf(er->msglog, MSGLOGSIZE, "invalid caid 0x%04X", er->caid); + } + break; + + case 2: + // invalid (srvid) + // matching srvids (or 0000) specified in betatunnel will bypass this filter + if(!chk_srvid(client, er)) + { + if(!chk_on_btun(SRVID_ZERO, client, er)) + { + er->rc = E_INVALID; + snprintf(er->msglog, MSGLOGSIZE, "invalid SID"); + } + } + break; + + case 3: + // invalid (ufilters) + if(!chk_ufilters(er)) + { er->rc = E_INVALID; } + break; + + case 4: + // invalid (sfilter) + if(!chk_sfilter(er, &get_module(client)->ptab)) + { er->rc = E_INVALID; } + break; + + case 5: + // corrupt + if((i = er->ecmlen - ecm_len)) + { + if(i > 0) + { + cs_log_dbg(D_TRACE, "warning: ecm size adjusted from %d to %d", er->ecmlen, ecm_len); + er->ecmlen = ecm_len; + } + else + { er->rc = E_CORRUPT; } + } + break; + } + } + } + + // Check for odd/even byte + if(get_odd_even(er) == 0) + { + cs_log_dbg(D_TRACE, "warning: ecm with null odd/even byte from %s", (check_client(er->client) ? er->client->account->usr : "-")); + er->rc = E_INVALID; + } + + // not continue, send rc to client + if(er->rc < E_UNHANDLED) + { + send_dcw(client, er); + free_ecm(er); + return; + } + + +#ifdef CS_CACHEEX + int8_t cacheex = client->account ? client->account->cacheex.mode : 0; + er->from_cacheex1_client = 0; + if(cacheex == 1) {er->from_cacheex1_client = 1;} +#endif + + + // set preferlocalcards for this ecm request (actually, paramter + // is per user based, maybe in fiture it will be caid based too) + er->preferlocalcards = cfg.preferlocalcards; + if(client->account && client->account->preferlocalcards > -1) + { + er->preferlocalcards = client->account->preferlocalcards; + } + if(er->preferlocalcards <0 || er->preferlocalcards >2) {er->preferlocalcards=0;} + + + if(chk_is_betatunnel_caid(er->caid) && client->ttab.ttnum) + { + cs_log_dump_dbg(D_TRACE, er->ecm, 13, "betatunnel? ecmlen=%d", er->ecmlen); + cs_betatunnel(er); + } + + + // ignore ecm... + int32_t offset = 3; + + // ...and betacrypt header for cache md5 calculation + if(caid_is_betacrypt(er->caid)) + { offset = 13; } + + uint8_t md5tmp[MD5_DIGEST_LENGTH]; + + // store ECM in cache + memcpy(er->ecmd5, MD5(er->ecm + offset, er->ecmlen - offset, md5tmp), CS_ECMSTORESIZE); + cacheex_update_hash(er); + ac_chk(client, er, 0); + + + //******** CHECK IF FOUND ECM IN CACHE + struct ecm_request_t *ecm = NULL; + ecm = check_cache(er, client); + if(ecm) // found in cache + { + cs_log_dbg(D_LB,"{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] cw found immediately in cache! ", (check_client(er->client)?er->client->account->usr:"-"),er->caid, er->prid, er->srvid); + + struct s_write_from_cache *wfc = NULL; + if(!cs_malloc(&wfc, sizeof(struct s_write_from_cache))) + { + NULLFREE(ecm); + free_ecm(er); + return; + } + + wfc->er_new = er; + wfc->er_cache = ecm; + write_ecm_answer_fromcache(wfc); + NULLFREE(wfc); + NULLFREE(ecm); + free_ecm(er); + + return; + } + +// zaplist ACoSC +#ifdef CS_ANTICASC + if(cfg.acosc_enabled) + { + cs_writelock(__func__, &clientlist_lock); + insert_zaplist(er, client); + cs_writeunlock(__func__, &clientlist_lock); + } +#endif + + er->reader_avail = 0; + er->readers = 0; + + struct s_ecm_answer *ea, *prv = NULL; + struct s_reader *rdr; + + cs_readlock(__func__, &readerlist_lock); + cs_readlock(__func__, &clientlist_lock); + + for(rdr = first_active_reader; rdr; rdr = rdr->next) + { + uint8_t is_fallback = chk_is_fixed_fallback(rdr, er); + int8_t match = matching_reader(er, rdr); + + if(!match) // if this reader does not match, check betatunnel for it + match = lb_check_auto_betatunnel(er, rdr); + + if(match) + { + er->reader_avail++; + +#ifdef CS_CACHEEX + if(cacheex == 1 && !cacheex_reader(rdr)) // ex1-cl only ask ex1-rdr + { continue; } +#endif + + if(!cs_malloc(&ea, sizeof(struct s_ecm_answer))) + { goto OUT; } + +#ifdef WITH_EXTENDED_CW + // Correct CSA mode is CBC - default to that instead + ea->cw_ex.algo_mode = CW_ALGO_MODE_CBC; +#endif + + er->readers++; + + ea->reader = rdr; + ea->er = er; + ea->rc = E_UNHANDLED; + if(prv) + { prv->next = ea; } + else + { er->matching_rdr = ea; } + prv = ea; + + ea->status = READER_ACTIVE; + if(cacheex_reader(rdr)) + { ea->status |= READER_CACHEEX; } + else if(is_localreader(rdr, er)) + { ea->status |= READER_LOCAL; } + + if(is_fallback && (!is_localreader(rdr, er) || (is_localreader(rdr, er) && !er->preferlocalcards))) + { ea->status |= READER_FALLBACK; } + + ea->pending = NULL; + ea->is_pending = false; + cs_lock_create(__func__, &ea->ecmanswer_lock, "ecmanswer_lock", 5000); + } + } + +OUT: + cs_readunlock(__func__, &clientlist_lock); + cs_readunlock(__func__, &readerlist_lock); + + lb_set_best_reader(er); + + // set reader_count and fallback_reader_count + set_readers_counter(er); + + // if preferlocalcards > 0, check if we have local readers selected: + // if not, switch to preferlocalcards = 0 for this ecm + if(er->preferlocalcards > 0) + { + if(er->localreader_count == 0) + { + er->preferlocalcards = 0; + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} NO local readers, set preferlocalcards = %d", + (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, er->preferlocalcards); + } + } + +#ifdef CS_CACHEEX + // WAIT_TIME + uint32_t cacheex_wait_time = 0; + uint32_t wait_time_no_hitcache = 0; + uint32_t wait_time_hitcache = 0; + + if(client->account && !client->account->no_wait_time +#ifdef CS_CACHEEX_AIO + && !chk_srvid_no_wait_time(er) +#endif + && er->preferlocalcards<2) + { + wait_time_no_hitcache = get_cacheex_wait_time(er,NULL); // NO check hitcache. Wait_time is dwtime, or, if 0, awtime. + wait_time_hitcache = get_cacheex_wait_time(er,client); // check hitcache for calculating wait_time! If hitcache wait_time is biggest value between dwtime and awtime, else it's awtime. + + if( + // If "normal" client and ex1-rdr>0, we cannot use hitcache for calculating wait_time because we cannot know if cw is available or not on ex1 server! + (cacheex != 1 && er->cacheex_reader_count) + || + /* Cw for ex1-cl comes from: INT. cache by "normal" readers (normal clients that ask normal readers), ex1-rdr and ex2-rdr and ex3-rdr. + * If readers, we have to wait cws generating by normal clients asking normal readers and answers by ex1-rdr (cannot use hitcache). + * If no readers, use hitcache for calculating wait_time. + */ + (cacheex == 1 && er->reader_avail) + ) + { cacheex_wait_time = wait_time_no_hitcache; } + else + { cacheex_wait_time = wait_time_hitcache; } + } + + cs_log_dbg(D_TRACE | D_CACHEEX, "[GET_CW] wait_time %d caid %04X prov %06X srvid %04X rc %d cacheex cl mode %d ex1rdr %d", cacheex_wait_time, er->caid, er->prid, er->srvid, er->rc, cacheex, er->cacheex_reader_count); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] wait_time %d - client cacheex mode %d, reader avail for ecm %d, hitcache %d, preferlocalcards %d", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid, cacheex_wait_time, cacheex == 1 ? 1 : 0, er->reader_avail, wait_time_hitcache ? 1 : 0, er->preferlocalcards); + // END WAIT_TIME calculation + + if(!cacheex_wait_time && (er->reader_count + er->fallback_reader_count) == 0) +#else + if((er->reader_count + er->fallback_reader_count) == 0) +#endif + { + er->rc = E_NOTFOUND; + if(!er->rcEx) + { er->rcEx = E2_GROUP; } + snprintf(er->msglog, MSGLOGSIZE, "no matching reader"); + cs_log_dbg(D_LB, "{client %s, caid %04X, prid %06X, srvid %04X} [get_cw] NO Readers and NO wait_time... not_found! ", (check_client(er->client) ? er->client->account->usr : "-"), er->caid, er->prid, er->srvid); + send_dcw(client, er); + free_ecm(er); + return; + } + + //insert it in ecmcwcache! + cs_writelock(__func__, &ecmcache_lock); + er->next = ecmcwcache; + ecmcwcache = er; + ecmcwcache_size++; + cs_writeunlock(__func__, &ecmcache_lock); + + er->rcEx = 0; +#ifdef CS_CACHEEX + er->cacheex_wait_time = 0; + er->cacheex_wait_time_expired = 1; + er->cacheex_hitcache = 0; + er->cacheex_mode1_delay = 0; + + if(cacheex_wait_time) // wait time for cacheex + { + er->cacheex_wait_time = cacheex_wait_time; + er->cacheex_wait_time_expired = 0; + er->cacheex_hitcache = wait_time_hitcache ? 1 : 0; // usefull only when cacheex mode 1 readers answers before wait_time and we have to decide if we have to wait until wait_time expires. + er->cacheex_mode1_delay = get_cacheex_mode1_delay(er); + + if(!er->cacheex_mode1_delay && er->cacheex_reader_count > 0) + { + request_cw_from_readers(er, 1); // setting stop_stage=1, we request only cacheex mode 1 readers. Others are requested at cacheex timeout! + } + } + else +#endif + request_cw_from_readers(er, 0); + +#ifdef WITH_DEBUG + if(D_CLIENTECM & cs_dblevel) + { + char buf[ECM_FMT_LEN]; + format_ecm(er, buf, ECM_FMT_LEN); + cs_log_dump_dbg(D_CLIENTECM, er->ecm, er->ecmlen, "Client %s ECM dump %s", username(client), buf); + } +#endif + + cw_process_thread_wakeup(); +} + +int32_t ecmfmt(char *result, size_t size, uint16_t caid, uint16_t onid, uint32_t prid, uint16_t chid, uint16_t pid, + uint16_t srvid, uint16_t l, char *ecmd5hex, char *csphash, char *cw, uint16_t origin_peer, uint8_t distance, char *payload, char *tier) +{ + if(!cfg.ecmfmt) + { + if(tier && payload) + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s:0F06%s:%s", caid, prid, chid, srvid, l, ecmd5hex, payload, tier); + } + else if(tier) + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s:%s", caid, prid, chid, srvid, l, ecmd5hex, tier); + } + else if(payload) + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s:0F06%s", caid, prid, chid, srvid, l, ecmd5hex, payload); + } + else + { + return snprintf(result, size, "%04X@%06X/%04X/%04X/%02X:%s", caid, prid, chid, srvid, l, ecmd5hex); + } + } + +#define ECMFMT_NUMBER 0 +#define ECMFMT_STRING 1 +#define ECMFMT_CHAR 2 + + uint8_t type = 0; + uint32_t ivalue = 0; + char *ifmt = NULL, *sfmt = NULL; + char *svalue = NULL, cvalue = '\0'; + uint8_t hide_if_zero = 0; + char *c; + uint32_t s = 0; + + for(c = cfg.ecmfmt; *c; c++) + { + if(*c == '0') + { + hide_if_zero = 1; + continue; + } + + sfmt = NULL; + + switch(*c) + { + case 't': + type = ECMFMT_STRING; + svalue = tier; + if(tier == NULL && !hide_if_zero) + { + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = 0; + } + break; + + case 'c': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = caid; + break; + + case 'o': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = onid; + break; + + case 'p': + type = ECMFMT_NUMBER; + ifmt = "%06X"; + ivalue = prid; + break; + + case 'i': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = chid; + break; + + case 'd': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = pid; + break; + + case 's': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = srvid; + break; + + case 'l': + type = ECMFMT_NUMBER; + ifmt = "%02X"; + ivalue = l; + break; + + case 'h': + type = ECMFMT_STRING; + svalue = ecmd5hex; + break; + + case 'e': + type = ECMFMT_STRING; + svalue = csphash; + break; + + case 'w': + type = ECMFMT_STRING; + svalue = cw; + break; + + case 'j': + type = ECMFMT_NUMBER; + ifmt = "%02X"; + ivalue = distance; + break; + + case 'g': + type = ECMFMT_NUMBER; + ifmt = "%04X"; + ivalue = origin_peer; + break; + + case '\\': + c++; + type = ECMFMT_CHAR; + cvalue = *c; + + if(cvalue == '\0') + { return s; } + break; + + case 'y': + type = ECMFMT_STRING; + svalue = payload; + sfmt = "0F06%.06s"; + if(payload == NULL && !hide_if_zero) + { + type = ECMFMT_NUMBER; + ifmt = "0F06%06X"; + ivalue = 0; + } + break; + + case 'Y': + type = ECMFMT_STRING; + svalue = payload; + sfmt = "0F06%s"; + if(payload == NULL && !hide_if_zero) + { + type = ECMFMT_NUMBER; + ifmt = "0F06%12X"; + ivalue = 0; + } + break; + + default: + type = ECMFMT_CHAR; + cvalue = *c; + break; + } + + if(hide_if_zero) + { + if(type == ECMFMT_NUMBER && ivalue == 0) + { + hide_if_zero = 0; + continue; + } + else if(type == ECMFMT_STRING && svalue == NULL) + { + hide_if_zero = 0; + continue; + } + } + + switch(type) + { + case ECMFMT_NUMBER: + s += snprintf(result + s, size - s, ifmt, ivalue); + break; + + case ECMFMT_STRING: + s += snprintf(result + s, size - s , sfmt != NULL ? sfmt : "%s", svalue); + break; + + case ECMFMT_CHAR: + if(size - s > 1) + { + result[s] = cvalue; + result[s+1] = '\0'; + s++; + } + break; + + default: + break; + } + } + + return s; +} + +int32_t format_ecm(ECM_REQUEST *ecm, char *result, size_t size) +{ + char ecmd5hex[(16*2)+1]; + char csphash[(4*2)+1] = { 0 }; + char cwhex[(16*2)+1]; + char *payload = NULL; + char *tier = NULL; +#ifdef READER_VIDEOGUARD + char payload_string[(6*2)+1]; + char tier_string[83]; + struct s_ecm_answer *ea; + + if(ecm->selected_reader && caid_is_videoguard(ecm->selected_reader->caid) && !is_network_reader(ecm->selected_reader)) + { + for(ea = ecm->matching_rdr; ea; ea = ea->next) + { + if(ea->tier && (ea->status & REQUEST_ANSWERED) && !is_network_reader(ea->reader)) + { + get_tiername_defaultid(ea->tier, ecm->selected_reader->caid, tier_string); + tier = tier_string; + break; + } + } + + cs_hexdump(0, ecm->selected_reader->VgLastPayload, 6, payload_string, sizeof(payload_string)); + payload = payload_string; + } +#endif + cs_hexdump(0, ecm->ecmd5, 16, ecmd5hex, sizeof(ecmd5hex)); +#ifdef CS_CACHEEX + cs_hexdump(0, (void *)&ecm->csp_hash, 4, csphash, sizeof(csphash)); +#endif + cs_hexdump(0, ecm->cw, 16, cwhex, sizeof(cwhex)); +#ifdef MODULE_GBOX + if(check_client(ecm->client) && get_module(ecm->client)->num == R_GBOX && ecm->gbox_ecm_dist) + { return ecmfmt(result, size, ecm->caid, ecm->onid, ecm->prid, ecm->chid, ecm->pid, ecm->srvid, ecm->ecmlen, ecmd5hex, csphash, cwhex, ecm->gbox_ecm_src_peer, ecm->gbox_ecm_dist, payload, tier); } + else if (ecm->selected_reader && ecm->selected_reader->typ == R_GBOX && !ecm->gbox_ecm_dist) + { return ecmfmt(result, size, ecm->caid, ecm->onid, ecm->prid, ecm->chid, ecm->pid, ecm->srvid, ecm->ecmlen, ecmd5hex, csphash, cwhex, ecm->selected_reader->gbox_cw_src_peer, ecm->selected_reader->currenthops, payload, tier); } + else +#endif + return ecmfmt(result, size, ecm->caid, ecm->onid, ecm->prid, ecm->chid, ecm->pid, ecm->srvid, ecm->ecmlen, ecmd5hex, csphash, cwhex, 0, + ((ecm->selected_reader && ecm->selected_reader->currenthops) ? ecm->selected_reader->currenthops : 0), payload, tier); +} diff --git a/oscam-ecm.h b/oscam-ecm.h old mode 100755 new mode 100644 index f40bfab..f31bfd0 --- a/oscam-ecm.h +++ b/oscam-ecm.h @@ -33,12 +33,6 @@ void request_cw_from_readers(ECM_REQUEST *er, uint8_t stop_stage); void checkCW(ECM_REQUEST *er); uint8_t checkCWpart(uint8_t *cw, int8_t part); -// CW VOTING -int cw_vote_add(struct ecm_request_t *er, uint8_t *cw, struct s_reader *rdr); -int cw_vote_decide(struct ecm_request_t *er); -int32_t is_cwvote_caid(ECM_REQUEST *er); -void refresh_cw_vote_config(void); - #ifdef CS_CACHEEX_AIO void init_ecm_cache(void); void free_ecm_cache(void); diff --git a/oscam-emm-cache.c b/oscam-emm-cache.c old mode 100755 new mode 100644 diff --git a/oscam-emm-cache.h b/oscam-emm-cache.h old mode 100755 new mode 100644 diff --git a/oscam-emm.c b/oscam-emm.c old mode 100755 new mode 100644 index 1bf94e5..486d62c --- a/oscam-emm.c +++ b/oscam-emm.c @@ -40,7 +40,6 @@ static int8_t cs_emmlen_is_blocked(struct s_reader *rdr, int16_t len) static int8_t do_simple_emm_filter(struct s_reader *rdr, const struct s_cardsystem *csystem, EMM_PACKET *ep, int8_t cl_dvbapi) { if(is_network_reader(rdr)) { return 1; } // don't evaluate on network readers, server with local reader will check it - if(rdr->typ == R_EMU) { return 1; } // don't evalutate on emu reader //copied and enhanced from module-dvbapi.c //dvbapi_start_emm_filter() @@ -243,24 +242,6 @@ int32_t emm_reader_match(struct s_reader *reader, uint16_t caid, uint32_t provid rdr_log_dbg(reader, D_EMM, "reader auprovid = %06X fixup to %06X (ignoring last digit)", reader->auprovid, prid); } -#ifdef WITH_EMU - if(reader->typ == R_EMU) - { - FILTER *emu_provids = get_emu_prids_for_caid(reader, caid); - if(emu_provids != NULL) - { - for(i = 0; i < emu_provids->nprids; i++) - { - if(provid == emu_provids->prids[i]) - { - return 1; - } - } - } - return 0; - } -#endif - if(prid == provid) { rdr_log_dbg(reader, D_EMM, "reader auprovid = %06X matching with emm provid = %06X -> SEND!", prid, provid); diff --git a/oscam-emm.h b/oscam-emm.h old mode 100755 new mode 100644 diff --git a/oscam-failban.c b/oscam-failban.c old mode 100755 new mode 100644 diff --git a/oscam-failban.h b/oscam-failban.h old mode 100755 new mode 100644 diff --git a/oscam-files.c b/oscam-files.c old mode 100755 new mode 100644 index ba9236c..73970bf --- a/oscam-files.c +++ b/oscam-files.c @@ -143,10 +143,6 @@ int32_t file_copy(char *srcfile, char *destfile) } } fclose(src); - if(fsync(fileno(dest)) == -1) // Dodano fsync, aby wymusić zapis na dysk - { - cs_log("ERROR: file_copy - fsync failed for %s (errno=%d %s)!", destfile, errno, strerror(errno)); - } fclose(dest); return (0); } @@ -157,17 +153,16 @@ int32_t file_copy(char *srcfile, char *destfile) int32_t safe_overwrite_with_bak(char *destfile, char *temp_file, char *bakfile, int32_t forceBakOverWrite) { int32_t rc; - if(file_exists(destfile)) { if(forceBakOverWrite != 0 || !file_exists(bakfile)) { if(file_copy(destfile, bakfile) < 0) { - cs_log("ERROR: safe_overwrite_with_bak - Error copying original config file %s to %s. The original config will be left untouched!", destfile, bakfile); + cs_log("Error copying original config file %s to %s. The original config will be left untouched!", destfile, bakfile); if(unlink(temp_file) < 0) { - cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno)); + cs_log("Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno)); } return 1; } @@ -177,21 +172,21 @@ int32_t safe_overwrite_with_bak(char *destfile, char *temp_file, char *bakfile, rc = file_copy(temp_file, destfile); if(rc < 0) { - cs_log("ERROR: safe_overwrite_with_bak - An error occured while writing the new config file %s. Return code: %d, errno: %d (%s).", destfile, rc, errno, strerror(errno)); + cs_log("An error occured while writing the new config file %s.", destfile); if(rc == -2) { - cs_log("ERROR: safe_overwrite_with_bak - The config will be missing or only partly filled upon next startup as this is a non-recoverable error! Please restore from backup or try again."); + cs_log("The config will be missing or only partly filled upon next startup as this is a non-recoverable error! Please restore from backup or try again."); } if(unlink(temp_file) < 0) { - cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno)); + cs_log("Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno)); } return 1; } if(unlink(temp_file) < 0) { - cs_log("ERROR: safe_overwrite_with_bak - Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno)); + cs_log("Error removing temp config file %s (errno=%d %s)!", temp_file, errno, strerror(errno)); } return 0; } diff --git a/oscam-files.h b/oscam-files.h old mode 100755 new mode 100644 diff --git a/oscam-garbage.c b/oscam-garbage.c old mode 100755 new mode 100644 diff --git a/oscam-garbage.h b/oscam-garbage.h old mode 100755 new mode 100644 diff --git a/oscam-hashtable.c b/oscam-hashtable.c old mode 100755 new mode 100644 diff --git a/oscam-hashtable.h b/oscam-hashtable.h old mode 100755 new mode 100644 diff --git a/oscam-llist.c b/oscam-llist.c old mode 100755 new mode 100644 diff --git a/oscam-llist.h b/oscam-llist.h old mode 100755 new mode 100644 diff --git a/oscam-lock.c b/oscam-lock.c old mode 100755 new mode 100644 diff --git a/oscam-lock.h b/oscam-lock.h old mode 100755 new mode 100644 diff --git a/oscam-log-reader.c b/oscam-log-reader.c old mode 100755 new mode 100644 diff --git a/oscam-log-reader.h b/oscam-log-reader.h old mode 100755 new mode 100644 diff --git a/oscam-log.c b/oscam-log.c old mode 100755 new mode 100644 diff --git a/oscam-log.h b/oscam-log.h old mode 100755 new mode 100644 diff --git a/oscam-net.c b/oscam-net.c old mode 100755 new mode 100644 diff --git a/oscam-net.h b/oscam-net.h old mode 100755 new mode 100644 diff --git a/oscam-reader.c b/oscam-reader.c old mode 100755 new mode 100644 diff --git a/oscam-reader.h b/oscam-reader.h old mode 100755 new mode 100644 diff --git a/oscam-signing.c b/oscam-signing.c old mode 100755 new mode 100644 diff --git a/oscam-signing.h b/oscam-signing.h old mode 100755 new mode 100644 diff --git a/oscam-simples.c b/oscam-simples.c old mode 100755 new mode 100644 index 6e1c713..576804c --- a/oscam-simples.c +++ b/oscam-simples.c @@ -468,8 +468,6 @@ const char *get_cardsystem_desc_by_caid(uint16_t caid) if(caid_is_viaccess(caid)) { return "viaccess"; } if(caid_is_irdeto(caid)) { return "irdeto"; } if(caid_is_videoguard(caid)) { return "videoguard"; } - if(caid_is_powervu(caid)) { return "powervu"; } - if(caid_is_director(caid)) { return "director"; } if(caid_is_conax(caid)) { return "conax"; } if(caid_is_cryptoworks(caid)) { return "cryptoworks"; } if(caid_is_betacrypt(caid)) { return "betacrypt"; } @@ -478,7 +476,6 @@ const char *get_cardsystem_desc_by_caid(uint16_t caid) if(caid >= 0x5501 && caid <= 0x551A) { return "griffin"; } if(caid_is_dre(caid)) { return "drecrypt"; } if(caid_is_bulcrypt(caid)) { return "bulcrypt"; } - if(caid_is_biss(caid)) { return "biss"; } if(caid == 0x4ABF) { return "dgcrypt"; } return "unknown"; } diff --git a/oscam-string-isotables.h b/oscam-string-isotables.h old mode 100755 new mode 100644 diff --git a/oscam-string.c b/oscam-string.c old mode 100755 new mode 100644 diff --git a/oscam-string.h b/oscam-string.h old mode 100755 new mode 100644 diff --git a/oscam-time.c b/oscam-time.c old mode 100755 new mode 100644 diff --git a/oscam-time.h b/oscam-time.h old mode 100755 new mode 100644 diff --git a/oscam-work.c b/oscam-work.c old mode 100755 new mode 100644 index 4df6ae8..9af35d4 --- a/oscam-work.c +++ b/oscam-work.c @@ -119,7 +119,7 @@ void *work_thread(void *ptr) struct s_module *module = get_module(cl); uint16_t bufsize = module->bufsize; // CCCam needs more than 1024bytes! if(!bufsize) - { bufsize = DEFAULT_MODULE_BUFsize; } + { bufsize = DEFAULT_MODULE_BUFSIZE; } uint8_t *mbuf; if(!cs_malloc(&mbuf, bufsize)) diff --git a/oscam-work.h b/oscam-work.h old mode 100755 new mode 100644 diff --git a/oscam.c b/oscam.c old mode 100755 new mode 100644 index eeea384..1385bd0 --- a/oscam.c +++ b/oscam.c @@ -44,10 +44,6 @@ #include "reader-common.h" #include "module-gbox.h" -#ifdef WITH_EMU - void add_emu_reader(void); -#endif - #ifdef WITH_SSL #include #include @@ -376,9 +372,9 @@ static void write_versionfile(bool use_stdout) st.tm_hour, st.tm_min, st.tm_sec); } - fprintf(fp, "Build Date: %s\n", "UNKNOWN"); // Placeholder if not defined by build system - fprintf(fp, "Version: %s@%s\n", "UNKNOWN", "UNKNOWN"); // Placeholder if not defined by build system - fprintf(fp, "Compiler: %s\n", "UNKNOWN"); // Placeholder if not defined by build system + fprintf(fp, "Build Date: %s\n", CS_BUILD_DATE); + fprintf(fp, "Version: %s@%s\n", CS_VERSION, CS_GIT_COMMIT); + fprintf(fp, "Compiler: %s\n", CS_TARGET); #ifdef USE_COMPRESS fprintf(fp, "Compression: %s, level %s\n", COMP_VERSION, COMP_LEVEL); #endif @@ -461,8 +457,6 @@ static void write_versionfile(bool use_stdout) #ifdef WITH_SIGNING write_conf(WITH_SIGNING, "Binary signing support"); #endif - write_conf(WITH_EMU, "Emulator support"); - write_conf(WITH_SOFTCAM, "Built-in SoftCam.Key"); fprintf(fp, "\n"); write_conf(MODULE_CAMD33, "camd 3.3x"); @@ -1688,9 +1682,6 @@ const struct s_cardreader *cardreaders[] = #ifdef CARDREADER_STINGER &cardreader_stinger, #endif -#ifdef WITH_EMU - &cardreader_emu, -#endif NULL }; @@ -1809,8 +1800,6 @@ int32_t main(int32_t argc, char *argv[]) parse_cmdline_params(argc, argv); - cs_log("DEBUG: main - cs_confdir after parsing command line and finding config dir: '%s'", cs_confdir); - if(bg && do_daemon(1, 0)) { printf("Error starting in background (errno=%d: %s)", errno, strerror(errno)); @@ -1872,9 +1861,6 @@ int32_t main(int32_t argc, char *argv[]) init_sidtab(); init_readerdb(); -#ifdef WITH_EMU - add_emu_reader(); -#endif cfg.account = init_userdb(); init_signal(); init_provid(); diff --git a/rcEx b/rcEx new file mode 100644 index 0000000..e69de29 diff --git a/reader-bulcrypt.c b/reader-bulcrypt.c old mode 100755 new mode 100644 diff --git a/reader-common.c b/reader-common.c old mode 100755 new mode 100644 index 8bc3283..bc03621 --- a/reader-common.c +++ b/reader-common.c @@ -15,7 +15,6 @@ #include "reader-common.h" //#include "csctapi/atr.h" #include "csctapi/icc_async.h" -#include "readers.h" // required by the EMU reader extern const struct s_cardsystem *cardsystems[]; extern char *RDR_CD_TXT[]; @@ -153,18 +152,6 @@ static int32_t reader_get_cardsystem(struct s_reader *reader, ATR *atr) { int32_t i; -#ifdef WITH_EMU - if(reader->typ == R_EMU) - { - NULLFREE(reader->csystem_data); - rdr_log(reader, "found card system %s", reader_emu.desc); - reader->csystem = &reader_emu; - reader->csystem_active = true; - led_status_found_cardsystem(); - return (reader->csystem_active); - } -#endif - for(i = 0; cardsystems[i]; i++) { NULLFREE(reader->csystem_data); diff --git a/reader-common.h b/reader-common.h old mode 100755 new mode 100644 diff --git a/reader-conax.c b/reader-conax.c old mode 100755 new mode 100644 diff --git a/reader-cryptoworks.c b/reader-cryptoworks.c old mode 100755 new mode 100644 diff --git a/reader-dgcrypt.c b/reader-dgcrypt.c old mode 100755 new mode 100644 diff --git a/reader-dre-cas.c b/reader-dre-cas.c old mode 100755 new mode 100644 diff --git a/reader-dre-common.c b/reader-dre-common.c old mode 100755 new mode 100644 diff --git a/reader-dre-common.h b/reader-dre-common.h old mode 100755 new mode 100644 diff --git a/reader-dre-st20.c b/reader-dre-st20.c old mode 100755 new mode 100644 diff --git a/reader-dre-st20.h b/reader-dre-st20.h old mode 100755 new mode 100644 diff --git a/reader-dre.c b/reader-dre.c old mode 100755 new mode 100644 diff --git a/reader-griffin.c b/reader-griffin.c old mode 100755 new mode 100644 diff --git a/reader-irdeto.c b/reader-irdeto.c old mode 100755 new mode 100644 diff --git a/reader-irdeto.h b/reader-irdeto.h old mode 100755 new mode 100644 diff --git a/reader-nagra-common.c b/reader-nagra-common.c old mode 100755 new mode 100644 diff --git a/reader-nagra-common.h b/reader-nagra-common.h old mode 100755 new mode 100644 diff --git a/reader-nagra.c b/reader-nagra.c old mode 100755 new mode 100644 diff --git a/reader-nagracak7.c b/reader-nagracak7.c old mode 100755 new mode 100644 diff --git a/reader-seca.c b/reader-seca.c old mode 100755 new mode 100644 diff --git a/reader-tongfang.c b/reader-tongfang.c old mode 100755 new mode 100644 diff --git a/reader-viaccess.c b/reader-viaccess.c old mode 100755 new mode 100644 diff --git a/reader-videoguard-common.c b/reader-videoguard-common.c old mode 100755 new mode 100644 diff --git a/reader-videoguard-common.h b/reader-videoguard-common.h old mode 100755 new mode 100644 diff --git a/reader-videoguard1.c b/reader-videoguard1.c old mode 100755 new mode 100644 diff --git a/reader-videoguard12.c b/reader-videoguard12.c old mode 100755 new mode 100644 diff --git a/reader-videoguard2.c b/reader-videoguard2.c old mode 100755 new mode 100644 diff --git a/readers.h b/readers.h old mode 100755 new mode 100644 index 316c2be..eec350e --- a/readers.h +++ b/readers.h @@ -17,6 +17,5 @@ extern const struct s_cardsystem reader_tongfang; extern const struct s_cardsystem reader_bulcrypt; extern const struct s_cardsystem reader_griffin; extern const struct s_cardsystem reader_dgcrypt; -extern const struct s_cardsystem reader_emu; #endif diff --git a/tests.c b/tests.c old mode 100755 new mode 100644 diff --git a/tier b/tier new file mode 100644 index 0000000..e69de29 diff --git a/tommyDS_hashlin/tommychain.h b/tommyDS_hashlin/tommychain.h old mode 100755 new mode 100644 diff --git a/tommyDS_hashlin/tommyhash.c b/tommyDS_hashlin/tommyhash.c old mode 100755 new mode 100644 diff --git a/tommyDS_hashlin/tommyhash.h b/tommyDS_hashlin/tommyhash.h old mode 100755 new mode 100644 diff --git a/tommyDS_hashlin/tommyhashlin.c b/tommyDS_hashlin/tommyhashlin.c old mode 100755 new mode 100644 diff --git a/tommyDS_hashlin/tommyhashlin.h b/tommyDS_hashlin/tommyhashlin.h old mode 100755 new mode 100644 diff --git a/tommyDS_hashlin/tommylist.c b/tommyDS_hashlin/tommylist.c old mode 100755 new mode 100644 diff --git a/tommyDS_hashlin/tommylist.h b/tommyDS_hashlin/tommylist.h old mode 100755 new mode 100644 diff --git a/tommyDS_hashlin/tommytypes.h b/tommyDS_hashlin/tommytypes.h old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-arm-coolstream.cmake b/toolchains/toolchain-arm-coolstream.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-arm-dockstar-openwrt.cmake b/toolchains/toolchain-arm-dockstar-openwrt.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-arm-friendlyarm.cmake b/toolchains/toolchain-arm-friendlyarm.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-arm-mca.cmake b/toolchains/toolchain-arm-mca.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-arm-none.cmake b/toolchains/toolchain-arm-none.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-arm-nslu2-openwrt.cmake b/toolchains/toolchain-arm-nslu2-openwrt.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-arm-nslu2-unslung.cmake b/toolchains/toolchain-arm-nslu2-unslung.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-arm-su980.cmake b/toolchains/toolchain-arm-su980.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-arm-wrt350nv2-openwrt.cmake b/toolchains/toolchain-arm-wrt350nv2-openwrt.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-mips-agv2_w.cmake b/toolchains/toolchain-mips-agv2_w.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-mips-azbox.cmake b/toolchains/toolchain-mips-azbox.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-mips-dir825.cmake b/toolchains/toolchain-mips-dir825.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-mips-fonera2.cmake b/toolchains/toolchain-mips-fonera2.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-mips-tuxbox.cmake b/toolchains/toolchain-mips-tuxbox.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-mips-wrt54g.cmake b/toolchains/toolchain-mips-wrt54g.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-mipsel-tuxbox-broken-pthread.cmake b/toolchains/toolchain-mipsel-tuxbox-broken-pthread.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-mipsel-tuxbox-linux-gnu.cmake b/toolchains/toolchain-mipsel-tuxbox-linux-gnu.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-mipsel-tuxbox.cmake b/toolchains/toolchain-mipsel-tuxbox.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-powerpc-tuxbox.cmake b/toolchains/toolchain-powerpc-tuxbox.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-sh4-amino.cmake b/toolchains/toolchain-sh4-amino.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-sh4-qboxhd.cmake b/toolchains/toolchain-sh4-qboxhd.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-sh4-tuxbox-stapi.cmake b/toolchains/toolchain-sh4-tuxbox-stapi.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-sh4-tuxbox.cmake b/toolchains/toolchain-sh4-tuxbox.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-sparc-padre.cmake b/toolchains/toolchain-sparc-padre.cmake old mode 100755 new mode 100644 diff --git a/toolchains/toolchain-tripledragon.cmake b/toolchains/toolchain-tripledragon.cmake old mode 100755 new mode 100644 diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt old mode 100755 new mode 100644 diff --git a/utils/list_smargo.c b/utils/list_smargo.c old mode 100755 new mode 100644 diff --git a/webif/.gitignore b/webif/.gitignore old mode 100755 new mode 100644 diff --git a/webif/Makefile b/webif/Makefile old mode 100755 new mode 100644 diff --git a/webif/README b/webif/README old mode 100755 new mode 100644 diff --git a/webif/api.json/cacheex.json b/webif/api.json/cacheex.json old mode 100755 new mode 100644 diff --git a/webif/api.json/cacheexaiobit.json b/webif/api.json/cacheexaiobit.json old mode 100755 new mode 100644 diff --git a/webif/api.json/cacheexbit.json b/webif/api.json/cacheexbit.json old mode 100755 new mode 100644 diff --git a/webif/api.json/entitlementbit.json b/webif/api.json/entitlementbit.json old mode 100755 new mode 100644 diff --git a/webif/api.json/entitlements.json b/webif/api.json/entitlements.json old mode 100755 new mode 100644 diff --git a/webif/api.json/footer.json b/webif/api.json/footer.json old mode 100755 new mode 100644 diff --git a/webif/api.json/header.json b/webif/api.json/header.json old mode 100755 new mode 100644 diff --git a/webif/api.json/reader.json b/webif/api.json/reader.json old mode 100755 new mode 100644 diff --git a/webif/api.json/readerbit.json b/webif/api.json/readerbit.json old mode 100755 new mode 100644 diff --git a/webif/api.json/status.json b/webif/api.json/status.json old mode 100755 new mode 100644 diff --git a/webif/api.json/status_statusbits.json b/webif/api.json/status_statusbits.json old mode 100755 new mode 100644 diff --git a/webif/api.json/user.json b/webif/api.json/user.json old mode 100755 new mode 100644 diff --git a/webif/api.json/userbit.json b/webif/api.json/userbit.json old mode 100755 new mode 100644 diff --git a/webif/api.xml/cccamcardlist.xml b/webif/api.xml/cccamcardlist.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/cccamcardlist_cardlist.xml b/webif/api.xml/cccamcardlist_cardlist.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/cccamcardlist_cardlist_nodelist.xml b/webif/api.xml/cccamcardlist_cardlist_nodelist.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/cccamcardlist_cardlist_providerlist.xml b/webif/api.xml/cccamcardlist_cardlist_providerlist.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/confirmation.xml b/webif/api.xml/confirmation.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/error.xml b/webif/api.xml/error.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/failban.xml b/webif/api.xml/failban.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/failban_failbanrow.xml b/webif/api.xml/failban_failbanrow.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/file.xml b/webif/api.xml/file.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/footer.xml b/webif/api.xml/footer.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/header.xml b/webif/api.xml/header.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/readers.xml b/webif/api.xml/readers.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/readers_readerlist.xml b/webif/api.xml/readers_readerlist.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/readerstats.xml b/webif/api.xml/readerstats.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/readerstats_ecmstats.xml b/webif/api.xml/readerstats_ecmstats.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/readerstats_emmstats.xml b/webif/api.xml/readerstats_emmstats.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/status.xml b/webif/api.xml/status.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/status_statusbits.xml b/webif/api.xml/status_statusbits.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/userconfiglist.xml b/webif/api.xml/userconfiglist.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/userconfiglist_userconfigs.xml b/webif/api.xml/userconfiglist_userconfigs.xml old mode 100755 new mode 100644 diff --git a/webif/api.xml/useredit.xml b/webif/api.xml/useredit.xml old mode 100755 new mode 100644 diff --git a/webif/cacheex/cacheex.html b/webif/cacheex/cacheex.html old mode 100755 new mode 100644 diff --git a/webif/cacheex/cacheex_tablerow.html b/webif/cacheex/cacheex_tablerow.html old mode 100755 new mode 100644 diff --git a/webif/cacheexaio/cacheex.html b/webif/cacheexaio/cacheex.html old mode 100755 new mode 100644 diff --git a/webif/cacheexaio/cacheex_tablerow.html b/webif/cacheexaio/cacheex_tablerow.html old mode 100755 new mode 100644 diff --git a/webif/cacheexaio/cacheex_tablerow_stats.html b/webif/cacheexaio/cacheex_tablerow_stats.html old mode 100755 new mode 100644 diff --git a/webif/config/anticasc.html b/webif/config/anticasc.html old mode 100755 new mode 100644 diff --git a/webif/config/cache.html b/webif/config/cache.html old mode 100755 new mode 100644 diff --git a/webif/config/cache_cacheexaiocsp.html b/webif/config/cache_cacheexaiocsp.html old mode 100755 new mode 100644 diff --git a/webif/config/cache_cacheexaiocsp.html.bak b/webif/config/cache_cacheexaiocsp.html.bak new file mode 100644 index 0000000..ede412e --- /dev/null +++ b/webif/config/cache_cacheexaiocsp.html.bak @@ -0,0 +1,32 @@ + CacheEx / CSP + CW Cache Settings:
Format: caid[&mask][@provid][$servid]:mode:timediff_old_cw[,n] + CW Cache Size: count of max. CWs for CW cache + CW Cache Memory: MB of max. memory used for CW cache size + ECM droptime:
s after known ECM requests are dropped + ECM Cache Size: count of max. ECMs for ECM cache + ECM Cache Memory: MB of max. memory used for ECM cache size + CacheEx CW Check:
Format: caid[&mask][@provid][$servid]:mode:counter[,n] + Wait time: ms + Wait time block start count:count of wait time timeouts to start blocking + Wait time block time:s block wait time + Mode1 delay time: ms + Max hit time: s keep hit for dynamic wait time + Write statistic: Wait until ctimeout: + Drop diff CWs: + No CW push after (from local/proxy-reader): ms
Format: caid:time[,n] + Allow client to overwrite 'Forward lg-only settings':(if disabled, only more restrictive settings are added) + Forward localgenerated flagged CWs only: + Forward localgenerated flagged CWs only(caid:provid[,N];): + Drop incoming not localgenerated flagged CWs: + Drop incoming not localgenerated flagged CWs(caid:provid[,N];): + Drop incoming settings only for CX-AIO peers: + Cache-EX ECM filter: + Cache-EX ECM filter(aio only): + Push localgenerated flagged CWs always to these groups: Valid values 1-64 + CSP + Port: + Serverip: + ECM filter: + ECM filter adv.: + Reforward cacheex: + Block fake cws: diff --git a/webif/config/cache_cacheexcsp.html b/webif/config/cache_cacheexcsp.html old mode 100755 new mode 100644 diff --git a/webif/config/cache_cwcycle.html b/webif/config/cache_cwcycle.html old mode 100755 new mode 100644 diff --git a/webif/config/cacheaio.html b/webif/config/cacheaio.html old mode 100755 new mode 100644 diff --git a/webif/config/camd33.html b/webif/config/camd33.html old mode 100755 new mode 100644 diff --git a/webif/config/camd35.html b/webif/config/camd35.html old mode 100755 new mode 100644 diff --git a/webif/config/camd35tcp.html b/webif/config/camd35tcp.html old mode 100755 new mode 100644 diff --git a/webif/config/cccam.html b/webif/config/cccam.html old mode 100755 new mode 100644 diff --git a/webif/config/cccam_control.html b/webif/config/cccam_control.html old mode 100755 new mode 100644 diff --git a/webif/config/cccreshare.html b/webif/config/cccreshare.html old mode 100755 new mode 100644 diff --git a/webif/config/config.html b/webif/config/config.html old mode 100755 new mode 100644 diff --git a/webif/config/dvbapi.html b/webif/config/dvbapi.html old mode 100755 new mode 100644 diff --git a/webif/config/dvbapi_demuxerfix.html b/webif/config/dvbapi_demuxerfix.html old mode 100755 new mode 100644 diff --git a/webif/config/dvbapi_extended_cw_api.html b/webif/config/dvbapi_extended_cw_api.html old mode 100755 new mode 100644 diff --git a/webif/config/gbox.html b/webif/config/gbox.html old mode 100755 new mode 100644 diff --git a/webif/config/global.html b/webif/config/global.html old mode 100755 new mode 100644 index ac3f374..21c0607 --- a/webif/config/global.html +++ b/webif/config/global.html @@ -142,29 +142,6 @@ Doublecheck ECM Doublecheck: Doublecheck caids: - Ai Fake DCW Detector - Ai Vote Enabled: - Ai Vote Log Enabled: - Ai Vote Timeout (ms): (default 1000) - Ai Vote Min Votes: (default min. 2) - Ai Vote Local Weight: (0.0-10.0, default 2.0) - Ai Vote Max Candidates: (default 8 caution! use max 16) - Ai Vote Compare Length: - - - - Ai Vote Fallback Mode: - - - - Ai Vote CAIDs: ##TPLSUPPRESSCMD08## ##TPLGETBLOCKEMMAUPROVID## ##TPLENABLELEDBIT## diff --git a/webif/config/global.html.bak b/webif/config/global.html.bak new file mode 100644 index 0000000..bc16cd0 --- /dev/null +++ b/webif/config/global.html.bak @@ -0,0 +1,145 @@ + + + + + + + + + + + + + +##TPLLOCALCARDS## + +##TPLUNLOCKPARENTAL## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +##TPLCACHEEXAIOLOGGING## + + + + + + + + + + + + + +##TPLSUPPRESSCMD08## +##TPLGETBLOCKEMMAUPROVID## +##TPLENABLELEDBIT## diff --git a/webif/config/global_cacheex_aio_logging.html b/webif/config/global_cacheex_aio_logging.html old mode 100755 new mode 100644 diff --git a/webif/config/global_enableledbit.html b/webif/config/global_enableledbit.html old mode 100755 new mode 100644 diff --git a/webif/config/global_getblockemmauprovid.html b/webif/config/global_getblockemmauprovid.html old mode 100755 new mode 100644 diff --git a/webif/config/global_localcards.html b/webif/config/global_localcards.html old mode 100755 new mode 100644 diff --git a/webif/config/global_suppresscmd08.html b/webif/config/global_suppresscmd08.html old mode 100755 new mode 100644 diff --git a/webif/config/global_unlockparental.html b/webif/config/global_unlockparental.html old mode 100755 new mode 100644 diff --git a/webif/config/lcd.html b/webif/config/lcd.html old mode 100755 new mode 100644 diff --git a/webif/config/loadbalancer.html b/webif/config/loadbalancer.html old mode 100755 new mode 100644 diff --git a/webif/config/loadbalancer_control.html b/webif/config/loadbalancer_control.html old mode 100755 new mode 100644 diff --git a/webif/config/menu.html b/webif/config/menu.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_anticasc.html b/webif/config/menu_anticasc.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_camd33.html b/webif/config/menu_camd33.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_camd35.html b/webif/config/menu_camd35.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_camd35tcp.html b/webif/config/menu_camd35tcp.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_cccam.html b/webif/config/menu_cccam.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_cmcaptioncwc.html b/webif/config/menu_cmcaptioncwc.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_dvbapi.html b/webif/config/menu_dvbapi.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_gbox.html b/webif/config/menu_gbox.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_lcd.html b/webif/config/menu_lcd.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_loadbalancer.html b/webif/config/menu_loadbalancer.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_monitor.html b/webif/config/menu_monitor.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_newcamd.html b/webif/config/menu_newcamd.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_radegast.html b/webif/config/menu_radegast.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_scam.html b/webif/config/menu_scam.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_serial.html b/webif/config/menu_serial.html old mode 100755 new mode 100644 diff --git a/webif/config/menu_streamrelay.html b/webif/config/menu_streamrelay.html old mode 100755 new mode 100644 diff --git a/webif/config/monitor.html b/webif/config/monitor.html old mode 100755 new mode 100644 diff --git a/webif/config/newcamd.html b/webif/config/newcamd.html old mode 100755 new mode 100644 diff --git a/webif/config/radegast.html b/webif/config/radegast.html old mode 100755 new mode 100644 diff --git a/webif/config/scam.html b/webif/config/scam.html old mode 100755 new mode 100644 diff --git a/webif/config/serial.html b/webif/config/serial.html old mode 100755 new mode 100644 diff --git a/webif/config/serial_devices.html b/webif/config/serial_devices.html old mode 100755 new mode 100644 diff --git a/webif/config/streamrelay.html b/webif/config/streamrelay.html old mode 100755 new mode 100644 index 3804d00..f79d5a1 --- a/webif/config/streamrelay.html +++ b/webif/config/streamrelay.html @@ -27,7 +27,6 @@ -##TPLSTREAMEMUSETTINGS##
Edit Global Config
Serverip:
Nice:
Net prio: + +
Bind wait: s
Resolver: + +
Prefer local cards:
SIGHUP reload:
Simple block:
Drop duplicate users: + +
Skip CWs checksum test: + +
Skip CWs checksum test only for: +
Logging
Usr file:
Mail file:
Log file / max size:
Log duplicated lines:
Initial debug level:
Pid file:
CW log dir:
EMM log dir:
ECM log format:
Loghistory Lines:
Syslog server:
Syslog port:
Failban
Failban time: min blocking IP based
Failban count: chances with wrong credentials
Timeouts / Times
Client timeout: ms to give up and return timeout
Fallback timeout: ms to switch to fallback reader
Fallback timeout per caid:
Client max idle: s to disconnect idle clients
Global sleep: min to switch a client in sleepmode
Reader restart seconds: s waittime to restart a reader
Doublecheck
ECM Doublecheck:
Doublecheck caids:
Source Stream Password:
Relay Buffer Time:
Relay Reconnect Count:
Relay Client Display Options: