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
|
||||
logCaller with DEBUG_LOCKCALL to preserve memory in normal operation.
|
||||
- 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
|
||||
* 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"
|
||||
@ -78,17 +78,18 @@ static char *CopyString(int Length, const uint8_t *Data)
|
||||
///< Copies the string at Data.
|
||||
///< Returns a pointer to a newly allocated string.
|
||||
{
|
||||
// Some CAMs send funny characters at the beginning of strings.
|
||||
// Let's just skip them:
|
||||
while (Length > 0 && (*Data == ' ' || *Data == 0x05 || *Data == 0x96 || *Data == 0x97)) {
|
||||
char *s = MALLOC(char, Length + 1);
|
||||
char *p = s;
|
||||
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--;
|
||||
Data++;
|
||||
}
|
||||
char *s = MALLOC(char, Length + 1);
|
||||
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');
|
||||
*p = 0;
|
||||
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 -----------------------------------------------------------------
|
||||
|
||||
#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->subTitleText = GetText(l, &d);
|
||||
if (l > 0) menu->bottomText = GetText(l, &d);
|
||||
int Action = CRA_NONE;
|
||||
int Select = -1;
|
||||
int Item = 0;
|
||||
while (l > 0) {
|
||||
char *s = GetText(l, &d);
|
||||
if (s) {
|
||||
if (!menu->AddEntry(s))
|
||||
free(s);
|
||||
else if (Action == CRA_NONE) {
|
||||
Action = CamResponses.GetMatch(CamSlot()->SlotNumber(), s);
|
||||
if (Action == CRA_SELECT)
|
||||
Select = Item;
|
||||
}
|
||||
}
|
||||
else
|
||||
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--;
|
||||
// I really wonder why there is no text length field here...
|
||||
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;
|
||||
@ -1449,6 +1620,7 @@ void cCiMenu::Abort(void)
|
||||
cCiEnquiry::cCiEnquiry(cCiMMI *MMI)
|
||||
{
|
||||
mmi = MMI;
|
||||
mutex = NULL;
|
||||
text = NULL;
|
||||
blind = false;
|
||||
expectedLength = 0;
|
||||
|
4
ci.h
4
ci.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -529,4 +529,6 @@ public:
|
||||
|
||||
extern cChannelCamRelations ChannelCamRelations;
|
||||
|
||||
bool CamResponsesLoad(const char *FileName, bool AllowComments = false, bool MustExist = false);
|
||||
|
||||
#endif //__CI_H
|
||||
|
41
vdr.5
41
vdr.5
@ -8,7 +8,7 @@
|
||||
.\" License as specified in the file COPYING that comes with the
|
||||
.\" 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"
|
||||
.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.
|
||||
|
||||
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
|
||||
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'.
|
||||
|
3
vdr.c
3
vdr.c
@ -22,7 +22,7 @@
|
||||
*
|
||||
* 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>
|
||||
@ -763,6 +763,7 @@ int main(int argc, char *argv[])
|
||||
Keys.Load(AddDirectory(ConfigDirectory, "remote.conf"));
|
||||
KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true);
|
||||
Folders.Load(AddDirectory(ConfigDirectory, "folders.conf"));
|
||||
CamResponsesLoad(AddDirectory(ConfigDirectory, "camresponses.conf"), true);
|
||||
|
||||
if (!*cFont::GetFontFileName(Setup.FontOsd)) {
|
||||
const char *msg = "no fonts available - OSD will not show any text!";
|
||||
|
Loading…
Reference in New Issue
Block a user