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
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];
511 else if (Code == 550)
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);
608 bool Execute(
const char *ServerName,
const char *Command,
cStringList *Response = NULL);
616 :
cThread(
"SVDRP client handler", true)
617 ,udpSocket(UdpPort, false)
664 bool TimersModified = Timers->StoreRemoteTimers(Client->
ServerName(), &RemoteTimers);
671 if (*PollTimersCmd) {
672 if (!Client->
Execute(PollTimersCmd))
706 if (ServerParams.
Ok())
733 return Client->Execute(Command, Response);
740 ServerNames->
Clear();
746 return ServerNames->
Size() > 0;
776 if ((
f = tmpfile()) != NULL) {
778 message =
"Enter EPG data, end with \".\" on a line by itself";
783 message =
"Error while opening temporary file";
796 if (strcmp(s,
".") != 0) {
806 message =
"EPG data processed";
810 message =
"Error while processing EPG data";
821 #define MAXHELPTOPIC 10
822 #define EITDISABLETIME 10
826 "CHAN [ + | - | <number> | <name> | <id> ]\n"
827 " Switch channel up, down or to the given channel number, name or id.\n"
828 " Without option (or after successfully switching to the channel)\n"
829 " it returns the current channel number and name.",
830 "CLRE [ <number> | <name> | <id> ]\n"
831 " Clear the EPG list of the given channel number, name or id.\n"
832 " Without option it clears the entire EPG list.\n"
833 " After a CLRE command, no further EPG processing is done for 10\n"
834 " seconds, so that data sent with subsequent PUTE commands doesn't\n"
835 " interfere with data from the broadcasters.",
836 "CONN name:<name> port:<port> vdrversion:<vdrversion> apiversion:<apiversion> timeout:<timeout>\n"
837 " Used by peer-to-peer connections between VDRs to tell the other VDR\n"
838 " to establish a connection to this VDR. The name is the SVDRP host name\n"
839 " of this VDR, which may differ from its DNS name.",
840 "CPYR <number> <new name>\n"
841 " Copy the recording with the given number. Before a recording can be\n"
842 " copied, an LSTR command must have been executed in order to retrieve\n"
843 " the recording numbers.\n",
844 "DELC <number> | <id>\n"
845 " Delete the channel with the given number or channel id.",
847 " Delete the recording with the given id. Before a recording can be\n"
848 " deleted, an LSTR command should have been executed in order to retrieve\n"
849 " the recording ids. The ids are unique and don't change while this\n"
850 " instance of VDR is running.\n"
851 " CAUTION: THERE IS NO CONFIRMATION PROMPT WHEN DELETING A\n"
852 " RECORDING - BE SURE YOU KNOW WHAT YOU ARE DOING!",
854 " Delete the timer with the given id. If this timer is currently recording,\n"
855 " the recording will be stopped without any warning.",
857 " Edit the recording with the given id. Before a recording can be\n"
858 " edited, an LSTR command should have been executed in order to retrieve\n"
859 " the recording ids.",
860 "GRAB <filename> [ <quality> [ <sizex> <sizey> ] ]\n"
861 " Grab the current frame and save it to the given file. Images can\n"
862 " be stored as JPEG or PNM, depending on the given file name extension.\n"
863 " The quality of the grabbed image can be in the range 0..100, where 100\n"
864 " (the default) means \"best\" (only applies to JPEG). The size parameters\n"
865 " define the size of the resulting image (default is full screen).\n"
866 " If the file name is just an extension (.jpg, .jpeg or .pnm) the image\n"
867 " data will be sent to the SVDRP connection encoded in base64. The same\n"
868 " happens if '-' (a minus sign) is given as file name, in which case the\n"
869 " image format defaults to JPEG.",
871 " The HELP command gives help info.",
872 "HITK [ <key> ... ]\n"
873 " Hit the given remote control key. Without option a list of all\n"
874 " valid key names is given. If more than one key is given, they are\n"
875 " entered into the remote control queue in the given sequence. There\n"
876 " can be up to 31 keys.",
877 "LSTC [ :ids ] [ :groups | <number> | <name> | <id> ]\n"
878 " List channels. Without option, all channels are listed. Otherwise\n"
879 " only the given channel is listed. If a name is given, all channels\n"
880 " containing the given string as part of their name are listed.\n"
881 " If ':groups' is given, all channels are listed including group\n"
882 " separators. The channel number of a group separator is always 0.\n"
883 " With ':ids' the channel ids are listed following the channel numbers.\n"
884 " The special number 0 can be given to list the current channel.",
886 " List all available devices. Each device is listed with its name and\n"
887 " whether it is currently the primary device ('P') or it implements a\n"
888 " decoder ('D') and can be used as output device.",
889 "LSTE [ <channel> ] [ now | next | at <time> ]\n"
890 " List EPG data. Without any parameters all data of all channels is\n"
891 " listed. If a channel is given (either by number or by channel ID),\n"
892 " only data for that channel is listed. 'now', 'next', or 'at <time>'\n"
893 " restricts the returned data to present events, following events, or\n"
894 " events at the given time (which must be in time_t form).",
895 "LSTR [ <id> [ path ] ]\n"
896 " List recordings. Without option, all recordings are listed. Otherwise\n"
897 " the information for the given recording is listed. If a recording\n"
898 " id and the keyword 'path' is given, the actual file name of that\n"
899 " recording's directory is listed.\n"
900 " Note that the ids of the recordings are not necessarily given in\n"
902 "LSTT [ <id> ] [ id ]\n"
903 " List timers. Without option, all timers are listed. Otherwise\n"
904 " only the timer with the given id is listed. If the keyword 'id' is\n"
905 " given, the channels will be listed with their unique channel ids\n"
906 " instead of their numbers. This command lists only the timers that are\n"
907 " defined locally on this VDR, not any remote timers from other VDRs.",
909 " Displays the given message on the OSD. The message will be queued\n"
910 " and displayed whenever this is suitable.\n",
911 "MODC <number> <settings>\n"
912 " Modify a channel. Settings must be in the same format as returned\n"
913 " by the LSTC command.",
914 "MODT <id> on | off | <settings>\n"
915 " Modify a timer. Settings must be in the same format as returned\n"
916 " by the LSTT command. The special keywords 'on' and 'off' can be\n"
917 " used to easily activate or deactivate a timer.",
918 "MOVC <number> <to>\n"
919 " Move a channel to a new position.",
920 "MOVR <id> <new name>\n"
921 " Move the recording with the given id. Before a recording can be\n"
922 " moved, an LSTR command should have been executed in order to retrieve\n"
923 " the recording ids. The ids don't change during subsequent MOVR\n"
926 " Create a new channel. Settings must be in the same format as returned\n"
927 " by the LSTC command.",
929 " Create a new timer. Settings must be in the same format as returned\n"
930 " by the LSTT command.",
931 "NEXT [ abs | rel ]\n"
932 " Show the next timer event. If no option is given, the output will be\n"
933 " in human readable form. With option 'abs' the absolute time of the next\n"
934 " event will be given as the number of seconds since the epoch (time_t\n"
935 " format), while with option 'rel' the relative time will be given as the\n"
936 " number of seconds from now until the event. If the absolute time given\n"
937 " is smaller than the current time, or if the relative time is less than\n"
938 " zero, this means that the timer is currently recording and has started\n"
939 " at the given time. The first value in the resulting line is the id\n"
942 " Used by peer-to-peer connections between VDRs to keep the connection\n"
943 " from timing out. May be used at any time and simply returns a line of\n"
944 " the form '<hostname> is alive'.",
945 "PLAY <id> [ begin | <position> ]\n"
946 " Play the recording with the given id. Before a recording can be\n"
947 " played, an LSTR command should have been executed in order to retrieve\n"
948 " the recording ids.\n"
949 " The keyword 'begin' plays the recording from its very beginning, while\n"
950 " a <position> (given as hh:mm:ss[.ff] or framenumber) starts at that\n"
951 " position. If neither 'begin' nor a <position> are given, replay is resumed\n"
952 " at the position where any previous replay was stopped, or from the beginning\n"
953 " by default. To control or stop the replay session, use the usual remote\n"
954 " control keypresses via the HITK command.",
955 "PLUG <name> [ help | main ] [ <command> [ <options> ]]\n"
956 " Send a command to a plugin.\n"
957 " The PLUG command without any parameters lists all plugins.\n"
958 " If only a name is given, all commands known to that plugin are listed.\n"
959 " If a command is given (optionally followed by parameters), that command\n"
960 " is sent to the plugin, and the result will be displayed.\n"
961 " The keyword 'help' lists all the SVDRP commands known to the named plugin.\n"
962 " If 'help' is followed by a command, the detailed help for that command is\n"
963 " given. The keyword 'main' initiates a call to the main menu function of the\n"
965 "POLL <name> timers\n"
966 " Used by peer-to-peer connections between VDRs to inform other machines\n"
967 " about changes to timers. The receiving VDR shall use LSTT to query the\n"
968 " remote machine with the given name about its timers and update its list\n"
969 " of timers accordingly.\n",
970 "PRIM [ <number> ]\n"
971 " Make the device with the given number the primary device.\n"
972 " Without option it returns the currently active primary device in the same\n"
973 " format as used by the LSTD command.",
975 " Put data into the EPG list. The data entered has to strictly follow the\n"
976 " format defined in vdr(5) for the 'epg.data' file. A '.' on a line\n"
977 " by itself terminates the input and starts processing of the data (all\n"
978 " entered data is buffered until the terminating '.' is seen).\n"
979 " If a file name is given, epg data will be read from this file (which\n"
980 " must be accessible under the given name from the machine VDR is running\n"
981 " on). In case of file input, no terminating '.' shall be given.\n",
982 "REMO [ on | off ]\n"
983 " Turns the remote control on or off. Without a parameter, the current\n"
984 " status of the remote control is reported.",
986 " Forces an EPG scan. If this is a single DVB device system, the scan\n"
987 " will be done on the primary device unless it is currently recording.",
989 " Return information about disk usage (total, free, percent).",
991 " Updates a timer. Settings must be in the same format as returned\n"
992 " by the LSTT command. If a timer with the same channel, day, start\n"
993 " and stop time does not yet exist, it will be created.",
995 " Initiates a re-read of the recordings directory, which is the SVDRP\n"
996 " equivalent to 'touch .update'.",
997 "VOLU [ <number> | + | - | mute ]\n"
998 " Set the audio volume to the given number (which is limited to the range\n"
999 " 0...255). If the special options '+' or '-' are given, the volume will\n"
1000 " be turned up or down, respectively. The option 'mute' will toggle the\n"
1001 " audio muting. If no option is given, the current audio volume level will\n"
1004 " Exit vdr (SVDRP).\n"
1005 " You can also hit Ctrl-D to exit.",
1033 const char *q = HelpPage;
1036 uint n = q - HelpPage;
1037 if (n >=
sizeof(topic))
1038 n =
sizeof(topic) - 1;
1039 strncpy(topic, HelpPage, n);
1053 if (strcasecmp(Cmd, t) == 0)
1074 void Close(
bool SendReply =
false,
bool Timeout =
false);
1075 bool Send(
const char *s);
1078 void CmdCHAN(const
char *Option);
1079 void CmdCLRE(const
char *Option);
1080 void CmdCONN(const
char *Option);
1081 void CmdCPYR(const
char *Option);
1082 void CmdDELC(const
char *Option);
1083 void CmdDELR(const
char *Option);
1084 void CmdDELT(const
char *Option);
1085 void CmdEDIT(const
char *Option);
1086 void CmdGRAB(const
char *Option);
1087 void CmdHELP(const
char *Option);
1088 void CmdHITK(const
char *Option);
1089 void CmdLSTC(const
char *Option);
1090 void CmdLSTD(const
char *Option);
1091 void CmdLSTE(const
char *Option);
1092 void CmdLSTR(const
char *Option);
1093 void CmdLSTT(const
char *Option);
1094 void CmdMESG(const
char *Option);
1095 void CmdMODC(const
char *Option);
1096 void CmdMODT(const
char *Option);
1097 void CmdMOVC(const
char *Option);
1098 void CmdMOVR(const
char *Option);
1099 void CmdNEWC(const
char *Option);
1100 void CmdNEWT(const
char *Option);
1101 void CmdNEXT(const
char *Option);
1102 void CmdPING(const
char *Option);
1103 void CmdPLAY(const
char *Option);
1104 void CmdPLUG(const
char *Option);
1105 void CmdPOLL(const
char *Option);
1106 void CmdPRIM(const
char *Option);
1107 void CmdPUTE(const
char *Option);
1108 void CmdREMO(const
char *Option);
1109 void CmdSCAN(const
char *Option);
1110 void CmdSTAT(const
char *Option);
1111 void CmdUPDT(const
char *Option);
1112 void CmdUPDR(const
char *Option);
1113 void CmdVOLU(const
char *Option);
1136 time_t now = time(NULL);
1179 char *buffer = NULL;
1182 if (vasprintf(&buffer, fmt, ap) >= 0) {
1185 char *n = strchr(s,
'\n');
1189 if (Code < 0 || n && *(n + 1))
1193 s = n ? n + 1 : NULL;
1197 Reply(451,
"Bad format - looks like a programming error!");
1204 Reply(451,
"Zero return code - looks like a programming error!");
1220 const int TopicsPerLine = 5;
1222 for (
int y = 0; (y * TopicsPerLine + x) < NumPages; y++) {
1225 q += sprintf(q,
" ");
1226 for (x = 0; x < TopicsPerLine && (y * TopicsPerLine + x) < NumPages; x++) {
1227 const char *topic =
GetHelpTopic(hp[(y * TopicsPerLine + x)]);
1232 Reply(-214,
"%s", buffer);
1243 int o = strtol(Option, NULL, 10);
1247 else if (strcmp(Option,
"-") == 0) {
1254 else if (strcmp(Option,
"+") == 0) {
1262 n = Channel->Number();
1264 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1265 if (!Channel->GroupSep()) {
1266 if (strcasecmp(Channel->Name(), Option) == 0) {
1267 n = Channel->Number();
1274 Reply(501,
"Undefined channel \"%s\"", Option);
1278 if (
const cChannel *Channel = Channels->GetByNumber(n)) {
1280 Reply(554,
"Error switching to channel \"%d\"", Channel->Number());
1285 Reply(550,
"Unable to find channel \"%s\"", Option);
1293 Reply(250,
"%d %s", Channel->Number(), Channel->Name());
1305 int o = strtol(Option, NULL, 10);
1307 if (
const cChannel *Channel = Channels->GetByNumber(o))
1308 ChannelID = Channel->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");
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");
1388 Channels->SetExplicitModify();
1391 Channel = Channels->GetByNumber(strtol(Option, NULL, 10));
1395 if (
const cTimer *Timer = Timers->UsesChannel(Channel)) {
1396 Reply(550,
"Channel \"%s\" is in use by timer %s", Option, *Timer->ToDescr());
1400 cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr);
1401 if (CurrentChannel && Channel == CurrentChannel) {
1402 int n = Channels->GetNextNormal(CurrentChannel->
Index());
1404 n = Channels->GetPrevNormal(CurrentChannel->
Index());
1406 Reply(501,
"Can't delete channel \"%s\" - list would be empty", Option);
1409 CurrentChannel = Channels->Get(n);
1410 CurrentChannelNr = 0;
1412 Channels->Del(Channel);
1413 Channels->ReNumber();
1414 Channels->SetModifiedByUser();
1415 Channels->SetModified();
1417 if (CurrentChannel && CurrentChannel->
Number() != CurrentChannelNr) {
1419 Channels->SwitchTo(CurrentChannel->
Number());
1423 Reply(250,
"Channel \"%s\" deleted", Option);
1426 Reply(501,
"Channel \"%s\" not defined", Option);
1429 Reply(501,
"Missing channel number or id");
1438 return cString::sprintf(
"Recording \"%s\" is being replayed", RecordingId);
1439 else if ((Reason &
ruCut) != 0)
1442 return cString::sprintf(
"Recording \"%s\" is being copied/moved", RecordingId);
1451 char *opt = strdup(Option);
1454 while (*option && !isspace(*option))
1460 Recordings->SetExplicitModify();
1461 if (
cRecording *Recording = Recordings->Get(strtol(num, NULL, 10) - 1)) {
1462 if (
int RecordingInUse = Recording->IsInUse())
1470 if (strcmp(newName, Recording->Name())) {
1475 Recordings->AddByName(fileName);
1476 Reply(250,
"Recording \"%s\" copied to \"%s\"", Recording->Name(), *newName);
1479 Reply(554,
"Error while copying recording \"%s\" to \"%s\"!", Recording->Name(), *newName);
1482 Reply(501,
"Identical new recording name");
1485 Reply(501,
"Missing new recording name");
1489 Reply(550,
"Recording \"%s\" not found", num);
1492 Reply(501,
"Error in recording number \"%s\"", num);
1496 Reply(501,
"Missing recording number");
1504 Recordings->SetExplicitModify();
1505 if (
cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1506 if (
int RecordingInUse = Recording->IsInUse())
1509 if (Recording->Delete()) {
1510 Recordings->DelByName(Recording->FileName());
1511 Recordings->SetModified();
1513 Reply(250,
"Recording \"%s\" deleted", Option);
1516 Reply(554,
"Error while deleting recording!");
1520 Reply(550,
"Recording \"%s\" not found", Option);
1523 Reply(501,
"Error in recording id \"%s\"", Option);
1526 Reply(501,
"Missing recording id");
1534 Timers->SetExplicitModify();
1535 if (
cTimer *Timer = Timers->GetById(strtol(Option, NULL, 10))) {
1536 if (Timer->Recording()) {
1540 Timer->TriggerRespawn();
1542 Timers->SetModified();
1544 Reply(250,
"Timer \"%s\" deleted", Option);
1547 Reply(501,
"Timer \"%s\" not defined", Option);
1550 Reply(501,
"Error in timer number \"%s\"", Option);
1553 Reply(501,
"Missing timer number");
1561 if (
const cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1563 if (Marks.
Load(Recording->FileName(), Recording->FramesPerSecond(), Recording->IsPesRecording()) && Marks.
Count()) {
1565 Reply(550,
"Not enough free disk space to start editing process");
1567 Reply(250,
"Editing recording \"%s\" [%s]", Option, Recording->Title());
1569 Reply(554,
"Can't start editing process");
1572 Reply(554,
"No editing marks defined");
1575 Reply(550,
"Recording \"%s\" not found", Option);
1578 Reply(501,
"Error in recording id \"%s\"", Option);
1581 Reply(501,
"Missing recording id");
1586 const char *FileName = NULL;
1588 int Quality = -1, SizeX = -1, SizeY = -1;
1590 char buf[strlen(Option) + 1];
1591 char *p = strcpy(buf, Option);
1592 const char *delim =
" \t";
1594 FileName = strtok_r(p, delim, &strtok_next);
1596 const char *Extension = strrchr(FileName,
'.');
1598 if (strcasecmp(Extension,
".jpg") == 0 || strcasecmp(Extension,
".jpeg") == 0)
1600 else if (strcasecmp(Extension,
".pnm") == 0)
1603 Reply(501,
"Unknown image type \"%s\"", Extension + 1);
1606 if (Extension == FileName)
1609 else if (strcmp(FileName,
"-") == 0)
1612 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1613 if (strcasecmp(p,
"JPEG") == 0 || strcasecmp(p,
"PNM") == 0) {
1615 p = strtok_r(NULL, delim, &strtok_next);
1621 Reply(501,
"Invalid quality \"%s\"", p);
1627 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1631 Reply(501,
"Invalid sizex \"%s\"", p);
1634 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1638 Reply(501,
"Invalid sizey \"%s\"", p);
1643 Reply(501,
"Missing sizey");
1647 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1648 Reply(501,
"Unexpected parameter \"%s\"", p);
1652 char RealFileName[PATH_MAX];
1657 const char *slash = strrchr(FileName,
'/');
1662 slash = strrchr(FileName,
'/');
1665 char *r = realpath(t, RealFileName);
1668 Reply(501,
"Invalid file name \"%s\"", FileName);
1671 strcat(RealFileName, slash);
1672 FileName = RealFileName;
1674 Reply(501,
"Invalid file name \"%s\"", FileName);
1679 Reply(550,
"Grabbing to file not allowed (use \"GRAB -\" instead)");
1688 int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE);
1690 if (
safe_write(fd, Image, ImageSize) == ImageSize) {
1692 Reply(250,
"Grabbed image %s", Option);
1696 Reply(451,
"Can't write to '%s'", FileName);
1702 Reply(451,
"Can't open '%s'", FileName);
1708 while ((s = Base64.
NextLine()) != NULL)
1709 Reply(-216,
"%s", s);
1710 Reply(216,
"Grabbed image %s", Option);
1715 Reply(451,
"Grab image failed");
1718 Reply(501,
"Missing filename");
1726 Reply(-214,
"%s", hp);
1728 Reply(504,
"HELP topic \"%s\" unknown", Option);
1734 Reply(-214,
"Topics:");
1743 Reply(-214,
"To report bugs in the implementation send email to");
1744 Reply(-214,
" vdr-bugs@tvdr.de");
1746 Reply(214,
"End of HELP info");
1753 Reply(550,
"Remote control currently disabled (key \"%s\" discarded)", Option);
1756 char buf[strlen(Option) + 1];
1757 strcpy(buf, Option);
1758 const char *delim =
" \t";
1760 char *p = strtok_r(buf, delim, &strtok_next);
1766 Reply(451,
"Too many keys in \"%s\" (only %d accepted)", Option, NumKeys);
1771 Reply(504,
"Unknown key: \"%s\"", p);
1775 p = strtok_r(NULL, delim, &strtok_next);
1777 Reply(250,
"Key%s \"%s\" accepted", NumKeys > 1 ?
"s" :
"", Option);
1780 Reply(-214,
"Valid <key> names for the HITK command:");
1781 for (
int i = 0; i <
kNone; i++) {
1784 Reply(214,
"End of key list");
1791 bool WithChannelIds =
startswith(Option,
":ids") && (Option[4] ==
' ' || Option[4] == 0);
1794 bool WithGroupSeps = strcasecmp(Option,
":groups") == 0;
1795 if (*Option && !WithGroupSeps) {
1797 int n = strtol(Option, NULL, 10);
1800 if (
const cChannel *Channel = Channels->GetByNumber(n))
1801 Reply(250,
"%d%s%s %s", Channel->Number(), WithChannelIds ?
" " :
"", WithChannelIds ? *Channel->GetChannelID().ToString() :
"", *Channel->ToText());
1803 Reply(501,
"Channel \"%s\" not defined", Option);
1808 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1809 if (!Channel->GroupSep()) {
1810 if (strcasestr(Channel->Name(), Option)) {
1821 Reply(501,
"Channel \"%s\" not defined", Option);
1825 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1827 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());
1828 else if (!Channel->GroupSep())
1829 Reply(Channel->Number() <
cChannels::MaxNumber() ? -250 : 250,
"%d%s%s %s", Channel->Number(), WithChannelIds ?
" " :
"", WithChannelIds ? *Channel->GetChannelID().ToString() :
"", *Channel->ToText());
1833 Reply(550,
"No channels defined");
1841 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());
1845 Reply(550,
"No devices found");
1856 char buf[strlen(Option) + 1];
1857 strcpy(buf, Option);
1858 const char *delim =
" \t";
1860 char *p = strtok_r(buf, delim, &strtok_next);
1861 while (p && DumpMode ==
dmAll) {
1862 if (strcasecmp(p,
"NOW") == 0)
1864 else if (strcasecmp(p,
"NEXT") == 0)
1866 else if (strcasecmp(p,
"AT") == 0) {
1868 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1870 AtTime = strtol(p, NULL, 10);
1872 Reply(501,
"Invalid time");
1877 Reply(501,
"Missing time");
1881 else if (!Schedule) {
1884 Channel = Channels->GetByNumber(strtol(Option, NULL, 10));
1888 Schedule = Schedules->GetSchedule(Channel);
1890 Reply(550,
"No schedule found");
1895 Reply(550,
"Channel \"%s\" not defined", p);
1900 Reply(501,
"Unknown option: \"%s\"", p);
1903 p = strtok_r(NULL, delim, &strtok_next);
1908 FILE *f = fdopen(fd,
"w");
1911 Schedule->
Dump(Channels, f,
"215-", DumpMode, AtTime);
1913 Schedules->Dump(f,
"215-", DumpMode, AtTime);
1915 Reply(215,
"End of EPG data");
1919 Reply(451,
"Can't open file connection");
1924 Reply(451,
"Can't dup stream descriptor");
1933 char buf[strlen(Option) + 1];
1934 strcpy(buf, Option);
1935 const char *delim =
" \t";
1937 char *p = strtok_r(buf, delim, &strtok_next);
1941 Number = strtol(p, NULL, 10);
1943 Reply(501,
"Error in recording id \"%s\"", Option);
1947 else if (strcasecmp(p,
"PATH") == 0)
1950 Reply(501,
"Unknown option: \"%s\"", p);
1953 p = strtok_r(NULL, delim, &strtok_next);
1956 if (
const cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1957 FILE *f = fdopen(
file,
"w");
1960 Reply(250,
"%s", Recording->FileName());
1962 Recording->Info()->Write(f,
"215-");
1964 Reply(215,
"End of recording information");
1969 Reply(451,
"Can't open file connection");
1972 Reply(550,
"Recording \"%s\" not found", Option);
1975 else if (Recordings->Count()) {
1976 const cRecording *Recording = Recordings->First();
1978 Reply(Recording == Recordings->Last() ? 250 : -250,
"%d %s", Recording->
Id(), Recording->
Title(
' ',
true));
1979 Recording = Recordings->
Next(Recording);
1983 Reply(550,
"No recordings available");
1989 bool UseChannelId =
false;
1991 char buf[strlen(Option) + 1];
1992 strcpy(buf, Option);
1993 const char *delim =
" \t";
1995 char *p = strtok_r(buf, delim, &strtok_next);
1998 Id = strtol(p, NULL, 10);
1999 else if (strcasecmp(p,
"ID") == 0)
2000 UseChannelId =
true;
2002 Reply(501,
"Unknown option: \"%s\"", p);
2005 p = strtok_r(NULL, delim, &strtok_next);
2010 for (
const cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
2011 if (!Timer->Remote()) {
2012 if (Timer->Id() == Id) {
2013 Reply(250,
"%d %s", Timer->Id(), *Timer->ToText(UseChannelId));
2018 Reply(501,
"Timer \"%s\" not defined", Option);
2022 const cTimer *LastLocalTimer = Timers->Last();
2023 while (LastLocalTimer) {
2024 if (LastLocalTimer->
Remote())
2025 LastLocalTimer = Timers->
Prev(LastLocalTimer);
2029 if (LastLocalTimer) {
2030 for (
const cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
2031 if (!Timer->Remote())
2032 Reply(Timer != LastLocalTimer ? -250 : 250,
"%d %s", Timer->
Id(), *Timer->ToText(UseChannelId));
2033 if (Timer == LastLocalTimer)
2039 Reply(550,
"No timers defined");
2047 Reply(250,
"Message queued");
2050 Reply(501,
"Missing message");
2057 int n = strtol(Option, &tail, 10);
2058 if (tail && tail != Option) {
2061 Channels->SetExplicitModify();
2062 if (
cChannel *Channel = Channels->GetByNumber(n)) {
2064 if (ch.
Parse(tail)) {
2065 if (Channels->HasUniqueChannelID(&ch, Channel)) {
2067 Channels->ReNumber();
2068 Channels->SetModifiedByUser();
2069 Channels->SetModified();
2071 Reply(250,
"%d %s", Channel->Number(), *Channel->ToText());
2074 Reply(501,
"Channel settings are not unique");
2077 Reply(501,
"Error in channel settings");
2080 Reply(501,
"Channel \"%d\" not defined", n);
2083 Reply(501,
"Error in channel number");
2086 Reply(501,
"Missing channel settings");
2093 int Id = strtol(Option, &tail, 10);
2094 if (tail && tail != Option) {
2097 Timers->SetExplicitModify();
2098 if (
cTimer *Timer = Timers->GetById(Id)) {
2101 if (strcasecmp(tail,
"ON") == 0)
2103 else if (strcasecmp(tail,
"OFF") == 0)
2105 else if (!t.
Parse(tail)) {
2106 Reply(501,
"Error in timer settings");
2110 Reply(550,
"Timer is recording");
2118 Timers->SetModified();
2120 if (Timer->IsPatternTimer())
2121 Timer->SetEvent(NULL);
2122 Timer->TriggerRespawn();
2123 Reply(250,
"%d %s", Timer->Id(), *Timer->ToText(
true));
2126 Reply(501,
"Timer \"%d\" not defined", Id);
2129 Reply(501,
"Error in timer id");
2132 Reply(501,
"Missing timer settings");
2139 int From = strtol(Option, &tail, 10);
2140 if (tail && tail != Option) {
2142 if (tail && tail != Option) {
2145 Channels->SetExplicitModify();
2146 int To = strtol(tail, NULL, 10);
2148 const cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr);
2149 cChannel *FromChannel = Channels->GetByNumber(From);
2151 cChannel *ToChannel = Channels->GetByNumber(To);
2153 int FromNumber = FromChannel->
Number();
2154 int ToNumber = ToChannel->
Number();
2155 if (FromNumber != ToNumber) {
2156 if (Channels->MoveNeedsDecrement(FromChannel, ToChannel))
2157 ToChannel = Channels->
Prev(ToChannel);
2158 Channels->Move(FromChannel, ToChannel);
2159 Channels->ReNumber();
2160 Channels->SetModifiedByUser();
2161 Channels->SetModified();
2162 if (CurrentChannel && CurrentChannel->
Number() != CurrentChannelNr) {
2164 Channels->SwitchTo(CurrentChannel->
Number());
2169 Reply(250,
"Channel \"%d\" moved to \"%d\"", From, To);
2172 Reply(501,
"Can't move channel to same position");
2175 Reply(501,
"Channel \"%d\" not defined", To);
2178 Reply(501,
"Channel \"%d\" not defined", From);
2181 Reply(501,
"Error in channel number");
2184 Reply(501,
"Error in channel number");
2187 Reply(501,
"Missing channel number");
2193 char *opt = strdup(Option);
2196 while (*option && !isspace(*option))
2202 Recordings->SetExplicitModify();
2203 if (
cRecording *Recording = Recordings->GetById(strtol(num, NULL, 10))) {
2204 if (
int RecordingInUse = Recording->IsInUse())
2210 cString oldName = Recording->Name();
2211 if ((Recording = Recordings->GetByName(Recording->FileName())) != NULL && Recording->ChangeName(option)) {
2212 Recordings->SetModified();
2213 Recordings->TouchUpdate();
2214 Reply(250,
"Recording \"%s\" moved to \"%s\"", *oldName, Recording->Name());
2217 Reply(554,
"Error while moving recording \"%s\" to \"%s\"!", *oldName, option);
2220 Reply(501,
"Missing new recording name");
2224 Reply(550,
"Recording \"%s\" not found", num);
2227 Reply(501,
"Error in recording id \"%s\"", num);
2231 Reply(501,
"Missing recording id");
2238 if (ch.
Parse(Option)) {
2240 Channels->SetExplicitModify();
2241 if (Channels->HasUniqueChannelID(&ch)) {
2244 Channels->Add(channel);
2245 Channels->ReNumber();
2246 Channels->SetModifiedByUser();
2247 Channels->SetModified();
2252 Reply(501,
"Channel settings are not unique");
2255 Reply(501,
"Error in channel settings");
2258 Reply(501,
"Missing channel settings");
2265 if (Timer->
Parse(Option)) {
2274 Reply(501,
"Error in timer settings");
2278 Reply(501,
"Missing timer settings");
2284 if (
const cTimer *t = Timers->GetNextActiveTimer()) {
2285 time_t Start = t->StartTime();
2289 else if (strcasecmp(Option,
"ABS") == 0)
2290 Reply(250,
"%d %jd", Id, intmax_t(Start));
2291 else if (strcasecmp(Option,
"REL") == 0)
2292 Reply(250,
"%d %jd", Id, intmax_t(Start - time(NULL)));
2294 Reply(501,
"Unknown option: \"%s\"", Option);
2297 Reply(550,
"No active timers");
2308 char *opt = strdup(Option);
2311 while (*option && !isspace(*option))
2318 if (
const cRecording *Recording = Recordings->GetById(strtol(num, NULL, 10))) {
2319 cString FileName = Recording->FileName();
2320 cString Title = Recording->Title();
2321 int FramesPerSecond = Recording->FramesPerSecond();
2322 bool IsPesRecording = Recording->IsPesRecording();
2330 if (strcasecmp(option,
"BEGIN") != 0)
2341 Reply(250,
"Playing recording \"%s\" [%s]", num, *Title);
2345 Reply(550,
"Recording \"%s\" not found", num);
2350 Reply(501,
"Error in recording id \"%s\"", num);
2354 Reply(501,
"Missing recording id");
2360 char *opt = strdup(Option);
2362 char *option = name;
2363 while (*option && !isspace(*option))
2372 while (*option && !isspace(*option))
2378 if (!*cmd || strcasecmp(cmd,
"HELP") == 0) {
2379 if (*cmd && *option) {
2382 Reply(-214,
"%s", hp);
2383 Reply(214,
"End of HELP info");
2386 Reply(504,
"HELP topic \"%s\" for plugin \"%s\" unknown", option, plugin->
Name());
2392 Reply(-214,
"SVDRP commands:");
2394 Reply(214,
"End of HELP info");
2397 Reply(214,
"This plugin has no SVDRP commands");
2400 else if (strcasecmp(cmd,
"MAIN") == 0) {
2402 Reply(250,
"Initiated call to main menu function of plugin \"%s\"", plugin->
Name());
2404 Reply(550,
"A plugin call is already pending - please try again later");
2407 int ReplyCode = 900;
2410 Reply(abs(ReplyCode),
"%s", *s);
2412 Reply(500,
"Command unrecognized: \"%s\"", cmd);
2416 Reply(550,
"Plugin \"%s\" not found (use PLUG for a list of plugins)", name);
2420 Reply(-214,
"Available plugins:");
2424 Reply(214,
"End of plugin list");
2431 char buf[strlen(Option) + 1];
2432 char *p = strcpy(buf, Option);
2433 const char *delim =
" \t";
2435 char *RemoteName = strtok_r(p, delim, &strtok_next);
2436 char *ListName = strtok_r(NULL, delim, &strtok_next);
2439 if (strcasecmp(ListName,
"timers") == 0) {
2444 Reply(501,
"Unknown list name: \"%s\"", ListName);
2447 Reply(501,
"Missing list name");
2450 Reply(501,
"No SVDRP client connections");
2453 Reply(501,
"Missing parameters");
2461 int o = strtol(Option, NULL, 10);
2465 Reply(501,
"Invalid device number \"%s\"", Option);
2468 Reply(501,
"Invalid parameter \"%s\"", Option);
2471 Reply(250,
"Primary device set to %d", n);
2476 Reply(250,
"%d [%s%s] %s", d->DeviceNumber() + 1, d->HasDecoder() ?
"D" :
"-", d->DeviceNumber() + 1 ==
Setup.
PrimaryDVB ?
"P" :
"-", *d->DeviceName());
2478 Reply(501,
"Failed to get primary device");
2485 FILE *f = fopen(Option,
"r");
2489 Reply(250,
"EPG data processed from \"%s\"", Option);
2492 Reply(451,
"Error while processing EPG from \"%s\"", Option);
2496 Reply(501,
"Cannot open file \"%s\"", Option);
2510 if (!strcasecmp(Option,
"ON")) {
2512 Reply(250,
"Remote control enabled");
2514 else if (!strcasecmp(Option,
"OFF")) {
2516 Reply(250,
"Remote control disabled");
2519 Reply(501,
"Invalid Option \"%s\"", Option);
2528 Reply(250,
"EPG scan triggered");
2534 if (strcasecmp(Option,
"DISK") == 0) {
2537 Reply(250,
"%dMB %dMB %d%%", FreeMB + UsedMB, FreeMB, Percent);
2540 Reply(501,
"Invalid Option \"%s\"", Option);
2543 Reply(501,
"No option given");
2550 if (Timer->
Parse(Option)) {
2552 if (
cTimer *t = Timers->GetTimer(Timer)) {
2572 Reply(501,
"Error in timer settings");
2576 Reply(501,
"Missing timer settings");
2582 Recordings->Update(
false);
2583 Reply(250,
"Re-read of recordings directory triggered");
2591 else if (strcmp(Option,
"+") == 0)
2593 else if (strcmp(Option,
"-") == 0)
2595 else if (strcasecmp(Option,
"MUTE") == 0)
2598 Reply(501,
"Unknown option: \"%s\"", Option);
2603 Reply(250,
"Audio is mute");
2608 #define CMD(c) (strcasecmp(Cmd, c) == 0)
2625 while (*s && !isspace(*s))
2666 else Reply(500,
"Command unrecognized: \"%s\"", Cmd);
2676 if (c ==
'\n' || c == 0x00) {
2692 else if (c == 0x04 &&
numChars == 0) {
2696 else if (c == 0x08 || c == 0x7F) {
2701 else if (c <= 0x03 || c == 0x0D) {
2706 int NewLength =
length + BUFSIZ;
2707 if (
char *NewBuffer = (
char *)realloc(
cmdLine, NewLength)) {
2756 virtual void Action(
void);
2766 :
cThread(
"SVDRP server handler", true)
2767 ,tcpSocket(TcpPort, true)
2850 bool Result =
false;
2859 bool Result =
false;
2872 for (
int i = 0; i < ServerNames.
Size(); i++)
#define LOCK_CHANNELS_READ
#define LOCK_CHANNELS_WRITE
const char * NextLine(void)
Returns the next line of encoded data (terminated by '\0'), or NULL if there is no more encoded data.
bool Parse(const char *s)
static cString ToText(const cChannel *Channel)
tChannelID GetChannelID(void) const
static int MaxNumber(void)
static const char * SystemCharacterTable(void)
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
static void Shutdown(void)
static void Launch(cControl *Control)
virtual uchar * GrabImage(int &Size, bool Jpeg=true, int Quality=-1, int SizeX=-1, int SizeY=-1)
Grabs the currently visible screen image.
static cDevice * GetDevice(int Index)
Gets the device with the given Index.
bool SwitchChannel(const cChannel *Channel, bool LiveView)
Switches the device to the given Channel, initiating transfer mode if necessary.
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
static void SetCurrentChannel(int ChannelNumber)
Sets the number of the current channel on the primary device, without actually switching to it.
void SetVolume(int Volume, bool Absolute=false)
Sets the volume to the given value, either absolutely or relative to the current volume.
static int NumDevices(void)
Returns the total number of devices.
static int CurrentVolume(void)
static cDevice * PrimaryDevice(void)
Returns the primary device.
bool ToggleMute(void)
Turns the volume off or on and returns the new mute state.
static void SetDisableUntil(time_t Time)
bool Ready(bool Wait=true)
bool Open(const char *FileName, int Flags, mode_t Mode=DEFFILEMODE)
const char * Connection(void) const
const char * Address(void) const
void Set(const char *Address, int Port)
static const char * ToString(eKeys Key, bool Translate=false)
static eKeys FromString(const char *Name)
cListObject * Prev(void) const
cListObject * Next(void) const
bool Load(const char *RecordingFileName, double FramesPerSecond=DEFAULTFRAMESPERSECOND, bool IsPesRecording=false)
bool Process(const char *s)
const char * Message(void)
static cPlugin * GetPlugin(int Index)
virtual const char * Description(void)=0
virtual const char * Version(void)=0
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
virtual const char ** SVDRPHelpPages(void)
bool Add(int FileHandle, bool Out)
bool Poll(int TimeoutMs=0)
void Del(int FileHandle, bool Out)
static bool Process(cTimers *Timers, time_t t)
static cRecordControl * GetRecordControl(const char *FileName)
const char * FileName(void) const
Returns the full path name to the recording directory, including the video directory and the actual '...
const char * Title(char Delimiter=' ', bool NewIndicator=false, int Level=-1) const
bool Add(int Usage, const char *FileNameSrc, const char *FileNameDst=NULL)
Adds the given FileNameSrc to the recordings handler for (later) processing.
static const cRecordings * GetRecordingsRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of recordings for read access.
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
static bool Enabled(void)
static bool CallPlugin(const char *Plugin)
Initiates calling the given plugin's main menu function.
static void SetEnabled(bool Enabled)
static void SetRecording(const char *FileName)
bool Execute(const char *ServerName, const char *Command, cStringList *Response=NULL)
void AddClient(cSVDRPServerParams &ServerParams, const char *IpAddress)
virtual ~cSVDRPClientHandler()
void ProcessConnections(void)
bool GetServerNames(cStringList *ServerNames)
cSVDRPClientHandler(int TcpPort, int UdpPort)
void HandleClientConnection(void)
cSVDRPClient * GetClientForServer(const char *ServerName)
cVector< cSVDRPClient * > clientConnections
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
bool TriggerFetchingTimers(const char *ServerName)
const char * Connection(void) const
const char * ServerName(void) const
cIpAddress serverIpAddress
bool Connected(void) const
bool Execute(const char *Command, cStringList *Response=NULL)
bool HasAddress(const char *Address, int Port) const
bool Send(const char *Command)
cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout)
bool GetRemoteTimers(cStringList &Response)
bool Process(cStringList *Response=NULL)
void SetFetchFlag(int Flag)
bool HasFetchFlag(int Flag)
void HandleServerConnection(void)
void ProcessConnections(void)
cSVDRPServerHandler(int TcpPort)
void WaitUntilReady(void)
virtual ~cSVDRPServerHandler()
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
cVector< cSVDRPServer * > serverConnections
const int Timeout(void) const
cSVDRPServerParams(const char *Params)
const char * Host(void) const
const char * VdrVersion(void) const
const char * ApiVersion(void) const
const char * Error(void) const
const char * Name(void) const
const int Port(void) const
void CmdMESG(const char *Option)
void CmdPOLL(const char *Option)
void CmdLSTT(const char *Option)
void CmdCLRE(const char *Option)
void Reply(int Code, const char *fmt,...) __attribute__((format(printf
void CmdGRAB(const char *Option)
void CmdMODC(const char *Option)
const char * ClientName(void) const
cPUTEhandler * PUTEhandler
void CmdDELC(const char *Option)
void CmdPLUG(const char *Option)
void CmdMODT(const char *Option)
cIpAddress clientIpAddress
void CmdCPYR(const char *Option)
void CmdLSTC(const char *Option)
void CmdSCAN(const char *Option)
void Close(bool SendReply=false, bool Timeout=false)
void CmdPUTE(const char *Option)
void CmdLSTR(const char *Option)
void CmdSTAT(const char *Option)
void CmdCHAN(const char *Option)
void CmdHELP(const char *Option)
void CmdUPDT(const char *Option)
void CmdREMO(const char *Option)
void CmdLSTE(const char *Option)
void CmdCONN(const char *Option)
void CmdDELR(const char *Option)
void CmdUPDR(const char *Option)
void CmdVOLU(const char *Option)
void CmdNEWT(const char *Option)
void CmdEDIT(const char *Option)
void CmdPLAY(const char *Option)
void CmdDELT(const char *Option)
void CmdLSTD(const char *Option)
cSVDRPServer(int Socket, const cIpAddress *ClientIpAddress)
void CmdNEXT(const char *Option)
void CmdHITK(const char *Option)
void CmdNEWC(const char *Option)
void CmdPRIM(const char *Option)
void CmdMOVR(const char *Option)
void CmdPING(const char *Option)
void CmdMOVC(const char *Option)
void void PrintHelpTopics(const char **hp)
bool Acceptable(in_addr_t Address)
void Cleanup(time_t Time)
void Dump(const cChannels *Channels, FILE *f, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0) const
static void Cleanup(bool Force=false)
static bool Read(FILE *f=NULL)
char SVDRPDefaultHost[HOST_NAME_MAX]
char SVDRPHostName[HOST_NAME_MAX]
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 bool SendDgram(const char *Dgram, int Port)
cSocket(int Port, bool Tcp)
bool Connect(const char *Address)
const cIpAddress * LastIpAddress(void) const
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
bool TimedOut(void) const
Returns true if the last lock attempt this key was used with failed due to a timeout.
void SortNumerically(void)
cString & CompactChars(char c)
Compact any sequence of characters 'c' to a single character, and strip all of them from the beginnin...
static cString sprintf(const char *fmt,...) __attribute__((format(printf
cString & Truncate(int Index)
Truncate the string at the given Index (if Index is < 0 it is counted from the end of the string).
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
void Set(int Ms=0)
Sets the timer.
bool TimedOut(void) const
const char * Remote(void) const
void ClrFlags(uint Flags)
void SetFlags(uint Flags)
bool IsPatternTimer(void) const
cString ToDescr(void) const
bool Parse(const char *s)
cString ToText(bool UseChannelID=false) const
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 cTimers * GetTimersWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for write access.
static const cTimers * GetTimersRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for read access.
virtual void Append(T Data)
virtual void Remove(int Index)
static int VideoDiskSpace(int *FreeMB=NULL, int *UsedMB=NULL)
#define LOCK_SCHEDULES_READ
#define LOCK_SCHEDULES_WRITE
char * ExchangeChars(char *s, bool ToFileSystem)
bool EnoughFreeDiskSpaceForEdit(const char *FileName)
int HMSFToIndex(const char *HMSF, double FramesPerSecond)
cRecordingsHandler RecordingsHandler
struct __attribute__((packed))
#define LOCK_RECORDINGS_READ
#define LOCK_RECORDINGS_WRITE
tChannelID & ClrRid(void)
static const tChannelID InvalidID
static tChannelID FromString(const char *s)
cString ToString(void) const
const char * GetHelpPage(const char *Cmd, const char **p)
void StopSVDRPHandler(void)
static cPoller SVDRPClientPoller
void SetSVDRPGrabImageDir(const char *GrabImageDir)
static cString grabImageDir
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...
static cString RecordingInUseMessage(int Reason, const char *RecordingId, cRecording *Recording)
static cMutex SVDRPHandlerMutex
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...
static cPoller SVDRPServerPoller
const char * GetHelpTopic(const char *HelpPage)
static cSVDRPServerHandler * SVDRPServerHandler
void StartSVDRPHandler(void)
cStateKey StateKeySVDRPRemoteTimersPoll(true)
void BroadcastSVDRPCommand(const char *Command)
Sends the given SVDRP Command string to all remote VDRs.
#define SVDRPResonseTimeout
static cSVDRPClientHandler * SVDRPClientHandler
static bool DumpSVDRPDataTransfer
void SetSVDRPPorts(int TcpPort, int UdpPort)
int SVDRPCode(const char *s)
Returns the value of the three digit reply code of the given SVDRP response string.
#define LOCK_TIMERS_WRITE