mirror of
https://github.com/DigitalDevices/octonet.git
synced 2023-10-10 13:36:52 +02:00
465 lines
12 KiB
C
465 lines
12 KiB
C
/*
|
|
(C) 2012-13 Digital Devices GmbH.
|
|
|
|
This file is part of the octoserve SAT>IP server.
|
|
|
|
Octoserve is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Octoserve is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with octoserve. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "octoserve.h"
|
|
|
|
extern uint32_t debug;
|
|
|
|
char httpxml[] =
|
|
"HTTP/1.0 200 OK\r\nConnection: close\r\n"
|
|
"Content-Length: %d\r\nContent-Type: text/xml\r\nMime-Version: 1.0\r\n"
|
|
"\r\n";
|
|
|
|
char xmldesc[] =
|
|
"<\?xml version=\"1.0\"\?>\r\n"
|
|
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\" configId=\"%d\">\r\n"
|
|
"<specVersion>\r\n<major>1</major>\r\n<minor>1</minor>\r\n</specVersion>\r\n"
|
|
"<device>\r\n"
|
|
"<deviceType>urn:ses-com:device:SatIPServer:1</deviceType>\r\n"
|
|
"<friendlyName>%s</friendlyName>\r\n"
|
|
"<manufacturer>Digital Devices GmbH</manufacturer>\r\n"
|
|
"<manufacturerURL>http://www.digitaldevices.de</manufacturerURL>\r\n"
|
|
"<modelDescription>OctopusNet</modelDescription>\r\n"
|
|
"<modelName>OctopusNet</modelName>\r\n"
|
|
"<modelNumber>1.0</modelNumber>\r\n"
|
|
"<modelURL>http://www.digitaldevices.de/OctopusNet.html</modelURL>\r\n"
|
|
"<serialNumber>%d</serialNumber>\r\n"
|
|
"<UDN>%s</UDN>\r\n"
|
|
//"<UPC>Universal Product Code</UPC>\r\n"
|
|
"<iconList>\r\n"
|
|
|
|
"<icon>\r\n<mimetype>image/png</mimetype>\r\n"
|
|
"<width>120</width>\r\n<height>120</height>\r\n"
|
|
"<depth>24</depth>\r\n<url>/icons/dd-120.png</url>\r\n"
|
|
"</icon>\r\n"
|
|
|
|
"<icon>\r\n<mimetype>image/jpeg</mimetype>\r\n"
|
|
"<width>120</width>\r\n<height>120</height>\r\n"
|
|
"<depth>24</depth>\r\n<url>/icons/dd-120.jpg</url>\r\n"
|
|
"</icon>\r\n"
|
|
|
|
"<icon>\r\n<mimetype>image/png</mimetype>\r\n"
|
|
"<width>48</width>\r\n<height>48</height>\r\n"
|
|
"<depth>24</depth>\r\n<url>/icons/dd-48.png</url>\r\n"
|
|
"</icon>\r\n"
|
|
|
|
"<icon>\r\n<mimetype>image/jpeg</mimetype>\r\n"
|
|
"<width>48</width>\r\n<height>48</height>\r\n"
|
|
"<depth>24</depth>\r\n<url>/icons/dd-48.jpg</url>\r\n"
|
|
"</icon>\r\n"
|
|
|
|
"</iconList>\r\n"
|
|
"<presentationURL>/</presentationURL>\r\n"
|
|
"<satip:X_SATIPCAP xmlns:satip=\"urn:ses-com:satip\">%s</satip:X_SATIPCAP>\r\n"
|
|
"<satip:X_SATIPM3U xmlns:satip=\"urn:ses-com:satip\">/channellist.lua?select=m3u</satip:X_SATIPM3U>\r\n"
|
|
"</device>\r\n"
|
|
"</root>\r\n"
|
|
"\r\n";
|
|
|
|
char httpfile[] =
|
|
"HTTP/1.1 200 OK\r\nConnection: close\r\n"
|
|
"Content-Length: %d\r\n"
|
|
"Content-Type: %s\r\n"
|
|
"\r\n";
|
|
|
|
void send_http_error(int sock, int nr)
|
|
{
|
|
char buf[2048], *str;
|
|
int len;
|
|
|
|
if (nr == 404)
|
|
str = "Not Found";
|
|
if (nr == 405)
|
|
str = "Method not allowed";
|
|
|
|
len = snprintf(buf, sizeof(buf), "HTTP/1.1 %d %s\r\nConnection: close\r\n\r\n", nr, str);
|
|
|
|
if (len <= 0 || len >= sizeof(buf))
|
|
return;
|
|
sendlen(sock, buf, len);
|
|
}
|
|
|
|
static char *mtypes[] = {
|
|
"jpg", "image/jpeg",
|
|
"png", "image/png",
|
|
"html", "text/html",
|
|
NULL
|
|
};
|
|
|
|
void send_http_file(int sock, char *fn)
|
|
{
|
|
uint8_t buf[1024];
|
|
int len, len2, fd;
|
|
char fn2[1024] = { 0 }, *d, **m;
|
|
|
|
strcat(fn2, "/var/satip");
|
|
strcat(fn2, fn);
|
|
d = strrchr(fn, '.');
|
|
if (d) {
|
|
printf("%s\n", d);
|
|
for (d++, m = &mtypes[0]; *m; m += 2) {
|
|
if (!strcmp(*m, d)) {
|
|
m++;
|
|
break;
|
|
}
|
|
}
|
|
d = *m;
|
|
}
|
|
if (!d)
|
|
d = "binary/octet-stream";
|
|
|
|
printf("open %s %s\n", fn2, d);
|
|
fd = open(fn2, O_RDONLY);
|
|
if (fd < 0) {
|
|
send_http_error(sock, 404);
|
|
return;
|
|
}
|
|
len = lseek(fd, 0, SEEK_END);
|
|
lseek(fd, 0, SEEK_SET);
|
|
len2 = sprintf(buf, httpfile, len, d);
|
|
sendlen(sock, buf, len2);
|
|
sendfile(sock, fd, 0, len);
|
|
close(fd);
|
|
}
|
|
|
|
static int read_boxname(char *name)
|
|
{
|
|
int fd, len;
|
|
|
|
strcpy(name, "OctopusNet");
|
|
fd = open("/config/boxname", O_RDONLY);
|
|
if (fd < 0)
|
|
return 0;
|
|
printf("read boxname from /etc/boxname\n");
|
|
len = read(fd, name, 79);
|
|
if (len > 0)
|
|
name[len] = 0;
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
void send_xml(struct os_ssdp *ss)
|
|
{
|
|
struct octoserve *os = ss->os;
|
|
uint8_t buf[2048], buf2[1024], cap[1024];
|
|
int len, len2;
|
|
uint8_t *mac = &os->mac[0];
|
|
int serial = (mac[5] | (mac[4] << 8) | (mac[3] << 16)) / 2;
|
|
char boxname[80];
|
|
|
|
read_boxname(boxname);
|
|
len = 0;
|
|
if (os->dvbs2num)
|
|
len += sprintf(cap + len, ",DVBS2-%u", os->dvbs2num);
|
|
if (os->dvbtnum)
|
|
len += sprintf(cap + len, ",DVBT-%u", os->dvbtnum);
|
|
if (os->dvbt2num)
|
|
len += sprintf(cap + len, ",DVBT2-%u", os->dvbt2num);
|
|
if (os->dvbcnum)
|
|
len += sprintf(cap + len, ",DVBC-%u", os->dvbcnum);
|
|
if (os->dvbc2num)
|
|
len += sprintf(cap + len, ",DVBC2-%u", os->dvbc2num);
|
|
len = snprintf(buf, sizeof(buf), xmldesc,
|
|
ss->configid, boxname,
|
|
serial, ss->uuid_str, cap + 1);
|
|
if (len <= 0 || len >= sizeof(buf))
|
|
return;
|
|
len2=sprintf(buf2, httpxml, len);
|
|
sendlen(ss->csock, buf2, len2);
|
|
sendlen(ss->csock, buf, len);
|
|
//printf("Send:\n%s", buf2);
|
|
//printf("%s\n", buf);
|
|
}
|
|
|
|
char httptxt[] =
|
|
"HTTP/1.0 200 OK\r\nConnection: close\r\n"
|
|
"Content-Type: text/html\r\n"
|
|
"\r\n";
|
|
|
|
char httpjava[] =
|
|
"HTTP/1.0 200 OK\r\nConnection: close\r\nPragma: no-cache\r\n"
|
|
"Content-Type: application/x-javascript\r\n\r\n";
|
|
|
|
#define sendstr(_fd_,...) do { \
|
|
len = snprintf(buf, sizeof(buf), __VA_ARGS__); \
|
|
if (len <= 0 || len >= sizeof(buf)) \
|
|
break; \
|
|
sendlen(_fd_, buf, len); \
|
|
} while (0);
|
|
|
|
void send_serverinfo(struct os_ssdp *ss)
|
|
{
|
|
struct octoserve *os = ss->os;
|
|
struct dvbfe *fe;
|
|
uint8_t buf[2048];
|
|
int i, j, fd = ss->csock, len;
|
|
uint8_t *mac = &ss->os->mac[0];
|
|
|
|
sendlen(fd, httpjava, sizeof(httpjava) - 1);
|
|
sendstr(fd, "Octoserve = new Object();\r\n");
|
|
sendstr(fd, "Octoserve.Version = \"" OCTOSERVE_VERSION "\";\r\n");
|
|
sendstr(fd, "Octoserve.BootID = %u;\r\n", ss->bootid);
|
|
sendstr(fd, "Octoserve.DeviceID = %u;\r\n", ss->devid);
|
|
sendstr(fd, "Octoserve.MAC = \"%02x:%02x:%02x:%02x:%02x:%02x\";\r\n",
|
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
sendstr(fd, "Octoserve.TunerList = new Array();\r\n");
|
|
for (i = 0; i < MAX_DVB_FE; i++) {
|
|
struct dvbfe *fe = &os->dvbfe[i];
|
|
|
|
if (fe->type) {
|
|
char types[80];
|
|
int pos;
|
|
|
|
types[0] = 0;
|
|
if (fe->type & 0xb00ee) {
|
|
strcat(types, "DVB");
|
|
pos = strlen(types);
|
|
if (fe->type & (1 << SYS_DVBS))
|
|
strcat(types, "/S");
|
|
if (fe->type & (1 << SYS_DVBS2))
|
|
strcat(types, "/S2");
|
|
if (fe->type & (1 << SYS_DVBC_ANNEX_A))
|
|
strcat(types, "/C");
|
|
if (fe->type & (1 << SYS_DVBC2))
|
|
strcat(types, "/C2");
|
|
if (fe->type & (1 << SYS_DVBT))
|
|
strcat(types, "/T");
|
|
if (fe->type & (1 << SYS_DVBT2))
|
|
strcat(types, "/T2");
|
|
strcat(types, " ");
|
|
types[pos] = '-';
|
|
}
|
|
if (fe->type & 0x700) {
|
|
strcat(types, "ISDB");
|
|
pos = strlen(types);
|
|
if (fe->type & (1 << SYS_ISDBT))
|
|
strcat(types, "/T");
|
|
if (fe->type & (1 << SYS_ISDBS))
|
|
strcat(types, "/S");
|
|
if (fe->type & (1 << SYS_ISDBC))
|
|
strcat(types, "/C");
|
|
types[pos] = '-';
|
|
}
|
|
sendstr(fd, "Octoserve.TunerList[%d] = new Object();\r\n", i);
|
|
sendstr(fd, "Octoserve.TunerList[%d].Type = 0;\r\n", i);
|
|
sendstr(fd, "Octoserve.TunerList[%d].Desc = \"%s\";\r\n", i, types);
|
|
} else {
|
|
sendstr(fd, "Octoserve.TunerList[%d] = false;\r\n", i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void send_tunerstatus(struct os_ssdp *ss)
|
|
{
|
|
struct octoserve *os = ss->os;
|
|
struct dvbfe *fe;
|
|
uint8_t buf[2048];
|
|
int len, i, fd = ss->csock;
|
|
|
|
sendlen(fd, httpjava, sizeof(httpjava) - 1);
|
|
sendstr(fd, "TunerList = new Array();\r\n");
|
|
for (i = 0; i < MAX_DVB_FE; i++) {
|
|
fe = &os->dvbfe[i];
|
|
sendstr(fd, "TunerList[%d] = new Object();\r\n", i);
|
|
sendstr(fd, "TunerList[%d].Active = %s;\r\n", i, fe->state ? "true" : "false");
|
|
if (!fe->state)
|
|
continue;
|
|
sendstr(fd, "TunerList[%d].Lock = %s;\r\n", i, fe->lock ? "true" : "false");
|
|
sendstr(fd, "TunerList[%d].Quality = %u;\r\n", i, fe->quality);
|
|
sendstr(fd, "TunerList[%d].Strength = %u;\r\n", i, fe->level);
|
|
}
|
|
}
|
|
|
|
static uint32_t ddreg(int fd, uint32_t reg)
|
|
{
|
|
struct ddb_reg ddr = { .reg = reg };
|
|
|
|
ioctl(fd, IOCTL_DDB_READ_REG, &ddr);
|
|
return ddr.val;
|
|
}
|
|
|
|
static uint32_t ddrmem(int fd, uint8_t *buf, uint32_t off, uint32_t len)
|
|
{
|
|
struct ddb_mem ddm = { .off = off, .buf = buf, .len = len };
|
|
|
|
return ioctl(fd, IOCTL_DDB_READ_MEM, &ddm);
|
|
}
|
|
|
|
static void send_streamstatus(struct os_ssdp *ss)
|
|
{
|
|
struct octoserve *os = ss->os;
|
|
struct osstrm *oss;
|
|
uint8_t buf[2048];
|
|
int len, i, fd = ss->csock;
|
|
struct timeval tval;
|
|
struct timespec tp;
|
|
int dd;
|
|
|
|
dd = open("/dev/ddbridge/card0", O_RDWR); /* FIXME: replace with ioctls */
|
|
if (dd < 0)
|
|
return;
|
|
clock_gettime(CLOCK_MONOTONIC, &tp);
|
|
gettimeofday(&tval, NULL);
|
|
sendlen(fd, httpjava, sizeof(httpjava) - 1);
|
|
sendstr(fd, "TimeStamp = %d;\r\n", (uint32_t) (tp.tv_sec * 1000 + tp.tv_nsec / 1000000));
|
|
sendstr(fd, "StreamList = new Array();\r\n");
|
|
for (i = 0; i < MAX_STREAM; i++) {
|
|
uint32_t ctrl = ddreg(dd, 0x400 + i*0x20);
|
|
|
|
sendstr(fd, "StreamList[%d] = new Object();\r\n", i);
|
|
sendstr(fd, "StreamList[%d].Status = \"%s\";\r\n", i, (ctrl & 1) ? "Active" : "Inactive");
|
|
sendstr(fd, "StreamList[%d].Input = %d;\r\n", i, (ctrl >> 8) & 7);
|
|
sendstr(fd, "StreamList[%d].Packets = %d;\r\n", i, ddreg(dd, 0x41c + i*0x20));
|
|
sendstr(fd, "StreamList[%d].Bytes = %d;\r\n", i, ddreg(dd, 0x418 + i*0x20));
|
|
if (ctrl & 1) {
|
|
uint32_t off = 0x2000 + i * 0x200;
|
|
uint8_t mem[64];
|
|
|
|
ddrmem(dd, mem, off, sizeof(mem));
|
|
off = 30;
|
|
if (mem[12] == 0x81)
|
|
off += 4;
|
|
sendstr(fd, "StreamList[%d].Client = \"%u.%u.%u.%u\";\r\n", i,
|
|
mem[off], mem[off + 1], mem[off + 2], mem[off + 3]);
|
|
} else
|
|
sendstr(fd, "StreamList[%d].Client = \"\";\r\n", i);
|
|
}
|
|
close(dd);
|
|
}
|
|
|
|
static void send_octoserve(struct os_ssdp *ss)
|
|
{
|
|
struct octoserve *os = ss->os;
|
|
struct dvbfe *fe;
|
|
uint8_t buf[2048], buf2[1024];
|
|
int len, i;
|
|
|
|
send(ss->csock, httptxt, sizeof(httptxt), 0);
|
|
for (i = 0; i < MAX_DVB_FE; i++) {
|
|
fe = &os->dvbfe[i];
|
|
if (!fe->state)
|
|
continue;
|
|
len = snprintf(buf, sizeof(buf), "tuner%d: level=%d, quality=%d\r\n",
|
|
i, fe->level, fe->quality);
|
|
if (len <= 0 || len >= sizeof(buf))
|
|
return;
|
|
send(ss->csock, buf, len, 0);
|
|
}
|
|
}
|
|
|
|
void handle_http(struct os_ssdp *ss)
|
|
{
|
|
int len;
|
|
uint8_t buf[2048];
|
|
int p = 0;
|
|
|
|
while (1) {
|
|
len = recv(ss->csock, buf + p, 2048 - p, 0);
|
|
if (!len)
|
|
return;
|
|
if (len > 0) {
|
|
p += len;
|
|
if (p > sizeof(buf)) {
|
|
send_http_error(ss->csock, 405);
|
|
break;
|
|
}
|
|
if (buf[p-4] == '\r' && buf[p-3] == '\n' &&
|
|
buf[p-2] == '\r' && buf[p-1] == '\n') {
|
|
if (strncasecmp("GET ", buf, 4)) {
|
|
send_http_error(ss->csock, 405);
|
|
break;
|
|
}
|
|
dbgprintf(DEBUG_SSDP, "%s\n", buf);
|
|
if (!strncasecmp("GET /octonet.xml", buf, 16)) {
|
|
send_xml(ss);
|
|
break;
|
|
} else if (!strncasecmp("GET /serverinfo.js", buf, 18)) {
|
|
send_serverinfo(ss);
|
|
break;
|
|
} else if (!strncasecmp("GET /streamstatus.js", buf, 20)) {
|
|
send_streamstatus(ss);
|
|
break;
|
|
} else if (!strncasecmp("GET /tunerstatus.js", buf, 19)) {
|
|
send_tunerstatus(ss);
|
|
break;
|
|
} else {
|
|
int i = 3, j, fd;
|
|
|
|
while (buf[i] == ' ')
|
|
i++;
|
|
j = i;
|
|
while (buf[j] && buf[j] != '\r' && buf[j] != ' ')
|
|
j++;
|
|
buf[j] = 0;
|
|
if (i == j) {
|
|
send_http_error(ss->csock, 404);
|
|
break;
|
|
}
|
|
send_http_file(ss->csock, buf + i);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
close(ss->csock);
|
|
return;
|
|
}
|
|
|
|
static void http_loop(struct octoserve *os)
|
|
{
|
|
struct os_http *http = &os->http;
|
|
int num;
|
|
int mfd;
|
|
fd_set fds;
|
|
struct timeval timeout;
|
|
|
|
while (!os->exit) {
|
|
timeout.tv_sec = 1;
|
|
timeout.tv_usec = 0;
|
|
|
|
FD_ZERO(&fds);
|
|
mfd = 0;
|
|
add_fd(http->sock, &mfd, &fds);
|
|
|
|
num = select(mfd + 1, &fds, NULL, NULL, &timeout);
|
|
if (num < 0)
|
|
break;
|
|
if (FD_ISSET(http->sock, &fds)) {
|
|
socklen_t len;
|
|
pthread_t pt;
|
|
|
|
http->csock = accept(http->sock, &http->cadr, &len);
|
|
//handle_http(http);
|
|
}
|
|
}
|
|
}
|
|
|
|
int init_http(struct octoserve *os)
|
|
{
|
|
struct os_http *http = &os->http;
|
|
|
|
http->os = os;
|
|
http->sock = streamsock("8888", AF_INET, &http->sadr);
|
|
if (listen(http->sock, 10) < 0) {
|
|
printf("http listen error");
|
|
return;
|
|
}
|
|
pthread_create(&os->http.pt, NULL, (void *) handle_http, os);
|
|
}
|