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;
359 SVDRPClientPoller.
Add(
file,
false);
377 SVDRPClientPoller.
Del(
file,
false);
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())
721 SVDRPClientPoller.
Poll(1000);
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.",
842 "CPYR <number> <new name>\n"
843 " Copy the recording with the given number. Before a recording can be\n"
844 " copied, an LSTR command must have been executed in order to retrieve\n"
845 " the recording numbers.\n",
849 " Delete the recording with the given id. Before a recording can be\n"
850 " deleted, an LSTR command should have been executed in order to retrieve\n"
851 " the recording ids. The ids are unique and don't change while this\n"
852 " instance of VDR is running.\n"
853 " CAUTION: THERE IS NO CONFIRMATION PROMPT WHEN DELETING A\n"
854 " RECORDING - BE SURE YOU KNOW WHAT YOU ARE DOING!",
856 " Delete the timer with the given id. If this timer is currently recording,\n"
857 " the recording will be stopped without any warning.",
859 " Edit the recording with the given id. Before a recording can be\n"
860 " edited, an LSTR command should have been executed in order to retrieve\n"
861 " the recording ids.",
862 "GRAB <filename> [ <quality> [ <sizex> <sizey> ] ]\n"
863 " Grab the current frame and save it to the given file. Images can\n"
864 " be stored as JPEG or PNM, depending on the given file name extension.\n"
865 " The quality of the grabbed image can be in the range 0..100, where 100\n"
866 " (the default) means \"best\" (only applies to JPEG). The size parameters\n"
867 " define the size of the resulting image (default is full screen).\n"
868 " If the file name is just an extension (.jpg, .jpeg or .pnm) the image\n"
869 " data will be sent to the SVDRP connection encoded in base64. The same\n"
870 " happens if '-' (a minus sign) is given as file name, in which case the\n"
871 " image format defaults to JPEG.",
873 " The HELP command gives help info.",
874 "HITK [ <key> ... ]\n"
875 " Hit the given remote control key. Without option a list of all\n"
876 " valid key names is given. If more than one key is given, they are\n"
877 " entered into the remote control queue in the given sequence. There\n"
878 " can be up to 31 keys.",
879 "LSTC [ :ids ] [ :groups | <number> | <name> | <id> ]\n"
880 " List channels. Without option, all channels are listed. Otherwise\n"
881 " only the given channel is listed. If a name is given, all channels\n"
882 " containing the given string as part of their name are listed.\n"
883 " If ':groups' is given, all channels are listed including group\n"
884 " separators. The channel number of a group separator is always 0.\n"
885 " With ':ids' the channel ids are listed following the channel numbers.\n"
886 " The special number 0 can be given to list the current channel.",
888 " List all available devices. Each device is listed with its name and\n"
889 " whether it is currently the primary device ('P') or it implements a\n"
890 " decoder ('D') and can be used as output device.",
891 "LSTE [ <channel> ] [ now | next | at <time> ]\n"
892 " List EPG data. Without any parameters all data of all channels is\n"
893 " listed. If a channel is given (either by number or by channel ID),\n"
894 " only data for that channel is listed. 'now', 'next', or 'at <time>'\n"
895 " restricts the returned data to present events, following events, or\n"
896 " events at the given time (which must be in time_t form).",
897 "LSTR [ <id> [ path ] ]\n"
898 " List recordings. Without option, all recordings are listed. Otherwise\n"
899 " the information for the given recording is listed. If a recording\n"
900 " id and the keyword 'path' is given, the actual file name of that\n"
901 " recording's directory is listed.\n"
902 " Note that the ids of the recordings are not necessarily given in\n"
904 "LSTT [ <id> ] [ id ]\n"
905 " List timers. Without option, all timers are listed. Otherwise\n"
906 " only the timer with the given id is listed. If the keyword 'id' is\n"
907 " given, the channels will be listed with their unique channel ids\n"
908 " instead of their numbers. This command lists only the timers that are\n"
909 " defined locally on this VDR, not any remote timers from other VDRs.",
911 " Displays the given message on the OSD. The message will be queued\n"
912 " and displayed whenever this is suitable.\n",
913 "MODC <number> <settings>\n"
914 " Modify a channel. Settings must be in the same format as returned\n"
915 " by the LSTC command.",
916 "MODT <id> on | off | <settings>\n"
917 " Modify a timer. Settings must be in the same format as returned\n"
918 " by the LSTT command. The special keywords 'on' and 'off' can be\n"
919 " used to easily activate or deactivate a timer.",
920 "MOVC <number> <to>\n"
921 " Move a channel to a new position.",
922 "MOVR <id> <new name>\n"
923 " Move the recording with the given id. Before a recording can be\n"
924 " moved, an LSTR command should have been executed in order to retrieve\n"
925 " the recording ids. The ids don't change during subsequent MOVR\n"
928 " Create a new channel. Settings must be in the same format as returned\n"
929 " by the LSTC command.",
931 " Create a new timer. Settings must be in the same format as returned\n"
932 " by the LSTT command.",
933 "NEXT [ abs | rel ]\n"
934 " Show the next timer event. If no option is given, the output will be\n"
935 " in human readable form. With option 'abs' the absolute time of the next\n"
936 " event will be given as the number of seconds since the epoch (time_t\n"
937 " format), while with option 'rel' the relative time will be given as the\n"
938 " number of seconds from now until the event. If the absolute time given\n"
939 " is smaller than the current time, or if the relative time is less than\n"
940 " zero, this means that the timer is currently recording and has started\n"
941 " at the given time. The first value in the resulting line is the id\n"
944 " Used by peer-to-peer connections between VDRs to keep the connection\n"
945 " from timing out. May be used at any time and simply returns a line of\n"
946 " the form '<hostname> is alive'.",
947 "PLAY <id> [ begin | <position> ]\n"
948 " Play the recording with the given id. Before a recording can be\n"
949 " played, an LSTR command should have been executed in order to retrieve\n"
950 " the recording ids.\n"
951 " The keyword 'begin' plays the recording from its very beginning, while\n"
952 " a <position> (given as hh:mm:ss[.ff] or framenumber) starts at that\n"
953 " position. If neither 'begin' nor a <position> are given, replay is resumed\n"
954 " at the position where any previous replay was stopped, or from the beginning\n"
955 " by default. To control or stop the replay session, use the usual remote\n"
956 " control keypresses via the HITK command.",
957 "PLUG <name> [ help | main ] [ <command> [ <options> ]]\n"
958 " Send a command to a plugin.\n"
959 " The PLUG command without any parameters lists all plugins.\n"
960 " If only a name is given, all commands known to that plugin are listed.\n"
961 " If a command is given (optionally followed by parameters), that command\n"
962 " is sent to the plugin, and the result will be displayed.\n"
963 " The keyword 'help' lists all the SVDRP commands known to the named plugin.\n"
964 " If 'help' is followed by a command, the detailed help for that command is\n"
965 " given. The keyword 'main' initiates a call to the main menu function of the\n"
967 "POLL <name> timers\n"
968 " Used by peer-to-peer connections between VDRs to inform other machines\n"
969 " about changes to timers. The receiving VDR shall use LSTT to query the\n"
970 " remote machine with the given name about its timers and update its list\n"
971 " of timers accordingly.\n",
972 "PRIM [ <number> ]\n"
973 " Make the device with the given number the primary device.\n"
974 " Without option it returns the currently active primary device in the same\n"
975 " format as used by the LSTD command.",
977 " Put data into the EPG list. The data entered has to strictly follow the\n"
978 " format defined in vdr(5) for the 'epg.data' file. A '.' on a line\n"
979 " by itself terminates the input and starts processing of the data (all\n"
980 " entered data is buffered until the terminating '.' is seen).\n"
981 " If a file name is given, epg data will be read from this file (which\n"
982 " must be accessible under the given name from the machine VDR is running\n"
983 " on). In case of file input, no terminating '.' shall be given.\n",
984 "REMO [ on | off ]\n"
985 " Turns the remote control on or off. Without a parameter, the current\n"
986 " status of the remote control is reported.",
988 " Forces an EPG scan. If this is a single DVB device system, the scan\n"
989 " will be done on the primary device unless it is currently recording.",
991 " Return information about disk usage (total, free, percent).",
993 " Updates a timer. Settings must be in the same format as returned\n"
994 " by the LSTT command. If a timer with the same channel, day, start\n"
995 " and stop time does not yet exist, it will be created.",
997 " Initiates a re-read of the recordings directory, which is the SVDRP\n"
998 " equivalent to 'touch .update'.",
999 "VOLU [ <number> | + | - | mute ]\n"
1000 " Set the audio volume to the given number (which is limited to the range\n"
1001 " 0...255). If the special options '+' or '-' are given, the volume will\n"
1002 " be turned up or down, respectively. The option 'mute' will toggle the\n"
1003 " audio muting. If no option is given, the current audio volume level will\n"
1006 " Exit vdr (SVDRP).\n"
1007 " You can also hit Ctrl-D to exit.",
1035 const char *q = HelpPage;
1038 uint n = q - HelpPage;
1039 if (n >=
sizeof(topic))
1040 n =
sizeof(topic) - 1;
1041 strncpy(topic, HelpPage, n);
1055 if (strcasecmp(Cmd, t) == 0)
1076 void Close(
bool SendReply =
false,
bool Timeout =
false);
1077 bool Send(
const char *s);
1078 void Reply(
int Code,
const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
1080 void CmdCHAN(const
char *Option);
1081 void CmdCLRE(const
char *Option);
1082 void CmdCONN(const
char *Option);
1083 void CmdCPYR(const
char *Option);
1084 void CmdDELC(const
char *Option);
1085 void CmdDELR(const
char *Option);
1086 void CmdDELT(const
char *Option);
1087 void CmdEDIT(const
char *Option);
1088 void CmdGRAB(const
char *Option);
1089 void CmdHELP(const
char *Option);
1090 void CmdHITK(const
char *Option);
1091 void CmdLSTC(const
char *Option);
1092 void CmdLSTD(const
char *Option);
1093 void CmdLSTE(const
char *Option);
1094 void CmdLSTR(const
char *Option);
1095 void CmdLSTT(const
char *Option);
1096 void CmdMESG(const
char *Option);
1097 void CmdMODC(const
char *Option);
1098 void CmdMODT(const
char *Option);
1099 void CmdMOVC(const
char *Option);
1100 void CmdMOVR(const
char *Option);
1101 void CmdNEWC(const
char *Option);
1102 void CmdNEWT(const
char *Option);
1103 void CmdNEXT(const
char *Option);
1104 void CmdPING(const
char *Option);
1105 void CmdPLAY(const
char *Option);
1106 void CmdPLUG(const
char *Option);
1107 void CmdPOLL(const
char *Option);
1108 void CmdPRIM(const
char *Option);
1109 void CmdPUTE(const
char *Option);
1110 void CmdREMO(const
char *Option);
1111 void CmdSCAN(const
char *Option);
1112 void CmdSTAT(const
char *Option);
1113 void CmdUPDT(const
char *Option);
1114 void CmdUPDR(const
char *Option);
1115 void CmdVOLU(const
char *Option);
1138 time_t now = time(NULL);
1140 SVDRPServerPoller.
Add(
file,
false);
1159 SVDRPServerPoller.
Del(
file,
false);
1181 char *buffer = NULL;
1184 if (vasprintf(&buffer, fmt, ap) >= 0) {
1187 char *n = strchr(s,
'\n');
1191 if (Code < 0 || n && *(n + 1))
1195 s = n ? n + 1 : NULL;
1199 Reply(451,
"Bad format - looks like a programming error!");
1206 Reply(451,
"Zero return code - looks like a programming error!");
1222 const int TopicsPerLine = 5;
1224 for (
int y = 0; (y * TopicsPerLine + x) < NumPages; y++) {
1227 q += sprintf(q,
" ");
1228 for (x = 0; x < TopicsPerLine && (y * TopicsPerLine + x) < NumPages; x++) {
1229 const char *topic =
GetHelpTopic(hp[(y * TopicsPerLine + x)]);
1234 Reply(-214,
"%s", buffer);
1245 int o = strtol(Option, NULL, 10);
1249 else if (strcmp(Option,
"-") == 0) {
1256 else if (strcmp(Option,
"+") == 0) {
1264 n = Channel->Number();
1266 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1267 if (!Channel->GroupSep()) {
1268 if (strcasecmp(Channel->Name(), Option) == 0) {
1269 n = Channel->Number();
1276 Reply(501,
"Undefined channel \"%s\"", Option);
1280 if (
const cChannel *Channel = Channels->GetByNumber(n)) {
1282 Reply(554,
"Error switching to channel \"%d\"", Channel->Number());
1287 Reply(550,
"Unable to find channel \"%s\"", Option);
1295 Reply(250,
"%d %s", Channel->Number(), Channel->Name());
1307 int o = strtol(Option, NULL, 10);
1309 ChannelID = Channels->GetByNumber(o)->GetChannelID();
1314 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1315 if (!Channel->GroupSep()) {
1316 if (strcasecmp(Channel->Name(), Option) == 0) {
1317 ChannelID = Channel->GetChannelID();
1328 for (
cSchedule *p = Schedules->First(); p; p = Schedules->
Next(p)) {
1329 if (p->ChannelID() == ChannelID) {
1335 for (
cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
1336 if (ChannelID == Timer->Channel()->GetChannelID().
ClrRid())
1337 Timer->SetEvent(NULL);
1341 Reply(250,
"EPG data of channel \"%s\" cleared", Option);
1344 Reply(550,
"No EPG data found for channel \"%s\"", Option);
1349 Reply(501,
"Undefined channel \"%s\"", Option);
1354 for (
cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer))
1355 Timer->SetEvent(NULL);
1356 for (
cSchedule *Schedule = Schedules->First(); Schedule; Schedule = Schedules->
Next(Schedule))
1357 Schedule->Cleanup(INT_MAX);
1359 Reply(250,
"EPG data cleared");
1366 if (SVDRPClientHandler) {
1368 if (ServerParams.
Ok()) {
1374 Reply(501,
"Error in server parameters: %s", ServerParams.
Error());
1377 Reply(451,
"No SVDRP client handler");
1380 Reply(501,
"Missing server parameters");
1389 Channels->SetExplicitModify();
1390 if (
cChannel *Channel = Channels->GetByNumber(strtol(Option, NULL, 10))) {
1391 if (
const cTimer *Timer = Timers->UsesChannel(Channel)) {
1392 Reply(550,
"Channel \"%s\" is in use by timer %s", Option, *Timer->ToDescr());
1396 cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr);
1397 if (CurrentChannel && Channel == CurrentChannel) {
1398 int n = Channels->GetNextNormal(CurrentChannel->
Index());
1400 n = Channels->GetPrevNormal(CurrentChannel->
Index());
1402 Reply(501,
"Can't delete channel \"%s\" - list would be empty", Option);
1405 CurrentChannel = Channels->Get(n);
1406 CurrentChannelNr = 0;
1408 Channels->Del(Channel);
1409 Channels->ReNumber();
1410 Channels->SetModifiedByUser();
1411 Channels->SetModified();
1413 if (CurrentChannel && CurrentChannel->
Number() != CurrentChannelNr) {
1415 Channels->SwitchTo(CurrentChannel->
Number());
1419 Reply(250,
"Channel \"%s\" deleted", Option);
1422 Reply(501,
"Channel \"%s\" not defined", Option);
1425 Reply(501,
"Error in channel number \"%s\"", Option);
1428 Reply(501,
"Missing channel number");
1437 return cString::sprintf(
"Recording \"%s\" is being replayed", RecordingId);
1438 else if ((Reason &
ruCut) != 0)
1441 return cString::sprintf(
"Recording \"%s\" is being copied/moved", RecordingId);
1450 char *opt = strdup(Option);
1453 while (*option && !isspace(*option))
1459 Recordings->SetExplicitModify();
1460 if (
cRecording *Recording = Recordings->Get(strtol(num, NULL, 10) - 1)) {
1461 if (
int RecordingInUse = Recording->IsInUse())
1469 if (strcmp(newName, Recording->Name())) {
1474 Recordings->AddByName(fileName);
1475 Reply(250,
"Recording \"%s\" copied to \"%s\"", Recording->Name(), *newName);
1478 Reply(554,
"Error while copying recording \"%s\" to \"%s\"!", Recording->Name(), *newName);
1481 Reply(501,
"Identical new recording name");
1484 Reply(501,
"Missing new recording name");
1488 Reply(550,
"Recording \"%s\" not found", num);
1491 Reply(501,
"Error in recording number \"%s\"", num);
1495 Reply(501,
"Missing recording number");
1503 Recordings->SetExplicitModify();
1504 if (
cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1505 if (
int RecordingInUse = Recording->IsInUse())
1508 if (Recording->Delete()) {
1509 Recordings->DelByName(Recording->FileName());
1510 Recordings->SetModified();
1512 Reply(250,
"Recording \"%s\" deleted", Option);
1515 Reply(554,
"Error while deleting recording!");
1519 Reply(550,
"Recording \"%s\" not found", Option);
1522 Reply(501,
"Error in recording id \"%s\"", Option);
1525 Reply(501,
"Missing recording id");
1533 Timers->SetExplicitModify();
1534 if (
cTimer *Timer = Timers->GetById(strtol(Option, NULL, 10))) {
1535 if (Timer->Recording()) {
1540 Timers->SetModified();
1542 Reply(250,
"Timer \"%s\" deleted", Option);
1545 Reply(501,
"Timer \"%s\" not defined", Option);
1548 Reply(501,
"Error in timer number \"%s\"", Option);
1551 Reply(501,
"Missing timer number");
1559 if (
const cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1561 if (Marks.
Load(Recording->FileName(), Recording->FramesPerSecond(), Recording->IsPesRecording()) && Marks.
Count()) {
1563 Reply(250,
"Editing recording \"%s\" [%s]", Option, Recording->Title());
1565 Reply(554,
"Can't start editing process");
1568 Reply(554,
"No editing marks defined");
1571 Reply(550,
"Recording \"%s\" not found", Option);
1574 Reply(501,
"Error in recording id \"%s\"", Option);
1577 Reply(501,
"Missing recording id");
1582 const char *FileName = NULL;
1584 int Quality = -1, SizeX = -1, SizeY = -1;
1586 char buf[strlen(Option) + 1];
1587 char *p = strcpy(buf, Option);
1588 const char *delim =
" \t";
1590 FileName = strtok_r(p, delim, &strtok_next);
1592 const char *Extension = strrchr(FileName,
'.');
1594 if (strcasecmp(Extension,
".jpg") == 0 || strcasecmp(Extension,
".jpeg") == 0)
1596 else if (strcasecmp(Extension,
".pnm") == 0)
1599 Reply(501,
"Unknown image type \"%s\"", Extension + 1);
1602 if (Extension == FileName)
1605 else if (strcmp(FileName,
"-") == 0)
1608 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1609 if (strcasecmp(p,
"JPEG") == 0 || strcasecmp(p,
"PNM") == 0) {
1611 p = strtok_r(NULL, delim, &strtok_next);
1617 Reply(501,
"Invalid quality \"%s\"", p);
1623 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1627 Reply(501,
"Invalid sizex \"%s\"", p);
1630 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1634 Reply(501,
"Invalid sizey \"%s\"", p);
1639 Reply(501,
"Missing sizey");
1643 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1644 Reply(501,
"Unexpected parameter \"%s\"", p);
1648 char RealFileName[PATH_MAX];
1650 if (*grabImageDir) {
1653 const char *slash = strrchr(FileName,
'/');
1658 slash = strrchr(FileName,
'/');
1661 char *r = realpath(t, RealFileName);
1664 Reply(501,
"Invalid file name \"%s\"", FileName);
1667 strcat(RealFileName, slash);
1668 FileName = RealFileName;
1669 if (strncmp(FileName, grabImageDir, strlen(grabImageDir)) != 0) {
1670 Reply(501,
"Invalid file name \"%s\"", FileName);
1675 Reply(550,
"Grabbing to file not allowed (use \"GRAB -\" instead)");
1684 int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE);
1686 if (
safe_write(fd, Image, ImageSize) == ImageSize) {
1688 Reply(250,
"Grabbed image %s", Option);
1692 Reply(451,
"Can't write to '%s'", FileName);
1698 Reply(451,
"Can't open '%s'", FileName);
1704 while ((s = Base64.
NextLine()) != NULL)
1705 Reply(-216,
"%s", s);
1706 Reply(216,
"Grabbed image %s", Option);
1711 Reply(451,
"Grab image failed");
1714 Reply(501,
"Missing filename");
1722 Reply(-214,
"%s", hp);
1724 Reply(504,
"HELP topic \"%s\" unknown", Option);
1730 Reply(-214,
"Topics:");
1739 Reply(-214,
"To report bugs in the implementation send email to");
1740 Reply(-214,
" vdr-bugs@tvdr.de");
1742 Reply(214,
"End of HELP info");
1749 Reply(550,
"Remote control currently disabled (key \"%s\" discarded)", Option);
1752 char buf[strlen(Option) + 1];
1753 strcpy(buf, Option);
1754 const char *delim =
" \t";
1756 char *p = strtok_r(buf, delim, &strtok_next);
1762 Reply(451,
"Too many keys in \"%s\" (only %d accepted)", Option, NumKeys);
1767 Reply(504,
"Unknown key: \"%s\"", p);
1771 p = strtok_r(NULL, delim, &strtok_next);
1773 Reply(250,
"Key%s \"%s\" accepted", NumKeys > 1 ?
"s" :
"", Option);
1776 Reply(-214,
"Valid <key> names for the HITK command:");
1777 for (
int i = 0; i <
kNone; i++) {
1780 Reply(214,
"End of key list");
1787 bool WithChannelIds =
startswith(Option,
":ids") && (Option[4] ==
' ' || Option[4] == 0);
1790 bool WithGroupSeps = strcasecmp(Option,
":groups") == 0;
1791 if (*Option && !WithGroupSeps) {
1793 int n = strtol(Option, NULL, 10);
1796 if (
const cChannel *Channel = Channels->GetByNumber(n))
1797 Reply(250,
"%d%s%s %s", Channel->Number(), WithChannelIds ?
" " :
"", WithChannelIds ? *Channel->GetChannelID().ToString() :
"", *Channel->ToText());
1799 Reply(501,
"Channel \"%s\" not defined", Option);
1804 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1805 if (!Channel->GroupSep()) {
1806 if (strcasestr(Channel->Name(), Option)) {
1817 Reply(501,
"Channel \"%s\" not defined", Option);
1821 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1823 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());
1824 else if (!Channel->GroupSep())
1825 Reply(Channel->Number() <
cChannels::MaxNumber() ? -250 : 250,
"%d%s%s %s", Channel->Number(), WithChannelIds ?
" " :
"", WithChannelIds ? *Channel->GetChannelID().ToString() :
"", *Channel->ToText());
1829 Reply(550,
"No channels defined");
1837 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());
1841 Reply(550,
"No devices found");
1852 char buf[strlen(Option) + 1];
1853 strcpy(buf, Option);
1854 const char *delim =
" \t";
1856 char *p = strtok_r(buf, delim, &strtok_next);
1857 while (p && DumpMode ==
dmAll) {
1858 if (strcasecmp(p,
"NOW") == 0)
1860 else if (strcasecmp(p,
"NEXT") == 0)
1862 else if (strcasecmp(p,
"AT") == 0) {
1864 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1866 AtTime = strtol(p, NULL, 10);
1868 Reply(501,
"Invalid time");
1873 Reply(501,
"Missing time");
1877 else if (!Schedule) {
1880 Channel = Channels->GetByNumber(strtol(Option, NULL, 10));
1884 Schedule = Schedules->GetSchedule(Channel);
1886 Reply(550,
"No schedule found");
1891 Reply(550,
"Channel \"%s\" not defined", p);
1896 Reply(501,
"Unknown option: \"%s\"", p);
1899 p = strtok_r(NULL, delim, &strtok_next);
1904 FILE *f = fdopen(fd,
"w");
1907 Schedule->
Dump(Channels, f,
"215-", DumpMode, AtTime);
1909 Schedules->Dump(f,
"215-", DumpMode, AtTime);
1911 Reply(215,
"End of EPG data");
1915 Reply(451,
"Can't open file connection");
1920 Reply(451,
"Can't dup stream descriptor");
1929 char buf[strlen(Option) + 1];
1930 strcpy(buf, Option);
1931 const char *delim =
" \t";
1933 char *p = strtok_r(buf, delim, &strtok_next);
1937 Number = strtol(p, NULL, 10);
1939 Reply(501,
"Error in recording id \"%s\"", Option);
1943 else if (strcasecmp(p,
"PATH") == 0)
1946 Reply(501,
"Unknown option: \"%s\"", p);
1949 p = strtok_r(NULL, delim, &strtok_next);
1952 if (
const cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1953 FILE *f = fdopen(
file,
"w");
1956 Reply(250,
"%s", Recording->FileName());
1958 Recording->Info()->Write(f,
"215-");
1960 Reply(215,
"End of recording information");
1965 Reply(451,
"Can't open file connection");
1968 Reply(550,
"Recording \"%s\" not found", Option);
1971 else if (Recordings->Count()) {
1972 const cRecording *Recording = Recordings->First();
1974 Reply(Recording == Recordings->Last() ? 250 : -250,
"%d %s", Recording->
Id(), Recording->
Title(
' ',
true));
1975 Recording = Recordings->
Next(Recording);
1979 Reply(550,
"No recordings available");
1985 bool UseChannelId =
false;
1987 char buf[strlen(Option) + 1];
1988 strcpy(buf, Option);
1989 const char *delim =
" \t";
1991 char *p = strtok_r(buf, delim, &strtok_next);
1994 Id = strtol(p, NULL, 10);
1995 else if (strcasecmp(p,
"ID") == 0)
1996 UseChannelId =
true;
1998 Reply(501,
"Unknown option: \"%s\"", p);
2001 p = strtok_r(NULL, delim, &strtok_next);
2006 for (
const cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
2007 if (!Timer->Remote()) {
2008 if (Timer->Id() == Id) {
2009 Reply(250,
"%d %s", Timer->Id(), *Timer->ToText(UseChannelId));
2014 Reply(501,
"Timer \"%s\" not defined", Option);
2018 const cTimer *LastLocalTimer = Timers->Last();
2019 while (LastLocalTimer) {
2020 if (LastLocalTimer->
Remote())
2021 LastLocalTimer = Timers->
Prev(LastLocalTimer);
2025 if (LastLocalTimer) {
2026 for (
const cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
2027 if (!Timer->Remote())
2028 Reply(Timer != LastLocalTimer ? -250 : 250,
"%d %s", Timer->
Id(), *Timer->ToText(UseChannelId));
2029 if (Timer == LastLocalTimer)
2035 Reply(550,
"No timers defined");
2043 Reply(250,
"Message queued");
2046 Reply(501,
"Missing message");
2053 int n = strtol(Option, &tail, 10);
2054 if (tail && tail != Option) {
2057 Channels->SetExplicitModify();
2058 if (
cChannel *Channel = Channels->GetByNumber(n)) {
2060 if (ch.
Parse(tail)) {
2061 if (Channels->HasUniqueChannelID(&ch, Channel)) {
2063 Channels->ReNumber();
2064 Channels->SetModifiedByUser();
2065 Channels->SetModified();
2067 Reply(250,
"%d %s", Channel->Number(), *Channel->ToText());
2070 Reply(501,
"Channel settings are not unique");
2073 Reply(501,
"Error in channel settings");
2076 Reply(501,
"Channel \"%d\" not defined", n);
2079 Reply(501,
"Error in channel number");
2082 Reply(501,
"Missing channel settings");
2089 int Id = strtol(Option, &tail, 10);
2090 if (tail && tail != Option) {
2093 Timers->SetExplicitModify();
2094 if (
cTimer *Timer = Timers->GetById(Id)) {
2097 if (strcasecmp(tail,
"ON") == 0)
2099 else if (strcasecmp(tail,
"OFF") == 0)
2101 else if (!t.
Parse(tail)) {
2102 Reply(501,
"Error in timer settings");
2110 Timers->SetModified();
2112 Reply(250,
"%d %s", Timer->Id(), *Timer->ToText(
true));
2115 Reply(501,
"Timer \"%d\" not defined", Id);
2118 Reply(501,
"Error in timer id");
2121 Reply(501,
"Missing timer settings");
2128 int From = strtol(Option, &tail, 10);
2129 if (tail && tail != Option) {
2131 if (tail && tail != Option) {
2134 Channels->SetExplicitModify();
2135 int To = strtol(tail, NULL, 10);
2137 const cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr);
2138 cChannel *FromChannel = Channels->GetByNumber(From);
2140 cChannel *ToChannel = Channels->GetByNumber(To);
2142 int FromNumber = FromChannel->
Number();
2143 int ToNumber = ToChannel->
Number();
2144 if (FromNumber != ToNumber) {
2145 Channels->Move(FromChannel, ToChannel);
2146 Channels->ReNumber();
2147 Channels->SetModifiedByUser();
2148 Channels->SetModified();
2149 if (CurrentChannel && CurrentChannel->
Number() != CurrentChannelNr) {
2151 Channels->SwitchTo(CurrentChannel->
Number());
2156 Reply(250,
"Channel \"%d\" moved to \"%d\"", From, To);
2159 Reply(501,
"Can't move channel to same position");
2162 Reply(501,
"Channel \"%d\" not defined", To);
2165 Reply(501,
"Channel \"%d\" not defined", From);
2168 Reply(501,
"Error in channel number");
2171 Reply(501,
"Error in channel number");
2174 Reply(501,
"Missing channel number");
2180 char *opt = strdup(Option);
2183 while (*option && !isspace(*option))
2189 Recordings->SetExplicitModify();
2190 if (
cRecording *Recording = Recordings->GetById(strtol(num, NULL, 10))) {
2191 if (
int RecordingInUse = Recording->IsInUse())
2197 cString oldName = Recording->Name();
2198 if ((Recording = Recordings->GetByName(Recording->FileName())) != NULL && Recording->ChangeName(option)) {
2199 Recordings->SetModified();
2200 Recordings->TouchUpdate();
2201 Reply(250,
"Recording \"%s\" moved to \"%s\"", *oldName, Recording->Name());
2204 Reply(554,
"Error while moving recording \"%s\" to \"%s\"!", *oldName, option);
2207 Reply(501,
"Missing new recording name");
2211 Reply(550,
"Recording \"%s\" not found", num);
2214 Reply(501,
"Error in recording id \"%s\"", num);
2218 Reply(501,
"Missing recording id");
2225 if (ch.
Parse(Option)) {
2227 Channels->SetExplicitModify();
2228 if (Channels->HasUniqueChannelID(&ch)) {
2231 Channels->Add(channel);
2232 Channels->ReNumber();
2233 Channels->SetModifiedByUser();
2234 Channels->SetModified();
2239 Reply(501,
"Channel settings are not unique");
2242 Reply(501,
"Error in channel settings");
2245 Reply(501,
"Missing channel settings");
2252 if (Timer->
Parse(Option)) {
2261 Reply(501,
"Error in timer settings");
2265 Reply(501,
"Missing timer settings");
2271 if (
const cTimer *t = Timers->GetNextActiveTimer()) {
2272 time_t Start = t->StartTime();
2276 else if (strcasecmp(Option,
"ABS") == 0)
2277 Reply(250,
"%d %ld", Id, Start);
2278 else if (strcasecmp(Option,
"REL") == 0)
2279 Reply(250,
"%d %ld", Id, Start - time(NULL));
2281 Reply(501,
"Unknown option: \"%s\"", Option);
2284 Reply(550,
"No active timers");
2295 char *opt = strdup(Option);
2298 while (*option && !isspace(*option))
2305 if (
const cRecording *Recording = Recordings->GetById(strtol(num, NULL, 10))) {
2306 cString FileName = Recording->FileName();
2307 cString Title = Recording->Title();
2308 int FramesPerSecond = Recording->FramesPerSecond();
2309 bool IsPesRecording = Recording->IsPesRecording();
2317 if (strcasecmp(option,
"BEGIN") != 0)
2328 Reply(250,
"Playing recording \"%s\" [%s]", num, *Title);
2332 Reply(550,
"Recording \"%s\" not found", num);
2337 Reply(501,
"Error in recording id \"%s\"", num);
2341 Reply(501,
"Missing recording id");
2347 char *opt = strdup(Option);
2349 char *option = name;
2350 while (*option && !isspace(*option))
2359 while (*option && !isspace(*option))
2365 if (!*cmd || strcasecmp(cmd,
"HELP") == 0) {
2366 if (*cmd && *option) {
2369 Reply(-214,
"%s", hp);
2370 Reply(214,
"End of HELP info");
2373 Reply(504,
"HELP topic \"%s\" for plugin \"%s\" unknown", option, plugin->
Name());
2379 Reply(-214,
"SVDRP commands:");
2381 Reply(214,
"End of HELP info");
2384 Reply(214,
"This plugin has no SVDRP commands");
2387 else if (strcasecmp(cmd,
"MAIN") == 0) {
2389 Reply(250,
"Initiated call to main menu function of plugin \"%s\"", plugin->
Name());
2391 Reply(550,
"A plugin call is already pending - please try again later");
2394 int ReplyCode = 900;
2397 Reply(abs(ReplyCode),
"%s", *s);
2399 Reply(500,
"Command unrecognized: \"%s\"", cmd);
2403 Reply(550,
"Plugin \"%s\" not found (use PLUG for a list of plugins)", name);
2407 Reply(-214,
"Available plugins:");
2411 Reply(214,
"End of plugin list");
2418 char buf[strlen(Option) + 1];
2419 char *p = strcpy(buf, Option);
2420 const char *delim =
" \t";
2422 char *RemoteName = strtok_r(p, delim, &strtok_next);
2423 char *ListName = strtok_r(NULL, delim, &strtok_next);
2424 if (SVDRPClientHandler) {
2426 if (strcasecmp(ListName,
"timers") == 0) {
2430 Reply(501,
"No connection to \"%s\"", RemoteName);
2433 Reply(501,
"Unknown list name: \"%s\"", ListName);
2436 Reply(501,
"Missing list name");
2439 Reply(501,
"No SVDRP client connections");
2442 Reply(501,
"Missing parameters");
2450 int o = strtol(Option, NULL, 10);
2454 Reply(501,
"Invalid device number \"%s\"", Option);
2457 Reply(501,
"Invalid parameter \"%s\"", Option);
2460 Reply(250,
"Primary device set to %d", n);
2465 Reply(250,
"%d [%s%s] %s", d->DeviceNumber() + 1, d->HasDecoder() ?
"D" :
"-", d->DeviceNumber() + 1 ==
Setup.
PrimaryDVB ?
"P" :
"-", *d->DeviceName());
2467 Reply(501,
"Failed to get primary device");
2474 FILE *f = fopen(Option,
"r");
2478 Reply(250,
"EPG data processed from \"%s\"", Option);
2481 Reply(451,
"Error while processing EPG from \"%s\"", Option);
2485 Reply(501,
"Cannot open file \"%s\"", Option);
2499 if (!strcasecmp(Option,
"ON")) {
2501 Reply(250,
"Remote control enabled");
2503 else if (!strcasecmp(Option,
"OFF")) {
2505 Reply(250,
"Remote control disabled");
2508 Reply(501,
"Invalid Option \"%s\"", Option);
2517 Reply(250,
"EPG scan triggered");
2523 if (strcasecmp(Option,
"DISK") == 0) {
2526 Reply(250,
"%dMB %dMB %d%%", FreeMB + UsedMB, FreeMB, Percent);
2529 Reply(501,
"Invalid Option \"%s\"", Option);
2532 Reply(501,
"No option given");
2539 if (Timer->
Parse(Option)) {
2541 if (
cTimer *t = Timers->GetTimer(Timer)) {
2561 Reply(501,
"Error in timer settings");
2565 Reply(501,
"Missing timer settings");
2571 Recordings->Update(
false);
2572 Reply(250,
"Re-read of recordings directory triggered");
2580 else if (strcmp(Option,
"+") == 0)
2582 else if (strcmp(Option,
"-") == 0)
2584 else if (strcasecmp(Option,
"MUTE") == 0)
2587 Reply(501,
"Unknown option: \"%s\"", Option);
2592 Reply(250,
"Audio is mute");
2597 #define CMD(c) (strcasecmp(Cmd, c) == 0)
2614 while (*s && !isspace(*s))
2655 else Reply(500,
"Command unrecognized: \"%s\"", Cmd);
2665 if (c ==
'\n' || c == 0x00) {
2681 else if (c == 0x04 &&
numChars == 0) {
2685 else if (c == 0x08 || c == 0x7F) {
2690 else if (c <= 0x03 || c == 0x0D) {
2695 int NewLength =
length + BUFSIZ;
2696 if (
char *NewBuffer = (
char *)realloc(
cmdLine, NewLength)) {
2732 grabImageDir = GrabImageDir;
2745 virtual void Action(
void);
2755 :
cThread(
"SVDRP server handler", true)
2756 ,tcpSocket(TcpPort, true)
2799 SVDRPServerPoller.
Poll(1000);
2816 if (!SVDRPServerHandler) {
2818 SVDRPServerHandler->
Start();
2823 SVDRPClientHandler->
Start();
2832 SVDRPClientHandler = NULL;
2834 SVDRPServerHandler = NULL;
2839 bool Result =
false;
2841 if (SVDRPClientHandler) {
2842 SVDRPClientHandler->
Lock();
2844 SVDRPClientHandler->
Unlock();
2851 bool Result =
false;
2853 if (SVDRPClientHandler) {
2854 SVDRPClientHandler->
Lock();
2855 Result = SVDRPClientHandler->
Execute(ServerName, Command, Response);
2856 SVDRPClientHandler->
Unlock();
2865 if (SVDRPClientHandler) {
2866 SVDRPClientHandler->
Lock();
2868 for (
int i = 0; i < ServerNames.
Size(); i++)
2871 SVDRPClientHandler->
Unlock();
void CmdLSTT(const char *Option)
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
bool Replaying(void) const
Returns true if we are currently replaying.
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
void CmdCONN(const char *Option)
void SetSVDRPPorts(int TcpPort, int UdpPort)
bool HasAddress(const char *Address, int Port) const
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)
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...
const char * Host(void) const
static cString ToText(const cChannel *Channel)
bool TimedOut(void) const
Returns true if the last lock attempt this key was used with failed due to a timeout.
void CmdSCAN(const char *Option)
virtual const char ** SVDRPHelpPages(void)
const char * ServerName(void) const
virtual const char * Version(void)=0
void CmdMESG(const char *Option)
void CmdMODC(const char *Option)
const char * Title(char Delimiter= ' ', bool NewIndicator=false, int Level=-1) const
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)
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 Dump(const cChannels *Channels, FILE *f, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0) const
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
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)
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.
void CmdCPYR(const char *Option)
bool Parse(const char *s)
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 GetRemoteTimers(cStringList &Response)
const int Port(void) const
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)
#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 CmdCHAN(const char *Option)
void HandleClientConnection(void)
virtual void Remove(int Index)
virtual const char * Description(void)=0
void CmdLSTD(const char *Option)
bool Transferring(void) const
Returns true if we are currently in Transfer Mode.
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)
void CmdDELC(const char *Option)
void CmdGRAB(const char *Option)
void void PrintHelpTopics(const char **hp)
const char * VdrVersion(void) const
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)
cStateKey StateKeySVDRPRemoteTimersPoll
Controls whether a change to the local list of timers needs to result in sending a POLL to the remote...
void StartSVDRPHandler(void)
bool Execute(const char *Command, cStringList *Response=NULL)
cListObject * Next(void) const
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
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
const int Timeout(void) const
const char * Name(void) const
bool TimedOut(void) const
#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)
const char * Connection(void) const
void SetSVDRPGrabImageDir(const char *GrabImageDir)
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)
const char * Remote(void) const
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)
tChannelID GetChannelID(void) const
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)
cString & CompactChars(char c)
Compact any sequence of characters 'c' to a single character, and strip all of them from the beginnin...
static void Launch(cControl *Control)
cSVDRPClientHandler(int TcpPort, int UdpPort)
const char * Error(void) const
void CmdPLAY(const char *Option)
static bool Enabled(void)
cString ToDescr(void) const
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)
const char * ApiVersion(void) const
cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout)
cListObject * Prev(void) const
static cPoller SVDRPServerPoller
cString ToString(void) const
void CmdLSTR(const char *Option)
const char * Address(void) const
static cDevice * PrimaryDevice(void)
Returns the primary device.
void CmdPLUG(const char *Option)
#define LOCK_RECORDINGS_READ
void SetFlags(uint Flags)
cSVDRPClient * GetClientForServer(const char *ServerName)
void CmdMODT(const char *Option)
virtual ~cSVDRPServerHandler()
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)
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
bool Connected(void) 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 cIpAddress * LastIpAddress(void) const
void BroadcastSVDRPCommand(const char *Command)
Sends the given SVDRP Command string to all remote VDRs.
cIpAddress clientIpAddress
cString ToText(bool UseChannelID=false) const
virtual ~cSVDRPClientHandler()
char * ExchangeChars(char *s, bool ToFileSystem)
static cSVDRPServerHandler * SVDRPServerHandler
#define LOCK_SCHEDULES_READ
const char * FileName(void) const
Returns the full path name to the recording directory, including the video directory and the actual '...
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)
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
const char * Connection(void) const
const char * ClientName(void) const
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.