17 #include <arpa/inet.h> 22 #include <netinet/in.h> 27 #include <sys/socket.h> 45 #define dbgsvdrp(a...) if (DumpSVDRPDataTransfer) fprintf(stderr, a) 70 void Set(
const sockaddr *SockAddr);
93 const sockaddr_in *Addr = (sockaddr_in *)SockAddr;
94 Set(inet_ntoa(Addr->sin_addr), ntohs(Addr->sin_port));
99 #define MAXUDPBUF 1024 111 bool Connect(
const char *Address);
146 sock =
tcp ? socket(PF_INET, SOCK_STREAM, IPPROTO_IP) : socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
153 setsockopt(
sock, SOL_SOCKET, SO_REUSEADDR, &ReUseAddr,
sizeof(ReUseAddr));
156 memset(&Addr, 0,
sizeof(Addr));
157 Addr.sin_family = AF_INET;
158 Addr.sin_port = htons(
port);
160 if (bind(
sock, (sockaddr *)&Addr,
sizeof(Addr)) < 0) {
166 int Flags = fcntl(
sock, F_GETFL, 0);
172 if (fcntl(
sock, F_SETFL, Flags) < 0) {
178 if (listen(
sock, 1) < 0) {
192 sock = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
199 memset(&Addr, 0,
sizeof(Addr));
200 Addr.sin_family = AF_INET;
201 Addr.sin_port = htons(
port);
202 Addr.sin_addr.s_addr = inet_addr(Address);
203 if (connect(
sock, (sockaddr *)&Addr,
sizeof(Addr)) < 0) {
209 int Flags = fcntl(
sock, F_GETFL, 0);
215 if (fcntl(
sock, F_SETFL, Flags) < 0) {
219 dbgsvdrp(
"> %s:%d server connection established\n", Address,
port);
229 int Socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
236 if (setsockopt(
Socket, SOL_SOCKET, SO_BROADCAST, &One,
sizeof(One)) < 0) {
243 memset(&Addr, 0,
sizeof(Addr));
244 Addr.sin_family = AF_INET;
245 Addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
246 Addr.sin_port = htons(
Port);
248 dbgsvdrp(
"> %s:%d %s\n", inet_ntoa(Addr.sin_addr),
Port, Dgram);
250 int Length = strlen(Dgram);
251 int Sent = sendto(
Socket, Dgram, Length, 0, (sockaddr *)&Addr,
sizeof(Addr));
255 return Sent == Length;
262 uint Size =
sizeof(Addr);
263 int NewSock = accept(
sock, (sockaddr *)&Addr, &Size);
267 const char *s =
"Access denied!\n";
268 if (write(NewSock, s, strlen(s)) < 0)
289 uint Size =
sizeof(Addr);
290 int NumBytes = recvfrom(
sock, buf,
sizeof(buf), 0, (sockaddr *)&Addr, &Size);
328 bool Send(
const char *Command);
335 bool HasAddress(
const char *Address,
int Port)
const;
347 :serverIpAddress(Address, Port)
353 timeout = Timeout * 1000 * 9 / 10;
403 #define SVDRPResonseTimeout 5000 // ms 410 if (c ==
'\n' || c == 0x00) {
412 while (numChars > 0 && strchr(
" \t\r\n",
input[numChars - 1]))
413 input[--numChars] = 0;
420 switch (atoi(
input)) {
421 case 220:
if (numChars > 4) {
423 if (
char *t = strchr(n,
' ')) {
440 if (numChars >= 4 &&
input[3] !=
'-')
445 if (numChars >=
length - 1) {
446 int NewLength =
length + BUFSIZ;
447 if (
char *NewBuffer = (
char *)realloc(
input, NewLength)) {
457 input[numChars++] = c;
472 else if (!Response && numChars == 0)
505 if (
Execute(
"LSTT ID", &Response)) {
506 for (
int i = 0; i < Response.
Size(); i++) {
507 char *s = Response[i];
511 else if (Code == 550)
550 if (Params && *Params) {
568 error =
"invalid timeout";
571 error =
"missing server timeout";
574 error =
"missing server apiversion";
577 error =
"missing server vdrversion";
580 error =
"missing server port";
583 error =
"missing server name";
586 error =
"missing server parameters";
604 virtual void Action(
void);
611 bool Execute(
const char *ServerName,
const char *Command,
cStringList *Response = NULL);
619 :
cThread(
"SVDRP client handler", true)
620 ,udpSocket(UdpPort, false)
667 bool TimersModified = Timers->StoreRemoteTimers(Client->
ServerName(), &RemoteTimers);
674 if (*PollTimersCmd) {
675 if (!Client->
Execute(PollTimersCmd))
709 if (ServerParams.
Ok())
736 return Client->Execute(Command, Response);
743 ServerNames->
Clear();
749 return ServerNames->
Size() > 0;
779 if ((
f = tmpfile()) != NULL) {
781 message =
"Enter EPG data, end with \".\" on a line by itself";
786 message =
"Error while opening temporary file";
799 if (strcmp(s,
".") != 0) {
809 message =
"EPG data processed";
813 message =
"Error while processing EPG data";
824 #define MAXHELPTOPIC 10 825 #define EITDISABLETIME 10 // seconds until EIT processing is enabled again after a CLRE command 829 "CHAN [ + | - | <number> | <name> | <id> ]\n" 830 " Switch channel up, down or to the given channel number, name or id.\n" 831 " Without option (or after successfully switching to the channel)\n" 832 " it returns the current channel number and name.",
833 "CLRE [ <number> | <name> | <id> ]\n" 834 " Clear the EPG list of the given channel number, name or id.\n" 835 " Without option it clears the entire EPG list.\n" 836 " After a CLRE command, no further EPG processing is done for 10\n" 837 " seconds, so that data sent with subsequent PUTE commands doesn't\n" 838 " interfere with data from the broadcasters.",
839 "CONN name:<name> port:<port> vdrversion:<vdrversion> apiversion:<apiversion> timeout:<timeout>\n" 840 " Used by peer-to-peer connections between VDRs to tell the other VDR\n" 841 " to establish a connection to this VDR. The name is the SVDRP host name\n" 842 " of this VDR, which may differ from its DNS name.",
843 "CPYR <number> <new name>\n" 844 " Copy the recording with the given number. Before a recording can be\n" 845 " copied, an LSTR command must have been executed in order to retrieve\n" 846 " the recording numbers.\n",
850 " Delete the recording with the given id. Before a recording can be\n" 851 " deleted, an LSTR command should have been executed in order to retrieve\n" 852 " the recording ids. The ids are unique and don't change while this\n" 853 " instance of VDR is running.\n" 854 " CAUTION: THERE IS NO CONFIRMATION PROMPT WHEN DELETING A\n" 855 " RECORDING - BE SURE YOU KNOW WHAT YOU ARE DOING!",
857 " Delete the timer with the given id. If this timer is currently recording,\n" 858 " the recording will be stopped without any warning.",
860 " Edit the recording with the given id. Before a recording can be\n" 861 " edited, an LSTR command should have been executed in order to retrieve\n" 862 " the recording ids.",
863 "GRAB <filename> [ <quality> [ <sizex> <sizey> ] ]\n" 864 " Grab the current frame and save it to the given file. Images can\n" 865 " be stored as JPEG or PNM, depending on the given file name extension.\n" 866 " The quality of the grabbed image can be in the range 0..100, where 100\n" 867 " (the default) means \"best\" (only applies to JPEG). The size parameters\n" 868 " define the size of the resulting image (default is full screen).\n" 869 " If the file name is just an extension (.jpg, .jpeg or .pnm) the image\n" 870 " data will be sent to the SVDRP connection encoded in base64. The same\n" 871 " happens if '-' (a minus sign) is given as file name, in which case the\n" 872 " image format defaults to JPEG.",
874 " The HELP command gives help info.",
875 "HITK [ <key> ... ]\n" 876 " Hit the given remote control key. Without option a list of all\n" 877 " valid key names is given. If more than one key is given, they are\n" 878 " entered into the remote control queue in the given sequence. There\n" 879 " can be up to 31 keys.",
880 "LSTC [ :ids ] [ :groups | <number> | <name> | <id> ]\n" 881 " List channels. Without option, all channels are listed. Otherwise\n" 882 " only the given channel is listed. If a name is given, all channels\n" 883 " containing the given string as part of their name are listed.\n" 884 " If ':groups' is given, all channels are listed including group\n" 885 " separators. The channel number of a group separator is always 0.\n" 886 " With ':ids' the channel ids are listed following the channel numbers.\n" 887 " The special number 0 can be given to list the current channel.",
889 " List all available devices. Each device is listed with its name and\n" 890 " whether it is currently the primary device ('P') or it implements a\n" 891 " decoder ('D') and can be used as output device.",
892 "LSTE [ <channel> ] [ now | next | at <time> ]\n" 893 " List EPG data. Without any parameters all data of all channels is\n" 894 " listed. If a channel is given (either by number or by channel ID),\n" 895 " only data for that channel is listed. 'now', 'next', or 'at <time>'\n" 896 " restricts the returned data to present events, following events, or\n" 897 " events at the given time (which must be in time_t form).",
898 "LSTR [ <id> [ path ] ]\n" 899 " List recordings. Without option, all recordings are listed. Otherwise\n" 900 " the information for the given recording is listed. If a recording\n" 901 " id and the keyword 'path' is given, the actual file name of that\n" 902 " recording's directory is listed.\n" 903 " Note that the ids of the recordings are not necessarily given in\n" 905 "LSTT [ <id> ] [ id ]\n" 906 " List timers. Without option, all timers are listed. Otherwise\n" 907 " only the timer with the given id is listed. If the keyword 'id' is\n" 908 " given, the channels will be listed with their unique channel ids\n" 909 " instead of their numbers. This command lists only the timers that are\n" 910 " defined locally on this VDR, not any remote timers from other VDRs.",
912 " Displays the given message on the OSD. The message will be queued\n" 913 " and displayed whenever this is suitable.\n",
914 "MODC <number> <settings>\n" 915 " Modify a channel. Settings must be in the same format as returned\n" 916 " by the LSTC command.",
917 "MODT <id> on | off | <settings>\n" 918 " Modify a timer. Settings must be in the same format as returned\n" 919 " by the LSTT command. The special keywords 'on' and 'off' can be\n" 920 " used to easily activate or deactivate a timer.",
921 "MOVC <number> <to>\n" 922 " Move a channel to a new position.",
923 "MOVR <id> <new name>\n" 924 " Move the recording with the given id. Before a recording can be\n" 925 " moved, an LSTR command should have been executed in order to retrieve\n" 926 " the recording ids. The ids don't change during subsequent MOVR\n" 929 " Create a new channel. Settings must be in the same format as returned\n" 930 " by the LSTC command.",
932 " Create a new timer. Settings must be in the same format as returned\n" 933 " by the LSTT command.",
934 "NEXT [ abs | rel ]\n" 935 " Show the next timer event. If no option is given, the output will be\n" 936 " in human readable form. With option 'abs' the absolute time of the next\n" 937 " event will be given as the number of seconds since the epoch (time_t\n" 938 " format), while with option 'rel' the relative time will be given as the\n" 939 " number of seconds from now until the event. If the absolute time given\n" 940 " is smaller than the current time, or if the relative time is less than\n" 941 " zero, this means that the timer is currently recording and has started\n" 942 " at the given time. The first value in the resulting line is the id\n" 945 " Used by peer-to-peer connections between VDRs to keep the connection\n" 946 " from timing out. May be used at any time and simply returns a line of\n" 947 " the form '<hostname> is alive'.",
948 "PLAY <id> [ begin | <position> ]\n" 949 " Play the recording with the given id. Before a recording can be\n" 950 " played, an LSTR command should have been executed in order to retrieve\n" 951 " the recording ids.\n" 952 " The keyword 'begin' plays the recording from its very beginning, while\n" 953 " a <position> (given as hh:mm:ss[.ff] or framenumber) starts at that\n" 954 " position. If neither 'begin' nor a <position> are given, replay is resumed\n" 955 " at the position where any previous replay was stopped, or from the beginning\n" 956 " by default. To control or stop the replay session, use the usual remote\n" 957 " control keypresses via the HITK command.",
958 "PLUG <name> [ help | main ] [ <command> [ <options> ]]\n" 959 " Send a command to a plugin.\n" 960 " The PLUG command without any parameters lists all plugins.\n" 961 " If only a name is given, all commands known to that plugin are listed.\n" 962 " If a command is given (optionally followed by parameters), that command\n" 963 " is sent to the plugin, and the result will be displayed.\n" 964 " The keyword 'help' lists all the SVDRP commands known to the named plugin.\n" 965 " If 'help' is followed by a command, the detailed help for that command is\n" 966 " given. The keyword 'main' initiates a call to the main menu function of the\n" 968 "POLL <name> timers\n" 969 " Used by peer-to-peer connections between VDRs to inform other machines\n" 970 " about changes to timers. The receiving VDR shall use LSTT to query the\n" 971 " remote machine with the given name about its timers and update its list\n" 972 " of timers accordingly.\n",
973 "PRIM [ <number> ]\n" 974 " Make the device with the given number the primary device.\n" 975 " Without option it returns the currently active primary device in the same\n" 976 " format as used by the LSTD command.",
978 " Put data into the EPG list. The data entered has to strictly follow the\n" 979 " format defined in vdr(5) for the 'epg.data' file. A '.' on a line\n" 980 " by itself terminates the input and starts processing of the data (all\n" 981 " entered data is buffered until the terminating '.' is seen).\n" 982 " If a file name is given, epg data will be read from this file (which\n" 983 " must be accessible under the given name from the machine VDR is running\n" 984 " on). In case of file input, no terminating '.' shall be given.\n",
985 "REMO [ on | off ]\n" 986 " Turns the remote control on or off. Without a parameter, the current\n" 987 " status of the remote control is reported.",
989 " Forces an EPG scan. If this is a single DVB device system, the scan\n" 990 " will be done on the primary device unless it is currently recording.",
992 " Return information about disk usage (total, free, percent).",
994 " Updates a timer. Settings must be in the same format as returned\n" 995 " by the LSTT command. If a timer with the same channel, day, start\n" 996 " and stop time does not yet exist, it will be created.",
998 " Initiates a re-read of the recordings directory, which is the SVDRP\n" 999 " equivalent to 'touch .update'.",
1000 "VOLU [ <number> | + | - | mute ]\n" 1001 " Set the audio volume to the given number (which is limited to the range\n" 1002 " 0...255). If the special options '+' or '-' are given, the volume will\n" 1003 " be turned up or down, respectively. The option 'mute' will toggle the\n" 1004 " audio muting. If no option is given, the current audio volume level will\n" 1007 " Exit vdr (SVDRP).\n" 1008 " You can also hit Ctrl-D to exit.",
1036 const char *q = HelpPage;
1039 uint n = q - HelpPage;
1040 if (n >=
sizeof(topic))
1041 n =
sizeof(topic) - 1;
1042 strncpy(topic, HelpPage, n);
1056 if (strcasecmp(Cmd, t) == 0)
1077 void Close(
bool SendReply =
false,
bool Timeout =
false);
1078 bool Send(
const char *s);
1081 void CmdCHAN(const
char *Option);
1082 void CmdCLRE(const
char *Option);
1083 void CmdCONN(const
char *Option);
1084 void CmdCPYR(const
char *Option);
1085 void CmdDELC(const
char *Option);
1086 void CmdDELR(const
char *Option);
1087 void CmdDELT(const
char *Option);
1088 void CmdEDIT(const
char *Option);
1089 void CmdGRAB(const
char *Option);
1090 void CmdHELP(const
char *Option);
1091 void CmdHITK(const
char *Option);
1092 void CmdLSTC(const
char *Option);
1093 void CmdLSTD(const
char *Option);
1094 void CmdLSTE(const
char *Option);
1095 void CmdLSTR(const
char *Option);
1096 void CmdLSTT(const
char *Option);
1097 void CmdMESG(const
char *Option);
1098 void CmdMODC(const
char *Option);
1099 void CmdMODT(const
char *Option);
1100 void CmdMOVC(const
char *Option);
1101 void CmdMOVR(const
char *Option);
1102 void CmdNEWC(const
char *Option);
1103 void CmdNEWT(const
char *Option);
1104 void CmdNEXT(const
char *Option);
1105 void CmdPING(const
char *Option);
1106 void CmdPLAY(const
char *Option);
1107 void CmdPLUG(const
char *Option);
1108 void CmdPOLL(const
char *Option);
1109 void CmdPRIM(const
char *Option);
1110 void CmdPUTE(const
char *Option);
1111 void CmdREMO(const
char *Option);
1112 void CmdSCAN(const
char *Option);
1113 void CmdSTAT(const
char *Option);
1114 void CmdUPDT(const
char *Option);
1115 void CmdUPDR(const
char *Option);
1116 void CmdVOLU(const
char *Option);
1139 time_t now = time(NULL);
1182 char *buffer = NULL;
1185 if (vasprintf(&buffer, fmt, ap) >= 0) {
1188 char *n = strchr(s,
'\n');
1192 if (Code < 0 || n && *(n + 1))
1196 s = n ? n + 1 : NULL;
1200 Reply(451,
"Bad format - looks like a programming error!");
1207 Reply(451,
"Zero return code - looks like a programming error!");
1223 const int TopicsPerLine = 5;
1225 for (
int y = 0; (y * TopicsPerLine + x) < NumPages; y++) {
1228 q += sprintf(q,
" ");
1229 for (x = 0; x < TopicsPerLine && (y * TopicsPerLine + x) < NumPages; x++) {
1230 const char *topic =
GetHelpTopic(hp[(y * TopicsPerLine + x)]);
1235 Reply(-214,
"%s", buffer);
1246 int o = strtol(Option, NULL, 10);
1250 else if (strcmp(Option,
"-") == 0) {
1257 else if (strcmp(Option,
"+") == 0) {
1265 n = Channel->Number();
1267 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1268 if (!Channel->GroupSep()) {
1269 if (strcasecmp(Channel->Name(), Option) == 0) {
1270 n = Channel->Number();
1277 Reply(501,
"Undefined channel \"%s\"", Option);
1281 if (
const cChannel *Channel = Channels->GetByNumber(n)) {
1283 Reply(554,
"Error switching to channel \"%d\"", Channel->Number());
1288 Reply(550,
"Unable to find channel \"%s\"", Option);
1296 Reply(250,
"%d %s", Channel->Number(), Channel->Name());
1308 int o = strtol(Option, NULL, 10);
1310 ChannelID = Channels->GetByNumber(o)->GetChannelID();
1315 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1316 if (!Channel->GroupSep()) {
1317 if (strcasecmp(Channel->Name(), Option) == 0) {
1318 ChannelID = Channel->GetChannelID();
1329 for (
cSchedule *p = Schedules->First(); p; p = Schedules->
Next(p)) {
1330 if (p->ChannelID() == ChannelID) {
1336 for (
cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
1337 if (ChannelID == Timer->Channel()->GetChannelID().
ClrRid())
1338 Timer->SetEvent(NULL);
1342 Reply(250,
"EPG data of channel \"%s\" cleared", Option);
1345 Reply(550,
"No EPG data found for channel \"%s\"", Option);
1350 Reply(501,
"Undefined channel \"%s\"", Option);
1355 for (
cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer))
1356 Timer->SetEvent(NULL);
1357 for (
cSchedule *Schedule = Schedules->First(); Schedule; Schedule = Schedules->
Next(Schedule))
1358 Schedule->Cleanup(INT_MAX);
1360 Reply(250,
"EPG data cleared");
1369 if (ServerParams.
Ok()) {
1375 Reply(501,
"Error in server parameters: %s", ServerParams.
Error());
1378 Reply(451,
"No SVDRP client handler");
1381 Reply(501,
"Missing server parameters");
1390 Channels->SetExplicitModify();
1391 if (
cChannel *Channel = Channels->GetByNumber(strtol(Option, NULL, 10))) {
1392 if (
const cTimer *Timer = Timers->UsesChannel(Channel)) {
1393 Reply(550,
"Channel \"%s\" is in use by timer %s", Option, *Timer->ToDescr());
1397 cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr);
1398 if (CurrentChannel && Channel == CurrentChannel) {
1399 int n = Channels->GetNextNormal(CurrentChannel->
Index());
1401 n = Channels->GetPrevNormal(CurrentChannel->
Index());
1403 Reply(501,
"Can't delete channel \"%s\" - list would be empty", Option);
1406 CurrentChannel = Channels->Get(n);
1407 CurrentChannelNr = 0;
1409 Channels->Del(Channel);
1410 Channels->ReNumber();
1411 Channels->SetModifiedByUser();
1412 Channels->SetModified();
1414 if (CurrentChannel && CurrentChannel->
Number() != CurrentChannelNr) {
1416 Channels->SwitchTo(CurrentChannel->
Number());
1420 Reply(250,
"Channel \"%s\" deleted", Option);
1423 Reply(501,
"Channel \"%s\" not defined", Option);
1426 Reply(501,
"Error in channel number \"%s\"", Option);
1429 Reply(501,
"Missing channel number");
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()) {
1541 Timers->SetModified();
1543 Reply(250,
"Timer \"%s\" deleted", Option);
1546 Reply(501,
"Timer \"%s\" not defined", Option);
1549 Reply(501,
"Error in timer number \"%s\"", Option);
1552 Reply(501,
"Missing timer number");
1560 if (
const cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1562 if (Marks.
Load(Recording->FileName(), Recording->FramesPerSecond(), Recording->IsPesRecording()) && Marks.
Count()) {
1564 Reply(250,
"Editing recording \"%s\" [%s]", Option, Recording->Title());
1566 Reply(554,
"Can't start editing process");
1569 Reply(554,
"No editing marks defined");
1572 Reply(550,
"Recording \"%s\" not found", Option);
1575 Reply(501,
"Error in recording id \"%s\"", Option);
1578 Reply(501,
"Missing recording id");
1583 const char *FileName = NULL;
1585 int Quality = -1, SizeX = -1, SizeY = -1;
1587 char buf[strlen(Option) + 1];
1588 char *p = strcpy(buf, Option);
1589 const char *delim =
" \t";
1591 FileName = strtok_r(p, delim, &strtok_next);
1593 const char *Extension = strrchr(FileName,
'.');
1595 if (strcasecmp(Extension,
".jpg") == 0 || strcasecmp(Extension,
".jpeg") == 0)
1597 else if (strcasecmp(Extension,
".pnm") == 0)
1600 Reply(501,
"Unknown image type \"%s\"", Extension + 1);
1603 if (Extension == FileName)
1606 else if (strcmp(FileName,
"-") == 0)
1609 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1610 if (strcasecmp(p,
"JPEG") == 0 || strcasecmp(p,
"PNM") == 0) {
1612 p = strtok_r(NULL, delim, &strtok_next);
1618 Reply(501,
"Invalid quality \"%s\"", p);
1624 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1628 Reply(501,
"Invalid sizex \"%s\"", p);
1631 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1635 Reply(501,
"Invalid sizey \"%s\"", p);
1640 Reply(501,
"Missing sizey");
1644 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1645 Reply(501,
"Unexpected parameter \"%s\"", p);
1649 char RealFileName[PATH_MAX];
1654 const char *slash = strrchr(FileName,
'/');
1659 slash = strrchr(FileName,
'/');
1662 char *r = realpath(t, RealFileName);
1665 Reply(501,
"Invalid file name \"%s\"", FileName);
1668 strcat(RealFileName, slash);
1669 FileName = RealFileName;
1671 Reply(501,
"Invalid file name \"%s\"", FileName);
1676 Reply(550,
"Grabbing to file not allowed (use \"GRAB -\" instead)");
1685 int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE);
1687 if (
safe_write(fd, Image, ImageSize) == ImageSize) {
1689 Reply(250,
"Grabbed image %s", Option);
1693 Reply(451,
"Can't write to '%s'", FileName);
1699 Reply(451,
"Can't open '%s'", FileName);
1705 while ((s = Base64.
NextLine()) != NULL)
1706 Reply(-216,
"%s", s);
1707 Reply(216,
"Grabbed image %s", Option);
1712 Reply(451,
"Grab image failed");
1715 Reply(501,
"Missing filename");
1723 Reply(-214,
"%s", hp);
1725 Reply(504,
"HELP topic \"%s\" unknown", Option);
1731 Reply(-214,
"Topics:");
1740 Reply(-214,
"To report bugs in the implementation send email to");
1741 Reply(-214,
" vdr-bugs@tvdr.de");
1743 Reply(214,
"End of HELP info");
1750 Reply(550,
"Remote control currently disabled (key \"%s\" discarded)", Option);
1753 char buf[strlen(Option) + 1];
1754 strcpy(buf, Option);
1755 const char *delim =
" \t";
1757 char *p = strtok_r(buf, delim, &strtok_next);
1763 Reply(451,
"Too many keys in \"%s\" (only %d accepted)", Option, NumKeys);
1768 Reply(504,
"Unknown key: \"%s\"", p);
1772 p = strtok_r(NULL, delim, &strtok_next);
1774 Reply(250,
"Key%s \"%s\" accepted", NumKeys > 1 ?
"s" :
"", Option);
1777 Reply(-214,
"Valid <key> names for the HITK command:");
1778 for (
int i = 0; i <
kNone; i++) {
1781 Reply(214,
"End of key list");
1788 bool WithChannelIds =
startswith(Option,
":ids") && (Option[4] ==
' ' || Option[4] == 0);
1791 bool WithGroupSeps = strcasecmp(Option,
":groups") == 0;
1792 if (*Option && !WithGroupSeps) {
1794 int n = strtol(Option, NULL, 10);
1797 if (
const cChannel *Channel = Channels->GetByNumber(n))
1798 Reply(250,
"%d%s%s %s", Channel->Number(), WithChannelIds ?
" " :
"", WithChannelIds ? *Channel->GetChannelID().ToString() :
"", *Channel->ToText());
1800 Reply(501,
"Channel \"%s\" not defined", Option);
1805 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1806 if (!Channel->GroupSep()) {
1807 if (strcasestr(Channel->Name(), Option)) {
1818 Reply(501,
"Channel \"%s\" not defined", Option);
1822 for (
const cChannel *Channel = Channels->First(); Channel; Channel = Channels->
Next(Channel)) {
1824 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());
1825 else if (!Channel->GroupSep())
1826 Reply(Channel->Number() <
cChannels::MaxNumber() ? -250 : 250,
"%d%s%s %s", Channel->Number(), WithChannelIds ?
" " :
"", WithChannelIds ? *Channel->GetChannelID().ToString() :
"", *Channel->ToText());
1830 Reply(550,
"No channels defined");
1838 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());
1842 Reply(550,
"No devices found");
1853 char buf[strlen(Option) + 1];
1854 strcpy(buf, Option);
1855 const char *delim =
" \t";
1857 char *p = strtok_r(buf, delim, &strtok_next);
1858 while (p && DumpMode ==
dmAll) {
1859 if (strcasecmp(p,
"NOW") == 0)
1861 else if (strcasecmp(p,
"NEXT") == 0)
1863 else if (strcasecmp(p,
"AT") == 0) {
1865 if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
1867 AtTime = strtol(p, NULL, 10);
1869 Reply(501,
"Invalid time");
1874 Reply(501,
"Missing time");
1878 else if (!Schedule) {
1881 Channel = Channels->GetByNumber(strtol(Option, NULL, 10));
1885 Schedule = Schedules->GetSchedule(Channel);
1887 Reply(550,
"No schedule found");
1892 Reply(550,
"Channel \"%s\" not defined", p);
1897 Reply(501,
"Unknown option: \"%s\"", p);
1900 p = strtok_r(NULL, delim, &strtok_next);
1905 FILE *f = fdopen(fd,
"w");
1908 Schedule->
Dump(Channels, f,
"215-", DumpMode, AtTime);
1910 Schedules->Dump(f,
"215-", DumpMode, AtTime);
1912 Reply(215,
"End of EPG data");
1916 Reply(451,
"Can't open file connection");
1921 Reply(451,
"Can't dup stream descriptor");
1930 char buf[strlen(Option) + 1];
1931 strcpy(buf, Option);
1932 const char *delim =
" \t";
1934 char *p = strtok_r(buf, delim, &strtok_next);
1938 Number = strtol(p, NULL, 10);
1940 Reply(501,
"Error in recording id \"%s\"", Option);
1944 else if (strcasecmp(p,
"PATH") == 0)
1947 Reply(501,
"Unknown option: \"%s\"", p);
1950 p = strtok_r(NULL, delim, &strtok_next);
1953 if (
const cRecording *Recording = Recordings->GetById(strtol(Option, NULL, 10))) {
1954 FILE *f = fdopen(
file,
"w");
1957 Reply(250,
"%s", Recording->FileName());
1959 Recording->Info()->Write(f,
"215-");
1961 Reply(215,
"End of recording information");
1966 Reply(451,
"Can't open file connection");
1969 Reply(550,
"Recording \"%s\" not found", Option);
1972 else if (Recordings->Count()) {
1973 const cRecording *Recording = Recordings->First();
1975 Reply(Recording == Recordings->Last() ? 250 : -250,
"%d %s", Recording->
Id(), Recording->
Title(
' ',
true));
1976 Recording = Recordings->
Next(Recording);
1980 Reply(550,
"No recordings available");
1986 bool UseChannelId =
false;
1988 char buf[strlen(Option) + 1];
1989 strcpy(buf, Option);
1990 const char *delim =
" \t";
1992 char *p = strtok_r(buf, delim, &strtok_next);
1995 Id = strtol(p, NULL, 10);
1996 else if (strcasecmp(p,
"ID") == 0)
1997 UseChannelId =
true;
1999 Reply(501,
"Unknown option: \"%s\"", p);
2002 p = strtok_r(NULL, delim, &strtok_next);
2007 for (
const cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
2008 if (!Timer->Remote()) {
2009 if (Timer->Id() == Id) {
2010 Reply(250,
"%d %s", Timer->Id(), *Timer->ToText(UseChannelId));
2015 Reply(501,
"Timer \"%s\" not defined", Option);
2019 const cTimer *LastLocalTimer = Timers->Last();
2020 while (LastLocalTimer) {
2021 if (LastLocalTimer->
Remote())
2022 LastLocalTimer = Timers->
Prev(LastLocalTimer);
2026 if (LastLocalTimer) {
2027 for (
const cTimer *Timer = Timers->First(); Timer; Timer = Timers->
Next(Timer)) {
2028 if (!Timer->Remote())
2029 Reply(Timer != LastLocalTimer ? -250 : 250,
"%d %s", Timer->
Id(), *Timer->ToText(UseChannelId));
2030 if (Timer == LastLocalTimer)
2036 Reply(550,
"No timers defined");
2044 Reply(250,
"Message queued");
2047 Reply(501,
"Missing message");
2054 int n = strtol(Option, &tail, 10);
2055 if (tail && tail != Option) {
2058 Channels->SetExplicitModify();
2059 if (
cChannel *Channel = Channels->GetByNumber(n)) {
2061 if (ch.
Parse(tail)) {
2062 if (Channels->HasUniqueChannelID(&ch, Channel)) {
2064 Channels->ReNumber();
2065 Channels->SetModifiedByUser();
2066 Channels->SetModified();
2068 Reply(250,
"%d %s", Channel->Number(), *Channel->ToText());
2071 Reply(501,
"Channel settings are not unique");
2074 Reply(501,
"Error in channel settings");
2077 Reply(501,
"Channel \"%d\" not defined", n);
2080 Reply(501,
"Error in channel number");
2083 Reply(501,
"Missing channel settings");
2090 int Id = strtol(Option, &tail, 10);
2091 if (tail && tail != Option) {
2094 Timers->SetExplicitModify();
2095 if (
cTimer *Timer = Timers->GetById(Id)) {
2098 if (strcasecmp(tail,
"ON") == 0)
2100 else if (strcasecmp(tail,
"OFF") == 0)
2102 else if (!t.
Parse(tail)) {
2103 Reply(501,
"Error in timer settings");
2111 Timers->SetModified();
2113 Reply(250,
"%d %s", Timer->Id(), *Timer->ToText(
true));
2116 Reply(501,
"Timer \"%d\" not defined", Id);
2119 Reply(501,
"Error in timer id");
2122 Reply(501,
"Missing timer settings");
2129 int From = strtol(Option, &tail, 10);
2130 if (tail && tail != Option) {
2132 if (tail && tail != Option) {
2135 Channels->SetExplicitModify();
2136 int To = strtol(tail, NULL, 10);
2138 const cChannel *CurrentChannel = Channels->GetByNumber(CurrentChannelNr);
2139 cChannel *FromChannel = Channels->GetByNumber(From);
2141 cChannel *ToChannel = Channels->GetByNumber(To);
2143 int FromNumber = FromChannel->
Number();
2144 int ToNumber = ToChannel->
Number();
2145 if (FromNumber != ToNumber) {
2146 Channels->Move(FromChannel, ToChannel);
2147 Channels->ReNumber();
2148 Channels->SetModifiedByUser();
2149 Channels->SetModified();
2150 if (CurrentChannel && CurrentChannel->
Number() != CurrentChannelNr) {
2152 Channels->SwitchTo(CurrentChannel->
Number());
2157 Reply(250,
"Channel \"%d\" moved to \"%d\"", From, To);
2160 Reply(501,
"Can't move channel to same position");
2163 Reply(501,
"Channel \"%d\" not defined", To);
2166 Reply(501,
"Channel \"%d\" not defined", From);
2169 Reply(501,
"Error in channel number");
2172 Reply(501,
"Error in channel number");
2175 Reply(501,
"Missing channel number");
2181 char *opt = strdup(Option);
2184 while (*option && !isspace(*option))
2190 Recordings->SetExplicitModify();
2191 if (
cRecording *Recording = Recordings->GetById(strtol(num, NULL, 10))) {
2192 if (
int RecordingInUse = Recording->IsInUse())
2198 cString oldName = Recording->Name();
2199 if ((Recording = Recordings->GetByName(Recording->FileName())) != NULL && Recording->ChangeName(option)) {
2200 Recordings->SetModified();
2201 Recordings->TouchUpdate();
2202 Reply(250,
"Recording \"%s\" moved to \"%s\"", *oldName, Recording->Name());
2205 Reply(554,
"Error while moving recording \"%s\" to \"%s\"!", *oldName, option);
2208 Reply(501,
"Missing new recording name");
2212 Reply(550,
"Recording \"%s\" not found", num);
2215 Reply(501,
"Error in recording id \"%s\"", num);
2219 Reply(501,
"Missing recording id");
2226 if (ch.
Parse(Option)) {
2228 Channels->SetExplicitModify();
2229 if (Channels->HasUniqueChannelID(&ch)) {
2232 Channels->Add(channel);
2233 Channels->ReNumber();
2234 Channels->SetModifiedByUser();
2235 Channels->SetModified();
2240 Reply(501,
"Channel settings are not unique");
2243 Reply(501,
"Error in channel settings");
2246 Reply(501,
"Missing channel settings");
2253 if (Timer->
Parse(Option)) {
2262 Reply(501,
"Error in timer settings");
2266 Reply(501,
"Missing timer settings");
2272 if (
const cTimer *t = Timers->GetNextActiveTimer()) {
2273 time_t Start = t->StartTime();
2277 else if (strcasecmp(Option,
"ABS") == 0)
2278 Reply(250,
"%d %ld", Id, Start);
2279 else if (strcasecmp(Option,
"REL") == 0)
2280 Reply(250,
"%d %ld", Id, Start - time(NULL));
2282 Reply(501,
"Unknown option: \"%s\"", Option);
2285 Reply(550,
"No active timers");
2296 char *opt = strdup(Option);
2299 while (*option && !isspace(*option))
2306 if (
const cRecording *Recording = Recordings->GetById(strtol(num, NULL, 10))) {
2307 cString FileName = Recording->FileName();
2308 cString Title = Recording->Title();
2309 int FramesPerSecond = Recording->FramesPerSecond();
2310 bool IsPesRecording = Recording->IsPesRecording();
2318 if (strcasecmp(option,
"BEGIN") != 0)
2329 Reply(250,
"Playing recording \"%s\" [%s]", num, *Title);
2333 Reply(550,
"Recording \"%s\" not found", num);
2338 Reply(501,
"Error in recording id \"%s\"", num);
2342 Reply(501,
"Missing recording id");
2348 char *opt = strdup(Option);
2350 char *option = name;
2351 while (*option && !isspace(*option))
2360 while (*option && !isspace(*option))
2366 if (!*cmd || strcasecmp(cmd,
"HELP") == 0) {
2367 if (*cmd && *option) {
2370 Reply(-214,
"%s", hp);
2371 Reply(214,
"End of HELP info");
2374 Reply(504,
"HELP topic \"%s\" for plugin \"%s\" unknown", option, plugin->
Name());
2380 Reply(-214,
"SVDRP commands:");
2382 Reply(214,
"End of HELP info");
2385 Reply(214,
"This plugin has no SVDRP commands");
2388 else if (strcasecmp(cmd,
"MAIN") == 0) {
2390 Reply(250,
"Initiated call to main menu function of plugin \"%s\"", plugin->
Name());
2392 Reply(550,
"A plugin call is already pending - please try again later");
2395 int ReplyCode = 900;
2398 Reply(abs(ReplyCode),
"%s", *s);
2400 Reply(500,
"Command unrecognized: \"%s\"", cmd);
2404 Reply(550,
"Plugin \"%s\" not found (use PLUG for a list of plugins)", name);
2408 Reply(-214,
"Available plugins:");
2412 Reply(214,
"End of plugin list");
2419 char buf[strlen(Option) + 1];
2420 char *p = strcpy(buf, Option);
2421 const char *delim =
" \t";
2423 char *RemoteName = strtok_r(p, delim, &strtok_next);
2424 char *ListName = strtok_r(NULL, delim, &strtok_next);
2427 if (strcasecmp(ListName,
"timers") == 0) {
2431 Reply(501,
"No connection to \"%s\"", RemoteName);
2434 Reply(501,
"Unknown list name: \"%s\"", ListName);
2437 Reply(501,
"Missing list name");
2440 Reply(501,
"No SVDRP client connections");
2443 Reply(501,
"Missing parameters");
2451 int o = strtol(Option, NULL, 10);
2455 Reply(501,
"Invalid device number \"%s\"", Option);
2458 Reply(501,
"Invalid parameter \"%s\"", Option);
2461 Reply(250,
"Primary device set to %d", n);
2466 Reply(250,
"%d [%s%s] %s", d->DeviceNumber() + 1, d->HasDecoder() ?
"D" :
"-", d->DeviceNumber() + 1 ==
Setup.
PrimaryDVB ?
"P" :
"-", *d->DeviceName());
2468 Reply(501,
"Failed to get primary device");
2475 FILE *f = fopen(Option,
"r");
2479 Reply(250,
"EPG data processed from \"%s\"", Option);
2482 Reply(451,
"Error while processing EPG from \"%s\"", Option);
2486 Reply(501,
"Cannot open file \"%s\"", Option);
2500 if (!strcasecmp(Option,
"ON")) {
2502 Reply(250,
"Remote control enabled");
2504 else if (!strcasecmp(Option,
"OFF")) {
2506 Reply(250,
"Remote control disabled");
2509 Reply(501,
"Invalid Option \"%s\"", Option);
2518 Reply(250,
"EPG scan triggered");
2524 if (strcasecmp(Option,
"DISK") == 0) {
2527 Reply(250,
"%dMB %dMB %d%%", FreeMB + UsedMB, FreeMB, Percent);
2530 Reply(501,
"Invalid Option \"%s\"", Option);
2533 Reply(501,
"No option given");
2540 if (Timer->
Parse(Option)) {
2542 if (
cTimer *t = Timers->GetTimer(Timer)) {
2562 Reply(501,
"Error in timer settings");
2566 Reply(501,
"Missing timer settings");
2572 Recordings->Update(
false);
2573 Reply(250,
"Re-read of recordings directory triggered");
2581 else if (strcmp(Option,
"+") == 0)
2583 else if (strcmp(Option,
"-") == 0)
2585 else if (strcasecmp(Option,
"MUTE") == 0)
2588 Reply(501,
"Unknown option: \"%s\"", Option);
2593 Reply(250,
"Audio is mute");
2598 #define CMD(c) (strcasecmp(Cmd, c) == 0) 2615 while (*s && !isspace(*s))
2656 else Reply(500,
"Command unrecognized: \"%s\"", Cmd);
2666 if (c ==
'\n' || c == 0x00) {
2682 else if (c == 0x04 &&
numChars == 0) {
2686 else if (c == 0x08 || c == 0x7F) {
2691 else if (c <= 0x03 || c == 0x0D) {
2696 int NewLength =
length + BUFSIZ;
2697 if (
char *NewBuffer = (
char *)realloc(
cmdLine, NewLength)) {
2746 virtual void Action(
void);
2756 :
cThread(
"SVDRP server handler", true)
2757 ,tcpSocket(TcpPort, true)
2840 bool Result =
false;
2852 bool Result =
false;
2869 for (
int i = 0; i < ServerNames.
Size(); i++)
void CmdLSTT(const char *Option)
const char * Title(char Delimiter=' ', bool NewIndicator=false, int Level=-1) const
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
const char * Address(void) const
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
void CmdCONN(const char *Option)
void SetSVDRPPorts(int TcpPort, int UdpPort)
void CmdNEWT(const char *Option)
const char * Message(void)
void Close(bool SendReply=false, bool Timeout=false)
static tChannelID FromString(const char *s)
bool ToggleMute(void)
Turns the volume off or on and returns the new mute state.
bool Ready(bool Wait=true)
const char * Host(void) const
void CmdNEWC(const char *Option)
cSVDRPServerParams(const char *Params)
bool GetSVDRPServerNames(cStringList *ServerNames)
Gets a list of all available VDRs this VDR is connected to via SVDRP, and stores it in the given Serv...
bool TimedOut(void) const
Returns true if the last lock attempt this key was used with failed due to a timeout.
cString ToText(bool UseChannelID=false) const
static cString ToText(const cChannel *Channel)
void CmdSCAN(const char *Option)
virtual const char ** SVDRPHelpPages(void)
virtual const char * Version(void)=0
void CmdMESG(const char *Option)
void CmdMODC(const char *Option)
const char * FileName(void) const
Returns the full path name to the recording directory, including the video directory and the actual '...
static const char * SystemCharacterTable(void)
static void SetDisableUntil(time_t Time)
static cString sprintf(const char *fmt,...) __attribute__((format(printf
void CmdCLRE(const char *Option)
void CmdPRIM(const char *Option)
virtual void Append(T Data)
int QueueMessage(eMessageType Type, const char *s, int Seconds=0, int Timeout=0)
Like Message(), but this function may be called from a background thread.
static cMutex SVDRPHandlerMutex
void CmdVOLU(const char *Option)
cString ToDescr(void) const
void Reply(int Code, const char *fmt,...) __attribute__((format(printf
char SVDRPDefaultHost[HOST_NAME_MAX]
static eKeys FromString(const char *Name)
bool Add(int FileHandle, bool Out)
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
const int Port(void) const
cString & Truncate(int Index)
Truncate the string at the given Index (if Index is < 0 it is counted from the end of the string).
bool TriggerFetchingTimers(const char *ServerName)
const char * Connection(void) const
static const cRecordings * GetRecordingsRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of recordings for read access.
static cDevice * GetDevice(int Index)
Gets the device with the given Index.
void CmdCPYR(const char *Option)
bool Parse(const char *s)
const char * Name(void) const
const char * GetHelpTopic(const char *HelpPage)
const char * GetHelpPage(const char *Cmd, const char **p)
#define SVDRPResonseTimeout
static int NumDevices(void)
Returns the total number of devices.
static cString grabImageDir
bool Connected(void) const
bool GetRemoteTimers(cStringList &Response)
static bool SendDgram(const char *Dgram, int Port)
void CmdMOVR(const char *Option)
void CmdSTAT(const char *Option)
void Del(int FileHandle, bool Out)
void CmdNEXT(const char *Option)
const cIpAddress * LastIpAddress(void) const
#define LOCK_CHANNELS_WRITE
static int MaxNumber(void)
void SetFetchFlag(int Flag)
bool Poll(int TimeoutMs=0)
void CmdUPDR(const char *Option)
bool Execute(const char *ServerName, const char *Command, cStringList *Response=NULL)
void StopSVDRPHandler(void)
static cTimers * GetTimersWrite(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for write access.
static void SetRecording(const char *FileName)
static int CurrentVolume(void)
void CmdLSTE(const char *Option)
void Dump(const cChannels *Channels, FILE *f, const char *Prefix="", eDumpMode DumpMode=dmAll, time_t AtTime=0) const
void CmdCHAN(const char *Option)
void HandleClientConnection(void)
const int Timeout(void) const
virtual void Remove(int Index)
virtual const char * Description(void)=0
void CmdLSTD(const char *Option)
void CmdPING(const char *Option)
cPUTEhandler * PUTEhandler
static int CurrentChannel(void)
Returns the number of the current channel on the primary device.
bool GetServerNames(cStringList *ServerNames)
const char * ApiVersion(void) const
void CmdDELC(const char *Option)
void CmdGRAB(const char *Option)
void void PrintHelpTopics(const char **hp)
cSVDRPServer(int Socket, const cIpAddress *ClientIpAddress)
void HandleServerConnection(void)
cSVDRPServerHandler(int TcpPort)
bool Process(const char *s)
void SetVolume(int Volume, bool Absolute=false)
Sets the volume to the given value, either absolutely or relative to the current volume.
bool Parse(const char *s)
void StartSVDRPHandler(void)
const char * VdrVersion(void) const
cString ToString(void) const
bool Execute(const char *Command, cStringList *Response=NULL)
bool ExecSVDRPCommand(const char *ServerName, const char *Command, cStringList *Response)
Sends the given SVDRP Command string to the remote VDR identified by ServerName and collects all of t...
cIpAddress serverIpAddress
cListObject * Prev(void) const
bool SwitchChannel(const cChannel *Channel, bool LiveView)
Switches the device to the given Channel, initiating transfer mode if necessary.
static void SetEnabled(bool Enabled)
#define LOCK_CHANNELS_READ
#define LOCK_RECORDINGS_WRITE
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
void CmdUPDT(const char *Option)
void Cleanup(time_t Time)
void SetSVDRPGrabImageDir(const char *GrabImageDir)
const char * Error(void) const
void CmdPUTE(const char *Option)
void CmdHELP(const char *Option)
int HMSFToIndex(const char *HMSF, double FramesPerSecond)
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
static bool Read(FILE *f=NULL)
void WaitUntilReady(void)
#define LOCK_TIMERS_WRITE
static bool Process(cTimers *Timers, time_t t)
bool Put(uint64_t Code, bool Repeat=false, bool Release=false)
bool Open(const char *FileName, int Flags, mode_t Mode=DEFFILEMODE)
const char * NextLine(void)
Returns the next line of encoded data (terminated by '\0'), or NULL if there is no more encoded data.
static void Cleanup(bool Force=false)
virtual uchar * GrabImage(int &Size, bool Jpeg=true, int Quality=-1, int SizeX=-1, int SizeY=-1)
Grabs the currently visible screen image.
bool HasFetchFlag(int Flag)
void ProcessConnections(void)
bool Running(void)
Returns false if a derived cThread object shall leave its Action() function.
cRecordingsHandler RecordingsHandler
bool Connect(const char *Address)
static cString RecordingInUseMessage(int Reason, const char *RecordingId, cRecording *Recording)
cStateKey StateKeySVDRPRemoteTimersPoll(true)
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)
void CmdPLAY(const char *Option)
struct __attribute__((packed))
static bool Enabled(void)
char SVDRPHostName[HOST_NAME_MAX]
void CmdREMO(const char *Option)
void CmdPOLL(const char *Option)
static int VideoDiskSpace(int *FreeMB=NULL, int *UsedMB=NULL)
void CmdHITK(const char *Option)
void ProcessConnections(void)
cSocket(int Port, bool Tcp)
cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout)
const char * ClientName(void) const
static cPoller SVDRPServerPoller
void CmdLSTR(const char *Option)
static cDevice * PrimaryDevice(void)
Returns the primary device.
void CmdPLUG(const char *Option)
#define LOCK_RECORDINGS_READ
tChannelID GetChannelID(void) const
void SetFlags(uint Flags)
cSVDRPClient * GetClientForServer(const char *ServerName)
void CmdMODT(const char *Option)
const char * ServerName(void) const
const char * Remote(void) const
virtual ~cSVDRPServerHandler()
bool Transferring(void) const
Returns true if we are currently in Transfer Mode.
cListObject * Next(void) const
static void SetCurrentChannel(const cChannel *Channel)
bool StoreRemoteTimers(const char *ServerName=NULL, const cStringList *RemoteTimers=NULL)
Stores the given list of RemoteTimers, which come from the VDR ServerName, in this list.
static cRecordControl * GetRecordControl(const char *FileName)
cVector< cSVDRPServer * > serverConnections
static cPlugin * GetPlugin(int Index)
void AddClient(cSVDRPServerParams &ServerParams, const char *IpAddress)
const char * Connection(void) const
bool Acceptable(in_addr_t Address)
void ClrFlags(uint Flags)
static const tChannelID InvalidID
bool Send(const char *Command)
bool Load(const char *RecordingFileName, double FramesPerSecond=DEFAULTFRAMESPERSECOND, bool IsPesRecording=false)
#define LOCK_SCHEDULES_WRITE
virtual void Action(void)
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
void BroadcastSVDRPCommand(const char *Command)
Sends the given SVDRP Command string to all remote VDRs.
cIpAddress clientIpAddress
virtual ~cSVDRPClientHandler()
bool HasAddress(const char *Address, int Port) const
char * ExchangeChars(char *s, bool ToFileSystem)
static cSVDRPServerHandler * SVDRPServerHandler
#define LOCK_SCHEDULES_READ
cVector< cSVDRPClient * > clientConnections
void CmdDELR(const char *Option)
tChannelID & ClrRid(void)
static const cTimers * GetTimersRead(cStateKey &StateKey, int TimeoutMs=0)
Gets the list of timers for read access.
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
static bool DumpSVDRPDataTransfer
void CmdEDIT(const char *Option)
void Set(const char *Address, int Port)
static void Shutdown(void)
bool Replaying(void) const
Returns true if we are currently replaying.
static cSVDRPClientHandler * SVDRPClientHandler
void CmdDELT(const char *Option)
void CmdLSTC(const char *Option)
bool Add(int Usage, const char *FileNameSrc, const char *FileNameDst=NULL)
Adds the given FileNameSrc to the recordings handler for (later) processing.
void SortNumerically(void)
static const char * ToString(eKeys Key, bool Translate=false)
void CmdMOVC(const char *Option)
static cPoller SVDRPClientPoller
bool Process(cStringList *Response=NULL)
static bool CallPlugin(const char *Plugin)
Initiates calling the given plugin's main menu function.
int SVDRPCode(const char *s)
Returns the value of the three digit reply code of the given SVDRP response string.
bool TimedOut(void) const