2000-07-23 15:01:31 +02:00
/*
* svdrp . c : Simple Video Disk Recorder Protocol
*
* See the main source file ' vdr . c ' for copyright information and
* how to reach the author .
*
* The " Simple Video Disk Recorder Protocol " ( SVDRP ) was inspired
* by the " Simple Mail Transfer Protocol " ( SMTP ) and is fully ASCII
* text based . Therefore you can simply ' telnet ' to your VDR port
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection .
*
2022-11-22 14:33:56 +01:00
* $ Id : svdrp . c 5.6 2022 / 11 / 22 14 : 33 : 48 kls Exp $
2000-07-23 15:01:31 +02:00
*/
# include "svdrp.h"
# include <arpa/inet.h>
# include <ctype.h>
2000-12-08 16:23:32 +01:00
# include <errno.h>
2000-07-23 15:01:31 +02:00
# include <fcntl.h>
2015-05-22 13:44:43 +02:00
# include <ifaddrs.h>
2000-07-23 15:01:31 +02:00
# include <netinet/in.h>
# include <stdarg.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/socket.h>
# include <sys/time.h>
# include <unistd.h>
2002-10-06 10:25:42 +02:00
# include "channels.h"
2000-07-23 15:01:31 +02:00
# include "config.h"
2002-06-16 12:57:31 +02:00
# include "device.h"
2004-01-17 15:41:52 +01:00
# include "eitscan.h"
2002-09-29 13:40:45 +02:00
# include "keys.h"
2005-05-26 10:00:59 +02:00
# include "menu.h"
2005-08-27 16:42:28 +02:00
# include "plugin.h"
2015-04-29 13:10:06 +02:00
# include "recording.h"
2002-09-29 13:40:45 +02:00
# include "remote.h"
2005-11-27 15:57:03 +01:00
# include "skins.h"
2002-10-20 12:28:55 +02:00
# include "timers.h"
2003-04-27 14:23:30 +02:00
# include "videodir.h"
2000-07-23 15:01:31 +02:00
2015-05-22 13:44:43 +02:00
static bool DumpSVDRPDataTransfer = false ;
# define dbgsvdrp(a...) if (DumpSVDRPDataTransfer) fprintf(stderr, a)
2015-09-08 11:08:06 +02:00
static int SVDRPTcpPort = 0 ;
static int SVDRPUdpPort = 0 ;
2015-05-22 13:44:43 +02:00
2018-03-17 10:07:19 +01:00
enum eSvdrpFetchFlags {
sffNone = 0 b00000000 ,
sffConn = 0 b00000001 ,
sffPing = 0 b00000010 ,
sffTimers = 0 b00000100 ,
} ;
2015-05-22 13:44:43 +02:00
// --- cIpAddress ------------------------------------------------------------
class cIpAddress {
private :
cString address ;
int port ;
cString connection ;
public :
cIpAddress ( void ) ;
cIpAddress ( const char * Address , int Port ) ;
const char * Address ( void ) const { return address ; }
int Port ( void ) const { return port ; }
void Set ( const char * Address , int Port ) ;
void Set ( const sockaddr * SockAddr ) ;
const char * Connection ( void ) const { return connection ; }
} ;
cIpAddress : : cIpAddress ( void )
{
Set ( INADDR_ANY , 0 ) ;
}
cIpAddress : : cIpAddress ( const char * Address , int Port )
{
Set ( Address , Port ) ;
}
void cIpAddress : : Set ( const char * Address , int Port )
{
address = Address ;
port = Port ;
connection = cString : : sprintf ( " %s:%d " , * address , port ) ;
}
void cIpAddress : : Set ( const sockaddr * SockAddr )
{
const sockaddr_in * Addr = ( sockaddr_in * ) SockAddr ;
Set ( inet_ntoa ( Addr - > sin_addr ) , ntohs ( Addr - > sin_port ) ) ;
}
2000-07-23 15:01:31 +02:00
// --- cSocket ---------------------------------------------------------------
2015-05-22 13:44:43 +02:00
# define MAXUDPBUF 1024
2015-04-29 13:10:06 +02:00
class cSocket {
private :
int port ;
2015-05-22 13:44:43 +02:00
bool tcp ;
2015-04-29 13:10:06 +02:00
int sock ;
2015-05-22 13:44:43 +02:00
cIpAddress lastIpAddress ;
2015-04-29 13:10:06 +02:00
public :
2015-05-22 13:44:43 +02:00
cSocket ( int Port , bool Tcp ) ;
2015-04-29 13:10:06 +02:00
~ cSocket ( ) ;
2015-05-22 13:44:43 +02:00
bool Listen ( void ) ;
bool Connect ( const char * Address ) ;
2015-04-29 13:10:06 +02:00
void Close ( void ) ;
2015-05-22 13:44:43 +02:00
int Port ( void ) const { return port ; }
2015-04-29 13:10:06 +02:00
int Socket ( void ) const { return sock ; }
2018-02-20 13:28:04 +01:00
static bool SendDgram ( const char * Dgram , int Port ) ;
2015-04-29 13:10:06 +02:00
int Accept ( void ) ;
2015-05-22 13:44:43 +02:00
cString Discover ( void ) ;
const cIpAddress * LastIpAddress ( void ) const { return & lastIpAddress ; }
2015-04-29 13:10:06 +02:00
} ;
2015-05-22 13:44:43 +02:00
cSocket : : cSocket ( int Port , bool Tcp )
2000-07-23 15:01:31 +02:00
{
port = Port ;
2015-05-22 13:44:43 +02:00
tcp = Tcp ;
2000-07-23 15:01:31 +02:00
sock = - 1 ;
}
cSocket : : ~ cSocket ( )
{
Close ( ) ;
}
void cSocket : : Close ( void )
{
if ( sock > = 0 ) {
close ( sock ) ;
sock = - 1 ;
}
}
2015-05-22 13:44:43 +02:00
bool cSocket : : Listen ( void )
2000-07-23 15:01:31 +02:00
{
if ( sock < 0 ) {
2018-02-15 14:30:14 +01:00
isyslog ( " SVDRP %s opening port %d/%s " , Setup . SVDRPHostName , port , tcp ? " tcp " : " udp " ) ;
2000-07-23 15:01:31 +02:00
// create socket:
2015-05-22 13:44:43 +02:00
sock = tcp ? socket ( PF_INET , SOCK_STREAM , IPPROTO_IP ) : socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ) ;
2000-07-23 15:01:31 +02:00
if ( sock < 0 ) {
LOG_ERROR ;
return false ;
}
2001-02-18 14:18:13 +01:00
// allow it to always reuse the same port:
int ReUseAddr = 1 ;
setsockopt ( sock , SOL_SOCKET , SO_REUSEADDR , & ReUseAddr , sizeof ( ReUseAddr ) ) ;
2015-05-22 13:44:43 +02:00
// configure port and ip:
sockaddr_in Addr ;
memset ( & Addr , 0 , sizeof ( Addr ) ) ;
Addr . sin_family = AF_INET ;
Addr . sin_port = htons ( port ) ;
Addr . sin_addr . s_addr = SVDRPhosts . LocalhostOnly ( ) ? htonl ( INADDR_LOOPBACK ) : htonl ( INADDR_ANY ) ;
if ( bind ( sock , ( sockaddr * ) & Addr , sizeof ( Addr ) ) < 0 ) {
LOG_ERROR ;
Close ( ) ;
return false ;
}
// make it non-blocking:
int Flags = fcntl ( sock , F_GETFL , 0 ) ;
if ( Flags < 0 ) {
LOG_ERROR ;
return false ;
}
Flags | = O_NONBLOCK ;
if ( fcntl ( sock , F_SETFL , Flags ) < 0 ) {
LOG_ERROR ;
return false ;
}
if ( tcp ) {
// listen to the socket:
if ( listen ( sock , 1 ) < 0 ) {
LOG_ERROR ;
return false ;
}
}
2018-02-15 14:30:14 +01:00
isyslog ( " SVDRP %s listening on port %d/%s " , Setup . SVDRPHostName , port , tcp ? " tcp " : " udp " ) ;
2015-05-22 13:44:43 +02:00
}
return true ;
}
bool cSocket : : Connect ( const char * Address )
{
if ( sock < 0 & & tcp ) {
// create socket:
sock = socket ( PF_INET , SOCK_STREAM , IPPROTO_IP ) ;
if ( sock < 0 ) {
LOG_ERROR ;
return false ;
}
// configure port and ip:
sockaddr_in Addr ;
memset ( & Addr , 0 , sizeof ( Addr ) ) ;
Addr . sin_family = AF_INET ;
Addr . sin_port = htons ( port ) ;
Addr . sin_addr . s_addr = inet_addr ( Address ) ;
if ( connect ( sock , ( sockaddr * ) & Addr , sizeof ( Addr ) ) < 0 ) {
2000-07-23 15:01:31 +02:00
LOG_ERROR ;
Close ( ) ;
return false ;
}
// make it non-blocking:
2015-05-22 13:44:43 +02:00
int Flags = fcntl ( sock , F_GETFL , 0 ) ;
if ( Flags < 0 ) {
2000-07-23 15:01:31 +02:00
LOG_ERROR ;
return false ;
}
2015-05-22 13:44:43 +02:00
Flags | = O_NONBLOCK ;
if ( fcntl ( sock , F_SETFL , Flags ) < 0 ) {
2000-07-23 15:01:31 +02:00
LOG_ERROR ;
return false ;
}
2018-02-20 13:28:04 +01:00
dbgsvdrp ( " > %s:%d server connection established \n " , Address , port ) ;
2018-02-15 14:30:14 +01:00
isyslog ( " SVDRP %s > %s:%d server connection established " , Setup . SVDRPHostName , Address , port ) ;
2015-05-22 13:44:43 +02:00
return true ;
}
return false ;
}
2018-02-20 13:28:04 +01:00
bool cSocket : : SendDgram ( const char * Dgram , int Port )
2015-05-22 13:44:43 +02:00
{
// Create a socket:
int Socket = socket ( PF_INET , SOCK_DGRAM , IPPROTO_UDP ) ;
if ( Socket < 0 ) {
LOG_ERROR ;
return false ;
}
2018-02-20 13:28:04 +01:00
// Enable broadcast:
int One = 1 ;
if ( setsockopt ( Socket , SOL_SOCKET , SO_BROADCAST , & One , sizeof ( One ) ) < 0 ) {
LOG_ERROR ;
close ( Socket ) ;
return false ;
2000-07-23 15:01:31 +02:00
}
2015-05-22 13:44:43 +02:00
// Configure port and ip:
sockaddr_in Addr ;
memset ( & Addr , 0 , sizeof ( Addr ) ) ;
Addr . sin_family = AF_INET ;
2018-02-20 13:28:04 +01:00
Addr . sin_addr . s_addr = htonl ( INADDR_BROADCAST ) ;
2015-05-22 13:44:43 +02:00
Addr . sin_port = htons ( Port ) ;
// Send datagram:
2018-02-15 14:30:14 +01:00
dbgsvdrp ( " > %s:%d %s \n " , inet_ntoa ( Addr . sin_addr ) , Port , Dgram ) ;
dsyslog ( " SVDRP %s > %s:%d send dgram '%s' " , Setup . SVDRPHostName , inet_ntoa ( Addr . sin_addr ) , Port , Dgram ) ;
2015-05-22 13:44:43 +02:00
int Length = strlen ( Dgram ) ;
int Sent = sendto ( Socket , Dgram , Length , 0 , ( sockaddr * ) & Addr , sizeof ( Addr ) ) ;
if ( Sent < 0 )
LOG_ERROR ;
close ( Socket ) ;
return Sent = = Length ;
2000-07-23 15:01:31 +02:00
}
int cSocket : : Accept ( void )
{
2015-05-22 13:44:43 +02:00
if ( sock > = 0 & & tcp ) {
sockaddr_in Addr ;
uint Size = sizeof ( Addr ) ;
int NewSock = accept ( sock , ( sockaddr * ) & Addr , & Size ) ;
if ( NewSock > = 0 ) {
bool Accepted = SVDRPhosts . Acceptable ( Addr . sin_addr . s_addr ) ;
if ( ! Accepted ) {
2002-02-02 17:20:54 +01:00
const char * s = " Access denied! \n " ;
2015-05-22 13:44:43 +02:00
if ( write ( NewSock , s , strlen ( s ) ) < 0 )
2005-03-20 10:10:38 +01:00
LOG_ERROR ;
2015-05-22 13:44:43 +02:00
close ( NewSock ) ;
NewSock = - 1 ;
2002-02-02 17:20:54 +01:00
}
2015-05-22 13:44:43 +02:00
lastIpAddress . Set ( ( sockaddr * ) & Addr ) ;
2018-02-15 14:30:14 +01:00
dbgsvdrp ( " < %s client connection %s \n " , lastIpAddress . Connection ( ) , Accepted ? " accepted " : " DENIED " ) ;
isyslog ( " SVDRP %s < %s client connection %s " , Setup . SVDRPHostName , lastIpAddress . Connection ( ) , Accepted ? " accepted " : " DENIED " ) ;
2002-02-02 17:20:54 +01:00
}
2015-05-22 13:44:43 +02:00
else if ( FATALERRNO )
2000-07-23 15:01:31 +02:00
LOG_ERROR ;
2015-05-22 13:44:43 +02:00
return NewSock ;
2000-07-23 15:01:31 +02:00
}
return - 1 ;
}
2015-05-22 13:44:43 +02:00
cString cSocket : : Discover ( void )
{
if ( sock > = 0 & & ! tcp ) {
char buf [ MAXUDPBUF ] ;
sockaddr_in Addr ;
uint Size = sizeof ( Addr ) ;
int NumBytes = recvfrom ( sock , buf , sizeof ( buf ) , 0 , ( sockaddr * ) & Addr , & Size ) ;
if ( NumBytes > = 0 ) {
buf [ NumBytes ] = 0 ;
2018-02-15 14:30:14 +01:00
lastIpAddress . Set ( ( sockaddr * ) & Addr ) ;
if ( ! SVDRPhosts . Acceptable ( Addr . sin_addr . s_addr ) ) {
dsyslog ( " SVDRP %s < %s discovery ignored (%s) " , Setup . SVDRPHostName , lastIpAddress . Connection ( ) , buf ) ;
return NULL ;
}
if ( ! startswith ( buf , " SVDRP:discover " ) ) {
dsyslog ( " SVDRP %s < %s discovery unrecognized (%s) " , Setup . SVDRPHostName , lastIpAddress . Connection ( ) , buf ) ;
return NULL ;
}
if ( strcmp ( strgetval ( buf , " name " , ' : ' ) , Setup . SVDRPHostName ) ! = 0 ) { // ignore our own broadcast
dbgsvdrp ( " < %s discovery received (%s) \n " , lastIpAddress . Connection ( ) , buf ) ;
isyslog ( " SVDRP %s < %s discovery received (%s) " , Setup . SVDRPHostName , lastIpAddress . Connection ( ) , buf ) ;
2015-05-22 13:44:43 +02:00
return buf ;
}
}
else if ( FATALERRNO )
LOG_ERROR ;
}
return NULL ;
}
2015-09-01 11:14:27 +02:00
// --- cSVDRPClient ----------------------------------------------------------
class cSVDRPClient {
private :
2018-02-20 13:28:04 +01:00
cIpAddress serverIpAddress ;
2015-09-01 11:14:27 +02:00
cSocket socket ;
cString serverName ;
2018-03-18 10:43:53 +01:00
int length ;
char * input ;
2015-09-01 11:14:27 +02:00
int timeout ;
cTimeMs pingTime ;
cFile file ;
int fetchFlags ;
2018-02-20 13:28:04 +01:00
bool connected ;
2018-02-25 13:26:17 +01:00
bool Send ( const char * Command ) ;
2015-09-01 11:14:27 +02:00
void Close ( void ) ;
public :
cSVDRPClient ( const char * Address , int Port , const char * ServerName , int Timeout ) ;
~ cSVDRPClient ( ) ;
const char * ServerName ( void ) const { return serverName ; }
2018-02-20 13:28:04 +01:00
const char * Connection ( void ) const { return serverIpAddress . Connection ( ) ; }
2015-09-01 11:14:27 +02:00
bool HasAddress ( const char * Address , int Port ) const ;
bool Process ( cStringList * Response = NULL ) ;
bool Execute ( const char * Command , cStringList * Response = NULL ) ;
2018-02-20 13:28:04 +01:00
bool Connected ( void ) const { return connected ; }
2018-03-17 10:07:19 +01:00
void SetFetchFlag ( int Flag ) ;
bool HasFetchFlag ( int Flag ) ;
2018-02-25 13:26:17 +01:00
bool GetRemoteTimers ( cStringList & Response ) ;
2015-09-01 11:14:27 +02:00
} ;
static cPoller SVDRPClientPoller ;
cSVDRPClient : : cSVDRPClient ( const char * Address , int Port , const char * ServerName , int Timeout )
2018-02-20 13:28:04 +01:00
: serverIpAddress ( Address , Port )
2015-09-01 11:14:27 +02:00
, socket ( Port , true )
{
serverName = ServerName ;
2018-03-18 10:43:53 +01:00
length = BUFSIZ ;
input = MALLOC ( char , length ) ;
2015-09-01 11:14:27 +02:00
timeout = Timeout * 1000 * 9 / 10 ; // ping after 90% of timeout
pingTime . Set ( timeout ) ;
2018-02-20 13:28:04 +01:00
fetchFlags = sffNone ;
connected = false ;
2015-09-01 11:14:27 +02:00
if ( socket . Connect ( Address ) ) {
if ( file . Open ( socket . Socket ( ) ) ) {
SVDRPClientPoller . Add ( file , false ) ;
2018-02-20 13:28:04 +01:00
dsyslog ( " SVDRP %s > %s client created for '%s' " , Setup . SVDRPHostName , serverIpAddress . Connection ( ) , * serverName ) ;
2015-09-01 11:14:27 +02:00
return ;
}
}
2018-02-20 13:28:04 +01:00
esyslog ( " SVDRP %s > %s ERROR: failed to create client for '%s' " , Setup . SVDRPHostName , serverIpAddress . Connection ( ) , * serverName ) ;
2015-09-01 11:14:27 +02:00
}
cSVDRPClient : : ~ cSVDRPClient ( )
{
Close ( ) ;
2018-03-18 10:43:53 +01:00
free ( input ) ;
2018-02-20 13:28:04 +01:00
dsyslog ( " SVDRP %s > %s client destroyed for '%s' " , Setup . SVDRPHostName , serverIpAddress . Connection ( ) , * serverName ) ;
2015-09-01 11:14:27 +02:00
}
void cSVDRPClient : : Close ( void )
{
if ( file . IsOpen ( ) ) {
SVDRPClientPoller . Del ( file , false ) ;
file . Close ( ) ;
socket . Close ( ) ;
}
}
bool cSVDRPClient : : HasAddress ( const char * Address , int Port ) const
{
2018-02-20 13:28:04 +01:00
return strcmp ( serverIpAddress . Address ( ) , Address ) = = 0 & & serverIpAddress . Port ( ) = = Port ;
2015-09-01 11:14:27 +02:00
}
bool cSVDRPClient : : Send ( const char * Command )
{
pingTime . Set ( timeout ) ;
2018-02-20 13:28:04 +01:00
dbgsvdrp ( " > C %s: %s \n " , * serverName , Command ) ;
2015-09-01 11:14:27 +02:00
if ( safe_write ( file , Command , strlen ( Command ) + 1 ) < 0 ) {
LOG_ERROR ;
return false ;
}
return true ;
}
bool cSVDRPClient : : Process ( cStringList * Response )
{
if ( file . IsOpen ( ) ) {
int numChars = 0 ;
# define SVDRPResonseTimeout 5000 // ms
cTimeMs Timeout ( SVDRPResonseTimeout ) ;
for ( ; ; ) {
if ( file . Ready ( false ) ) {
unsigned char c ;
int r = safe_read ( file , & c , 1 ) ;
if ( r > 0 ) {
if ( c = = ' \n ' | | c = = 0x00 ) {
// strip trailing whitespace:
while ( numChars > 0 & & strchr ( " \t \r \n " , input [ numChars - 1 ] ) )
input [ - - numChars ] = 0 ;
// make sure the string is terminated:
input [ numChars ] = 0 ;
2018-02-20 13:28:04 +01:00
dbgsvdrp ( " < C %s: %s \n " , * serverName , input ) ;
2018-02-05 15:18:19 +01:00
if ( Response )
2015-09-01 11:14:27 +02:00
Response - > Append ( strdup ( input ) ) ;
else {
switch ( atoi ( input ) ) {
case 220 : if ( numChars > 4 ) {
char * n = input + 4 ;
if ( char * t = strchr ( n , ' ' ) ) {
* t = 0 ;
if ( strcmp ( n , serverName ) ! = 0 ) {
serverName = n ;
2018-02-20 13:28:04 +01:00
dsyslog ( " SVDRP %s < %s remote server name is '%s' " , Setup . SVDRPHostName , serverIpAddress . Connection ( ) , * serverName ) ;
2015-09-01 11:14:27 +02:00
}
2018-03-17 10:07:19 +01:00
SetFetchFlag ( sffConn | sffTimers ) ;
2018-02-20 13:28:04 +01:00
connected = true ;
2015-09-01 11:14:27 +02:00
}
}
break ;
2018-02-20 13:28:04 +01:00
case 221 : dsyslog ( " SVDRP %s < %s remote server closed connection to '%s' " , Setup . SVDRPHostName , serverIpAddress . Connection ( ) , * serverName ) ;
connected = false ;
2015-09-01 11:14:27 +02:00
Close ( ) ;
break ;
}
}
2018-02-05 15:18:19 +01:00
if ( numChars > = 4 & & input [ 3 ] ! = ' - ' ) // no more lines will follow
break ;
2015-09-01 11:14:27 +02:00
numChars = 0 ;
}
else {
2018-03-18 10:43:53 +01:00
if ( numChars > = length - 1 ) {
int NewLength = length + BUFSIZ ;
if ( char * NewBuffer = ( char * ) realloc ( input , NewLength ) ) {
length = NewLength ;
input = NewBuffer ;
}
else {
esyslog ( " SVDRP %s < %s ERROR: out of memory " , Setup . SVDRPHostName , serverIpAddress . Connection ( ) ) ;
Close ( ) ;
break ;
}
2015-09-01 11:14:27 +02:00
}
input [ numChars + + ] = c ;
input [ numChars ] = 0 ;
}
Timeout . Set ( SVDRPResonseTimeout ) ;
}
else if ( r < = 0 ) {
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s lost connection to remote server '%s' " , Setup . SVDRPHostName , serverIpAddress . Connection ( ) , * serverName ) ;
2015-09-01 11:14:27 +02:00
Close ( ) ;
2017-05-31 14:06:07 +02:00
return false ;
2015-09-01 11:14:27 +02:00
}
}
else if ( Timeout . TimedOut ( ) ) {
2018-02-20 13:28:04 +01:00
esyslog ( " SVDRP %s < %s timeout while waiting for response from '%s' " , Setup . SVDRPHostName , serverIpAddress . Connection ( ) , * serverName ) ;
2015-09-01 11:14:27 +02:00
return false ;
}
2018-02-05 15:18:19 +01:00
else if ( ! Response & & numChars = = 0 )
break ; // we read all or nothing!
2015-09-01 11:14:27 +02:00
}
if ( pingTime . TimedOut ( ) )
2018-03-17 10:07:19 +01:00
SetFetchFlag ( sffPing ) ;
2015-09-01 11:14:27 +02:00
}
return file . IsOpen ( ) ;
}
bool cSVDRPClient : : Execute ( const char * Command , cStringList * Response )
{
2018-03-19 12:32:16 +01:00
cStringList Dummy ;
2015-09-01 11:14:27 +02:00
if ( Response )
Response - > Clear ( ) ;
2018-03-19 12:32:16 +01:00
else
Response = & Dummy ;
2015-09-01 11:14:27 +02:00
return Send ( Command ) & & Process ( Response ) ;
}
2018-03-17 10:07:19 +01:00
void cSVDRPClient : : SetFetchFlag ( int Flags )
2015-09-01 11:14:27 +02:00
{
fetchFlags | = Flags ;
}
2018-03-17 10:07:19 +01:00
bool cSVDRPClient : : HasFetchFlag ( int Flag )
2015-09-01 11:14:27 +02:00
{
bool Result = ( fetchFlags & Flag ) ;
fetchFlags & = ~ Flag ;
return Result ;
}
2018-02-25 13:26:17 +01:00
bool cSVDRPClient : : GetRemoteTimers ( cStringList & Response )
{
2018-03-01 14:45:57 +01:00
if ( Execute ( " LSTT ID " , & Response ) ) {
for ( int i = 0 ; i < Response . Size ( ) ; i + + ) {
char * s = Response [ i ] ;
int Code = SVDRPCode ( s ) ;
if ( Code = = 250 )
strshift ( s , 4 ) ;
2019-05-06 15:14:04 +02:00
else if ( Code = = 550 )
Response . Clear ( ) ;
2018-03-01 14:45:57 +01:00
else {
2019-05-06 15:14:04 +02:00
esyslog ( " ERROR: %s: %s " , ServerName ( ) , s ) ;
2018-03-01 14:45:57 +01:00
return false ;
2018-02-28 10:06:47 +01:00
}
2018-03-01 14:45:57 +01:00
}
Response . SortNumerically ( ) ;
return true ;
2018-02-28 10:06:47 +01:00
}
2018-02-25 13:26:17 +01:00
return false ;
}
2018-02-20 13:28:04 +01:00
// --- cSVDRPServerParams ----------------------------------------------------
class cSVDRPServerParams {
private :
cString name ;
int port ;
cString vdrversion ;
cString apiversion ;
int timeout ;
cString host ;
cString error ;
public :
cSVDRPServerParams ( const char * Params ) ;
const char * Name ( void ) const { return name ; }
const int Port ( void ) const { return port ; }
const char * VdrVersion ( void ) const { return vdrversion ; }
const char * ApiVersion ( void ) const { return apiversion ; }
const int Timeout ( void ) const { return timeout ; }
const char * Host ( void ) const { return host ; }
bool Ok ( void ) const { return ! * error ; }
const char * Error ( void ) const { return error ; }
} ;
cSVDRPServerParams : : cSVDRPServerParams ( const char * Params )
{
if ( Params & & * Params ) {
name = strgetval ( Params , " name " , ' : ' ) ;
if ( * name ) {
cString p = strgetval ( Params , " port " , ' : ' ) ;
if ( * p ) {
port = atoi ( p ) ;
vdrversion = strgetval ( Params , " vdrversion " , ' : ' ) ;
if ( * vdrversion ) {
apiversion = strgetval ( Params , " apiversion " , ' : ' ) ;
if ( * apiversion ) {
cString t = strgetval ( Params , " timeout " , ' : ' ) ;
if ( * t ) {
timeout = atoi ( t ) ;
if ( timeout > 10 ) { // don't let it get too small
host = strgetval ( Params , " host " , ' : ' ) ;
// no error if missing - this parameter is optional!
}
else
error = " invalid timeout " ;
}
else
error = " missing server timeout " ;
}
else
error = " missing server apiversion " ;
}
else
error = " missing server vdrversion " ;
}
else
error = " missing server port " ;
}
else
error = " missing server name " ;
}
else
error = " missing server parameters " ;
}
2015-09-01 11:14:27 +02:00
// --- cSVDRPClientHandler ---------------------------------------------------
2018-03-04 14:15:07 +01:00
cStateKey StateKeySVDRPRemoteTimersPoll ( true ) ;
2015-09-01 11:14:27 +02:00
class cSVDRPClientHandler : public cThread {
private :
cMutex mutex ;
int tcpPort ;
2015-09-08 11:08:06 +02:00
cSocket udpSocket ;
2015-09-01 11:14:27 +02:00
cVector < cSVDRPClient * > clientConnections ;
2018-02-25 13:26:17 +01:00
void SendDiscover ( void ) ;
2015-09-01 11:14:27 +02:00
void HandleClientConnection ( void ) ;
void ProcessConnections ( void ) ;
2018-02-25 13:26:17 +01:00
cSVDRPClient * GetClientForServer ( const char * ServerName ) ;
2015-09-01 11:14:27 +02:00
protected :
virtual void Action ( void ) ;
public :
2015-09-08 11:08:06 +02:00
cSVDRPClientHandler ( int TcpPort , int UdpPort ) ;
2015-09-01 11:14:27 +02:00
virtual ~ cSVDRPClientHandler ( ) ;
2018-02-20 13:28:04 +01:00
void AddClient ( cSVDRPServerParams & ServerParams , const char * IpAddress ) ;
2015-09-10 10:39:45 +02:00
bool Execute ( const char * ServerName , const char * Command , cStringList * Response = NULL ) ;
2018-03-17 10:07:19 +01:00
bool GetServerNames ( cStringList * ServerNames ) ;
2015-09-01 11:14:27 +02:00
bool TriggerFetchingTimers ( const char * ServerName ) ;
} ;
static cSVDRPClientHandler * SVDRPClientHandler = NULL ;
2015-09-08 11:08:06 +02:00
cSVDRPClientHandler : : cSVDRPClientHandler ( int TcpPort , int UdpPort )
2015-09-01 11:14:27 +02:00
: cThread ( " SVDRP client handler " , true )
, udpSocket ( UdpPort , false )
{
tcpPort = TcpPort ;
}
cSVDRPClientHandler : : ~ cSVDRPClientHandler ( )
{
Cancel ( 3 ) ;
for ( int i = 0 ; i < clientConnections . Size ( ) ; i + + )
delete clientConnections [ i ] ;
}
cSVDRPClient * cSVDRPClientHandler : : GetClientForServer ( const char * ServerName )
{
for ( int i = 0 ; i < clientConnections . Size ( ) ; i + + ) {
if ( strcmp ( clientConnections [ i ] - > ServerName ( ) , ServerName ) = = 0 )
return clientConnections [ i ] ;
}
return NULL ;
}
2018-02-20 13:28:04 +01:00
void cSVDRPClientHandler : : SendDiscover ( void )
2015-09-01 11:14:27 +02:00
{
2017-06-30 09:50:44 +02:00
cString Dgram = cString : : sprintf ( " SVDRP:discover name:%s port:%d vdrversion:%d apiversion:%d timeout:%d%s " , Setup . SVDRPHostName , tcpPort , VDRVERSNUM , APIVERSNUM , Setup . SVDRPTimeout , ( Setup . SVDRPPeering = = spmOnly & & * Setup . SVDRPDefaultHost ) ? * cString : : sprintf ( " host:%s " , Setup . SVDRPDefaultHost ) : " " ) ;
2018-02-20 13:28:04 +01:00
udpSocket . SendDgram ( Dgram , udpSocket . Port ( ) ) ;
2015-09-01 11:14:27 +02:00
}
void cSVDRPClientHandler : : ProcessConnections ( void )
{
2018-02-25 13:26:17 +01:00
cString PollTimersCmd ;
2018-03-04 14:15:07 +01:00
if ( cTimers : : GetTimersRead ( StateKeySVDRPRemoteTimersPoll , 100 ) ) {
2018-02-25 13:26:17 +01:00
PollTimersCmd = cString : : sprintf ( " POLL %s TIMERS " , Setup . SVDRPHostName ) ;
2018-03-04 14:15:07 +01:00
StateKeySVDRPRemoteTimersPoll . Remove ( ) ;
2018-02-25 13:26:17 +01:00
}
2018-03-04 14:15:07 +01:00
else if ( StateKeySVDRPRemoteTimersPoll . TimedOut ( ) )
2018-03-01 14:45:57 +01:00
return ; // try again next time
2015-09-01 11:14:27 +02:00
for ( int i = 0 ; i < clientConnections . Size ( ) ; i + + ) {
2018-02-25 13:26:17 +01:00
cSVDRPClient * Client = clientConnections [ i ] ;
if ( Client - > Process ( ) ) {
2018-03-17 10:07:19 +01:00
if ( Client - > HasFetchFlag ( sffConn ) )
Client - > Execute ( cString : : sprintf ( " CONN name:%s port:%d vdrversion:%d apiversion:%d timeout:%d " , Setup . SVDRPHostName , SVDRPTcpPort , VDRVERSNUM , APIVERSNUM , Setup . SVDRPTimeout ) ) ;
if ( Client - > HasFetchFlag ( sffPing ) )
Client - > Execute ( " PING " ) ;
2018-03-01 14:45:57 +01:00
if ( Client - > HasFetchFlag ( sffTimers ) ) {
cStringList RemoteTimers ;
if ( Client - > GetRemoteTimers ( RemoteTimers ) ) {
2018-03-04 14:15:07 +01:00
if ( cTimers * Timers = cTimers : : GetTimersWrite ( StateKeySVDRPRemoteTimersPoll , 100 ) ) {
2018-03-01 14:45:57 +01:00
bool TimersModified = Timers - > StoreRemoteTimers ( Client - > ServerName ( ) , & RemoteTimers ) ;
2018-03-04 14:15:07 +01:00
StateKeySVDRPRemoteTimersPoll . Remove ( TimersModified ) ;
2018-03-01 14:45:57 +01:00
}
else
Client - > SetFetchFlag ( sffTimers ) ; // try again next time
}
2018-02-25 13:26:17 +01:00
}
if ( * PollTimersCmd ) {
if ( ! Client - > Execute ( PollTimersCmd ) )
esyslog ( " ERROR: can't send '%s' to '%s' " , * PollTimersCmd , Client - > ServerName ( ) ) ;
}
}
else {
2018-03-04 14:15:07 +01:00
cTimers * Timers = cTimers : : GetTimersWrite ( StateKeySVDRPRemoteTimersPoll ) ;
2018-02-25 13:26:17 +01:00
bool TimersModified = Timers - > StoreRemoteTimers ( Client - > ServerName ( ) , NULL ) ;
2018-03-04 14:15:07 +01:00
StateKeySVDRPRemoteTimersPoll . Remove ( TimersModified ) ;
2018-02-25 13:26:17 +01:00
delete Client ;
2015-09-01 11:14:27 +02:00
clientConnections . Remove ( i ) ;
i - - ;
}
}
}
2018-02-20 13:28:04 +01:00
void cSVDRPClientHandler : : AddClient ( cSVDRPServerParams & ServerParams , const char * IpAddress )
{
2018-02-25 13:26:17 +01:00
cMutexLock MutexLock ( & mutex ) ;
2018-02-20 13:28:04 +01:00
for ( int i = 0 ; i < clientConnections . Size ( ) ; i + + ) {
2018-02-25 13:26:17 +01:00
if ( clientConnections [ i ] - > HasAddress ( IpAddress , ServerParams . Port ( ) ) )
2018-02-20 13:28:04 +01:00
return ;
}
if ( Setup . SVDRPPeering = = spmOnly & & strcmp ( ServerParams . Name ( ) , Setup . SVDRPDefaultHost ) ! = 0 )
return ; // we only want to peer with the default host, but this isn't the default host
if ( ServerParams . Host ( ) & & strcmp ( ServerParams . Host ( ) , Setup . SVDRPHostName ) ! = 0 )
return ; // the remote VDR requests a specific host, but it's not us
clientConnections . Append ( new cSVDRPClient ( IpAddress , ServerParams . Port ( ) , ServerParams . Name ( ) , ServerParams . Timeout ( ) ) ) ;
}
2015-09-01 11:14:27 +02:00
void cSVDRPClientHandler : : HandleClientConnection ( void )
{
cString NewDiscover = udpSocket . Discover ( ) ;
if ( * NewDiscover ) {
2018-02-20 13:28:04 +01:00
cSVDRPServerParams ServerParams ( NewDiscover ) ;
if ( ServerParams . Ok ( ) )
AddClient ( ServerParams , udpSocket . LastIpAddress ( ) - > Address ( ) ) ;
2015-09-01 11:14:27 +02:00
else
2018-02-20 13:28:04 +01:00
esyslog ( " SVDRP %s < %s ERROR: %s " , Setup . SVDRPHostName , udpSocket . LastIpAddress ( ) - > Connection ( ) , ServerParams . Error ( ) ) ;
2015-09-01 11:14:27 +02:00
}
}
void cSVDRPClientHandler : : Action ( void )
{
if ( udpSocket . Listen ( ) ) {
SVDRPClientPoller . Add ( udpSocket . Socket ( ) , false ) ;
SendDiscover ( ) ;
while ( Running ( ) ) {
SVDRPClientPoller . Poll ( 1000 ) ;
cMutexLock MutexLock ( & mutex ) ;
HandleClientConnection ( ) ;
ProcessConnections ( ) ;
}
SVDRPClientPoller . Del ( udpSocket . Socket ( ) , false ) ;
udpSocket . Close ( ) ;
}
}
bool cSVDRPClientHandler : : Execute ( const char * ServerName , const char * Command , cStringList * Response )
{
cMutexLock MutexLock ( & mutex ) ;
if ( cSVDRPClient * Client = GetClientForServer ( ServerName ) )
return Client - > Execute ( Command , Response ) ;
return false ;
}
2018-03-17 10:07:19 +01:00
bool cSVDRPClientHandler : : GetServerNames ( cStringList * ServerNames )
2015-09-01 11:14:27 +02:00
{
cMutexLock MutexLock ( & mutex ) ;
ServerNames - > Clear ( ) ;
for ( int i = 0 ; i < clientConnections . Size ( ) ; i + + ) {
cSVDRPClient * Client = clientConnections [ i ] ;
2018-03-17 10:07:19 +01:00
if ( Client - > Connected ( ) )
ServerNames - > Append ( strdup ( Client - > ServerName ( ) ) ) ;
2015-09-01 11:14:27 +02:00
}
return ServerNames - > Size ( ) > 0 ;
}
bool cSVDRPClientHandler : : TriggerFetchingTimers ( const char * ServerName )
{
cMutexLock MutexLock ( & mutex ) ;
if ( cSVDRPClient * Client = GetClientForServer ( ServerName ) ) {
Client - > SetFetchFlag ( sffTimers ) ;
return true ;
}
return false ;
}
2002-02-24 11:13:21 +01:00
// --- cPUTEhandler ----------------------------------------------------------
2015-04-29 13:10:06 +02:00
class cPUTEhandler {
private :
FILE * f ;
int status ;
const char * message ;
public :
cPUTEhandler ( void ) ;
~ cPUTEhandler ( ) ;
bool Process ( const char * s ) ;
int Status ( void ) { return status ; }
const char * Message ( void ) { return message ; }
} ;
2002-02-24 11:13:21 +01:00
cPUTEhandler : : cPUTEhandler ( void )
{
if ( ( f = tmpfile ( ) ) ! = NULL ) {
status = 354 ;
message = " Enter EPG data, end with \" . \" on a line by itself " ;
}
else {
LOG_ERROR ;
status = 554 ;
message = " Error while opening temporary file " ;
}
}
cPUTEhandler : : ~ cPUTEhandler ( )
{
if ( f )
fclose ( f ) ;
}
bool cPUTEhandler : : Process ( const char * s )
{
if ( f ) {
if ( strcmp ( s , " . " ) ! = 0 ) {
fputs ( s , f ) ;
fputc ( ' \n ' , f ) ;
return true ;
}
else {
rewind ( f ) ;
if ( cSchedules : : Read ( f ) ) {
2003-12-22 13:29:24 +01:00
cSchedules : : Cleanup ( true ) ;
2002-02-24 11:13:21 +01:00
status = 250 ;
message = " EPG data processed " ;
}
else {
status = 451 ;
message = " Error while processing EPG data " ;
}
fclose ( f ) ;
f = NULL ;
}
}
return false ;
}
2015-05-22 13:44:43 +02:00
// --- cSVDRPServer ----------------------------------------------------------
2000-07-23 15:01:31 +02:00
# define MAXHELPTOPIC 10
2010-01-03 15:45:23 +01:00
# define EITDISABLETIME 10 // seconds until EIT processing is enabled again after a CLRE command
// adjust the help for CLRE accordingly if changing this!
2000-07-23 15:01:31 +02:00
const char * HelpPages [ ] = {
2002-12-22 14:07:51 +01:00
" CHAN [ + | - | <number> | <name> | <id> ] \n "
" Switch channel up, down or to the given channel number, name or id. \n "
2000-07-23 15:01:31 +02:00
" Without option (or after successfully switching to the channel) \n "
" it returns the current channel number and name. " ,
2008-01-13 15:06:25 +01:00
" CLRE [ <number> | <name> | <id> ] \n "
" Clear the EPG list of the given channel number, name or id. \n "
2010-01-03 15:45:23 +01:00
" Without option it clears the entire EPG list. \n "
" After a CLRE command, no further EPG processing is done for 10 \n "
" seconds, so that data sent with subsequent PUTE commands doesn't \n "
" interfere with data from the broadcasters. " ,
2018-02-20 13:28:04 +01:00
" CONN name:<name> port:<port> vdrversion:<vdrversion> apiversion:<apiversion> timeout:<timeout> \n "
" Used by peer-to-peer connections between VDRs to tell the other VDR \n "
" to establish a connection to this VDR. The name is the SVDRP host name \n "
" of this VDR, which may differ from its DNS name. " ,
2020-05-05 15:37:49 +02:00
" DELC <number> | <id> \n "
" Delete the channel with the given number or channel id. " ,
2017-04-03 14:11:41 +02:00
" DELR <id> \n "
" Delete the recording with the given id. Before a recording can be \n "
" deleted, an LSTR command should have been executed in order to retrieve \n "
" the recording ids. The ids are unique and don't change while this \n "
" instance of VDR is running. \n "
" CAUTION: THERE IS NO CONFIRMATION PROMPT WHEN DELETING A \n "
2001-10-27 11:47:46 +02:00
" RECORDING - BE SURE YOU KNOW WHAT YOU ARE DOING! " ,
2017-04-03 14:32:33 +02:00
" DELT <id> \n "
" Delete the timer with the given id. If this timer is currently recording, \n "
" the recording will be stopped without any warning. " ,
2017-04-03 14:11:41 +02:00
" EDIT <id> \n "
" Edit the recording with the given id. Before a recording can be \n "
" edited, an LSTR command should have been executed in order to retrieve \n "
" the recording ids. " ,
2005-12-29 12:18:27 +01:00
" GRAB <filename> [ <quality> [ <sizex> <sizey> ] ] \n "
2000-09-17 11:53:35 +02:00
" Grab the current frame and save it to the given file. Images can \n "
2005-12-29 12:18:27 +01:00
" be stored as JPEG or PNM, depending on the given file name extension. \n "
2005-12-30 10:27:23 +01:00
" The quality of the grabbed image can be in the range 0..100, where 100 \n "
" (the default) means \" best \" (only applies to JPEG). The size parameters \n "
" define the size of the resulting image (default is full screen). \n "
" If the file name is just an extension (.jpg, .jpeg or .pnm) the image \n "
" data will be sent to the SVDRP connection encoded in base64. The same \n "
" happens if '-' (a minus sign) is given as file name, in which case the \n "
" image format defaults to JPEG. " ,
2000-07-23 15:01:31 +02:00
" HELP [ <topic> ] \n "
" The HELP command gives help info. " ,
2011-08-27 10:43:18 +02:00
" HITK [ <key> ... ] \n "
2000-09-17 09:36:50 +02:00
" Hit the given remote control key. Without option a list of all \n "
2011-08-27 10:43:18 +02:00
" valid key names is given. If more than one key is given, they are \n "
" entered into the remote control queue in the given sequence. There \n "
" can be up to 31 keys. " ,
2017-04-22 15:02:56 +02:00
" LSTC [ :ids ] [ :groups | <number> | <name> | <id> ] \n "
2000-07-23 15:01:31 +02:00
" List channels. Without option, all channels are listed. Otherwise \n "
" only the given channel is listed. If a name is given, all channels \n "
2007-08-25 09:31:13 +02:00
" containing the given string as part of their name are listed. \n "
" If ':groups' is given, all channels are listed including group \n "
2017-04-22 15:02:56 +02:00
" separators. The channel number of a group separator is always 0. \n "
" With ':ids' the channel ids are listed following the channel numbers. \n "
" The special number 0 can be given to list the current channel. " ,
2017-11-11 12:04:17 +01:00
" LSTD \n "
" List all available devices. Each device is listed with its name and \n "
" whether it is currently the primary device ('P') or it implements a \n "
" decoder ('D') and can be used as output device. " ,
2004-02-22 15:36:36 +01:00
" LSTE [ <channel> ] [ now | next | at <time> ] \n "
" List EPG data. Without any parameters all data of all channels is \n "
2005-08-07 14:25:31 +02:00
" listed. If a channel is given (either by number or by channel ID), \n "
2004-02-22 15:36:36 +01:00
" only data for that channel is listed. 'now', 'next', or 'at <time>' \n "
" restricts the returned data to present events, following events, or \n "
2004-03-25 17:01:03 +01:00
" events at the given time (which must be in time_t form). " ,
2017-04-03 14:11:41 +02:00
" LSTR [ <id> [ path ] ] \n "
2001-10-27 11:47:46 +02:00
" List recordings. Without option, all recordings are listed. Otherwise \n "
2013-01-15 13:29:39 +01:00
" the information for the given recording is listed. If a recording \n "
2017-04-03 14:11:41 +02:00
" id and the keyword 'path' is given, the actual file name of that \n "
" recording's directory is listed. \n "
" Note that the ids of the recordings are not necessarily given in \n "
" numeric order. " ,
2017-04-03 14:32:33 +02:00
" LSTT [ <id> ] [ id ] \n "
2000-07-23 15:01:31 +02:00
" List timers. Without option, all timers are listed. Otherwise \n "
2017-04-03 14:32:33 +02:00
" only the timer with the given id is listed. If the keyword 'id' is \n "
" given, the channels will be listed with their unique channel ids \n "
" instead of their numbers. This command lists only the timers that are \n "
" defined locally on this VDR, not any remote timers from other VDRs. " ,
2005-11-27 15:57:03 +01:00
" MESG <message> \n "
" Displays the given message on the OSD. The message will be queued \n "
" and displayed whenever this is suitable. \n " ,
2000-07-23 15:01:31 +02:00
" MODC <number> <settings> \n "
" Modify a channel. Settings must be in the same format as returned \n "
" by the LSTC command. " ,
2017-04-03 14:32:33 +02:00
" MODT <id> on | off | <settings> \n "
2000-07-23 15:01:31 +02:00
" Modify a timer. Settings must be in the same format as returned \n "
" by the LSTT command. The special keywords 'on' and 'off' can be \n "
" used to easily activate or deactivate a timer. " ,
" MOVC <number> <to> \n "
" Move a channel to a new position. " ,
2017-04-03 14:11:41 +02:00
" MOVR <id> <new name> \n "
" Move the recording with the given id. Before a recording can be \n "
" moved, an LSTR command should have been executed in order to retrieve \n "
" the recording ids. The ids don't change during subsequent MOVR \n "
2015-01-12 11:19:11 +01:00
" commands. \n " ,
2000-07-23 15:01:31 +02:00
" NEWC <settings> \n "
" Create a new channel. Settings must be in the same format as returned \n "
" by the LSTC command. " ,
" NEWT <settings> \n "
" Create a new timer. Settings must be in the same format as returned \n "
2013-01-17 15:21:07 +01:00
" by the LSTT command. " ,
2001-09-01 09:53:01 +02:00
" NEXT [ abs | rel ] \n "
" Show the next timer event. If no option is given, the output will be \n "
" in human readable form. With option 'abs' the absolute time of the next \n "
" event will be given as the number of seconds since the epoch (time_t \n "
" format), while with option 'rel' the relative time will be given as the \n "
" number of seconds from now until the event. If the absolute time given \n "
" is smaller than the current time, or if the relative time is less than \n "
" zero, this means that the timer is currently recording and has started \n "
2017-04-03 14:32:33 +02:00
" at the given time. The first value in the resulting line is the id \n "
2001-09-01 09:53:01 +02:00
" of the timer. " ,
2015-05-22 13:44:43 +02:00
" PING \n "
" Used by peer-to-peer connections between VDRs to keep the connection \n "
" from timing out. May be used at any time and simply returns a line of \n "
" the form '<hostname> is alive'. " ,
2017-04-03 14:11:41 +02:00
" PLAY <id> [ begin | <position> ] \n "
" Play the recording with the given id. Before a recording can be \n "
" played, an LSTR command should have been executed in order to retrieve \n "
" the recording ids. \n "
2005-08-28 10:45:19 +02:00
" The keyword 'begin' plays the recording from its very beginning, while \n "
" a <position> (given as hh:mm:ss[.ff] or framenumber) starts at that \n "
" position. If neither 'begin' nor a <position> are given, replay is resumed \n "
" at the position where any previous replay was stopped, or from the beginning \n "
" by default. To control or stop the replay session, use the usual remote \n "
" control keypresses via the HITK command. " ,
2005-09-03 12:49:36 +02:00
" PLUG <name> [ help | main ] [ <command> [ <options> ]] \n "
2005-08-27 16:42:28 +02:00
" Send a command to a plugin. \n "
" The PLUG command without any parameters lists all plugins. \n "
" If only a name is given, all commands known to that plugin are listed. \n "
" If a command is given (optionally followed by parameters), that command \n "
2005-09-03 12:20:24 +02:00
" is sent to the plugin, and the result will be displayed. \n "
" The keyword 'help' lists all the SVDRP commands known to the named plugin. \n "
" If 'help' is followed by a command, the detailed help for that command is \n "
2005-09-03 12:49:36 +02:00
" given. The keyword 'main' initiates a call to the main menu function of the \n "
" given plugin. \n " ,
2018-03-17 13:00:19 +01:00
" POLL <name> timers \n "
2015-09-01 11:14:27 +02:00
" Used by peer-to-peer connections between VDRs to inform other machines \n "
" about changes to timers. The receiving VDR shall use LSTT to query the \n "
2018-03-17 13:00:19 +01:00
" remote machine with the given name about its timers and update its list \n "
" of timers accordingly. \n " ,
2017-11-11 12:04:17 +01:00
" PRIM [ <number> ] \n "
" Make the device with the given number the primary device. \n "
" Without option it returns the currently active primary device in the same \n "
" format as used by the LSTD command. " ,
2020-05-06 11:51:33 +02:00
" PUTE [ <file> ] \n "
2002-02-24 11:13:21 +01:00
" Put data into the EPG list. The data entered has to strictly follow the \n "
2002-05-10 15:08:51 +02:00
" format defined in vdr(5) for the 'epg.data' file. A '.' on a line \n "
2002-02-24 11:13:21 +01:00
" by itself terminates the input and starts processing of the data (all \n "
2009-04-13 13:35:29 +02:00
" entered data is buffered until the terminating '.' is seen). \n "
" If a file name is given, epg data will be read from this file (which \n "
" must be accessible under the given name from the machine VDR is running \n "
" on). In case of file input, no terminating '.' shall be given. \n " ,
2007-04-30 12:53:35 +02:00
" REMO [ on | off ] \n "
" Turns the remote control on or off. Without a parameter, the current \n "
" status of the remote control is reported. " ,
2004-01-17 15:41:52 +01:00
" SCAN \n "
" Forces an EPG scan. If this is a single DVB device system, the scan \n "
" will be done on the primary device unless it is currently recording. " ,
2003-04-27 14:23:30 +02:00
" STAT disk \n "
" Return information about disk usage (total, free, percent). " ,
2000-08-06 12:56:49 +02:00
" UPDT <settings> \n "
" Updates a timer. Settings must be in the same format as returned \n "
" by the LSTT command. If a timer with the same channel, day, start \n "
2015-09-01 11:14:27 +02:00
" and stop time does not yet exist, it will be created. " ,
2011-12-04 13:58:33 +01:00
" UPDR \n "
" Initiates a re-read of the recordings directory, which is the SVDRP \n "
" equivalent to 'touch .update'. " ,
2002-03-09 10:07:40 +01:00
" VOLU [ <number> | + | - | mute ] \n "
" Set the audio volume to the given number (which is limited to the range \n "
" 0...255). If the special options '+' or '-' are given, the volume will \n "
" be turned up or down, respectively. The option 'mute' will toggle the \n "
" audio muting. If no option is given, the current audio volume level will \n "
" be returned. " ,
2000-07-23 15:01:31 +02:00
" QUIT \n "
" Exit vdr (SVDRP). \n "
" You can also hit Ctrl-D to exit. " ,
NULL
} ;
/* SVDRP Reply Codes:
214 Help message
2005-05-16 14:45:11 +02:00
215 EPG or recording data record
2005-12-30 10:27:23 +01:00
216 Image grab data ( base 64 )
2000-07-23 15:01:31 +02:00
220 VDR service ready
221 VDR service closing transmission channel
250 Requested VDR action okay , completed
2002-02-24 11:13:21 +01:00
354 Start sending EPG data
2000-07-23 15:01:31 +02:00
451 Requested action aborted : local error in processing
500 Syntax error , command unrecognized
501 Syntax error in parameters or arguments
502 Command not implemented
504 Command parameter not implemented
550 Requested action not taken
554 Transaction failed
2005-08-27 16:42:28 +02:00
900 Default plugin reply code
901. .999 Plugin specific reply codes
2000-07-23 15:01:31 +02:00
*/
const char * GetHelpTopic ( const char * HelpPage )
{
static char topic [ MAXHELPTOPIC ] ;
const char * q = HelpPage ;
while ( * q ) {
if ( isspace ( * q ) ) {
uint n = q - HelpPage ;
if ( n > = sizeof ( topic ) )
n = sizeof ( topic ) - 1 ;
strncpy ( topic , HelpPage , n ) ;
topic [ n ] = 0 ;
return topic ;
}
q + + ;
}
return NULL ;
}
2005-08-27 16:42:28 +02:00
const char * GetHelpPage ( const char * Cmd , const char * * p )
2000-07-23 15:01:31 +02:00
{
2005-08-27 16:42:28 +02:00
if ( p ) {
while ( * p ) {
const char * t = GetHelpTopic ( * p ) ;
if ( strcasecmp ( Cmd , t ) = = 0 )
return * p ;
p + + ;
}
}
2000-07-23 15:01:31 +02:00
return NULL ;
}
2015-04-29 13:10:06 +02:00
static cString grabImageDir ;
2015-05-22 13:44:43 +02:00
class cSVDRPServer {
2015-04-29 13:10:06 +02:00
private :
int socket ;
2018-02-20 13:28:04 +01:00
cIpAddress clientIpAddress ;
cString clientName ;
2015-04-29 13:10:06 +02:00
cFile file ;
cPUTEhandler * PUTEhandler ;
int numChars ;
int length ;
char * cmdLine ;
time_t lastActivity ;
void Close ( bool SendReply = false , bool Timeout = false ) ;
2018-02-15 14:30:14 +01:00
bool Send ( const char * s ) ;
2015-04-29 13:10:06 +02:00
void Reply ( int Code , const char * fmt , . . . ) __attribute__ ( ( format ( printf , 3 , 4 ) ) ) ;
void PrintHelpTopics ( const char * * hp ) ;
void CmdCHAN ( const char * Option ) ;
void CmdCLRE ( const char * Option ) ;
2018-02-20 13:28:04 +01:00
void CmdCONN ( const char * Option ) ;
2015-04-29 13:10:06 +02:00
void CmdDELC ( const char * Option ) ;
void CmdDELR ( const char * Option ) ;
void CmdDELT ( const char * Option ) ;
void CmdEDIT ( const char * Option ) ;
void CmdGRAB ( const char * Option ) ;
void CmdHELP ( const char * Option ) ;
void CmdHITK ( const char * Option ) ;
void CmdLSTC ( const char * Option ) ;
2017-11-11 12:04:17 +01:00
void CmdLSTD ( const char * Option ) ;
2015-04-29 13:10:06 +02:00
void CmdLSTE ( const char * Option ) ;
void CmdLSTR ( const char * Option ) ;
void CmdLSTT ( const char * Option ) ;
void CmdMESG ( const char * Option ) ;
void CmdMODC ( const char * Option ) ;
void CmdMODT ( const char * Option ) ;
void CmdMOVC ( const char * Option ) ;
void CmdMOVR ( const char * Option ) ;
void CmdNEWC ( const char * Option ) ;
void CmdNEWT ( const char * Option ) ;
void CmdNEXT ( const char * Option ) ;
2015-05-22 13:44:43 +02:00
void CmdPING ( const char * Option ) ;
2015-04-29 13:10:06 +02:00
void CmdPLAY ( const char * Option ) ;
void CmdPLUG ( const char * Option ) ;
2015-09-01 11:14:27 +02:00
void CmdPOLL ( const char * Option ) ;
2017-11-11 12:04:17 +01:00
void CmdPRIM ( const char * Option ) ;
2015-04-29 13:10:06 +02:00
void CmdPUTE ( const char * Option ) ;
void CmdREMO ( const char * Option ) ;
void CmdSCAN ( const char * Option ) ;
void CmdSTAT ( const char * Option ) ;
void CmdUPDT ( const char * Option ) ;
void CmdUPDR ( const char * Option ) ;
void CmdVOLU ( const char * Option ) ;
void Execute ( char * Cmd ) ;
public :
2018-02-20 13:28:04 +01:00
cSVDRPServer ( int Socket , const cIpAddress * ClientIpAddress ) ;
2015-05-22 13:44:43 +02:00
~ cSVDRPServer ( ) ;
2018-02-20 13:28:04 +01:00
const char * ClientName ( void ) const { return clientName ; }
2015-04-29 13:10:06 +02:00
bool HasConnection ( void ) { return file . IsOpen ( ) ; }
bool Process ( void ) ;
} ;
2005-12-30 15:11:16 +01:00
2015-05-22 13:44:43 +02:00
static cPoller SVDRPServerPoller ;
2018-02-20 13:28:04 +01:00
cSVDRPServer : : cSVDRPServer ( int Socket , const cIpAddress * ClientIpAddress )
2000-07-23 15:01:31 +02:00
{
2015-04-29 13:10:06 +02:00
socket = Socket ;
2018-02-20 13:28:04 +01:00
clientIpAddress = * ClientIpAddress ;
clientName = clientIpAddress . Connection ( ) ; // will be set to actual name by a CONN command
2002-02-24 11:13:21 +01:00
PUTEhandler = NULL ;
2001-04-01 14:13:42 +02:00
numChars = 0 ;
2005-11-05 11:21:38 +01:00
length = BUFSIZ ;
cmdLine = MALLOC ( char , length ) ;
2015-04-29 13:10:06 +02:00
lastActivity = time ( NULL ) ;
if ( file . Open ( socket ) ) {
time_t now = time ( NULL ) ;
2015-09-08 11:08:06 +02:00
Reply ( 220 , " %s SVDRP VideoDiskRecorder %s; %s; %s " , Setup . SVDRPHostName , VDRVERSION , * TimeToString ( now ) , cCharSetConv : : SystemCharacterTable ( ) ? cCharSetConv : : SystemCharacterTable ( ) : " UTF-8 " ) ;
2015-05-22 13:44:43 +02:00
SVDRPServerPoller . Add ( file , false ) ;
2015-04-29 13:10:06 +02:00
}
2018-02-20 13:28:04 +01:00
dsyslog ( " SVDRP %s > %s server created " , Setup . SVDRPHostName , * clientName ) ;
2000-07-23 15:01:31 +02:00
}
2015-05-22 13:44:43 +02:00
cSVDRPServer : : ~ cSVDRPServer ( )
2000-07-23 15:01:31 +02:00
{
2006-08-06 09:20:04 +02:00
Close ( true ) ;
2005-11-05 11:21:38 +01:00
free ( cmdLine ) ;
2018-02-20 13:28:04 +01:00
dsyslog ( " SVDRP %s < %s server destroyed " , Setup . SVDRPHostName , * clientName ) ;
2000-07-23 15:01:31 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : Close ( bool SendReply , bool Timeout )
2000-07-23 15:01:31 +02:00
{
2000-09-17 08:23:46 +02:00
if ( file . IsOpen ( ) ) {
2006-08-06 09:20:04 +02:00
if ( SendReply ) {
2015-09-08 11:08:06 +02:00
Reply ( 221 , " %s closing connection%s " , Setup . SVDRPHostName , Timeout ? " (timeout) " : " " ) ;
2006-08-06 09:20:04 +02:00
}
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s connection closed " , Setup . SVDRPHostName , * clientName ) ;
2015-05-22 13:44:43 +02:00
SVDRPServerPoller . Del ( file , false ) ;
2000-09-17 08:23:46 +02:00
file . Close ( ) ;
2002-02-24 11:13:21 +01:00
DELETENULL ( PUTEhandler ) ;
2000-07-23 15:01:31 +02:00
}
2015-04-29 13:10:06 +02:00
close ( socket ) ;
2000-07-23 15:01:31 +02:00
}
2018-02-15 14:30:14 +01:00
bool cSVDRPServer : : Send ( const char * s )
2000-07-23 15:01:31 +02:00
{
2018-02-20 13:28:04 +01:00
dbgsvdrp ( " > S %s: %s " , * clientName , s ) ; // terminating newline is already in the string!
2018-02-15 14:30:14 +01:00
if ( safe_write ( file , s , strlen ( s ) ) < 0 ) {
2000-07-23 15:01:31 +02:00
LOG_ERROR ;
2006-08-06 09:20:04 +02:00
Close ( ) ;
2002-03-23 16:17:39 +01:00
return false ;
2000-09-17 13:47:06 +02:00
}
2002-03-23 16:17:39 +01:00
return true ;
2000-07-23 15:01:31 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : Reply ( int Code , const char * fmt , . . . )
2000-07-23 15:01:31 +02:00
{
2000-09-17 08:23:46 +02:00
if ( file . IsOpen ( ) ) {
2000-07-23 15:01:31 +02:00
if ( Code ! = 0 ) {
2018-02-15 14:30:14 +01:00
char * buffer = NULL ;
2000-07-23 15:01:31 +02:00
va_list ap ;
va_start ( ap , fmt ) ;
2018-02-15 14:30:14 +01:00
if ( vasprintf ( & buffer , fmt , ap ) > = 0 ) {
char * s = buffer ;
while ( s & & * s ) {
char * n = strchr ( s , ' \n ' ) ;
if ( n )
* n = 0 ;
char cont = ' ' ;
if ( Code < 0 | | n & & * ( n + 1 ) ) // trailing newlines don't count!
cont = ' - ' ;
if ( ! Send ( cString : : sprintf ( " %03d%c%s \r \n " , abs ( Code ) , cont , s ) ) )
break ;
s = n ? n + 1 : NULL ;
}
}
else {
Reply ( 451 , " Bad format - looks like a programming error! " ) ;
2018-02-20 13:28:04 +01:00
esyslog ( " SVDRP %s < %s bad format! " , Setup . SVDRPHostName , * clientName ) ;
2018-02-15 14:30:14 +01:00
}
2008-02-15 14:57:48 +01:00
va_end ( ap ) ;
2018-02-15 14:30:14 +01:00
free ( buffer ) ;
2000-07-23 15:01:31 +02:00
}
else {
Reply ( 451 , " Zero return code - looks like a programming error! " ) ;
2018-02-20 13:28:04 +01:00
esyslog ( " SVDRP %s < %s zero return code! " , Setup . SVDRPHostName , * clientName ) ;
2000-07-23 15:01:31 +02:00
}
}
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : PrintHelpTopics ( const char * * hp )
2005-08-27 16:42:28 +02:00
{
int NumPages = 0 ;
if ( hp ) {
while ( * hp ) {
NumPages + + ;
hp + + ;
}
hp - = NumPages ;
}
const int TopicsPerLine = 5 ;
int x = 0 ;
for ( int y = 0 ; ( y * TopicsPerLine + x ) < NumPages ; y + + ) {
char buffer [ TopicsPerLine * MAXHELPTOPIC + 5 ] ;
char * q = buffer ;
q + = sprintf ( q , " " ) ;
for ( x = 0 ; x < TopicsPerLine & & ( y * TopicsPerLine + x ) < NumPages ; x + + ) {
const char * topic = GetHelpTopic ( hp [ ( y * TopicsPerLine + x ) ] ) ;
if ( topic )
q + = sprintf ( q , " %*s " , - MAXHELPTOPIC , topic ) ;
}
x = 0 ;
2006-03-26 09:27:30 +02:00
Reply ( - 214 , " %s " , buffer ) ;
2005-08-27 16:42:28 +02:00
}
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdCHAN ( const char * Option )
2000-07-23 15:01:31 +02:00
{
2015-09-01 11:14:27 +02:00
LOCK_CHANNELS_READ ;
2000-07-23 15:01:31 +02:00
if ( * Option ) {
int n = - 1 ;
2002-09-08 11:46:53 +02:00
int d = 0 ;
2000-07-23 15:01:31 +02:00
if ( isnumber ( Option ) ) {
2000-09-09 14:57:43 +02:00
int o = strtol ( Option , NULL , 10 ) ;
2015-09-01 11:14:27 +02:00
if ( o > = 1 & & o < = cChannels : : MaxNumber ( ) )
2000-07-23 15:01:31 +02:00
n = o ;
}
else if ( strcmp ( Option , " - " ) = = 0 ) {
2002-06-16 12:57:31 +02:00
n = cDevice : : CurrentChannel ( ) ;
2002-09-08 11:46:53 +02:00
if ( n > 1 ) {
2000-07-23 15:01:31 +02:00
n - - ;
2002-09-08 11:46:53 +02:00
d = - 1 ;
}
2000-07-23 15:01:31 +02:00
}
else if ( strcmp ( Option , " + " ) = = 0 ) {
2002-06-16 12:57:31 +02:00
n = cDevice : : CurrentChannel ( ) ;
2015-09-01 11:14:27 +02:00
if ( n < cChannels : : MaxNumber ( ) ) {
2000-07-23 15:01:31 +02:00
n + + ;
2002-09-08 11:46:53 +02:00
d = 1 ;
}
2000-07-23 15:01:31 +02:00
}
2015-09-01 11:14:27 +02:00
else if ( const cChannel * Channel = Channels - > GetByChannelID ( tChannelID : : FromString ( Option ) ) )
n = Channel - > Number ( ) ;
2000-07-23 15:01:31 +02:00
else {
2015-09-01 11:14:27 +02:00
for ( const cChannel * Channel = Channels - > First ( ) ; Channel ; Channel = Channels - > Next ( Channel ) ) {
if ( ! Channel - > GroupSep ( ) ) {
if ( strcasecmp ( Channel - > Name ( ) , Option ) = = 0 ) {
n = Channel - > Number ( ) ;
break ;
2007-06-23 13:24:00 +02:00
}
}
2015-09-01 11:14:27 +02:00
}
2000-07-23 15:01:31 +02:00
}
if ( n < 0 ) {
Reply ( 501 , " Undefined channel \" %s \" " , Option ) ;
return ;
}
2002-09-08 11:46:53 +02:00
if ( ! d ) {
2015-09-01 11:14:27 +02:00
if ( const cChannel * Channel = Channels - > GetByNumber ( n ) ) {
if ( ! cDevice : : PrimaryDevice ( ) - > SwitchChannel ( Channel , true ) ) {
Reply ( 554 , " Error switching to channel \" %d \" " , Channel - > Number ( ) ) ;
2002-09-08 11:46:53 +02:00
return ;
}
}
else {
Reply ( 550 , " Unable to find channel \" %s \" " , Option ) ;
2000-07-23 15:01:31 +02:00
return ;
}
}
2002-09-08 11:46:53 +02:00
else
cDevice : : SwitchChannel ( d ) ;
2000-07-23 15:01:31 +02:00
}
2015-09-01 11:14:27 +02:00
if ( const cChannel * Channel = Channels - > GetByNumber ( cDevice : : CurrentChannel ( ) ) )
Reply ( 250 , " %d %s " , Channel - > Number ( ) , Channel - > Name ( ) ) ;
2000-07-23 15:01:31 +02:00
else
2002-06-16 12:57:31 +02:00
Reply ( 550 , " Unable to find channel \" %d \" " , cDevice : : CurrentChannel ( ) ) ;
2000-07-23 15:01:31 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdCLRE ( const char * Option )
2002-08-25 10:49:02 +02:00
{
2008-01-13 15:06:25 +01:00
if ( * Option ) {
2015-09-01 11:14:27 +02:00
LOCK_TIMERS_WRITE ;
LOCK_CHANNELS_READ ;
2008-01-13 15:06:25 +01:00
tChannelID ChannelID = tChannelID : : InvalidID ;
if ( isnumber ( Option ) ) {
int o = strtol ( Option , NULL , 10 ) ;
2021-01-01 21:23:00 +01:00
if ( o > = 1 & & o < = cChannels : : MaxNumber ( ) ) {
if ( const cChannel * Channel = Channels - > GetByNumber ( o ) )
ChannelID = Channel - > GetChannelID ( ) ;
}
2008-01-13 15:06:25 +01:00
}
else {
ChannelID = tChannelID : : FromString ( Option ) ;
if ( ChannelID = = tChannelID : : InvalidID ) {
2015-09-01 11:14:27 +02:00
for ( const cChannel * Channel = Channels - > First ( ) ; Channel ; Channel = Channels - > Next ( Channel ) ) {
2008-01-13 15:06:25 +01:00
if ( ! Channel - > GroupSep ( ) ) {
if ( strcasecmp ( Channel - > Name ( ) , Option ) = = 0 ) {
ChannelID = Channel - > GetChannelID ( ) ;
break ;
}
}
}
}
}
if ( ! ( ChannelID = = tChannelID : : InvalidID ) ) {
2015-09-01 11:14:27 +02:00
LOCK_SCHEDULES_WRITE ;
cSchedule * Schedule = NULL ;
ChannelID . ClrRid ( ) ;
for ( cSchedule * p = Schedules - > First ( ) ; p ; p = Schedules - > Next ( p ) ) {
if ( p - > ChannelID ( ) = = ChannelID ) {
Schedule = p ;
break ;
2008-01-13 15:06:25 +01:00
}
2015-09-01 11:14:27 +02:00
}
if ( Schedule ) {
for ( cTimer * Timer = Timers - > First ( ) ; Timer ; Timer = Timers - > Next ( Timer ) ) {
if ( ChannelID = = Timer - > Channel ( ) - > GetChannelID ( ) . ClrRid ( ) )
Timer - > SetEvent ( NULL ) ;
}
Schedule - > Cleanup ( INT_MAX ) ;
cEitFilter : : SetDisableUntil ( time ( NULL ) + EITDISABLETIME ) ;
Reply ( 250 , " EPG data of channel \" %s \" cleared " , Option ) ;
}
else {
Reply ( 550 , " No EPG data found for channel \" %s \" " , Option ) ;
return ;
2008-01-13 15:06:25 +01:00
}
}
else
Reply ( 501 , " Undefined channel \" %s \" " , Option ) ;
}
else {
2015-09-01 11:14:27 +02:00
LOCK_TIMERS_WRITE ;
LOCK_SCHEDULES_WRITE ;
for ( cTimer * Timer = Timers - > First ( ) ; Timer ; Timer = Timers - > Next ( Timer ) )
Timer - > SetEvent ( NULL ) ; // processing all timers here (local *and* remote)
for ( cSchedule * Schedule = Schedules - > First ( ) ; Schedule ; Schedule = Schedules - > Next ( Schedule ) )
Schedule - > Cleanup ( INT_MAX ) ;
2010-01-03 15:45:23 +01:00
cEitFilter : : SetDisableUntil ( time ( NULL ) + EITDISABLETIME ) ;
2015-09-01 11:14:27 +02:00
Reply ( 250 , " EPG data cleared " ) ;
2008-01-13 15:06:25 +01:00
}
2002-08-25 10:49:02 +02:00
}
2018-02-20 13:28:04 +01:00
void cSVDRPServer : : CmdCONN ( const char * Option )
{
if ( * Option ) {
if ( SVDRPClientHandler ) {
cSVDRPServerParams ServerParams ( Option ) ;
if ( ServerParams . Ok ( ) ) {
clientName = ServerParams . Name ( ) ;
Reply ( 250 , " OK " ) ; // must finish this transaction before creating the new client
2018-02-25 13:26:17 +01:00
SVDRPClientHandler - > AddClient ( ServerParams , clientIpAddress . Address ( ) ) ;
2018-02-20 13:28:04 +01:00
}
else
Reply ( 501 , " Error in server parameters: %s " , ServerParams . Error ( ) ) ;
}
else
Reply ( 451 , " No SVDRP client handler " ) ;
}
else
Reply ( 501 , " Missing server parameters " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdDELC ( const char * Option )
2000-07-23 15:01:31 +02:00
{
2003-08-31 11:26:46 +02:00
if ( * Option ) {
2020-05-05 15:37:49 +02:00
LOCK_TIMERS_READ ;
LOCK_CHANNELS_WRITE ;
Channels - > SetExplicitModify ( ) ;
cChannel * Channel = NULL ;
if ( isnumber ( Option ) )
Channel = Channels - > GetByNumber ( strtol ( Option , NULL , 10 ) ) ;
else
Channel = Channels - > GetByChannelID ( tChannelID : : FromString ( Option ) ) ;
if ( Channel ) {
if ( const cTimer * Timer = Timers - > UsesChannel ( Channel ) ) {
Reply ( 550 , " Channel \" %s \" is in use by timer %s " , Option , * Timer - > ToDescr ( ) ) ;
return ;
}
int CurrentChannelNr = cDevice : : CurrentChannel ( ) ;
cChannel * CurrentChannel = Channels - > GetByNumber ( CurrentChannelNr ) ;
if ( CurrentChannel & & Channel = = CurrentChannel ) {
int n = Channels - > GetNextNormal ( CurrentChannel - > Index ( ) ) ;
if ( n < 0 )
n = Channels - > GetPrevNormal ( CurrentChannel - > Index ( ) ) ;
if ( n < 0 ) {
Reply ( 501 , " Can't delete channel \" %s \" - list would be empty " , Option ) ;
2015-09-01 11:14:27 +02:00
return ;
2005-07-31 14:34:38 +02:00
}
2020-05-05 15:37:49 +02:00
CurrentChannel = Channels - > Get ( n ) ;
CurrentChannelNr = 0 ; // triggers channel switch below
2003-08-31 11:26:46 +02:00
}
2020-05-05 15:37:49 +02:00
Channels - > Del ( Channel ) ;
Channels - > ReNumber ( ) ;
Channels - > SetModifiedByUser ( ) ;
Channels - > SetModified ( ) ;
isyslog ( " SVDRP %s < %s deleted channel %s " , Setup . SVDRPHostName , * clientName , Option ) ;
if ( CurrentChannel & & CurrentChannel - > Number ( ) ! = CurrentChannelNr ) {
if ( ! cDevice : : PrimaryDevice ( ) - > Replaying ( ) | | cDevice : : PrimaryDevice ( ) - > Transferring ( ) )
Channels - > SwitchTo ( CurrentChannel - > Number ( ) ) ;
else
cDevice : : SetCurrentChannel ( CurrentChannel - > Number ( ) ) ;
}
Reply ( 250 , " Channel \" %s \" deleted " , Option ) ;
2003-08-31 11:26:46 +02:00
}
else
2020-05-05 15:37:49 +02:00
Reply ( 501 , " Channel \" %s \" not defined " , Option ) ;
2003-08-31 11:26:46 +02:00
}
else
2020-05-05 15:37:49 +02:00
Reply ( 501 , " Missing channel number or id " ) ;
2000-07-23 15:01:31 +02:00
}
2013-10-10 13:13:30 +02:00
static cString RecordingInUseMessage ( int Reason , const char * RecordingId , cRecording * Recording )
{
cRecordControl * rc ;
if ( ( Reason & ruTimer ) ! = 0 & & ( rc = cRecordControls : : GetRecordControl ( Recording - > FileName ( ) ) ) ! = NULL )
2015-09-06 09:14:53 +02:00
return cString : : sprintf ( " Recording \" %s \" is in use by timer %d " , RecordingId , rc - > Timer ( ) - > Id ( ) ) ;
2013-10-10 13:13:30 +02:00
else if ( ( Reason & ruReplay ) ! = 0 )
return cString : : sprintf ( " Recording \" %s \" is being replayed " , RecordingId ) ;
else if ( ( Reason & ruCut ) ! = 0 )
return cString : : sprintf ( " Recording \" %s \" is being edited " , RecordingId ) ;
else if ( ( Reason & ( ruMove | ruCopy ) ) ! = 0 )
return cString : : sprintf ( " Recording \" %s \" is being copied/moved " , RecordingId ) ;
else if ( Reason )
return cString : : sprintf ( " Recording \" %s \" is in use " , RecordingId ) ;
return NULL ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdDELR ( const char * Option )
2001-10-27 11:47:46 +02:00
{
if ( * Option ) {
if ( isnumber ( Option ) ) {
2015-09-01 11:14:27 +02:00
LOCK_RECORDINGS_WRITE ;
Recordings - > SetExplicitModify ( ) ;
2017-04-03 14:11:41 +02:00
if ( cRecording * Recording = Recordings - > GetById ( strtol ( Option , NULL , 10 ) ) ) {
2015-09-01 11:14:27 +02:00
if ( int RecordingInUse = Recording - > IsInUse ( ) )
Reply ( 550 , " %s " , * RecordingInUseMessage ( RecordingInUse , Option , Recording ) ) ;
2013-10-10 13:13:30 +02:00
else {
2015-09-01 11:14:27 +02:00
if ( Recording - > Delete ( ) ) {
Recordings - > DelByName ( Recording - > FileName ( ) ) ;
Recordings - > SetModified ( ) ;
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s deleted recording %s " , Setup . SVDRPHostName , * clientName , Option ) ;
2013-10-10 13:13:30 +02:00
Reply ( 250 , " Recording \" %s \" deleted " , Option ) ;
2005-05-26 10:00:59 +02:00
}
else
2013-10-10 13:13:30 +02:00
Reply ( 554 , " Error while deleting recording! " ) ;
2004-06-13 20:26:51 +02:00
}
2001-10-27 11:47:46 +02:00
}
else
2015-09-01 11:14:27 +02:00
Reply ( 550 , " Recording \" %s \" not found " , Option ) ;
2001-10-27 11:47:46 +02:00
}
else
2017-04-04 11:09:14 +02:00
Reply ( 501 , " Error in recording id \" %s \" " , Option ) ;
2001-10-27 11:47:46 +02:00
}
else
2017-04-04 11:09:14 +02:00
Reply ( 501 , " Missing recording id " ) ;
2001-10-27 11:47:46 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdDELT ( const char * Option )
2000-07-23 15:01:31 +02:00
{
if ( * Option ) {
if ( isnumber ( Option ) ) {
2015-09-01 11:14:27 +02:00
LOCK_TIMERS_WRITE ;
Timers - > SetExplicitModify ( ) ;
2015-09-10 10:39:45 +02:00
if ( cTimer * Timer = Timers - > GetById ( strtol ( Option , NULL , 10 ) ) ) {
2018-02-13 09:33:41 +01:00
if ( Timer - > Recording ( ) ) {
2015-09-10 10:39:45 +02:00
Timer - > Skip ( ) ;
2018-02-13 09:33:41 +01:00
cRecordControls : : Process ( Timers , time ( NULL ) ) ;
}
2021-01-14 10:29:05 +01:00
Timer - > TriggerRespawn ( ) ;
2015-09-10 10:39:45 +02:00
Timers - > Del ( Timer ) ;
Timers - > SetModified ( ) ;
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s deleted timer %s " , Setup . SVDRPHostName , * clientName , * Timer - > ToDescr ( ) ) ;
2015-09-10 10:39:45 +02:00
Reply ( 250 , " Timer \" %s \" deleted " , Option ) ;
2000-07-23 15:01:31 +02:00
}
else
2015-09-01 11:14:27 +02:00
Reply ( 501 , " Timer \" %s \" not defined " , Option ) ;
2000-07-23 15:01:31 +02:00
}
else
Reply ( 501 , " Error in timer number \" %s \" " , Option ) ;
}
else
Reply ( 501 , " Missing timer number " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdEDIT ( const char * Option )
2005-08-28 14:18:24 +02:00
{
if ( * Option ) {
if ( isnumber ( Option ) ) {
2015-09-01 11:14:27 +02:00
LOCK_RECORDINGS_READ ;
2017-04-04 09:39:36 +02:00
if ( const cRecording * Recording = Recordings - > GetById ( strtol ( Option , NULL , 10 ) ) ) {
2005-08-28 14:18:24 +02:00
cMarks Marks ;
2015-09-01 11:14:27 +02:00
if ( Marks . Load ( Recording - > FileName ( ) , Recording - > FramesPerSecond ( ) , Recording - > IsPesRecording ( ) ) & & Marks . Count ( ) ) {
if ( RecordingsHandler . Add ( ruCut , Recording - > FileName ( ) ) )
Reply ( 250 , " Editing recording \" %s \" [%s] " , Option , Recording - > Title ( ) ) ;
2005-08-28 14:18:24 +02:00
else
2013-10-10 13:13:30 +02:00
Reply ( 554 , " Can't start editing process " ) ;
2005-08-28 14:18:24 +02:00
}
else
Reply ( 554 , " No editing marks defined " ) ;
}
else
2015-09-01 11:14:27 +02:00
Reply ( 550 , " Recording \" %s \" not found " , Option ) ;
2005-08-28 14:18:24 +02:00
}
else
2017-04-04 11:09:14 +02:00
Reply ( 501 , " Error in recording id \" %s \" " , Option ) ;
2005-08-28 14:18:24 +02:00
}
else
2017-04-04 11:09:14 +02:00
Reply ( 501 , " Missing recording id " ) ;
2005-08-28 14:18:24 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdGRAB ( const char * Option )
2000-09-17 11:53:35 +02:00
{
2008-02-15 14:57:48 +01:00
const char * FileName = NULL ;
2000-09-17 11:53:35 +02:00
bool Jpeg = true ;
int Quality = - 1 , SizeX = - 1 , SizeY = - 1 ;
if ( * Option ) {
char buf [ strlen ( Option ) + 1 ] ;
char * p = strcpy ( buf , Option ) ;
const char * delim = " \t " ;
2004-12-19 16:33:34 +01:00
char * strtok_next ;
FileName = strtok_r ( p , delim , & strtok_next ) ;
2005-12-30 15:11:16 +01:00
// image type:
2009-06-06 13:48:41 +02:00
const char * Extension = strrchr ( FileName , ' . ' ) ;
2005-12-29 12:18:27 +01:00
if ( Extension ) {
if ( strcasecmp ( Extension , " .jpg " ) = = 0 | | strcasecmp ( Extension , " .jpeg " ) = = 0 )
2000-09-17 11:53:35 +02:00
Jpeg = true ;
2005-12-29 12:18:27 +01:00
else if ( strcasecmp ( Extension , " .pnm " ) = = 0 )
2000-09-17 11:53:35 +02:00
Jpeg = false ;
else {
2005-12-29 12:18:27 +01:00
Reply ( 501 , " Unknown image type \" %s \" " , Extension + 1 ) ;
2000-09-17 11:53:35 +02:00
return ;
}
2005-12-30 10:27:23 +01:00
if ( Extension = = FileName )
FileName = NULL ;
2000-09-17 11:53:35 +02:00
}
2005-12-30 10:27:23 +01:00
else if ( strcmp ( FileName , " - " ) = = 0 )
FileName = NULL ;
2005-12-30 15:11:16 +01:00
// image quality (and obsolete type):
2004-12-19 16:33:34 +01:00
if ( ( p = strtok_r ( NULL , delim , & strtok_next ) ) ! = NULL ) {
2005-12-29 12:18:27 +01:00
if ( strcasecmp ( p , " JPEG " ) = = 0 | | strcasecmp ( p , " PNM " ) = = 0 ) {
// tolerate for backward compatibility
p = strtok_r ( NULL , delim , & strtok_next ) ;
}
if ( p ) {
if ( isnumber ( p ) )
Quality = atoi ( p ) ;
else {
2005-12-30 15:43:21 +01:00
Reply ( 501 , " Invalid quality \" %s \" " , p ) ;
2005-12-29 12:18:27 +01:00
return ;
}
2000-09-17 11:53:35 +02:00
}
}
2005-12-30 15:11:16 +01:00
// image size:
2004-12-19 16:33:34 +01:00
if ( ( p = strtok_r ( NULL , delim , & strtok_next ) ) ! = NULL ) {
2000-09-17 11:53:35 +02:00
if ( isnumber ( p ) )
SizeX = atoi ( p ) ;
else {
2005-12-30 15:43:21 +01:00
Reply ( 501 , " Invalid sizex \" %s \" " , p ) ;
2000-09-17 11:53:35 +02:00
return ;
}
2004-12-19 16:33:34 +01:00
if ( ( p = strtok_r ( NULL , delim , & strtok_next ) ) ! = NULL ) {
2000-09-17 11:53:35 +02:00
if ( isnumber ( p ) )
SizeY = atoi ( p ) ;
else {
2005-12-30 15:43:21 +01:00
Reply ( 501 , " Invalid sizey \" %s \" " , p ) ;
2000-09-17 11:53:35 +02:00
return ;
}
}
else {
Reply ( 501 , " Missing sizey " ) ;
return ;
}
}
2004-12-19 16:33:34 +01:00
if ( ( p = strtok_r ( NULL , delim , & strtok_next ) ) ! = NULL ) {
2000-09-17 11:53:35 +02:00
Reply ( 501 , " Unexpected parameter \" %s \" " , p ) ;
return ;
}
2005-12-30 15:11:16 +01:00
// canonicalize the file name:
char RealFileName [ PATH_MAX ] ;
if ( FileName ) {
2015-04-29 13:10:06 +02:00
if ( * grabImageDir ) {
2009-06-06 14:07:02 +02:00
cString s ( FileName ) ;
FileName = s ;
const char * slash = strrchr ( FileName , ' / ' ) ;
2006-01-14 11:45:43 +01:00
if ( ! slash ) {
2008-02-15 14:57:48 +01:00
s = AddDirectory ( grabImageDir , FileName ) ;
2006-01-14 11:45:43 +01:00
FileName = s ;
}
slash = strrchr ( FileName , ' / ' ) ; // there definitely is one
2009-06-06 14:07:02 +02:00
cString t ( s ) ;
t . Truncate ( slash - FileName ) ;
char * r = realpath ( t , RealFileName ) ;
2005-12-30 15:11:16 +01:00
if ( ! r ) {
LOG_ERROR_STR ( FileName ) ;
2005-12-30 15:43:21 +01:00
Reply ( 501 , " Invalid file name \" %s \" " , FileName ) ;
2005-12-30 15:11:16 +01:00
return ;
}
strcat ( RealFileName , slash ) ;
FileName = RealFileName ;
if ( strncmp ( FileName , grabImageDir , strlen ( grabImageDir ) ) ! = 0 ) {
2005-12-30 15:43:21 +01:00
Reply ( 501 , " Invalid file name \" %s \" " , FileName ) ;
2005-12-30 15:11:16 +01:00
return ;
}
}
else {
Reply ( 550 , " Grabbing to file not allowed (use \" GRAB - \" instead) " ) ;
return ;
}
}
// actual grabbing:
2005-12-30 10:27:23 +01:00
int ImageSize ;
uchar * Image = cDevice : : PrimaryDevice ( ) - > GrabImage ( ImageSize , Jpeg , Quality , SizeX , SizeY ) ;
if ( Image ) {
if ( FileName ) {
2005-12-30 15:11:16 +01:00
int fd = open ( FileName , O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC , DEFFILEMODE ) ;
if ( fd > = 0 ) {
if ( safe_write ( fd , Image , ImageSize ) = = ImageSize ) {
2018-02-20 13:28:04 +01:00
dsyslog ( " SVDRP %s < %s grabbed image to %s " , Setup . SVDRPHostName , * clientName , FileName ) ;
2005-12-30 10:27:23 +01:00
Reply ( 250 , " Grabbed image %s " , Option ) ;
2005-12-30 15:11:16 +01:00
}
2005-12-30 10:27:23 +01:00
else {
LOG_ERROR_STR ( FileName ) ;
Reply ( 451 , " Can't write to '%s' " , FileName ) ;
}
2005-12-30 15:11:16 +01:00
close ( fd ) ;
2005-12-30 10:27:23 +01:00
}
else {
LOG_ERROR_STR ( FileName ) ;
Reply ( 451 , " Can't open '%s' " , FileName ) ;
}
}
else {
cBase64Encoder Base64 ( Image , ImageSize ) ;
const char * s ;
while ( ( s = Base64 . NextLine ( ) ) ! = NULL )
2006-03-26 09:27:30 +02:00
Reply ( - 216 , " %s " , s ) ;
2005-12-30 10:27:23 +01:00
Reply ( 216 , " Grabbed image %s " , Option ) ;
}
free ( Image ) ;
}
2000-09-17 11:53:35 +02:00
else
Reply ( 451 , " Grab image failed " ) ;
}
else
Reply ( 501 , " Missing filename " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdHELP ( const char * Option )
2000-07-23 15:01:31 +02:00
{
if ( * Option ) {
2005-08-27 16:42:28 +02:00
const char * hp = GetHelpPage ( Option , HelpPages ) ;
2000-07-23 15:01:31 +02:00
if ( hp )
2007-10-13 10:23:19 +02:00
Reply ( - 214 , " %s " , hp ) ;
2000-07-23 15:01:31 +02:00
else {
Reply ( 504 , " HELP topic \" %s \" unknown " , Option ) ;
return ;
}
}
else {
2000-07-29 18:19:49 +02:00
Reply ( - 214 , " This is VDR version %s " , VDRVERSION ) ;
2000-07-23 15:01:31 +02:00
Reply ( - 214 , " Topics: " ) ;
2005-08-27 16:42:28 +02:00
PrintHelpTopics ( HelpPages ) ;
cPlugin * plugin ;
for ( int i = 0 ; ( plugin = cPluginManager : : GetPlugin ( i ) ) ! = NULL ; i + + ) {
const char * * hp = plugin - > SVDRPHelpPages ( ) ;
if ( hp )
Reply ( - 214 , " Plugin %s v%s - %s " , plugin - > Name ( ) , plugin - > Version ( ) , plugin - > Description ( ) ) ;
PrintHelpTopics ( hp ) ;
2001-04-01 16:07:58 +02:00
}
2000-07-23 15:01:31 +02:00
Reply ( - 214 , " To report bugs in the implementation send email to " ) ;
2009-10-18 14:18:22 +02:00
Reply ( - 214 , " vdr-bugs@tvdr.de " ) ;
2000-07-23 15:01:31 +02:00
}
Reply ( 214 , " End of HELP info " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdHITK ( const char * Option )
2000-09-17 09:36:50 +02:00
{
if ( * Option ) {
2012-12-04 12:13:14 +01:00
if ( ! cRemote : : Enabled ( ) ) {
Reply ( 550 , " Remote control currently disabled (key \" %s \" discarded) " , Option ) ;
return ;
}
2011-08-27 10:43:18 +02:00
char buf [ strlen ( Option ) + 1 ] ;
strcpy ( buf , Option ) ;
const char * delim = " \t " ;
char * strtok_next ;
char * p = strtok_r ( buf , delim , & strtok_next ) ;
int NumKeys = 0 ;
while ( p ) {
eKeys k = cKey : : FromString ( p ) ;
if ( k ! = kNone ) {
if ( ! cRemote : : Put ( k ) ) {
Reply ( 451 , " Too many keys in \" %s \" (only %d accepted) " , Option , NumKeys ) ;
return ;
}
}
else {
Reply ( 504 , " Unknown key: \" %s \" " , p ) ;
return ;
}
NumKeys + + ;
p = strtok_r ( NULL , delim , & strtok_next ) ;
}
Reply ( 250 , " Key%s \" %s \" accepted " , NumKeys > 1 ? " s " : " " , Option ) ;
2000-09-17 09:36:50 +02:00
}
else {
Reply ( - 214 , " Valid <key> names for the HITK command: " ) ;
for ( int i = 0 ; i < kNone ; i + + ) {
2002-09-29 13:40:45 +02:00
Reply ( - 214 , " %s " , cKey : : ToString ( eKeys ( i ) ) ) ;
2000-09-17 09:36:50 +02:00
}
Reply ( 214 , " End of key list " ) ;
}
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdLSTC ( const char * Option )
2000-07-23 15:01:31 +02:00
{
2015-09-01 11:14:27 +02:00
LOCK_CHANNELS_READ ;
2017-04-22 15:02:56 +02:00
bool WithChannelIds = startswith ( Option , " :ids " ) & & ( Option [ 4 ] = = ' ' | | Option [ 4 ] = = 0 ) ;
if ( WithChannelIds )
Option = skipspace ( Option + 4 ) ;
2007-08-25 09:31:13 +02:00
bool WithGroupSeps = strcasecmp ( Option , " :groups " ) = = 0 ;
if ( * Option & & ! WithGroupSeps ) {
2000-07-23 15:01:31 +02:00
if ( isnumber ( Option ) ) {
2017-04-22 15:02:56 +02:00
int n = strtol ( Option , NULL , 10 ) ;
if ( n = = 0 )
n = cDevice : : CurrentChannel ( ) ;
if ( const cChannel * Channel = Channels - > GetByNumber ( n ) )
Reply ( 250 , " %d%s%s %s " , Channel - > Number ( ) , WithChannelIds ? " " : " " , WithChannelIds ? * Channel - > GetChannelID ( ) . ToString ( ) : " " , * Channel - > ToText ( ) ) ;
2000-07-23 15:01:31 +02:00
else
Reply ( 501 , " Channel \" %s \" not defined " , Option ) ;
}
else {
2015-09-01 11:14:27 +02:00
const cChannel * Next = Channels - > GetByChannelID ( tChannelID : : FromString ( Option ) ) ;
if ( ! Next ) {
for ( const cChannel * Channel = Channels - > First ( ) ; Channel ; Channel = Channels - > Next ( Channel ) ) {
2017-04-22 15:02:56 +02:00
if ( ! Channel - > GroupSep ( ) ) {
if ( strcasestr ( Channel - > Name ( ) , Option ) ) {
if ( Next )
Reply ( - 250 , " %d%s%s %s " , Next - > Number ( ) , WithChannelIds ? " " : " " , WithChannelIds ? * Next - > GetChannelID ( ) . ToString ( ) : " " , * Next - > ToText ( ) ) ;
Next = Channel ;
}
}
}
2011-09-11 14:49:33 +02:00
}
2015-09-01 11:14:27 +02:00
if ( Next )
2017-04-22 15:02:56 +02:00
Reply ( 250 , " %d%s%s %s " , Next - > Number ( ) , WithChannelIds ? " " : " " , WithChannelIds ? * Next - > GetChannelID ( ) . ToString ( ) : " " , * Next - > ToText ( ) ) ;
2002-02-24 14:16:46 +01:00
else
Reply ( 501 , " Channel \" %s \" not defined " , Option ) ;
2000-07-23 15:01:31 +02:00
}
}
2015-09-01 11:14:27 +02:00
else if ( cChannels : : MaxNumber ( ) > = 1 ) {
for ( const cChannel * Channel = Channels - > First ( ) ; Channel ; Channel = Channels - > Next ( Channel ) ) {
2007-08-25 09:31:13 +02:00
if ( WithGroupSeps )
2017-04-22 15:02:56 +02:00
Reply ( Channel - > Next ( ) ? - 250 : 250 , " %d%s%s %s " , Channel - > GroupSep ( ) ? 0 : Channel - > Number ( ) , ( WithChannelIds & & ! Channel - > GroupSep ( ) ) ? " " : " " , ( WithChannelIds & & ! Channel - > GroupSep ( ) ) ? * Channel - > GetChannelID ( ) . ToString ( ) : " " , * Channel - > ToText ( ) ) ;
2015-09-01 11:14:27 +02:00
else if ( ! Channel - > GroupSep ( ) )
2017-04-22 15:02:56 +02:00
Reply ( Channel - > Number ( ) < cChannels : : MaxNumber ( ) ? - 250 : 250 , " %d%s%s %s " , Channel - > Number ( ) , WithChannelIds ? " " : " " , WithChannelIds ? * Channel - > GetChannelID ( ) . ToString ( ) : " " , * Channel - > ToText ( ) ) ;
2007-06-23 13:24:00 +02:00
}
2000-07-23 15:01:31 +02:00
}
2001-03-02 23:00:53 +01:00
else
Reply ( 550 , " No channels defined " ) ;
2000-07-23 15:01:31 +02:00
}
2017-11-11 12:04:17 +01:00
void cSVDRPServer : : CmdLSTD ( const char * Option )
{
if ( cDevice : : NumDevices ( ) ) {
for ( int i = 0 ; i < cDevice : : NumDevices ( ) ; i + + ) {
if ( const cDevice * d = cDevice : : GetDevice ( i ) )
Reply ( d - > DeviceNumber ( ) + 1 = = cDevice : : NumDevices ( ) ? 250 : - 250 , " %d [%s%s] %s " , d - > DeviceNumber ( ) + 1 , d - > HasDecoder ( ) ? " D " : " - " , d - > DeviceNumber ( ) + 1 = = Setup . PrimaryDVB ? " P " : " - " , * d - > DeviceName ( ) ) ;
}
}
else
Reply ( 550 , " No devices found " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdLSTE ( const char * Option )
2001-04-01 15:40:43 +02:00
{
2017-05-28 13:08:09 +02:00
LOCK_CHANNELS_READ ;
2015-09-01 11:14:27 +02:00
LOCK_SCHEDULES_READ ;
const cSchedule * Schedule = NULL ;
eDumpMode DumpMode = dmAll ;
time_t AtTime = 0 ;
if ( * Option ) {
char buf [ strlen ( Option ) + 1 ] ;
strcpy ( buf , Option ) ;
const char * delim = " \t " ;
char * strtok_next ;
char * p = strtok_r ( buf , delim , & strtok_next ) ;
while ( p & & DumpMode = = dmAll ) {
if ( strcasecmp ( p , " NOW " ) = = 0 )
DumpMode = dmPresent ;
else if ( strcasecmp ( p , " NEXT " ) = = 0 )
DumpMode = dmFollowing ;
else if ( strcasecmp ( p , " AT " ) = = 0 ) {
DumpMode = dmAtTime ;
if ( ( p = strtok_r ( NULL , delim , & strtok_next ) ) ! = NULL ) {
if ( isnumber ( p ) )
AtTime = strtol ( p , NULL , 10 ) ;
2004-02-22 15:36:36 +01:00
else {
2015-09-01 11:14:27 +02:00
Reply ( 501 , " Invalid time " ) ;
2004-02-22 15:36:36 +01:00
return ;
}
}
2015-09-01 11:14:27 +02:00
else {
Reply ( 501 , " Missing time " ) ;
return ;
}
}
else if ( ! Schedule ) {
const cChannel * Channel = NULL ;
if ( isnumber ( p ) )
Channel = Channels - > GetByNumber ( strtol ( Option , NULL , 10 ) ) ;
else
Channel = Channels - > GetByChannelID ( tChannelID : : FromString ( Option ) ) ;
if ( Channel ) {
Schedule = Schedules - > GetSchedule ( Channel ) ;
if ( ! Schedule ) {
Reply ( 550 , " No schedule found " ) ;
2004-02-22 15:36:36 +01:00
return ;
}
}
else {
2015-09-01 11:14:27 +02:00
Reply ( 550 , " Channel \" %s \" not defined " , p ) ;
2004-02-22 15:36:36 +01:00
return ;
}
}
2015-09-01 11:14:27 +02:00
else {
Reply ( 501 , " Unknown option: \" %s \" " , p ) ;
return ;
}
p = strtok_r ( NULL , delim , & strtok_next ) ;
2005-08-07 14:25:31 +02:00
}
2015-09-01 11:14:27 +02:00
}
int fd = dup ( file ) ;
if ( fd ) {
FILE * f = fdopen ( fd , " w " ) ;
if ( f ) {
if ( Schedule )
2017-05-28 13:08:09 +02:00
Schedule - > Dump ( Channels , f , " 215- " , DumpMode , AtTime ) ;
2015-09-01 11:14:27 +02:00
else
Schedules - > Dump ( f , " 215- " , DumpMode , AtTime ) ;
fflush ( f ) ;
Reply ( 215 , " End of EPG data " ) ;
fclose ( f ) ;
}
else {
Reply ( 451 , " Can't open file connection " ) ;
close ( fd ) ;
2001-04-01 15:40:43 +02:00
}
}
else
2015-09-01 11:14:27 +02:00
Reply ( 451 , " Can't dup stream descriptor " ) ;
2001-04-01 15:40:43 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdLSTR ( const char * Option )
2001-10-27 11:47:46 +02:00
{
2013-01-15 13:29:39 +01:00
int Number = 0 ;
bool Path = false ;
2015-09-01 11:14:27 +02:00
LOCK_RECORDINGS_READ ;
2001-10-27 11:47:46 +02:00
if ( * Option ) {
2013-01-15 13:29:39 +01:00
char buf [ strlen ( Option ) + 1 ] ;
strcpy ( buf , Option ) ;
const char * delim = " \t " ;
char * strtok_next ;
char * p = strtok_r ( buf , delim , & strtok_next ) ;
while ( p ) {
if ( ! Number ) {
if ( isnumber ( p ) )
Number = strtol ( p , NULL , 10 ) ;
else {
2017-04-04 11:09:14 +02:00
Reply ( 501 , " Error in recording id \" %s \" " , Option ) ;
2013-01-15 13:29:39 +01:00
return ;
}
}
else if ( strcasecmp ( p , " PATH " ) = = 0 )
Path = true ;
else {
Reply ( 501 , " Unknown option: \" %s \" " , p ) ;
return ;
}
p = strtok_r ( NULL , delim , & strtok_next ) ;
}
if ( Number ) {
2017-04-03 14:11:41 +02:00
if ( const cRecording * Recording = Recordings - > GetById ( strtol ( Option , NULL , 10 ) ) ) {
2005-05-16 14:45:11 +02:00
FILE * f = fdopen ( file , " w " ) ;
if ( f ) {
2013-01-15 13:29:39 +01:00
if ( Path )
2015-09-01 11:14:27 +02:00
Reply ( 250 , " %s " , Recording - > FileName ( ) ) ;
2013-01-15 13:29:39 +01:00
else {
2015-09-01 11:14:27 +02:00
Recording - > Info ( ) - > Write ( f , " 215- " ) ;
2013-01-15 13:29:39 +01:00
fflush ( f ) ;
Reply ( 215 , " End of recording information " ) ;
}
2005-05-16 14:45:11 +02:00
// don't 'fclose(f)' here!
2001-10-27 11:47:46 +02:00
}
else
2005-05-16 14:45:11 +02:00
Reply ( 451 , " Can't open file connection " ) ;
2001-10-27 11:47:46 +02:00
}
else
Reply ( 550 , " Recording \" %s \" not found " , Option ) ;
}
}
2015-09-01 11:14:27 +02:00
else if ( Recordings - > Count ( ) ) {
const cRecording * Recording = Recordings - > First ( ) ;
while ( Recording ) {
2017-04-03 14:11:41 +02:00
Reply ( Recording = = Recordings - > Last ( ) ? 250 : - 250 , " %d %s " , Recording - > Id ( ) , Recording - > Title ( ' ' , true ) ) ;
2015-09-01 11:14:27 +02:00
Recording = Recordings - > Next ( Recording ) ;
2001-10-27 11:47:46 +02:00
}
}
else
Reply ( 550 , " No recordings available " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdLSTT ( const char * Option )
2000-07-23 15:01:31 +02:00
{
2015-09-06 09:14:53 +02:00
int Id = 0 ;
bool UseChannelId = false ;
2000-07-23 15:01:31 +02:00
if ( * Option ) {
2006-01-08 13:33:57 +01:00
char buf [ strlen ( Option ) + 1 ] ;
strcpy ( buf , Option ) ;
const char * delim = " \t " ;
char * strtok_next ;
char * p = strtok_r ( buf , delim , & strtok_next ) ;
while ( p ) {
if ( isnumber ( p ) )
2015-09-06 09:14:53 +02:00
Id = strtol ( p , NULL , 10 ) ;
2006-01-08 13:33:57 +01:00
else if ( strcasecmp ( p , " ID " ) = = 0 )
2015-09-06 09:14:53 +02:00
UseChannelId = true ;
2006-01-08 13:33:57 +01:00
else {
Reply ( 501 , " Unknown option: \" %s \" " , p ) ;
return ;
}
p = strtok_r ( NULL , delim , & strtok_next ) ;
}
}
2015-09-01 11:14:27 +02:00
LOCK_TIMERS_READ ;
2015-09-06 09:14:53 +02:00
if ( Id ) {
2015-09-01 11:14:27 +02:00
for ( const cTimer * Timer = Timers - > First ( ) ; Timer ; Timer = Timers - > Next ( Timer ) ) {
if ( ! Timer - > Remote ( ) ) {
2015-09-06 09:14:53 +02:00
if ( Timer - > Id ( ) = = Id ) {
Reply ( 250 , " %d %s " , Timer - > Id ( ) , * Timer - > ToText ( UseChannelId ) ) ;
2015-09-01 11:14:27 +02:00
return ;
}
}
}
Reply ( 501 , " Timer \" %s \" not defined " , Option ) ;
return ;
2000-07-23 15:01:31 +02:00
}
2015-09-01 11:14:27 +02:00
else {
2015-09-06 09:14:53 +02:00
const cTimer * LastLocalTimer = Timers - > Last ( ) ;
while ( LastLocalTimer ) {
if ( LastLocalTimer - > Remote ( ) )
LastLocalTimer = Timers - > Prev ( LastLocalTimer ) ;
else
break ;
}
if ( LastLocalTimer ) {
for ( const cTimer * Timer = Timers - > First ( ) ; Timer ; Timer = Timers - > Next ( Timer ) ) {
if ( ! Timer - > Remote ( ) )
Reply ( Timer ! = LastLocalTimer ? - 250 : 250 , " %d %s " , Timer - > Id ( ) , * Timer - > ToText ( UseChannelId ) ) ;
if ( Timer = = LastLocalTimer )
break ;
2015-09-01 11:14:27 +02:00
}
return ;
}
2000-07-23 15:01:31 +02:00
}
2015-09-01 11:14:27 +02:00
Reply ( 550 , " No timers defined " ) ;
2000-07-23 15:01:31 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdMESG ( const char * Option )
2001-02-18 14:18:13 +01:00
{
if ( * Option ) {
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s message '%s' " , Setup . SVDRPHostName , * clientName , Option ) ;
2005-11-27 15:57:03 +01:00
Skins . QueueMessage ( mtInfo , Option ) ;
Reply ( 250 , " Message queued " ) ;
2001-02-18 14:18:13 +01:00
}
else
2005-11-27 15:57:03 +01:00
Reply ( 501 , " Missing message " ) ;
2001-02-18 14:18:13 +01:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdMODC ( const char * Option )
2000-07-23 15:01:31 +02:00
{
if ( * Option ) {
char * tail ;
int n = strtol ( Option , & tail , 10 ) ;
if ( tail & & tail ! = Option ) {
tail = skipspace ( tail ) ;
2015-09-01 11:14:27 +02:00
LOCK_CHANNELS_WRITE ;
Channels - > SetExplicitModify ( ) ;
if ( cChannel * Channel = Channels - > GetByNumber ( n ) ) {
cChannel ch ;
if ( ch . Parse ( tail ) ) {
if ( Channels - > HasUniqueChannelID ( & ch , Channel ) ) {
* Channel = ch ;
Channels - > ReNumber ( ) ;
Channels - > SetModifiedByUser ( ) ;
Channels - > SetModified ( ) ;
2020-06-22 20:59:49 +02:00
isyslog ( " SVDRP %s < %s modified channel %d %s " , Setup . SVDRPHostName , * clientName , Channel - > Number ( ) , * Channel - > ToText ( ) ) ;
2015-09-01 11:14:27 +02:00
Reply ( 250 , " %d %s " , Channel - > Number ( ) , * Channel - > ToText ( ) ) ;
2002-11-10 15:50:21 +01:00
}
else
2015-09-01 11:14:27 +02:00
Reply ( 501 , " Channel settings are not unique " ) ;
2000-07-23 15:01:31 +02:00
}
2002-11-10 15:50:21 +01:00
else
2015-09-01 11:14:27 +02:00
Reply ( 501 , " Error in channel settings " ) ;
2000-07-23 15:01:31 +02:00
}
else
2015-09-01 11:14:27 +02:00
Reply ( 501 , " Channel \" %d \" not defined " , n ) ;
2000-07-23 15:01:31 +02:00
}
else
Reply ( 501 , " Error in channel number " ) ;
}
else
Reply ( 501 , " Missing channel settings " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdMODT ( const char * Option )
2000-07-23 15:01:31 +02:00
{
if ( * Option ) {
char * tail ;
2015-09-06 09:14:53 +02:00
int Id = strtol ( Option , & tail , 10 ) ;
2000-07-23 15:01:31 +02:00
if ( tail & & tail ! = Option ) {
tail = skipspace ( tail ) ;
2015-09-01 11:14:27 +02:00
LOCK_TIMERS_WRITE ;
Timers - > SetExplicitModify ( ) ;
2015-09-06 09:14:53 +02:00
if ( cTimer * Timer = Timers - > GetById ( Id ) ) {
2018-04-19 09:45:08 +02:00
bool IsRecording = Timer - > HasFlags ( tfRecording ) ;
2015-09-01 11:14:27 +02:00
cTimer t = * Timer ;
if ( strcasecmp ( tail , " ON " ) = = 0 )
t . SetFlags ( tfActive ) ;
else if ( strcasecmp ( tail , " OFF " ) = = 0 )
t . ClrFlags ( tfActive ) ;
else if ( ! t . Parse ( tail ) ) {
Reply ( 501 , " Error in timer settings " ) ;
return ;
2000-07-23 15:01:31 +02:00
}
2020-12-26 15:49:01 +01:00
if ( IsRecording & & t . IsPatternTimer ( ) ) {
Reply ( 550 , " Timer is recording " ) ;
return ;
}
2015-09-01 11:14:27 +02:00
* Timer = t ;
2018-04-19 09:45:08 +02:00
if ( IsRecording )
Timer - > SetFlags ( tfRecording ) ;
else
Timer - > ClrFlags ( tfRecording ) ;
2015-09-01 11:14:27 +02:00
Timers - > SetModified ( ) ;
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s modified timer %s (%s) " , Setup . SVDRPHostName , * clientName , * Timer - > ToDescr ( ) , Timer - > HasFlags ( tfActive ) ? " active " : " inactive " ) ;
2020-12-26 15:49:01 +01:00
if ( Timer - > IsPatternTimer ( ) )
Timer - > SetEvent ( NULL ) ;
2021-01-14 10:29:05 +01:00
Timer - > TriggerRespawn ( ) ;
2015-09-13 13:57:51 +02:00
Reply ( 250 , " %d %s " , Timer - > Id ( ) , * Timer - > ToText ( true ) ) ;
2000-07-23 15:01:31 +02:00
}
else
2015-09-06 09:14:53 +02:00
Reply ( 501 , " Timer \" %d \" not defined " , Id ) ;
2000-07-23 15:01:31 +02:00
}
else
2017-04-04 11:09:14 +02:00
Reply ( 501 , " Error in timer id " ) ;
2000-07-23 15:01:31 +02:00
}
else
Reply ( 501 , " Missing timer settings " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdMOVC ( const char * Option )
2000-07-23 15:01:31 +02:00
{
2005-09-03 14:10:01 +02:00
if ( * Option ) {
2015-09-01 11:14:27 +02:00
char * tail ;
int From = strtol ( Option , & tail , 10 ) ;
if ( tail & & tail ! = Option ) {
tail = skipspace ( tail ) ;
2005-09-03 14:10:01 +02:00
if ( tail & & tail ! = Option ) {
2015-09-01 11:14:27 +02:00
LOCK_TIMERS_READ ; // necessary to keep timers and channels in sync!
LOCK_CHANNELS_WRITE ;
Channels - > SetExplicitModify ( ) ;
int To = strtol ( tail , NULL , 10 ) ;
int CurrentChannelNr = cDevice : : CurrentChannel ( ) ;
const cChannel * CurrentChannel = Channels - > GetByNumber ( CurrentChannelNr ) ;
cChannel * FromChannel = Channels - > GetByNumber ( From ) ;
if ( FromChannel ) {
cChannel * ToChannel = Channels - > GetByNumber ( To ) ;
if ( ToChannel ) {
int FromNumber = FromChannel - > Number ( ) ;
int ToNumber = ToChannel - > Number ( ) ;
if ( FromNumber ! = ToNumber ) {
2020-04-11 09:22:05 +02:00
if ( Channels - > MoveNeedsDecrement ( FromChannel , ToChannel ) )
ToChannel = Channels - > Prev ( ToChannel ) ; // cListBase::Move() doesn't know about the channel list's numbered groups!
2015-09-01 11:14:27 +02:00
Channels - > Move ( FromChannel , ToChannel ) ;
Channels - > ReNumber ( ) ;
Channels - > SetModifiedByUser ( ) ;
Channels - > SetModified ( ) ;
if ( CurrentChannel & & CurrentChannel - > Number ( ) ! = CurrentChannelNr ) {
if ( ! cDevice : : PrimaryDevice ( ) - > Replaying ( ) | | cDevice : : PrimaryDevice ( ) - > Transferring ( ) )
Channels - > SwitchTo ( CurrentChannel - > Number ( ) ) ;
else
2016-12-08 10:48:53 +01:00
cDevice : : SetCurrentChannel ( CurrentChannel - > Number ( ) ) ;
2005-09-03 14:10:01 +02:00
}
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s moved channel %d to %d " , Setup . SVDRPHostName , * clientName , FromNumber , ToNumber ) ;
2015-09-01 11:14:27 +02:00
Reply ( 250 , " Channel \" %d \" moved to \" %d \" " , From , To ) ;
2005-09-03 14:10:01 +02:00
}
else
2015-09-01 11:14:27 +02:00
Reply ( 501 , " Can't move channel to same position " ) ;
2005-09-03 14:10:01 +02:00
}
else
2015-09-01 11:14:27 +02:00
Reply ( 501 , " Channel \" %d \" not defined " , To ) ;
2005-09-03 14:10:01 +02:00
}
else
2015-09-01 11:14:27 +02:00
Reply ( 501 , " Channel \" %d \" not defined " , From ) ;
2005-09-03 14:10:01 +02:00
}
else
Reply ( 501 , " Error in channel number " ) ;
}
else
2015-09-01 11:14:27 +02:00
Reply ( 501 , " Error in channel number " ) ;
2005-09-03 14:10:01 +02:00
}
else
Reply ( 501 , " Missing channel number " ) ;
2000-07-23 15:01:31 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdMOVR ( const char * Option )
2013-10-21 07:55:23 +02:00
{
if ( * Option ) {
char * opt = strdup ( Option ) ;
char * num = skipspace ( opt ) ;
char * option = num ;
while ( * option & & ! isspace ( * option ) )
option + + ;
char c = * option ;
* option = 0 ;
if ( isnumber ( num ) ) {
2015-09-01 11:14:27 +02:00
LOCK_RECORDINGS_WRITE ;
Recordings - > SetExplicitModify ( ) ;
2017-04-03 14:11:41 +02:00
if ( cRecording * Recording = Recordings - > GetById ( strtol ( num , NULL , 10 ) ) ) {
2015-09-01 11:14:27 +02:00
if ( int RecordingInUse = Recording - > IsInUse ( ) )
Reply ( 550 , " %s " , * RecordingInUseMessage ( RecordingInUse , Option , Recording ) ) ;
2013-10-21 07:55:23 +02:00
else {
if ( c )
option = skipspace ( + + option ) ;
if ( * option ) {
2015-09-01 11:14:27 +02:00
cString oldName = Recording - > Name ( ) ;
if ( ( Recording = Recordings - > GetByName ( Recording - > FileName ( ) ) ) ! = NULL & & Recording - > ChangeName ( option ) ) {
Recordings - > SetModified ( ) ;
Recordings - > TouchUpdate ( ) ;
Reply ( 250 , " Recording \" %s \" moved to \" %s \" " , * oldName , Recording - > Name ( ) ) ;
}
2013-10-21 07:55:23 +02:00
else
Reply ( 554 , " Error while moving recording \" %s \" to \" %s \" ! " , * oldName , option ) ;
}
else
Reply ( 501 , " Missing new recording name " ) ;
}
}
else
2015-09-01 11:14:27 +02:00
Reply ( 550 , " Recording \" %s \" not found " , num ) ;
2013-10-21 07:55:23 +02:00
}
else
2017-04-04 11:09:14 +02:00
Reply ( 501 , " Error in recording id \" %s \" " , num ) ;
2013-10-21 07:55:23 +02:00
free ( opt ) ;
}
else
2017-04-04 11:09:14 +02:00
Reply ( 501 , " Missing recording id " ) ;
2013-10-21 07:55:23 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdNEWC ( const char * Option )
2000-07-23 15:01:31 +02:00
{
if ( * Option ) {
2002-11-10 15:50:21 +01:00
cChannel ch ;
2005-05-06 13:49:01 +02:00
if ( ch . Parse ( Option ) ) {
2015-09-01 11:14:27 +02:00
LOCK_CHANNELS_WRITE ;
Channels - > SetExplicitModify ( ) ;
if ( Channels - > HasUniqueChannelID ( & ch ) ) {
2002-11-10 15:50:21 +01:00
cChannel * channel = new cChannel ;
* channel = ch ;
2015-09-01 11:14:27 +02:00
Channels - > Add ( channel ) ;
Channels - > ReNumber ( ) ;
Channels - > SetModifiedByUser ( ) ;
Channels - > SetModified ( ) ;
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s new channel %d %s " , Setup . SVDRPHostName , * clientName , channel - > Number ( ) , * channel - > ToText ( ) ) ;
2004-12-26 12:45:22 +01:00
Reply ( 250 , " %d %s " , channel - > Number ( ) , * channel - > ToText ( ) ) ;
2002-11-10 15:50:21 +01:00
}
else
Reply ( 501 , " Channel settings are not unique " ) ;
2000-07-23 15:01:31 +02:00
}
else
Reply ( 501 , " Error in channel settings " ) ;
}
else
Reply ( 501 , " Missing channel settings " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdNEWT ( const char * Option )
2000-07-23 15:01:31 +02:00
{
if ( * Option ) {
2015-09-01 11:14:27 +02:00
cTimer * Timer = new cTimer ;
if ( Timer - > Parse ( Option ) ) {
LOCK_TIMERS_WRITE ;
2015-09-14 13:23:06 +02:00
Timer - > ClrFlags ( tfRecording ) ;
2015-09-01 11:14:27 +02:00
Timers - > Add ( Timer ) ;
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s added timer %s " , Setup . SVDRPHostName , * clientName , * Timer - > ToDescr ( ) ) ;
2015-09-13 13:57:51 +02:00
Reply ( 250 , " %d %s " , Timer - > Id ( ) , * Timer - > ToText ( true ) ) ;
2013-01-17 15:21:07 +01:00
return ;
2000-08-06 12:56:49 +02:00
}
else
Reply ( 501 , " Error in timer settings " ) ;
2015-09-01 11:14:27 +02:00
delete Timer ;
2000-08-06 12:56:49 +02:00
}
else
Reply ( 501 , " Missing timer settings " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdNEXT ( const char * Option )
2001-09-01 09:53:01 +02:00
{
2015-09-01 11:14:27 +02:00
LOCK_TIMERS_READ ;
if ( const cTimer * t = Timers - > GetNextActiveTimer ( ) ) {
2001-09-01 09:53:01 +02:00
time_t Start = t - > StartTime ( ) ;
2015-09-06 09:14:53 +02:00
int Id = t - > Id ( ) ;
2004-12-19 16:33:34 +01:00
if ( ! * Option )
2015-09-06 09:14:53 +02:00
Reply ( 250 , " %d %s " , Id , * TimeToString ( Start ) ) ;
2001-09-01 09:53:01 +02:00
else if ( strcasecmp ( Option , " ABS " ) = = 0 )
2022-11-22 14:33:56 +01:00
Reply ( 250 , " %d %jd " , Id , intmax_t ( Start ) ) ;
2001-09-01 09:53:01 +02:00
else if ( strcasecmp ( Option , " REL " ) = = 0 )
2022-11-22 14:33:56 +01:00
Reply ( 250 , " %d %jd " , Id , intmax_t ( Start - time ( NULL ) ) ) ;
2001-09-01 09:53:01 +02:00
else
Reply ( 501 , " Unknown option: \" %s \" " , Option ) ;
}
else
Reply ( 550 , " No active timers " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdPING ( const char * Option )
{
2015-09-08 11:08:06 +02:00
Reply ( 250 , " %s is alive " , Setup . SVDRPHostName ) ;
2015-05-22 13:44:43 +02:00
}
void cSVDRPServer : : CmdPLAY ( const char * Option )
2005-08-28 10:45:19 +02:00
{
if ( * Option ) {
char * opt = strdup ( Option ) ;
char * num = skipspace ( opt ) ;
char * option = num ;
while ( * option & & ! isspace ( * option ) )
option + + ;
char c = * option ;
* option = 0 ;
if ( isnumber ( num ) ) {
2017-04-04 11:05:38 +02:00
cStateKey StateKey ;
if ( const cRecordings * Recordings = cRecordings : : GetRecordingsRead ( StateKey ) ) {
if ( const cRecording * Recording = Recordings - > GetById ( strtol ( num , NULL , 10 ) ) ) {
cString FileName = Recording - > FileName ( ) ;
cString Title = Recording - > Title ( ) ;
int FramesPerSecond = Recording - > FramesPerSecond ( ) ;
bool IsPesRecording = Recording - > IsPesRecording ( ) ;
StateKey . Remove ( ) ; // must give up the lock for the call to cControl::Shutdown()
if ( c )
option = skipspace ( + + option ) ;
cReplayControl : : SetRecording ( NULL ) ;
cControl : : Shutdown ( ) ;
if ( * option ) {
int pos = 0 ;
if ( strcasecmp ( option , " BEGIN " ) ! = 0 )
pos = HMSFToIndex ( option , FramesPerSecond ) ;
cResumeFile Resume ( FileName , IsPesRecording ) ;
if ( pos < = 0 )
Resume . Delete ( ) ;
else
Resume . Save ( pos ) ;
}
cReplayControl : : SetRecording ( FileName ) ;
cControl : : Launch ( new cReplayControl ) ;
cControl : : Attach ( ) ;
Reply ( 250 , " Playing recording \" %s \" [%s] " , num , * Title ) ;
}
else {
StateKey . Remove ( ) ;
Reply ( 550 , " Recording \" %s \" not found " , num ) ;
2005-08-28 10:45:19 +02:00
}
}
}
else
2017-04-04 11:09:14 +02:00
Reply ( 501 , " Error in recording id \" %s \" " , num ) ;
2005-08-28 10:45:19 +02:00
free ( opt ) ;
}
else
2017-04-04 11:09:14 +02:00
Reply ( 501 , " Missing recording id " ) ;
2005-08-28 10:45:19 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdPLUG ( const char * Option )
2005-08-27 16:42:28 +02:00
{
if ( * Option ) {
char * opt = strdup ( Option ) ;
char * name = skipspace ( opt ) ;
char * option = name ;
while ( * option & & ! isspace ( * option ) )
option + + ;
char c = * option ;
* option = 0 ;
cPlugin * plugin = cPluginManager : : GetPlugin ( name ) ;
if ( plugin ) {
if ( c )
option = skipspace ( + + option ) ;
char * cmd = option ;
while ( * option & & ! isspace ( * option ) )
option + + ;
if ( * option ) {
* option + + = 0 ;
option = skipspace ( option ) ;
}
if ( ! * cmd | | strcasecmp ( cmd , " HELP " ) = = 0 ) {
if ( * cmd & & * option ) {
const char * hp = GetHelpPage ( option , plugin - > SVDRPHelpPages ( ) ) ;
if ( hp ) {
2006-03-26 09:27:30 +02:00
Reply ( - 214 , " %s " , hp ) ;
2005-08-27 16:42:28 +02:00
Reply ( 214 , " End of HELP info " ) ;
}
else
Reply ( 504 , " HELP topic \" %s \" for plugin \" %s \" unknown " , option , plugin - > Name ( ) ) ;
}
else {
Reply ( - 214 , " Plugin %s v%s - %s " , plugin - > Name ( ) , plugin - > Version ( ) , plugin - > Description ( ) ) ;
const char * * hp = plugin - > SVDRPHelpPages ( ) ;
if ( hp ) {
Reply ( - 214 , " SVDRP commands: " ) ;
PrintHelpTopics ( hp ) ;
Reply ( 214 , " End of HELP info " ) ;
}
else
Reply ( 214 , " This plugin has no SVDRP commands " ) ;
}
}
2005-09-03 12:49:36 +02:00
else if ( strcasecmp ( cmd , " MAIN " ) = = 0 ) {
2006-04-17 09:10:39 +02:00
if ( cRemote : : CallPlugin ( plugin - > Name ( ) ) )
Reply ( 250 , " Initiated call to main menu function of plugin \" %s \" " , plugin - > Name ( ) ) ;
else
Reply ( 550 , " A plugin call is already pending - please try again later " ) ;
2005-09-03 12:49:36 +02:00
}
2005-08-27 16:42:28 +02:00
else {
int ReplyCode = 900 ;
cString s = plugin - > SVDRPCommand ( cmd , option , ReplyCode ) ;
2008-02-17 13:47:12 +01:00
if ( * s )
2006-03-26 09:27:30 +02:00
Reply ( abs ( ReplyCode ) , " %s " , * s ) ;
2005-08-27 16:42:28 +02:00
else
Reply ( 500 , " Command unrecognized: \" %s \" " , cmd ) ;
}
}
else
Reply ( 550 , " Plugin \" %s \" not found (use PLUG for a list of plugins) " , name ) ;
free ( opt ) ;
}
else {
Reply ( - 214 , " Available plugins: " ) ;
cPlugin * plugin ;
for ( int i = 0 ; ( plugin = cPluginManager : : GetPlugin ( i ) ) ! = NULL ; i + + )
Reply ( - 214 , " %s v%s - %s " , plugin - > Name ( ) , plugin - > Version ( ) , plugin - > Description ( ) ) ;
Reply ( 214 , " End of plugin list " ) ;
}
}
2015-09-01 11:14:27 +02:00
void cSVDRPServer : : CmdPOLL ( const char * Option )
{
if ( * Option ) {
char buf [ strlen ( Option ) + 1 ] ;
char * p = strcpy ( buf , Option ) ;
const char * delim = " \t " ;
char * strtok_next ;
char * RemoteName = strtok_r ( p , delim , & strtok_next ) ;
char * ListName = strtok_r ( NULL , delim , & strtok_next ) ;
if ( SVDRPClientHandler ) {
if ( ListName ) {
if ( strcasecmp ( ListName , " timers " ) = = 0 ) {
2022-11-19 15:49:27 +01:00
Reply ( 250 , " OK " ) ; // must send reply before calling TriggerFetchingTimers() to avoid a deadlock if two clients send each other POLL commands at the same time
SVDRPClientHandler - > TriggerFetchingTimers ( RemoteName ) ;
2015-09-01 11:14:27 +02:00
}
else
Reply ( 501 , " Unknown list name: \" %s \" " , ListName ) ;
}
else
Reply ( 501 , " Missing list name " ) ;
}
else
Reply ( 501 , " No SVDRP client connections " ) ;
}
else
Reply ( 501 , " Missing parameters " ) ;
}
2017-11-11 12:04:17 +01:00
void cSVDRPServer : : CmdPRIM ( const char * Option )
{
int n = - 1 ;
if ( * Option ) {
if ( isnumber ( Option ) ) {
int o = strtol ( Option , NULL , 10 ) ;
if ( o > 0 & & o < = cDevice : : NumDevices ( ) )
n = o ;
else
Reply ( 501 , " Invalid device number \" %s \" " , Option ) ;
}
else
Reply ( 501 , " Invalid parameter \" %s \" " , Option ) ;
if ( n > = 0 ) {
Setup . PrimaryDVB = n ;
Reply ( 250 , " Primary device set to %d " , n ) ;
}
}
else {
if ( const cDevice * d = cDevice : : PrimaryDevice ( ) )
Reply ( 250 , " %d [%s%s] %s " , d - > DeviceNumber ( ) + 1 , d - > HasDecoder ( ) ? " D " : " - " , d - > DeviceNumber ( ) + 1 = = Setup . PrimaryDVB ? " P " : " - " , * d - > DeviceName ( ) ) ;
else
Reply ( 501 , " Failed to get primary device " ) ;
}
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdPUTE ( const char * Option )
2002-02-24 11:13:21 +01:00
{
2009-04-13 13:35:29 +02:00
if ( * Option ) {
FILE * f = fopen ( Option , " r " ) ;
if ( f ) {
if ( cSchedules : : Read ( f ) ) {
cSchedules : : Cleanup ( true ) ;
Reply ( 250 , " EPG data processed from \" %s \" " , Option ) ;
}
else
Reply ( 451 , " Error while processing EPG from \" %s \" " , Option ) ;
fclose ( f ) ;
}
else
Reply ( 501 , " Cannot open file \" %s \" " , Option ) ;
}
2013-02-17 13:19:36 +01:00
else {
2009-04-13 13:35:29 +02:00
delete PUTEhandler ;
PUTEhandler = new cPUTEhandler ;
Reply ( PUTEhandler - > Status ( ) , " %s " , PUTEhandler - > Message ( ) ) ;
if ( PUTEhandler - > Status ( ) ! = 354 )
DELETENULL ( PUTEhandler ) ;
}
2002-02-24 11:13:21 +01:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdREMO ( const char * Option )
2007-04-30 12:53:35 +02:00
{
if ( * Option ) {
if ( ! strcasecmp ( Option , " ON " ) ) {
cRemote : : SetEnabled ( true ) ;
Reply ( 250 , " Remote control enabled " ) ;
}
else if ( ! strcasecmp ( Option , " OFF " ) ) {
cRemote : : SetEnabled ( false ) ;
Reply ( 250 , " Remote control disabled " ) ;
}
else
Reply ( 501 , " Invalid Option \" %s \" " , Option ) ;
}
else
Reply ( 250 , " Remote control is %s " , cRemote : : Enabled ( ) ? " enabled " : " disabled " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdSCAN ( const char * Option )
2004-01-17 15:41:52 +01:00
{
EITScanner . ForceScan ( ) ;
Reply ( 250 , " EPG scan triggered " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdSTAT ( const char * Option )
2003-04-27 14:23:30 +02:00
{
if ( * Option ) {
if ( strcasecmp ( Option , " DISK " ) = = 0 ) {
2004-01-31 10:15:58 +01:00
int FreeMB , UsedMB ;
2013-09-11 12:20:37 +02:00
int Percent = cVideoDirectory : : VideoDiskSpace ( & FreeMB , & UsedMB ) ;
2004-01-31 10:15:58 +01:00
Reply ( 250 , " %dMB %dMB %d%% " , FreeMB + UsedMB , FreeMB , Percent ) ;
2003-04-27 14:23:30 +02:00
}
else
Reply ( 501 , " Invalid Option \" %s \" " , Option ) ;
}
else
Reply ( 501 , " No option given " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdUPDT ( const char * Option )
2000-08-06 12:56:49 +02:00
{
if ( * Option ) {
2015-09-01 11:14:27 +02:00
cTimer * Timer = new cTimer ;
if ( Timer - > Parse ( Option ) ) {
LOCK_TIMERS_WRITE ;
if ( cTimer * t = Timers - > GetTimer ( Timer ) ) {
2018-04-19 09:45:08 +02:00
bool IsRecording = t - > HasFlags ( tfRecording ) ;
2015-09-01 11:14:27 +02:00
t - > Parse ( Option ) ;
delete Timer ;
Timer = t ;
2018-04-19 09:45:08 +02:00
if ( IsRecording )
Timer - > SetFlags ( tfRecording ) ;
else
Timer - > ClrFlags ( tfRecording ) ;
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s updated timer %s " , Setup . SVDRPHostName , * clientName , * Timer - > ToDescr ( ) ) ;
2000-08-06 12:56:49 +02:00
}
2015-09-01 11:14:27 +02:00
else {
2018-04-19 09:45:08 +02:00
Timer - > ClrFlags ( tfRecording ) ;
2015-09-01 11:14:27 +02:00
Timers - > Add ( Timer ) ;
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s added timer %s " , Setup . SVDRPHostName , * clientName , * Timer - > ToDescr ( ) ) ;
2015-09-01 11:14:27 +02:00
}
2015-09-13 13:57:51 +02:00
Reply ( 250 , " %d %s " , Timer - > Id ( ) , * Timer - > ToText ( true ) ) ;
2015-09-01 11:14:27 +02:00
return ;
2000-07-23 15:01:31 +02:00
}
else
Reply ( 501 , " Error in timer settings " ) ;
2015-09-01 11:14:27 +02:00
delete Timer ;
2000-07-23 15:01:31 +02:00
}
else
Reply ( 501 , " Missing timer settings " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdUPDR ( const char * Option )
2011-12-04 13:58:33 +01:00
{
2015-09-01 11:14:27 +02:00
LOCK_RECORDINGS_WRITE ;
Recordings - > Update ( false ) ;
2011-12-04 13:58:33 +01:00
Reply ( 250 , " Re-read of recordings directory triggered " ) ;
}
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : CmdVOLU ( const char * Option )
2002-03-09 10:07:40 +01:00
{
if ( * Option ) {
if ( isnumber ( Option ) )
2002-06-16 12:57:31 +02:00
cDevice : : PrimaryDevice ( ) - > SetVolume ( strtol ( Option , NULL , 10 ) , true ) ;
2002-03-09 10:07:40 +01:00
else if ( strcmp ( Option , " + " ) = = 0 )
2002-06-16 12:57:31 +02:00
cDevice : : PrimaryDevice ( ) - > SetVolume ( VOLUMEDELTA ) ;
2002-03-09 10:07:40 +01:00
else if ( strcmp ( Option , " - " ) = = 0 )
2002-06-16 12:57:31 +02:00
cDevice : : PrimaryDevice ( ) - > SetVolume ( - VOLUMEDELTA ) ;
2002-03-09 10:07:40 +01:00
else if ( strcasecmp ( Option , " MUTE " ) = = 0 )
2002-06-16 12:57:31 +02:00
cDevice : : PrimaryDevice ( ) - > ToggleMute ( ) ;
2002-03-09 10:07:40 +01:00
else {
Reply ( 501 , " Unknown option: \" %s \" " , Option ) ;
return ;
}
}
2002-06-16 12:57:31 +02:00
if ( cDevice : : PrimaryDevice ( ) - > IsMute ( ) )
2002-03-09 10:07:40 +01:00
Reply ( 250 , " Audio is mute " ) ;
else
2002-06-16 12:57:31 +02:00
Reply ( 250 , " Audio volume is %d " , cDevice : : CurrentVolume ( ) ) ;
2002-03-09 10:07:40 +01:00
}
2000-07-23 15:01:31 +02:00
# define CMD(c) (strcasecmp(Cmd, c) == 0)
2015-05-22 13:44:43 +02:00
void cSVDRPServer : : Execute ( char * Cmd )
2000-07-23 15:01:31 +02:00
{
2002-02-24 11:13:21 +01:00
// handle PUTE data:
if ( PUTEhandler ) {
if ( ! PUTEhandler - > Process ( Cmd ) ) {
2006-03-26 09:27:30 +02:00
Reply ( PUTEhandler - > Status ( ) , " %s " , PUTEhandler - > Message ( ) ) ;
2002-02-24 11:13:21 +01:00
DELETENULL ( PUTEhandler ) ;
}
2012-03-04 12:05:56 +01:00
cEitFilter : : SetDisableUntil ( time ( NULL ) + EITDISABLETIME ) ; // re-trigger the timeout, in case there is very much EPG data
2002-02-24 11:13:21 +01:00
return ;
}
2000-07-23 15:01:31 +02:00
// skip leading whitespace:
Cmd = skipspace ( Cmd ) ;
// find the end of the command word:
char * s = Cmd ;
while ( * s & & ! isspace ( * s ) )
s + + ;
2001-04-01 14:13:42 +02:00
if ( * s )
* s + + = 0 ;
2001-09-22 13:31:29 +02:00
s = skipspace ( s ) ;
2000-09-17 09:36:50 +02:00
if ( CMD ( " CHAN " ) ) CmdCHAN ( s ) ;
2002-08-25 10:49:02 +02:00
else if ( CMD ( " CLRE " ) ) CmdCLRE ( s ) ;
2018-02-20 13:28:04 +01:00
else if ( CMD ( " CONN " ) ) CmdCONN ( s ) ;
2000-09-17 09:36:50 +02:00
else if ( CMD ( " DELC " ) ) CmdDELC ( s ) ;
2001-10-27 11:47:46 +02:00
else if ( CMD ( " DELR " ) ) CmdDELR ( s ) ;
2000-09-17 09:36:50 +02:00
else if ( CMD ( " DELT " ) ) CmdDELT ( s ) ;
2005-08-28 14:18:24 +02:00
else if ( CMD ( " EDIT " ) ) CmdEDIT ( s ) ;
2000-09-17 11:53:35 +02:00
else if ( CMD ( " GRAB " ) ) CmdGRAB ( s ) ;
2000-09-17 09:36:50 +02:00
else if ( CMD ( " HELP " ) ) CmdHELP ( s ) ;
else if ( CMD ( " HITK " ) ) CmdHITK ( s ) ;
else if ( CMD ( " LSTC " ) ) CmdLSTC ( s ) ;
2017-11-11 12:04:17 +01:00
else if ( CMD ( " LSTD " ) ) CmdLSTD ( s ) ;
2001-04-01 15:40:43 +02:00
else if ( CMD ( " LSTE " ) ) CmdLSTE ( s ) ;
2001-10-27 11:47:46 +02:00
else if ( CMD ( " LSTR " ) ) CmdLSTR ( s ) ;
2000-09-17 09:36:50 +02:00
else if ( CMD ( " LSTT " ) ) CmdLSTT ( s ) ;
2001-02-18 14:18:13 +01:00
else if ( CMD ( " MESG " ) ) CmdMESG ( s ) ;
2000-09-17 09:36:50 +02:00
else if ( CMD ( " MODC " ) ) CmdMODC ( s ) ;
else if ( CMD ( " MODT " ) ) CmdMODT ( s ) ;
else if ( CMD ( " MOVC " ) ) CmdMOVC ( s ) ;
2013-10-21 07:55:23 +02:00
else if ( CMD ( " MOVR " ) ) CmdMOVR ( s ) ;
2000-09-17 09:36:50 +02:00
else if ( CMD ( " NEWC " ) ) CmdNEWC ( s ) ;
else if ( CMD ( " NEWT " ) ) CmdNEWT ( s ) ;
2001-09-01 09:53:01 +02:00
else if ( CMD ( " NEXT " ) ) CmdNEXT ( s ) ;
2015-05-22 13:44:43 +02:00
else if ( CMD ( " PING " ) ) CmdPING ( s ) ;
2005-08-28 10:45:19 +02:00
else if ( CMD ( " PLAY " ) ) CmdPLAY ( s ) ;
2005-08-27 16:42:28 +02:00
else if ( CMD ( " PLUG " ) ) CmdPLUG ( s ) ;
2015-09-01 11:14:27 +02:00
else if ( CMD ( " POLL " ) ) CmdPOLL ( s ) ;
2017-11-11 12:04:17 +01:00
else if ( CMD ( " PRIM " ) ) CmdPRIM ( s ) ;
2002-02-24 11:13:21 +01:00
else if ( CMD ( " PUTE " ) ) CmdPUTE ( s ) ;
2007-04-30 12:53:35 +02:00
else if ( CMD ( " REMO " ) ) CmdREMO ( s ) ;
2004-01-17 15:41:52 +01:00
else if ( CMD ( " SCAN " ) ) CmdSCAN ( s ) ;
2003-04-27 14:23:30 +02:00
else if ( CMD ( " STAT " ) ) CmdSTAT ( s ) ;
2011-12-04 13:58:33 +01:00
else if ( CMD ( " UPDR " ) ) CmdUPDR ( s ) ;
2002-03-09 10:07:40 +01:00
else if ( CMD ( " UPDT " ) ) CmdUPDT ( s ) ;
else if ( CMD ( " VOLU " ) ) CmdVOLU ( s ) ;
2006-08-06 09:20:04 +02:00
else if ( CMD ( " QUIT " ) ) Close ( true ) ;
2015-09-01 11:14:27 +02:00
else Reply ( 500 , " Command unrecognized: \" %s \" " , Cmd ) ;
2015-05-22 13:44:43 +02:00
}
2015-09-01 11:14:27 +02:00
bool cSVDRPServer : : Process ( void )
2015-05-22 13:44:43 +02:00
{
if ( file . IsOpen ( ) ) {
2015-09-01 11:14:27 +02:00
while ( file . Ready ( false ) ) {
unsigned char c ;
int r = safe_read ( file , & c , 1 ) ;
if ( r > 0 ) {
if ( c = = ' \n ' | | c = = 0x00 ) {
// strip trailing whitespace:
while ( numChars > 0 & & strchr ( " \t \r \n " , cmdLine [ numChars - 1 ] ) )
cmdLine [ - - numChars ] = 0 ;
// make sure the string is terminated:
cmdLine [ numChars ] = 0 ;
// showtime!
2018-02-20 13:28:04 +01:00
dbgsvdrp ( " < S %s: %s \n " , * clientName , cmdLine ) ;
2015-09-01 11:14:27 +02:00
Execute ( cmdLine ) ;
numChars = 0 ;
if ( length > BUFSIZ ) {
free ( cmdLine ) ; // let's not tie up too much memory
length = BUFSIZ ;
cmdLine = MALLOC ( char , length ) ;
}
}
else if ( c = = 0x04 & & numChars = = 0 ) {
// end of file (only at beginning of line)
Close ( true ) ;
}
else if ( c = = 0x08 | | c = = 0x7F ) {
// backspace or delete (last character)
if ( numChars > 0 )
numChars - - ;
}
else if ( c < = 0x03 | | c = = 0x0D ) {
// ignore control characters
}
else {
if ( numChars > = length - 1 ) {
int NewLength = length + BUFSIZ ;
if ( char * NewBuffer = ( char * ) realloc ( cmdLine , NewLength ) ) {
length = NewLength ;
cmdLine = NewBuffer ;
2015-05-22 13:44:43 +02:00
}
2015-09-01 11:14:27 +02:00
else {
2018-02-20 13:28:04 +01:00
esyslog ( " SVDRP %s < %s ERROR: out of memory " , Setup . SVDRPHostName , * clientName ) ;
2015-09-01 11:14:27 +02:00
Close ( ) ;
break ;
}
}
cmdLine [ numChars + + ] = c ;
cmdLine [ numChars ] = 0 ;
}
lastActivity = time ( NULL ) ;
}
else if ( r < = 0 ) {
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s lost connection to client " , Setup . SVDRPHostName , * clientName ) ;
2015-09-01 11:14:27 +02:00
Close ( ) ;
}
}
if ( Setup . SVDRPTimeout & & time ( NULL ) - lastActivity > Setup . SVDRPTimeout ) {
2018-02-20 13:28:04 +01:00
isyslog ( " SVDRP %s < %s timeout on connection " , Setup . SVDRPHostName , * clientName ) ;
2015-09-01 11:14:27 +02:00
Close ( true , true ) ;
}
2015-05-22 13:44:43 +02:00
}
return file . IsOpen ( ) ;
}
2015-09-08 11:08:06 +02:00
void SetSVDRPPorts ( int TcpPort , int UdpPort )
{
SVDRPTcpPort = TcpPort ;
SVDRPUdpPort = UdpPort ;
}
2015-09-01 11:14:27 +02:00
void SetSVDRPGrabImageDir ( const char * GrabImageDir )
2015-05-22 13:44:43 +02:00
{
2015-09-01 11:14:27 +02:00
grabImageDir = GrabImageDir ;
2015-05-22 13:44:43 +02:00
}
// --- cSVDRPServerHandler ---------------------------------------------------
class cSVDRPServerHandler : public cThread {
private :
bool ready ;
cSocket tcpSocket ;
cVector < cSVDRPServer * > serverConnections ;
void HandleServerConnection ( void ) ;
2015-04-29 13:10:06 +02:00
void ProcessConnections ( void ) ;
protected :
virtual void Action ( void ) ;
public :
2015-05-22 13:44:43 +02:00
cSVDRPServerHandler ( int TcpPort ) ;
virtual ~ cSVDRPServerHandler ( ) ;
void WaitUntilReady ( void ) ;
2015-04-29 13:10:06 +02:00
} ;
2015-09-01 11:14:27 +02:00
static cSVDRPServerHandler * SVDRPServerHandler = NULL ;
2015-05-22 13:44:43 +02:00
cSVDRPServerHandler : : cSVDRPServerHandler ( int TcpPort )
: cThread ( " SVDRP server handler " , true )
, tcpSocket ( TcpPort , true )
2015-04-29 13:10:06 +02:00
{
2015-05-22 13:44:43 +02:00
ready = false ;
2015-04-29 13:10:06 +02:00
}
2015-05-22 13:44:43 +02:00
cSVDRPServerHandler : : ~ cSVDRPServerHandler ( )
2015-04-29 13:10:06 +02:00
{
Cancel ( 3 ) ;
2015-05-22 13:44:43 +02:00
for ( int i = 0 ; i < serverConnections . Size ( ) ; i + + )
delete serverConnections [ i ] ;
2015-04-29 13:10:06 +02:00
}
2015-05-22 13:44:43 +02:00
void cSVDRPServerHandler : : WaitUntilReady ( void )
2015-04-29 13:10:06 +02:00
{
2015-05-22 13:44:43 +02:00
cTimeMs Timeout ( 3000 ) ;
while ( ! ready & & ! Timeout . TimedOut ( ) )
cCondWait : : SleepMs ( 10 ) ;
}
void cSVDRPServerHandler : : ProcessConnections ( void )
{
for ( int i = 0 ; i < serverConnections . Size ( ) ; i + + ) {
if ( ! serverConnections [ i ] - > Process ( ) ) {
delete serverConnections [ i ] ;
serverConnections . Remove ( i ) ;
i - - ;
2015-04-29 13:10:06 +02:00
}
}
}
2015-05-22 13:44:43 +02:00
void cSVDRPServerHandler : : HandleServerConnection ( void )
{
int NewSocket = tcpSocket . Accept ( ) ;
if ( NewSocket > = 0 )
2018-02-20 13:28:04 +01:00
serverConnections . Append ( new cSVDRPServer ( NewSocket , tcpSocket . LastIpAddress ( ) ) ) ;
2015-05-22 13:44:43 +02:00
}
void cSVDRPServerHandler : : Action ( void )
2015-04-29 13:10:06 +02:00
{
2015-05-22 13:44:43 +02:00
if ( tcpSocket . Listen ( ) ) {
SVDRPServerPoller . Add ( tcpSocket . Socket ( ) , false ) ;
ready = true ;
2015-04-29 13:10:06 +02:00
while ( Running ( ) ) {
2015-05-22 13:44:43 +02:00
SVDRPServerPoller . Poll ( 1000 ) ;
HandleServerConnection ( ) ;
2015-04-29 13:10:06 +02:00
ProcessConnections ( ) ;
}
2015-05-22 13:44:43 +02:00
SVDRPServerPoller . Del ( tcpSocket . Socket ( ) , false ) ;
tcpSocket . Close ( ) ;
}
}
// --- SVDRP Handler ---------------------------------------------------------
static cMutex SVDRPHandlerMutex ;
2018-02-25 13:57:21 +01:00
void StartSVDRPHandler ( void )
2015-05-22 13:44:43 +02:00
{
cMutexLock MutexLock ( & SVDRPHandlerMutex ) ;
2018-02-25 13:57:21 +01:00
if ( SVDRPTcpPort ) {
if ( ! SVDRPServerHandler ) {
SVDRPServerHandler = new cSVDRPServerHandler ( SVDRPTcpPort ) ;
SVDRPServerHandler - > Start ( ) ;
SVDRPServerHandler - > WaitUntilReady ( ) ;
}
if ( Setup . SVDRPPeering & & SVDRPUdpPort & & ! SVDRPClientHandler ) {
SVDRPClientHandler = new cSVDRPClientHandler ( SVDRPTcpPort , SVDRPUdpPort ) ;
SVDRPClientHandler - > Start ( ) ;
}
2015-04-29 13:10:06 +02:00
}
2005-12-30 15:11:16 +01:00
}
2018-02-25 13:57:21 +01:00
void StopSVDRPHandler ( void )
2015-09-08 11:08:06 +02:00
{
cMutexLock MutexLock ( & SVDRPHandlerMutex ) ;
2015-05-22 13:44:43 +02:00
delete SVDRPClientHandler ;
SVDRPClientHandler = NULL ;
2018-02-25 13:57:21 +01:00
delete SVDRPServerHandler ;
SVDRPServerHandler = NULL ;
2015-05-22 13:44:43 +02:00
}
2018-03-17 10:07:19 +01:00
bool GetSVDRPServerNames ( cStringList * ServerNames )
2015-05-22 13:44:43 +02:00
{
2018-02-26 15:42:15 +01:00
bool Result = false ;
2015-05-22 13:44:43 +02:00
cMutexLock MutexLock ( & SVDRPHandlerMutex ) ;
2022-11-19 15:47:03 +01:00
if ( SVDRPClientHandler )
2018-03-17 10:07:19 +01:00
Result = SVDRPClientHandler - > GetServerNames ( ServerNames ) ;
2018-02-26 15:42:15 +01:00
return Result ;
2015-05-22 13:44:43 +02:00
}
2015-09-10 10:39:45 +02:00
bool ExecSVDRPCommand ( const char * ServerName , const char * Command , cStringList * Response )
2015-05-22 13:44:43 +02:00
{
2018-02-26 15:42:15 +01:00
bool Result = false ;
2015-05-22 13:44:43 +02:00
cMutexLock MutexLock ( & SVDRPHandlerMutex ) ;
2022-11-19 15:47:03 +01:00
if ( SVDRPClientHandler )
2018-02-26 15:42:15 +01:00
Result = SVDRPClientHandler - > Execute ( ServerName , Command , Response ) ;
return Result ;
2015-04-29 13:10:06 +02:00
}
2017-06-25 12:43:09 +02:00
void BroadcastSVDRPCommand ( const char * Command )
{
cMutexLock MutexLock ( & SVDRPHandlerMutex ) ;
cStringList ServerNames ;
if ( SVDRPClientHandler ) {
if ( SVDRPClientHandler - > GetServerNames ( & ServerNames ) ) {
for ( int i = 0 ; i < ServerNames . Size ( ) ; i + + )
ExecSVDRPCommand ( ServerNames [ i ] , Command ) ;
}
}
}