17 #include <arpa/inet.h> 22 #include <netinet/in.h> 27 #include <sys/socket.h> 45 #define dbgsvdrp(a...) if (DumpSVDRPDataTransfer) fprintf(stderr, a) 70 void Set(
const sockaddr *SockAddr);
93 const sockaddr_in *Addr = (sockaddr_in *)SockAddr;
94 Set(inet_ntoa(Addr->sin_addr), ntohs(Addr->sin_port));
99 #define MAXUDPBUF 1024 111 bool Connect(
const char *Address);
146 sock =
tcp ? socket(PF_INET, SOCK_STREAM, IPPROTO_IP) : socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
153 setsockopt(
sock, SOL_SOCKET, SO_REUSEADDR, &ReUseAddr,
sizeof(ReUseAddr));
156 memset(&Addr, 0,
sizeof(Addr));
157 Addr.sin_family = AF_INET;
158 Addr.sin_port = htons(
port);
160 if (bind(
sock, (sockaddr *)&Addr,
sizeof(Addr)) < 0) {
166 int Flags = fcntl(
sock, F_GETFL, 0);
172 if (fcntl(
sock, F_SETFL, Flags) < 0) {
178 if (listen(
sock, 1) < 0) {
192 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
199 memset(&Addr, 0,
sizeof(Addr));
200 Addr.sin_family = AF_INET;
201 Addr.sin_port = htons(
port);
202 Addr.sin_addr.s_addr = inet_addr(Address);
203 if (connect(
sock, (sockaddr *)&Addr,
sizeof(Addr)) < 0) {
209 int Flags = fcntl(
sock, F_GETFL, 0);
215 if (fcntl(
sock, F_SETFL, Flags) < 0) {
219 dbgsvdrp(
"> %s:%d server connection established\n", Address,
port);
229 int Socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
236 if (setsockopt(
Socket, SOL_SOCKET, SO_BROADCAST, &One,
sizeof(One)) < 0) {
243 memset(&Addr, 0,
sizeof(Addr));
244 Addr.sin_family = AF_INET;
245 Addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
246 Addr.sin_port = htons(
Port);
248 dbgsvdrp(
"> %s:%d %s\n", inet_ntoa(Addr.sin_addr),
Port, Dgram);
250 int Length = strlen(Dgram);
251 int Sent = sendto(
Socket, Dgram, Length, 0, (sockaddr *)&Addr,
sizeof(Addr));
255 return Sent == Length;
262 uint Size =
sizeof(Addr);
263 int NewSock = accept(
sock, (sockaddr *)&Addr, &Size);
267 const char *s =
"Access denied!\n";
268 if (write(NewSock, s, strlen(s)) < 0)
289 uint Size =
sizeof(Addr);
290 int NumBytes = recvfrom(
sock, buf,
sizeof(buf), 0, (sockaddr *)&Addr, &Size);
328 bool Send(
const char *Command);
335 bool HasAddress(
const char *Address,
int Port)
const;
347 :serverIpAddress(Address, Port)
353 timeout = Timeout * 1000 * 9 / 10;
403 #define SVDRPResonseTimeout 5000 // ms 410 if (c ==
'\n' || c == 0x00) {
412 while (numChars > 0 && strchr(
" \t\r\n",
input[numChars - 1]))
413 input[--numChars] = 0;
420 switch (atoi(
input)) {
421 case 220:
if (numChars > 4) {
423 if (
char *t = strchr(n,
' ')) {
440 if (numChars >= 4 &&
input[3] !=
'-')
445 if (numChars >=
length - 1) {
446 int NewLength =
length + BUFSIZ;
447 if (
char *NewBuffer = (
char *)realloc(
input, NewLength)) {
457 input[numChars++] = c;
472 else if (!Response && numChars == 0)
505 if (
Execute(
"LSTT ID", &Response)) {
506 for (
int i = 0; i < Response.
Size(); i++) {
507 char *s = Response[i];
549 if (Params && *Params) {
567 error =
"invalid timeout";
570 error =
"missing server timeout";
573 error =
"missing server apiversion";
576 error =
"missing server vdrversion";
579 error =
"missing server port";
582 error =
"missing server name";
585 error =
"missing server parameters";
603 virtual void Action(
void);
610 bool Execute(
const char *ServerName,
const char *Command,
cStringList *Response = NULL);
618 :
cThread(
"SVDRP client handler", true)
619 ,udpSocket(UdpPort, false)
666 bool TimersModified = Timers->StoreRemoteTimers(Client->
ServerName(), &RemoteTimers);
673 if (*PollTimersCmd) {
674 if (!Client->
Execute(PollTimersCmd))
708 if (ServerParams.
Ok())
735 return Client->Execute(Command, Response);
742 ServerNames->
Clear();
748 return ServerNames->
Size() > 0;
778 if ((
f = tmpfile()) != NULL) {
780 message =
"Enter EPG data, end with \".\" on a line by itself";
785 message =
"Error while opening temporary file";
798 if (strcmp(s,
".") != 0) {
808 message =
"EPG data processed";
812 message =
"Error while processing EPG data";
823 #define MAXHELPTOPIC 10 824 #define EITDISABLETIME 10 // seconds until EIT processing is enabled again after a CLRE command 828 "CHAN [ + | - | <number> | <name> | <id> ]\n" 829 " Switch channel up, down or to the given channel number, name or id.\n" 830 " Without option (or after successfully switching to the channel)\n" 831 " it returns the current channel number and name.",
832 "CLRE [ <number> | <name> | <id> ]\n" 833 " Clear the EPG list of the given channel number, name or id.\n" 834 " Without option it clears the entire EPG list.\n" 835 " After a CLRE command, no further EPG processing is done for 10\n" 836 " seconds, so that data sent with subsequent PUTE commands doesn't\n" 837 " interfere with data from the broadcasters.",
838 "CONN name:<name> port:<port> vdrversion:<vdrversion> apiversion:<apiversion> timeout:<timeout>\n" 839 " Used by peer-to-peer connections between VDRs to tell the other VDR\n" 840 " to establish a connection to this VDR. The name is the SVDRP host name\n" 841 " of this VDR, which may differ from its DNS name.",
845 " Delete the recording with the given id. Before a recording can be\n" 846 " deleted, an LSTR command should have been executed in order to retrieve\n" 847 " the recording ids. The ids are unique and don't change while this\n" 848 " instance of VDR is running.\n" 849 " CAUTION: THERE IS NO CONFIRMATION PROMPT WHEN DELETING A\n" 850 " RECORDING - BE SURE YOU KNOW WHAT YOU ARE DOING!",
852 " Delete the timer with the given id. If this timer is currently recording,\n" 853 " the recording will be stopped without any warning.",
855 " Edit the recording with the given id. Before a recording can be\n" 856 " edited, an LSTR command should have been executed in order to retrieve\n" 857 " the recording ids.",
858 "GRAB <filename> [ <quality> [ <sizex> <sizey> ] ]\n" 859 " Grab the current frame and save it to the given file. Images can\n" 860 " be stored as JPEG or PNM, depending on the given file name extension.\n" 861 " The quality of the grabbed image can be in the range 0..100, where 100\n" 862 " (the default) means \"best\" (only applies to JPEG). The size parameters\n" 863 " define the size of the resulting image (default is full screen).\n" 864 " If the file name is just an extension (.jpg, .jpeg or .pnm) the image\n" 865 " data will be sent to the SVDRP connection encoded in base64. The same\n" 866 " happens if '-' (a minus sign) is given as file name, in which case the\n" 867 " image format defaults to JPEG.",
869 " The HELP command gives help info.",
870 "HITK [ <key> ... ]\n" 871 " Hit the given remote control key. Without option a list of all\n" 872 " valid key names is given. If more than one key is given, they are\n" 873 " entered into the remote control queue in the given sequence. There\n" 874 " can be up to 31 keys.",
875 "LSTC [ :ids ] [ :groups | <number> | <name> | <id> ]\n" 876 " List channels. Without option, all channels are listed. Otherwise\n" 877 " only the given channel is listed. If a name is given, all channels\n" 878 " containing the given string as part of their name are listed.\n" 879 " If ':groups' is given, all channels are listed including group\n" 880 " separators. The channel number of a group separator is always 0.\n" 881 " With ':ids' the channel ids are listed following the channel numbers.\n" 882 " The special number 0 can be given to list the current channel.",
884 " List all available devices. Each device is listed with its name and\n" 885 " whether it is currently the primary device ('P') or it implements a\n" 886 " decoder ('D') and can be used as output device.",
887 "LSTE [ <channel> ] [ now | next | at <time> ]\n" 888 " List EPG data. Without any parameters all data of all channels is\n" 889 " listed. If a channel is given (either by number or by channel ID),\n" 890 " only data for that channel is listed. 'now', 'next', or 'at <time>'\n" 891 " restricts the returned data to present events, following events, or\n" 892 " events at the given time (which must be in time_t form).",
893 "LSTR [ <id> [ path ] ]\n" 894 " List recordings. Without option, all recordings are listed. Otherwise\n" 895 " the information for the given recording is listed. If a recording\n" 896 " id and the keyword 'path' is given, the actual file name of that\n" 897 " recording's directory is listed.\n" 898 " Note that the ids of the recordings are not necessarily given in\n" 900 "LSTT [ <id> ] [ id ]\n" 901 " List timers. Without option, all timers are listed. Otherwise\n" 902 " only the timer with the given id is listed. If the keyword 'id' is\n" 903 " given, the channels will be listed with their unique channel ids\n" 904 " instead of their numbers. This command lists only the timers that are\n" 905 " defined locally on this VDR, not any remote timers from other VDRs.",
907 " Displays the given message on the OSD. The message will be queued\n" 908 " and displayed whenever this is suitable.\n",
909 "MODC <number> <settings>\n" 910 " Modify a channel. Settings must be in the same format as returned\n" 911 " by the LSTC command.",
912 "MODT <id> on | off | <settings>\n" 913 " Modify a timer. Settings must be in the same format as returned\n" 914 " by the LSTT command. The special keywords 'on' and 'off' can be\n" 915 " used to easily activate or deactivate a timer.",
916 "MOVC <number> <to>\n" 917 " Move a channel to a new position.",
918 "MOVR <id> <new name>\n" 919 " Move the recording with the given id. Before a recording can be\n" 920 " moved, an LSTR command should have been executed in order to retrieve\n" 921 " the recording ids. The ids don't change during subsequent MOVR\n" 924 " Create a new channel. Settings must be in the same format as returned\n" 925 " by the LSTC command.",
927 " Create a new timer. Settings must be in the same format as returned\n" 928 " by the LSTT command.",
929 "NEXT [ abs | rel ]\n" 930 " Show the next timer event. If no option is given, the output will be\n" 931 " in human readable form. With option 'abs' the absolute time of the next\n" 932 " event will be given as the number of seconds since the epoch (time_t\n" 933 " format), while with option 'rel' the relative time will be given as the\n" 934 " number of seconds from now until the event. If the absolute time given\n" 935 " is smaller than the current time, or if the relative time is less than\n" 936 " zero, this means that the timer is currently recording and has started\n" 937 " at the given time. The first value in the resulting line is the id\n" 940 " Used by peer-to-peer connections between VDRs to keep the connection\n" 941 " from timing out. May be used at any time and simply returns a line of\n" 942 " the form '<hostname> is alive'.",
943 "PLAY <id> [ begin | <position> ]\n" 944 " Play the recording with the given id. Before a recording can be\n" 945 " played, an LSTR command should have been executed in order to retrieve\n" 946 " the recording ids.\n" 947 " The keyword 'begin' plays the recording from its very beginning, while\n" 948 " a <position> (given as hh:mm:ss[.ff] or framenumber) starts at that\n" 949 " position. If neither 'begin' nor a <position> are given, replay is resumed\n" 950 " at the position where any previous replay was stopped, or from the beginning\n" 951 " by default. To control or stop the replay session, use the usual remote\n" 952 " control keypresses via the HITK command.",
953 "PLUG <name> [ help | main ] [ <command> [ <options> ]]\n" 954 " Send a command to a plugin.\n" 955 " The PLUG command without any parameters lists all plugins.\n" 956 " If only a name is given, all commands known to that plugin are listed.\n" 957 " If a command is given (optionally followed by parameters), that command\n" 958 " is sent to the plugin, and the result will be displayed.\n" 959 " The keyword 'help' lists all the SVDRP commands known to the named plugin.\n" 960 " If 'help' is followed by a command, the detailed help for that command is\n" 961 " given. The keyword 'main' initiates a call to the main menu function of the\n" 963 "POLL <name> timers\n" 964 " Used by peer-to-peer connections between VDRs to inform other machines\n" 965 " about changes to timers. The receiving VDR shall use LSTT to query the\n" 966 " remote machine with the given name about its timers and update its list\n" 967 " of timers accordingly.\n",
968 "PRIM [ <number> ]\n" 969 " Make the device with the given number the primary device.\n" 970 " Without option it returns the currently active primary device in the same\n" 971 " format as used by the LSTD command.",
973 " Put data into the EPG list. The data entered has to strictly follow the\n" 974 " format defined in vdr(5) for the 'epg.data' file. A '.' on a line\n" 975 " by itself terminates the input and starts processing of the data (all\n" 976 " entered data is buffered until the terminating '.' is seen).\n" 977 " If a file name is given, epg data will be read from this file (which\n" 978 " must be accessible under the given name from the machine VDR is running\n" 979 " on). In case of file input, no terminating '.' shall be given.\n",
980 "REMO [ on | off ]\n" 981 " Turns the remote control on or off. Without a parameter, the current\n" 982 " status of the remote control is reported.",
984 " Forces an EPG scan. If this is a single DVB device system, the scan\n" 985 " will be done on the primary device unless it is currently recording.",
987 " Return information about disk usage (total, free, percent).",
989 " Updates a timer. Settings must be in the same format as returned\n" 990 " by the LSTT command. If a timer with the same channel, day, start\n" 991 " and stop time does not yet exist, it will be created.",
993 " Initiates a re-read of the recordings directory, which is the SVDRP\n" 994 " equivalent to 'touch .update'.",
995 "VOLU [ <number> | + | - | mute ]\n" 996 " Set the audio volume to the given number (which is limited to the range\n" 997 " 0...255). If the special options '+' or '-' are given, the volume will\n" 998 " be turned up or down, respectively. The option 'mute' will toggle the\n" 999 " audio muting. If no option is given, the current audio volume level will\n" 1002 " Exit vdr (SVDRP).\n" 1003 " You can also hit Ctrl-D to exit.",
1031 const char *q = HelpPage;
1034 uint n = q - HelpPage;
1035 if (n >=
sizeof(topic))
1036 n =
sizeof(topic) - 1;
1037 strncpy(topic, HelpPage, n);
1051 if (strcasecmp(Cmd, t) == 0)
1072 void Close(
bool SendReply =
false,
bool Timeout =
false);
1073 bool Send(
const char *s);
1074 void Reply(
int Code,
const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
1076 void CmdCHAN(const
char *Option);
1077 void CmdCLRE(const
char *Option);
1078 void CmdCONN(const
char *Option);
1079 void CmdDELC(const
char *Option);
1080 void CmdDELR(const
char *Option);
1081 void CmdDELT(const
char *Option);
1082 void CmdEDIT(const
char *Option);
1083 void CmdGRAB(const
char *Option);
1084 void CmdHELP(const
char *Option);
1085 void CmdHITK(const
char *Option);
1086 void CmdLSTC(const
char *Option);
1087 void CmdLSTD(const
char *Option);
1088 void CmdLSTE(const
char *Option);
1089 void CmdLSTR(const
char *Option);
1090 void CmdLSTT(const
char *Option);
1091 void CmdMESG(const
char *Option);
1092 void CmdMODC(const
char *Option);
1093 void CmdMODT(const
char *Option);
1094 void CmdMOVC(const
char *Option);
1095 void CmdMOVR(const
char *Option);
1096 void CmdNEWC(const
char *Option);
1097 void CmdNEWT(const
char *Option);
1098 void CmdNEXT(const
char *Option);
1099 void CmdPING(const
char *Option);
1100 void CmdPLAY(const
char *Option);
1101 void CmdPLUG(const
char *Option);
1102 void CmdPOLL(const
char *Option);
1103 void CmdPRIM(const
char *Option);
1104 void CmdPUTE(const
char *Option);
1105 void CmdREMO(const
char *Option);
1106 void CmdSCAN(const
char *Option);
1107 void CmdSTAT(const
char *Option);
1108 void CmdUPDT(const
char *Option);
1109 void CmdUPDR(const
char *Option);
1110 void CmdVOLU(const
char *Option);
1133 time_t now = time(NULL);
1176 char *buffer = NULL;
1179 if (vasprintf(&buffer, fmt, ap) >= 0) {
1182 char *n = strchr(s,
'\n');
1186 if (Code < 0 || n && *(n + 1))
1190 s = n ? n + 1 : NULL;
1194 Reply(451,
"Bad format - looks like a programming error!");
1201 Reply(451,
"Zero return code - looks like a programming error!");
1217 const int TopicsPerLine = 5;
1219 for (
int y = 0; (y * TopicsPerLine + x) < NumPages; y++) {
1222 q += sprintf(q,
" ");
1223 for (x = 0; x < TopicsPerLine && (y * TopicsPerLine + x) < NumPages; x++) {
1224 const char *topic =
GetHelpTopic(hp[(y * TopicsPerLine + x)]);
1229 Reply(-214,
"%s", buffer);
1240 int o = strtol(Option, NULL, 10);
1244 else if (strcmp(Option,
"-") == 0) {
1251 else if (strcmp(Option,
"+") == 0) {
1259 n = Channel->Number();
1261 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1262 if (!Channel->GroupSep()) {
1263 if (strcasecmp(Channel->Name(), Option) == 0) {
1264 n = Channel->Number();
1271 Reply(501,
"Undefined channel \"%s\"", Option);
1275 if (
const cChannel *Channel = Channels->GetByNumber(n)) {
1277 Reply(554,
"Error switching to channel \"%d\"", Channel->Number());
1282 Reply(550,
"Unable to find channel \"%s\"", Option);
1290 Reply(250,
"%d %s", Channel->Number(), Channel->Name());
1302 int o = strtol(Option, NULL, 10);
1304 ChannelID = Channels->GetByNumber(o)->GetChannelID();
1309 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1310 if (!Channel->GroupSep()) {
1311 if (strcasecmp(Channel->Name(), Option) == 0) {
1312 ChannelID = Channel->GetChannelID();
1323 for (
cSchedule *p = Schedules->First(); p; p = Schedules->
Next(p)) {
1324 if (p->ChannelID() == ChannelID) {
1330 for (
cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
1331 if (ChannelID == Timer->Channel()->GetChannelID().
ClrRid())
1332 Timer->SetEvent(NULL);
1336 Reply(250,
"EPG data of channel \"%s\" cleared", Option);
1339 Reply(550,
"No EPG data found for channel \"%s\"", Option);
1344 Reply(501,
"Undefined channel \"%s\"", Option);
1349 for (
cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer))
1350 Timer->SetEvent(NULL);
1351 for (
cSchedule *Schedule = Schedules->First(); Schedule; Schedule = Schedules->
Next(Schedule))
1352 Schedule->Cleanup(INT_MAX);
1354 Reply(250,
"EPG data cleared");
1363 if (ServerParams.
Ok()) {
1369 Reply(501,
"Error in server parameters: %s", ServerParams.
Error());
1372 Reply(451,
"No SVDRP client handler");
1375 Reply(501,
"Missing server parameters");
1384 Channels->SetExplicitModify();
1385 if (
cChannel *Channel = Channels->GetByNumber(strtol(Option, NULL, 10))) {
1386 if (
const cTimer *Timer = Timers->UsesChannel(Channel)) {
1387 Reply(550,
"Channel \"%s\" is in use by timer %s", Option, *Timer->ToDescr());
1391 cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr);
1392 if (CurrentChannel && Channel == CurrentChannel) {
1393 int n = Channels->GetNextNormal(CurrentChannel->
Index());
1395 n = Channels->GetPrevNormal(CurrentChannel->
Index());
1397 Reply(501,
"Can't delete channel \"%s\" - list would be empty", Option);
1400 CurrentChannel = Channels->Get(n);
1401 CurrentChannelNr = 0;
1403 Channels->Del(Channel);
1404 Channels->ReNumber();
1405 Channels->SetModifiedByUser();
1406 Channels->SetModified();
1408 if (CurrentChannel && CurrentChannel->
Number() != CurrentChannelNr) {
1410 Channels->SwitchTo(CurrentChannel->
Number());
1414 Reply(250,
"Channel \"%s\" deleted", Option);
1417 Reply(501,
"Channel \"%s\" not defined", Option);
1420 Reply(501,
"Error in channel number \"%s\"", Option);
1423 Reply(501,
"Missing channel number");
1432 return cString::sprintf(
"Recording \"%s\" is being replayed", RecordingId);
1433 else if ((Reason &
ruCut) != 0)
1436 return cString::sprintf(
"Recording \"%s\" is being copied/moved", RecordingId);
1447 Recordings->SetExplicitModify();
1448 if (
cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1449 if (
int RecordingInUse = Recording->IsInUse())
1452 if (Recording->Delete()) {
1453 Recordings->DelByName(Recording->FileName());
1454 Recordings->SetModified();
1456 Reply(250,
"Recording \"%s\" deleted", Option);
1459 Reply(554,
"Error while deleting recording!");
1463 Reply(550,
"Recording \"%s\" not found", Option);
1466 Reply(501,
"Error in recording id \"%s\"", Option);
1469 Reply(501,
"Missing recording id");
1477 Timers->SetExplicitModify();
1478 if (
cTimer *Timer = Timers->GetById(strtol(Option, NULL, 10))) {
1479 if (Timer->Recording()) {
1484 Timers->SetModified();
1486 Reply(250,
"Timer \"%s\" deleted", Option);
1489 Reply(501,
"Timer \"%s\" not defined", Option);
1492 Reply(501,
"Error in timer number \"%s\"", Option);
1495 Reply(501,
"Missing timer number");
1503 if (
const cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1505 if (Marks.
Load(Recording->FileName(), Recording->FramesPerSecond(), Recording->IsPesRecording()) && Marks.
Count()) {
1507 Reply(250,
"Editing recording \"%s\" [%s]", Option, Recording->Title());
1509 Reply(554,
"Can't start editing process");
1512 Reply(554,
"No editing marks defined");
1515 Reply(550,
"Recording \"%s\" not found", Option);
1518 Reply(501,
"Error in recording id \"%s\"", Option);
1521 Reply(501,
"Missing recording id");
1526 const char *FileName = NULL;
1528 int Quality = -1, SizeX = -1, SizeY = -1;
1530 char buf[strlen(Option) + 1];
1531 char *p = strcpy(buf, Option);
1532 const char *delim =
" \t";
1534 FileName = strtok_r(p, delim, &strtok_next);
1536 const char *Extension = strrchr(FileName,
'.');
1538 if (strcasecmp(Extension,
".jpg") == 0 || strcasecmp(Extension,
".jpeg") == 0)
1540 else if (strcasecmp(Extension,
".pnm") == 0)
1543 Reply(501,
"Unknown image type \"%s\"", Extension + 1);
1546 if (Extension == FileName)
1549 else if (strcmp(FileName,
"-") == 0)
1552 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1553 if (strcasecmp(p,
"JPEG") == 0 || strcasecmp(p,
"PNM") == 0) {
1555 p = strtok_r(NULL, delim, &strtok_next);
1561 Reply(501,
"Invalid quality \"%s\"", p);
1567 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1571 Reply(501,
"Invalid sizex \"%s\"", p);
1574 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1578 Reply(501,
"Invalid sizey \"%s\"", p);
1583 Reply(501,
"Missing sizey");
1587 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1588 Reply(501,
"Unexpected parameter \"%s\"", p);
1592 char RealFileName[PATH_MAX];
1597 const char *slash = strrchr(FileName,
'/');
1602 slash = strrchr(FileName,
'/');
1605 char *r = realpath(t, RealFileName);
1608 Reply(501,
"Invalid file name \"%s\"", FileName);
1611 strcat(RealFileName, slash);
1612 FileName = RealFileName;
1614 Reply(501,
"Invalid file name \"%s\"", FileName);
1619 Reply(550,
"Grabbing to file not allowed (use \"GRAB -\" instead)");
1628 int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE);
1630 if (
safe_write(fd, Image, ImageSize) == ImageSize) {
1632 Reply(250,
"Grabbed image %s", Option);
1636 Reply(451,
"Can't write to '%s'", FileName);
1642 Reply(451,
"Can't open '%s'", FileName);
1648 while ((s = Base64.
NextLine()) != NULL)
1649 Reply(-216,
"%s", s);
1650 Reply(216,
"Grabbed image %s", Option);
1655 Reply(451,
"Grab image failed");
1658 Reply(501,
"Missing filename");
1666 Reply(-214,
"%s", hp);
1668 Reply(504,
"HELP topic \"%s\" unknown", Option);
1674 Reply(-214,
"Topics:");
1683 Reply(-214,
"To report bugs in the implementation send email to");
1684 Reply(-214,
" vdr-bugs@tvdr.de");
1686 Reply(214,
"End of HELP info");
1693 Reply(550,
"Remote control currently disabled (key \"%s\" discarded)", Option);
1696 char buf[strlen(Option) + 1];
1697 strcpy(buf, Option);
1698 const char *delim =
" \t";
1700 char *p = strtok_r(buf, delim, &strtok_next);
1706 Reply(451,
"Too many keys in \"%s\" (only %d accepted)", Option, NumKeys);
1711 Reply(504,
"Unknown key: \"%s\"", p);
1715 p = strtok_r(NULL, delim, &strtok_next);
1717 Reply(250,
"Key%s \"%s\" accepted", NumKeys > 1 ?
"s" :
"", Option);
1720 Reply(-214,
"Valid <key> names for the HITK command:");
1721 for (
int i = 0; i <
kNone; i++) {
1724 Reply(214,
"End of key list");
1731 bool WithChannelIds =
startswith(Option,
":ids") && (Option[4] ==
' ' || Option[4] == 0);
1734 bool WithGroupSeps = strcasecmp(Option,
":groups") == 0;
1735 if (*Option && !WithGroupSeps) {
1737 int n = strtol(Option, NULL, 10);
1740 if (
const cChannel *Channel = Channels->GetByNumber(n))
1741 Reply(250,
"%d%s%s %s", Channel->Number(), WithChannelIds ?
" " :
"", WithChannelIds ? *Channel->GetChannelID().ToString() :
"", *Channel->ToText());
1743 Reply(501,
"Channel \"%s\" not defined", Option);
1748 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1749 if (!Channel->GroupSep()) {
1750 if (strcasestr(Channel->Name(), Option)) {
1761 Reply(501,
"Channel \"%s\" not defined", Option);
1765 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1767 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());
1768 else if (!Channel->GroupSep())
1769 Reply(Channel->Number() <
cChannels::MaxNumber() ? -250 : 250,
"%d%s%s %s", Channel->Number(), WithChannelIds ?
" " :
"", WithChannelIds ? *Channel->GetChannelID().ToString() :
"", *Channel->ToText());
1773 Reply(550,
"No channels defined");
1781 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());
1785 Reply(550,
"No devices found");
1796 char buf[strlen(Option) + 1];
1797 strcpy(buf, Option);
1798 const char *delim =
" \t";
1800 char *p = strtok_r(buf, delim, &strtok_next);
1801 while (p && DumpMode ==
dmAll) {
1802 if (strcasecmp(p,
"NOW") == 0)
1804 else if (strcasecmp(p,
"NEXT") == 0)
1806 else if (strcasecmp(p,
"AT") == 0) {
1808 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1810 AtTime = strtol(p, NULL, 10);
1812 Reply(501,
"Invalid time");
1817 Reply(501,
"Missing time");
1821 else if (!Schedule) {
1824 Channel = Channels->GetByNumber(strtol(Option, NULL, 10));
1828 Schedule = Schedules->GetSchedule(Channel);
1830 Reply(550,
"No schedule found");
1835 Reply(550,
"Channel \"%s\" not defined", p);
1840 Reply(501,
"Unknown option: \"%s\"", p);
1843 p = strtok_r(NULL, delim, &strtok_next);
1848 FILE *f = fdopen(fd,
"w");
1851 Schedule->
Dump(Channels, f,
"215-", DumpMode, AtTime);
1853 Schedules->Dump(f,
"215-", DumpMode, AtTime);
1855 Reply(215,
"End of EPG data");
1859 Reply(451,
"Can't open file connection");
1864 Reply(451,
"Can't dup stream descriptor");
1873 char buf[strlen(Option) + 1];
1874 strcpy(buf, Option);
1875 const char *delim =
" \t";
1877 char *p = strtok_r(buf, delim, &strtok_next);
1881 Number = strtol(p, NULL, 10);
1883 Reply(501,
"Error in recording id \"%s\"", Option);
1887 else if (strcasecmp(p,
"PATH") == 0)
1890 Reply(501,
"Unknown option: \"%s\"", p);
1893 p = strtok_r(NULL, delim, &strtok_next);
1896 if (
const cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1897 FILE *f = fdopen(
file,
"w");
1900 Reply(250,
"%s", Recording->FileName());
1902 Recording->Info()->Write(f,
"215-");
1904 Reply(215,
"End of recording information");
1909 Reply(451,
"Can't open file connection");
1912 Reply(550,
"Recording \"%s\" not found", Option);
1915 else if (Recordings->Count()) {
1916 const cRecording *Recording = Recordings->First();
1918 Reply(Recording == Recordings->Last() ? 250 : -250,
"%d %s", Recording->
Id(), Recording->
Title(
' ',
true));
1919 Recording = Recordings->
Next(Recording);
1923 Reply(550,
"No recordings available");
1929 bool UseChannelId =
false;
1931 char buf[strlen(Option) + 1];
1932 strcpy(buf, Option);
1933 const char *delim =
" \t";
1935 char *p = strtok_r(buf, delim, &strtok_next);
1938 Id = strtol(p, NULL, 10);
1939 else if (strcasecmp(p,
"ID") == 0)
1940 UseChannelId =
true;
1942 Reply(501,
"Unknown option: \"%s\"", p);
1945 p = strtok_r(NULL, delim, &strtok_next);
1950 for (
const cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
1951 if (!Timer->Remote()) {
1952 if (Timer->Id() == Id) {
1953 Reply(250,
"%d %s", Timer->Id(), *Timer->ToText(UseChannelId));
1958 Reply(501,
"Timer \"%s\" not defined", Option);
1962 const cTimer *LastLocalTimer = Timers->Last();
1963 while (LastLocalTimer) {
1964 if (LastLocalTimer->
Remote())
1965 LastLocalTimer = Timers->
Prev(LastLocalTimer);
1969 if (LastLocalTimer) {
1970 for (
const cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
1971 if (!Timer->Remote())
1972 Reply(Timer != LastLocalTimer ? -250 : 250,
"%d %s", Timer->
Id(), *Timer->ToText(UseChannelId));
1973 if (Timer == LastLocalTimer)
1979 Reply(550,
"No timers defined");
1987 Reply(250,
"Message queued");
1990 Reply(501,
"Missing message");
1997 int n = strtol(Option, &tail, 10);
1998 if (tail && tail != Option) {
2001 Channels->SetExplicitModify();
2002 if (
cChannel *Channel = Channels->GetByNumber(n)) {
2004 if (ch.
Parse(tail)) {
2005 if (Channels->HasUniqueChannelID(&ch, Channel)) {
2007 Channels->ReNumber();
2008 Channels->SetModifiedByUser();
2009 Channels->SetModified();
2011 Reply(250,
"%d %s", Channel->Number(), *Channel->ToText());
2014 Reply(501,
"Channel settings are not unique");
2017 Reply(501,
"Error in channel settings");
2020 Reply(501,
"Channel \"%d\" not defined", n);
2023 Reply(501,
"Error in channel number");
2026 Reply(501,
"Missing channel settings");
2033 int Id = strtol(Option, &tail, 10);
2034 if (tail && tail != Option) {
2037 Timers->SetExplicitModify();
2038 if (
cTimer *Timer = Timers->GetById(Id)) {
2041 if (strcasecmp(tail,
"ON") == 0)
2043 else if (strcasecmp(tail,
"OFF") == 0)
2045 else if (!t.
Parse(tail)) {
2046 Reply(501,
"Error in timer settings");
2054 Timers->SetModified();
2056 Reply(250,
"%d %s", Timer->Id(), *Timer->ToText(
true));
2059 Reply(501,
"Timer \"%d\" not defined", Id);
2062 Reply(501,
"Error in timer id");
2065 Reply(501,
"Missing timer settings");
2072 int From = strtol(Option, &tail, 10);
2073 if (tail && tail != Option) {
2075 if (tail && tail != Option) {
2078 Channels->SetExplicitModify();
2079 int To = strtol(tail, NULL, 10);
2081 const cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr);
2082 cChannel *FromChannel = Channels->GetByNumber(From);
2084 cChannel *ToChannel = Channels->GetByNumber(To);
2086 int FromNumber = FromChannel->
Number();
2087 int ToNumber = ToChannel->
Number();
2088 if (FromNumber != ToNumber) {
2089 Channels->Move(FromChannel, ToChannel);
2090 Channels->ReNumber();
2091 Channels->SetModifiedByUser();
2092 Channels->SetModified();
2093 if (CurrentChannel && CurrentChannel->
Number() != CurrentChannelNr) {
2095 Channels->SwitchTo(CurrentChannel->
Number());
2100 Reply(250,
"Channel \"%d\" moved to \"%d\"", From, To);
2103 Reply(501,
"Can't move channel to same position");
2106 Reply(501,
"Channel \"%d\" not defined", To);
2109 Reply(501,
"Channel \"%d\" not defined", From);
2112 Reply(501,
"Error in channel number");
2115 Reply(501,
"Error in channel number");
2118 Reply(501,
"Missing channel number");
2124 char *opt = strdup(Option);
2127 while (*option && !isspace(*option))
2133 Recordings->SetExplicitModify();
2134 if (
cRecording *Recording = Recordings->GetById(strtol(num, NULL, 10))) {
2135 if (
int RecordingInUse = Recording->IsInUse())
2141 cString oldName = Recording->Name();
2142 if ((Recording = Recordings->GetByName(Recording->FileName())) != NULL && Recording->ChangeName(option)) {
2143 Recordings->SetModified();
2144 Recordings->TouchUpdate();
2145 Reply(250,
"Recording \"%s\" moved to \"%s\"", *oldName, Recording->Name());
2148 Reply(554,
"Error while moving recording \"%s\" to \"%s\"!", *oldName, option);
2151 Reply(501,
"Missing new recording name");
2155 Reply(550,
"Recording \"%s\" not found", num);
2158 Reply(501,
"Error in recording id \"%s\"", num);
2162 Reply(501,
"Missing recording id");
2169 if (ch.
Parse(Option)) {
2171 Channels->SetExplicitModify();
2172 if (Channels->HasUniqueChannelID(&ch)) {
2175 Channels->Add(channel);
2176 Channels->ReNumber();
2177 Channels->SetModifiedByUser();
2178 Channels->SetModified();
2183 Reply(501,
"Channel settings are not unique");
2186 Reply(501,
"Error in channel settings");
2189 Reply(501,
"Missing channel settings");
2196 if (Timer->
Parse(Option)) {
2205 Reply(501,
"Error in timer settings");
2209 Reply(501,
"Missing timer settings");
2215 if (
const cTimer *t = Timers->GetNextActiveTimer()) {
2216 time_t Start = t->StartTime();
2220 else if (strcasecmp(Option,
"ABS") == 0)
2221 Reply(250,
"%d %ld", Id, Start);
2222 else if (strcasecmp(Option,
"REL") == 0)
2223 Reply(250,
"%d %ld", Id, Start - time(NULL));
2225 Reply(501,
"Unknown option: \"%s\"", Option);
2228 Reply(550,
"No active timers");
2239 char *opt = strdup(Option);
2242 while (*option && !isspace(*option))
2249 if (
const cRecording *Recording = Recordings->GetById(strtol(num, NULL, 10))) {
2250 cString FileName = Recording->FileName();
2251 cString Title = Recording->Title();
2252 int FramesPerSecond = Recording->FramesPerSecond();
2253 bool IsPesRecording = Recording->IsPesRecording();
2261 if (strcasecmp(option,
"BEGIN") != 0)
2272 Reply(250,
"Playing recording \"%s\" [%s]", num, *Title);
2276 Reply(550,
"Recording \"%s\" not found", num);
2281 Reply(501,
"Error in recording id \"%s\"", num);
2285 Reply(501,
"Missing recording id");
2291 char *opt = strdup(Option);
2293 char *option = name;
2294 while (*option && !isspace(*option))
2303 while (*option && !isspace(*option))
2309 if (!*cmd || strcasecmp(cmd,
"HELP") == 0) {
2310 if (*cmd && *option) {
2313 Reply(-214,
"%s", hp);
2314 Reply(214,
"End of HELP info");
2317 Reply(504,
"HELP topic \"%s\" for plugin \"%s\" unknown", option, plugin->
Name());
2323 Reply(-214,
"SVDRP commands:");
2325 Reply(214,
"End of HELP info");
2328 Reply(214,
"This plugin has no SVDRP commands");
2331 else if (strcasecmp(cmd,
"MAIN") == 0) {
2333 Reply(250,
"Initiated call to main menu function of plugin \"%s\"", plugin->
Name());
2335 Reply(550,
"A plugin call is already pending - please try again later");
2338 int ReplyCode = 900;
2341 Reply(abs(ReplyCode),
"%s", *s);
2343 Reply(500,
"Command unrecognized: \"%s\"", cmd);
2347 Reply(550,
"Plugin \"%s\" not found (use PLUG for a list of plugins)", name);
2351 Reply(-214,
"Available plugins:");
2355 Reply(214,
"End of plugin list");
2362 char buf[strlen(Option) + 1];
2363 char *p = strcpy(buf, Option);
2364 const char *delim =
" \t";
2366 char *RemoteName = strtok_r(p, delim, &strtok_next);
2367 char *ListName = strtok_r(NULL, delim, &strtok_next);
2370 if (strcasecmp(ListName,
"timers") == 0) {
2374 Reply(501,
"No connection to \"%s\"", RemoteName);
2377 Reply(501,
"Unknown list name: \"%s\"", ListName);
2380 Reply(501,
"Missing list name");
2383 Reply(501,
"No SVDRP client connections");
2386 Reply(501,
"Missing parameters");
2394 int o = strtol(Option, NULL, 10);
2398 Reply(501,
"Invalid device number \"%s\"", Option);
2401 Reply(501,
"Invalid parameter \"%s\"", Option);
2404 Reply(250,
"Primary device set to %d", n);
2409 Reply(250,
"%d [%s%s] %s", d->DeviceNumber() + 1, d->HasDecoder() ?
"D" :
"-", d->DeviceNumber() + 1 ==
Setup.
PrimaryDVB ?
"P" :
"-", *d->DeviceName());
2411 Reply(501,
"Failed to get primary device");
2418 FILE *f = fopen(Option,
"r");
2422 Reply(250,
"EPG data processed from \"%s\"", Option);
2425 Reply(451,
"Error while processing EPG from \"%s\"", Option);
2429 Reply(501,
"Cannot open file \"%s\"", Option);
2443 if (!strcasecmp(Option,
"ON")) {
2445 Reply(250,
"Remote control enabled");
2447 else if (!strcasecmp(Option,
"OFF")) {
2449 Reply(250,
"Remote control disabled");
2452 Reply(501,
"Invalid Option \"%s\"", Option);
2461 Reply(250,
"EPG scan triggered");
2467 if (strcasecmp(Option,
"DISK") == 0) {
2470 Reply(250,
"%dMB %dMB %d%%", FreeMB + UsedMB, FreeMB, Percent);
2473 Reply(501,
"Invalid Option \"%s\"", Option);
2476 Reply(501,
"No option given");
2483 if (Timer->
Parse(Option)) {
2485 if (
cTimer *t = Timers->GetTimer(Timer)) {
2505 Reply(501,
"Error in timer settings");
2509 Reply(501,
"Missing timer settings");
2515 Recordings->Update(
false);
2516 Reply(250,
"Re-read of recordings directory triggered");
2524 else if (strcmp(Option,
"+") == 0)
2526 else if (strcmp(Option,
"-") == 0)
2528 else if (strcasecmp(Option,
"MUTE") == 0)
2531 Reply(501,
"Unknown option: \"%s\"", Option);
2536 Reply(250,
"Audio is mute");
2541 #define CMD(c) (strcasecmp(Cmd, c) == 0) 2558 while (*s && !isspace(*s))
2599 else Reply(500,
"Command unrecognized: \"%s\"", Cmd);
2609 if (c ==
'\n' || c == 0x00) {
2625 else if (c == 0x04 &&
numChars == 0) {
2629 else if (c == 0x08 || c == 0x7F) {
2634 else if (c <= 0x03 || c == 0x0D) {
2639 int NewLength =
length + BUFSIZ;
2640 if (
char *NewBuffer = (
char *)realloc(
cmdLine, NewLength)) {
2689 virtual void Action(
void);
2699 :
cThread(
"SVDRP server handler", true)
2700 ,tcpSocket(TcpPort, true)
2783 bool Result =
false;
2795 bool Result =
false;
2812 for (
int i = 0; i < ServerNames.
Size(); i++)
void CmdLSTT(const char *Option)
const char * Title(char Delimiter=' ', bool NewIndicator=false, int Level=-1) const
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
const char * Address(void) const
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
void CmdCONN(const char *Option)
void SetSVDRPPorts(int TcpPort, int UdpPort)
void CmdNEWT(const char *Option)
const char * Message(void)
void Close(bool SendReply=false, bool Timeout=false)
static tChannelID FromString(const char *s)
bool ToggleMute(void)
Turns the volume off or on and returns the new mute state.
bool Ready(bool Wait=true)
const char * Host(void) const
void CmdNEWC(const char *Option)
cSVDRPServerParams(const char *Params)
bool GetSVDRPServerNames(cStringList *ServerNames)
Gets a list of all available VDRs this VDR is connected to via SVDRP, and stores it in the given Serv...
bool TimedOut(void) const
Returns true if the last lock attempt this key was used with failed due to a timeout.
cString ToText(bool UseChannelID=false) const
static cString ToText(const cChannel *Channel)
void CmdSCAN(const char *Option)
virtual const char ** SVDRPHelpPages(void)
virtual const char * Version(void)=0
void CmdMESG(const char *Option)
void CmdMODC(const char *Option)
const char * FileName(void) const
Returns the full path name to the recording directory, including the video directory and the actual '...
static const char * SystemCharacterTable(void)
static void SetDisableUntil(time_t Time)
static cString sprintf(const char *fmt,...) __attribute__((format(printf
void CmdCLRE(const char *Option)
void CmdPRIM(const char *Option)
virtual void Append(T Data)
int QueueMessage(eMessageType Type, const char *s, int Seconds=0, int Timeout=0)
Like Message(), but this function may be called from a background thread.
static cMutex SVDRPHandlerMutex
void CmdVOLU(const char *Option)
cString ToDescr(void) const
void Reply(int Code, const char *fmt,...) __attribute__((format(printf
char SVDRPDefaultHost[HOST_NAME_MAX]
static eKeys FromString(const char *Name)
bool Add(int FileHandle, bool Out)
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
const int Port(void) const
cString & Truncate(int Index)
Truncate the string at the given Index (if Index is < 0 it is counted from the end of the string).
bool TriggerFetchingTimers(const char *ServerName)
const char * Connection(void) const
static const cRecordings * GetRecordingsRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of recordings for read access.
static cDevice * GetDevice(int Index)
Gets the device with the given Index.
bool Parse(const char *s)
const char * Name(void) const
const char * GetHelpTopic(const char *HelpPage)
const char * GetHelpPage(const char *Cmd, const char **p)
#define SVDRPResonseTimeout
static int NumDevices(void)
Returns the total number of devices.
static cString grabImageDir
bool Connected(void) const
bool GetRemoteTimers(cStringList &Response)
static bool SendDgram(const char *Dgram, int Port)
void CmdMOVR(const char *Option)
void CmdSTAT(const char *Option)
void Del(int FileHandle, bool Out)
void CmdNEXT(const char *Option)
const cIpAddress * LastIpAddress(void) const
#define LOCK_CHANNELS_WRITE
static int MaxNumber(void)
void SetFetchFlag(int Flag)
bool Poll(int TimeoutMs=0)
void CmdUPDR(const char *Option)
bool Execute(const char *ServerName, const char *Command, cStringList *Response=NULL)
void StopSVDRPHandler(void)
static cTimers * GetTimersWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for write access.
static void SetRecording(const char *FileName)
static int CurrentVolume(void)
void CmdLSTE(const char *Option)
void Dump(const cChannels *Channels, FILE *f, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0) const
void CmdCHAN(const char *Option)
void HandleClientConnection(void)
const int Timeout(void) const
virtual void Remove(int Index)
virtual const char * Description(void)=0
void CmdLSTD(const char *Option)
void CmdPING(const char *Option)
cPUTEhandler * PUTEhandler
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
bool GetServerNames(cStringList *ServerNames)
const char * ApiVersion(void) const
void CmdDELC(const char *Option)
void CmdGRAB(const char *Option)
void void PrintHelpTopics(const char **hp)
cSVDRPServer(int Socket, const cIpAddress *ClientIpAddress)
void HandleServerConnection(void)
cSVDRPServerHandler(int TcpPort)
bool Process(const char *s)
void SetVolume(int Volume, bool Absolute=false)
Sets the volume to the given value, either absolutely or relative to the current volume.
bool Parse(const char *s)
void StartSVDRPHandler(void)
const char * VdrVersion(void) const
cString ToString(void) const
bool Execute(const char *Command, cStringList *Response=NULL)
bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response)
Sends the given SVDRP Command string to the remote VDR identified by ServerName and collects all of t...
cIpAddress serverIpAddress
cListObject * Prev(void) const
bool SwitchChannel(const cChannel *Channel, bool LiveView)
Switches the device to the given Channel, initiating transfer mode if necessary.
static void SetEnabled(bool Enabled)
#define LOCK_CHANNELS_READ
#define LOCK_RECORDINGS_WRITE
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
void CmdUPDT(const char *Option)
void Cleanup(time_t Time)
void SetSVDRPGrabImageDir(const char *GrabImageDir)
const char * Error(void) const
void CmdPUTE(const char *Option)
void CmdHELP(const char *Option)
int HMSFToIndex(const char *HMSF, double FramesPerSecond)
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
static bool Read(FILE *f=NULL)
void WaitUntilReady(void)
#define LOCK_TIMERS_WRITE
static bool Process(cTimers *Timers, time_t t)
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
bool Open(const char *FileName, int Flags, mode_t Mode=DEFFILEMODE)
const char * NextLine(void)
Returns the next line of encoded data (terminated by '\0'), or NULL if there is no more encoded data.
static void Cleanup(bool Force=false)
virtual uchar * GrabImage(int &Size, bool Jpeg=true, int Quality=-1, int SizeX=-1, int SizeY=-1)
Grabs the currently visible screen image.
bool HasFetchFlag(int Flag)
void ProcessConnections(void)
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
cRecordingsHandler RecordingsHandler
bool Connect(const char *Address)
static cString RecordingInUseMessage(int Reason, const char *RecordingId, cRecording *Recording)
cStateKey StateKeySVDRPRemoteTimersPoll(true)
static void Launch(cControl *Control)
cSVDRPClientHandler(int TcpPort, int UdpPort)
void CmdPLAY(const char *Option)
static bool Enabled(void)
char SVDRPHostName[HOST_NAME_MAX]
void CmdREMO(const char *Option)
void CmdPOLL(const char *Option)
static int VideoDiskSpace(int *FreeMB=NULL, int *UsedMB=NULL)
void CmdHITK(const char *Option)
void ProcessConnections(void)
cSocket(int Port, bool Tcp)
cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout)
const char * ClientName(void) const
static cPoller SVDRPServerPoller
void CmdLSTR(const char *Option)
static cDevice * PrimaryDevice(void)
Returns the primary device.
void CmdPLUG(const char *Option)
#define LOCK_RECORDINGS_READ
tChannelID GetChannelID(void) const
void SetFlags(uint Flags)
cSVDRPClient * GetClientForServer(const char *ServerName)
void CmdMODT(const char *Option)
const char * ServerName(void) const
const char * Remote(void) const
virtual ~cSVDRPServerHandler()
bool Transferring(void) const
Returns true if we are currently in Transfer Mode.
cListObject * Next(void) const
static void SetCurrentChannel(const cChannel *Channel)
bool StoreRemoteTimers(const char *ServerName=NULL, const cStringList *RemoteTimers=NULL)
Stores the given list of RemoteTimers, which come from the VDR ServerName, in this list.
static cRecordControl * GetRecordControl(const char *FileName)
cVector< cSVDRPServer * > serverConnections
static cPlugin * GetPlugin(int Index)
void AddClient(cSVDRPServerParams &ServerParams, const char *IpAddress)
const char * Connection(void) const
bool Acceptable(in_addr_t Address)
void ClrFlags(uint Flags)
static const tChannelID InvalidID
bool Send(const char *Command)
bool Load(const char *RecordingFileName, double FramesPerSecond=DEFAULTFRAMESPERSECOND, bool IsPesRecording=false)
#define LOCK_SCHEDULES_WRITE
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
void BroadcastSVDRPCommand(const char *Command)
Sends the given SVDRP Command string to all remote VDRs.
cIpAddress clientIpAddress
virtual ~cSVDRPClientHandler()
bool HasAddress(const char *Address, int Port) const
static cSVDRPServerHandler * SVDRPServerHandler
#define LOCK_SCHEDULES_READ
cVector< cSVDRPClient * > clientConnections
void CmdDELR(const char *Option)
tChannelID & ClrRid(void)
static const cTimers * GetTimersRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for read access.
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
static bool DumpSVDRPDataTransfer
void CmdEDIT(const char *Option)
void Set(const char *Address, int Port)
static void Shutdown(void)
bool Replaying(void) const
Returns true if we are currently replaying.
static cSVDRPClientHandler * SVDRPClientHandler
void CmdDELT(const char *Option)
void CmdLSTC(const char *Option)
bool Add(int Usage, const char *FileNameSrc, const char *FileNameDst=NULL)
Adds the given FileNameSrc to the recordings handler for (later) processing.
void SortNumerically(void)
static const char * ToString(eKeys Key, bool Translate=false)
void CmdMOVC(const char *Option)
static cPoller SVDRPClientPoller
bool Process(cStringList *Response=NULL)
static bool CallPlugin(const char *Plugin)
Initiates calling the given plugin's main menu function.
int SVDRPCode(const char *s)
Returns the value of the three digit reply code of the given SVDRP response string.
bool TimedOut(void) const