mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Implemented CAM auto responses
This commit is contained in:
parent
93102b45e0
commit
360d8fe6b1
3
HISTORY
3
HISTORY
@ -9113,3 +9113,6 @@ Video Disk Recorder Revision History
|
|||||||
- Increased SLL_LENGTH in thread.c to better handle long caller lines, and enclosed
|
- Increased SLL_LENGTH in thread.c to better handle long caller lines, and enclosed
|
||||||
logCaller with DEBUG_LOCKCALL to preserve memory in normal operation.
|
logCaller with DEBUG_LOCKCALL to preserve memory in normal operation.
|
||||||
- Fixed a typo in CAMMENURETYTIMEOUT and added logging CAM enquiries.
|
- Fixed a typo in CAMMENURETYTIMEOUT and added logging CAM enquiries.
|
||||||
|
- The new configuration file 'camresponses.conf' can be used to define automatic
|
||||||
|
responses to CAM menus, for instance to avoid annyoing popup messages or entering
|
||||||
|
the parental rating PIN. See vdr.5 for details.
|
||||||
|
29
camresponses.conf
Normal file
29
camresponses.conf
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# CAM responses for VDR
|
||||||
|
#
|
||||||
|
# Format:
|
||||||
|
#
|
||||||
|
# nr text action
|
||||||
|
#
|
||||||
|
# nr: the number of the CAM this action applies to (0 = all CAMs)
|
||||||
|
# text: the text in the CAM menu to react on (must be quoted with '"' if it contains
|
||||||
|
# blanks, escape '"' with '\')
|
||||||
|
# action: the action to take if the given text is encountered
|
||||||
|
#
|
||||||
|
# Possible actions are:
|
||||||
|
#
|
||||||
|
# - DISCARD: simply discard the menu (equivalent to pressing 'Back' on the RC)
|
||||||
|
# - CONFIRM: confirm the menu (equivalent to pressing 'OK' without selecting a
|
||||||
|
# particular item)
|
||||||
|
# - SELECT: select the menu item containing the text (equivalent to positioning
|
||||||
|
# the cursor on the item and pressing 'OK')
|
||||||
|
# - <number>: the given number is sent to the CAM as if it were typed in by the user
|
||||||
|
# (provided this is an input field).
|
||||||
|
#
|
||||||
|
# Note that the text given in a rule must match exactly, including any leading or
|
||||||
|
# trailing blanks. If in doubt, you can get the exact text from the log file.
|
||||||
|
# Action keywords are case insensitive.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
|
||||||
|
# * "Hello! This is your annoying \"nag\" message!" DISCARD
|
||||||
|
# 3 "Please enter your PIN" 1234
|
190
ci.c
190
ci.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: ci.c 4.16 2017/05/18 09:05:46 kls Exp $
|
* $Id: ci.c 4.17 2017/06/10 11:53:39 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ci.h"
|
#include "ci.h"
|
||||||
@ -78,17 +78,18 @@ static char *CopyString(int Length, const uint8_t *Data)
|
|||||||
///< Copies the string at Data.
|
///< Copies the string at Data.
|
||||||
///< Returns a pointer to a newly allocated string.
|
///< Returns a pointer to a newly allocated string.
|
||||||
{
|
{
|
||||||
// Some CAMs send funny characters at the beginning of strings.
|
char *s = MALLOC(char, Length + 1);
|
||||||
// Let's just skip them:
|
char *p = s;
|
||||||
while (Length > 0 && (*Data == ' ' || *Data == 0x05 || *Data == 0x96 || *Data == 0x97)) {
|
while (Length > 0) {
|
||||||
|
char c = *Data;
|
||||||
|
if (isprint(c)) // some CAMs send funny characters in their strings, let's just skip them
|
||||||
|
*p++ = c;
|
||||||
|
else if (c == 0x8A) // the character 0x8A is used as newline, so let's put a real '\n' in there
|
||||||
|
*p++ = '\n';
|
||||||
Length--;
|
Length--;
|
||||||
Data++;
|
Data++;
|
||||||
}
|
}
|
||||||
char *s = MALLOC(char, Length + 1);
|
*p = 0;
|
||||||
strncpy(s, (char *)Data, Length);
|
|
||||||
s[Length] = 0;
|
|
||||||
// The character 0x8A is used as newline, so let's put a real '\n' in there:
|
|
||||||
strreplace(s, 0x8A, '\n');
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,6 +291,137 @@ void cCaActivationReceiver::Receive(const uchar *Data, int Length)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- cCamResponse ----------------------------------------------------------
|
||||||
|
|
||||||
|
// CAM Response Actions:
|
||||||
|
|
||||||
|
#define CRA_NONE 0
|
||||||
|
#define CRA_DISCARD -1
|
||||||
|
#define CRA_CONFIRM -2
|
||||||
|
#define CRA_SELECT -3
|
||||||
|
|
||||||
|
class cCamResponse : public cListObject {
|
||||||
|
private:
|
||||||
|
int camNumber;
|
||||||
|
char *text;
|
||||||
|
int action;
|
||||||
|
public:
|
||||||
|
cCamResponse(void);
|
||||||
|
~cCamResponse();
|
||||||
|
bool Parse(const char *s);
|
||||||
|
int Matches(int CamNumber, const char *Text) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
cCamResponse::cCamResponse(void)
|
||||||
|
{
|
||||||
|
camNumber = -1;
|
||||||
|
text = NULL;
|
||||||
|
action = CRA_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cCamResponse::~cCamResponse()
|
||||||
|
{
|
||||||
|
free(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cCamResponse::Parse(const char *s)
|
||||||
|
{
|
||||||
|
// Number:
|
||||||
|
s = skipspace(s);
|
||||||
|
if (*s == '*') {
|
||||||
|
camNumber = 0; // all CAMs
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char *e;
|
||||||
|
camNumber = strtol(s, &e, 10);
|
||||||
|
if (e == s || camNumber <= 0)
|
||||||
|
return false;
|
||||||
|
s = e;
|
||||||
|
}
|
||||||
|
// Text:
|
||||||
|
s = skipspace(s);
|
||||||
|
char *t = const_cast<char *>(s); // might have to modify it
|
||||||
|
char *q = NULL; // holds a copy in case of backslashes
|
||||||
|
bool InQuotes = false;
|
||||||
|
while (*t) {
|
||||||
|
if (*t == '"') {
|
||||||
|
if (t == s) { // opening quotes
|
||||||
|
InQuotes = true;
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
else if (InQuotes) // closing quotes
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (*t == '\\') {
|
||||||
|
if (!q) { // need to make a copy in order to strip backslashes
|
||||||
|
q = strdup(s);
|
||||||
|
t = q + (t - s);
|
||||||
|
s = q;
|
||||||
|
}
|
||||||
|
memmove(t, t + 1, strlen(t));
|
||||||
|
}
|
||||||
|
else if (*t == ' ') {
|
||||||
|
if (!InQuotes)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t++;
|
||||||
|
}
|
||||||
|
free(text); // just for safety
|
||||||
|
text = NULL;
|
||||||
|
if (t != s) {
|
||||||
|
text = strndup(s, t - s);
|
||||||
|
s = t + 1;
|
||||||
|
}
|
||||||
|
free(q);
|
||||||
|
if (!text)
|
||||||
|
return false;
|
||||||
|
// Action:
|
||||||
|
s = skipspace(s);
|
||||||
|
if (strcasecmp(s, "DISCARD") == 0) action = CRA_DISCARD;
|
||||||
|
else if (strcasecmp(s, "CONFIRM") == 0) action = CRA_CONFIRM;
|
||||||
|
else if (strcasecmp(s, "SELECT") == 0) action = CRA_SELECT;
|
||||||
|
else if (isnumber(s)) action = atoi(s);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cCamResponse::Matches(int CamNumber, const char *Text) const
|
||||||
|
{
|
||||||
|
if (!camNumber || camNumber == CamNumber) {
|
||||||
|
if (strcmp(text, Text) == 0)
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
return CRA_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cCamResponses --------------------------------------------------------
|
||||||
|
|
||||||
|
class cCamResponses : public cConfig<cCamResponse> {
|
||||||
|
public:
|
||||||
|
int GetMatch(int CamNumber, const char *Text) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
int cCamResponses::GetMatch(int CamNumber, const char *Text) const
|
||||||
|
{
|
||||||
|
for (const cCamResponse *cr = First(); cr; cr = Next(cr)) {
|
||||||
|
int Action = cr->Matches(CamNumber, Text);
|
||||||
|
if (Action != CRA_NONE) {
|
||||||
|
dsyslog("CAM %d: auto response %4d to '%s'\n", CamNumber, Action, Text);
|
||||||
|
return Action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CRA_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
cCamResponses CamResponses;
|
||||||
|
|
||||||
|
bool CamResponsesLoad(const char *FileName, bool AllowComments, bool MustExist)
|
||||||
|
{
|
||||||
|
return CamResponses.Load(FileName, AllowComments, MustExist);
|
||||||
|
}
|
||||||
|
|
||||||
// --- cTPDU -----------------------------------------------------------------
|
// --- cTPDU -----------------------------------------------------------------
|
||||||
|
|
||||||
#define MAX_TPDU_SIZE 4096
|
#define MAX_TPDU_SIZE 4096
|
||||||
@ -1292,14 +1424,40 @@ void cCiMMI::Process(int Length, const uint8_t *Data)
|
|||||||
if (l > 0) menu->titleText = GetText(l, &d);
|
if (l > 0) menu->titleText = GetText(l, &d);
|
||||||
if (l > 0) menu->subTitleText = GetText(l, &d);
|
if (l > 0) menu->subTitleText = GetText(l, &d);
|
||||||
if (l > 0) menu->bottomText = GetText(l, &d);
|
if (l > 0) menu->bottomText = GetText(l, &d);
|
||||||
|
int Action = CRA_NONE;
|
||||||
|
int Select = -1;
|
||||||
|
int Item = 0;
|
||||||
while (l > 0) {
|
while (l > 0) {
|
||||||
char *s = GetText(l, &d);
|
char *s = GetText(l, &d);
|
||||||
if (s) {
|
if (s) {
|
||||||
if (!menu->AddEntry(s))
|
if (!menu->AddEntry(s))
|
||||||
free(s);
|
free(s);
|
||||||
|
else if (Action == CRA_NONE) {
|
||||||
|
Action = CamResponses.GetMatch(CamSlot()->SlotNumber(), s);
|
||||||
|
if (Action == CRA_SELECT)
|
||||||
|
Select = Item;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
|
Item++;
|
||||||
|
}
|
||||||
|
if (Action != CRA_NONE) {
|
||||||
|
delete menu;
|
||||||
|
menu = NULL;
|
||||||
|
cCondWait::SleepMs(100);
|
||||||
|
if (Action == CRA_DISCARD) {
|
||||||
|
SendCloseMMI();
|
||||||
|
dsyslog("CAM %d: DISCARD", CamSlot()->SlotNumber());
|
||||||
|
}
|
||||||
|
else if (Action == CRA_CONFIRM) {
|
||||||
|
SendMenuAnswer(1);
|
||||||
|
dsyslog("CAM %d: CONFIRM", CamSlot()->SlotNumber());
|
||||||
|
}
|
||||||
|
else if (Action == CRA_SELECT) {
|
||||||
|
SendMenuAnswer(Select + 1);
|
||||||
|
dsyslog("CAM %d: SELECT %d", CamSlot()->SlotNumber(), Select + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1319,6 +1477,19 @@ void cCiMMI::Process(int Length, const uint8_t *Data)
|
|||||||
l--;
|
l--;
|
||||||
// I really wonder why there is no text length field here...
|
// I really wonder why there is no text length field here...
|
||||||
enquiry->text = CopyString(l, d);
|
enquiry->text = CopyString(l, d);
|
||||||
|
int Action = CamResponses.GetMatch(CamSlot()->SlotNumber(), enquiry->text);
|
||||||
|
if (Action > CRA_NONE) {
|
||||||
|
char s[enquiry->expectedLength * 2];
|
||||||
|
snprintf(s, sizeof(s), "%d", Action);
|
||||||
|
if (int(strlen(s)) == enquiry->expectedLength) {
|
||||||
|
delete enquiry;
|
||||||
|
enquiry = NULL;
|
||||||
|
SendAnswer(s);
|
||||||
|
dsyslog("CAM %d: PIN", CamSlot()->SlotNumber());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esyslog("CAM %d: ERROR: unexpected PIN length %d, expected %d", CamSlot()->SlotNumber(), int(strlen(s)), enquiry->expectedLength);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1449,6 +1620,7 @@ void cCiMenu::Abort(void)
|
|||||||
cCiEnquiry::cCiEnquiry(cCiMMI *MMI)
|
cCiEnquiry::cCiEnquiry(cCiMMI *MMI)
|
||||||
{
|
{
|
||||||
mmi = MMI;
|
mmi = MMI;
|
||||||
|
mutex = NULL;
|
||||||
text = NULL;
|
text = NULL;
|
||||||
blind = false;
|
blind = false;
|
||||||
expectedLength = 0;
|
expectedLength = 0;
|
||||||
|
4
ci.h
4
ci.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: ci.h 4.9 2017/05/18 09:05:46 kls Exp $
|
* $Id: ci.h 4.10 2017/06/10 11:53:39 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __CI_H
|
#ifndef __CI_H
|
||||||
@ -529,4 +529,6 @@ public:
|
|||||||
|
|
||||||
extern cChannelCamRelations ChannelCamRelations;
|
extern cChannelCamRelations ChannelCamRelations;
|
||||||
|
|
||||||
|
bool CamResponsesLoad(const char *FileName, bool AllowComments = false, bool MustExist = false);
|
||||||
|
|
||||||
#endif //__CI_H
|
#endif //__CI_H
|
||||||
|
41
vdr.5
41
vdr.5
@ -8,7 +8,7 @@
|
|||||||
.\" License as specified in the file COPYING that comes with the
|
.\" License as specified in the file COPYING that comes with the
|
||||||
.\" vdr distribution.
|
.\" vdr distribution.
|
||||||
.\"
|
.\"
|
||||||
.\" $Id: vdr.5 4.2 2017/04/02 11:41:51 kls Exp $
|
.\" $Id: vdr.5 4.3 2017/06/10 11:53:39 kls Exp $
|
||||||
.\"
|
.\"
|
||||||
.TH vdr 5 "19 Feb 2015" "2.2" "Video Disk Recorder Files"
|
.TH vdr 5 "19 Feb 2015" "2.2" "Video Disk Recorder Files"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
@ -918,6 +918,45 @@ in this file, other CAMs will be tried just as well. The main purpose of this
|
|||||||
file is to speed up channel switching in systems with more than one CAM.
|
file is to speed up channel switching in systems with more than one CAM.
|
||||||
|
|
||||||
This file will be read at program startup and saved when the program ends.
|
This file will be read at program startup and saved when the program ends.
|
||||||
|
.SS CAM AUTO RESPONSE
|
||||||
|
If your CAM keeps popping up annoying messages or you want to make sure VDR
|
||||||
|
can record programmes with parental rating without having to enter the PIN
|
||||||
|
(in case you can't turn that off in your CAM), you can set up auto responses
|
||||||
|
in the file \fIcamresponses.conf\fR.
|
||||||
|
|
||||||
|
Each line in this file specifies one rule to apply to texts received from
|
||||||
|
the CAM. If the CAM's menu text matches the text in one of these rules,
|
||||||
|
the given action is taken and sent to the CAM as an automatic response,
|
||||||
|
without any menu appearing on the screen. The first match wins.
|
||||||
|
|
||||||
|
The format of these rules is:
|
||||||
|
|
||||||
|
nr text action
|
||||||
|
|
||||||
|
where
|
||||||
|
.TS
|
||||||
|
tab (@);
|
||||||
|
l l.
|
||||||
|
nr @is the number of the CAM this action applies to (0 = all CAMs)
|
||||||
|
text @is the text in the CAM menu to react on (must be quoted with '"' if it contains blanks, escape '"' with '\\')
|
||||||
|
action @is the action to take if the given text is encountered
|
||||||
|
.TE
|
||||||
|
|
||||||
|
Possible actions are:
|
||||||
|
.TS
|
||||||
|
tab (@);
|
||||||
|
l l.
|
||||||
|
DISCARD @simply discard the menu (equivalent to pressing 'Back' on the RC)
|
||||||
|
CONFIRM @confirm the menu (equivalent to pressing 'OK' without selecting a particular item)
|
||||||
|
SELECT @select the menu item containing the text (equivalent to positioning the cursor on the item and pressing 'OK')
|
||||||
|
<number> @the given number is sent to the CAM as if it were tyed in by the user (provided this is an input field).
|
||||||
|
.TE
|
||||||
|
|
||||||
|
Note that the text given in a rule must match exactly, including any leading or
|
||||||
|
trailing blanks. If in doubt, you can get the exact text from the log file.
|
||||||
|
Action keywords are case insensitive.
|
||||||
|
|
||||||
|
Everything following (and including) a '#' character is considered to be comment.
|
||||||
.SS COMMANDLINE OPTIONS
|
.SS COMMANDLINE OPTIONS
|
||||||
If started without any options, vdr tries to read any files in the directory
|
If started without any options, vdr tries to read any files in the directory
|
||||||
/etc/vdr/conf.d with names that do not begin with a '.' and that end with '.conf'.
|
/etc/vdr/conf.d with names that do not begin with a '.' and that end with '.conf'.
|
||||||
|
3
vdr.c
3
vdr.c
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
* The project's page is at http://www.tvdr.de
|
* The project's page is at http://www.tvdr.de
|
||||||
*
|
*
|
||||||
* $Id: vdr.c 4.17 2017/06/06 10:53:44 kls Exp $
|
* $Id: vdr.c 4.18 2017/06/10 11:53:39 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -763,6 +763,7 @@ int main(int argc, char *argv[])
|
|||||||
Keys.Load(AddDirectory(ConfigDirectory, "remote.conf"));
|
Keys.Load(AddDirectory(ConfigDirectory, "remote.conf"));
|
||||||
KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true);
|
KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true);
|
||||||
Folders.Load(AddDirectory(ConfigDirectory, "folders.conf"));
|
Folders.Load(AddDirectory(ConfigDirectory, "folders.conf"));
|
||||||
|
CamResponsesLoad(AddDirectory(ConfigDirectory, "camresponses.conf"), true);
|
||||||
|
|
||||||
if (!*cFont::GetFontFileName(Setup.FontOsd)) {
|
if (!*cFont::GetFontFileName(Setup.FontOsd)) {
|
||||||
const char *msg = "no fonts available - OSD will not show any text!";
|
const char *msg = "no fonts available - OSD will not show any text!";
|
||||||
|
Loading…
Reference in New Issue
Block a user