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