/* (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 . */ #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" "\r\n" "\r\n1\r\n1\r\n\r\n" "\r\n" "urn:ses-com:device:SatIPServer:1\r\n" "%s\r\n" "Digital Devices GmbH\r\n" "http://www.digitaldevices.de\r\n" "OctopusNet\r\n" "OctopusNet\r\n" "1.0\r\n" "http://www.digitaldevices.de/OctopusNet.html\r\n" "%d\r\n" "%s\r\n" //"Universal Product Code\r\n" "\r\n" "\r\nimage/png\r\n" "120\r\n120\r\n" "24\r\n/icons/dd-120.png\r\n" "\r\n" "\r\nimage/jpeg\r\n" "120\r\n120\r\n" "24\r\n/icons/dd-120.jpg\r\n" "\r\n" "\r\nimage/png\r\n" "48\r\n48\r\n" "24\r\n/icons/dd-48.png\r\n" "\r\n" "\r\nimage/jpeg\r\n" "48\r\n48\r\n" "24\r\n/icons/dd-48.jpg\r\n" "\r\n" "\r\n" "/\r\n" "%s\r\n" "/channellist.lua?select=m3u\r\n" "\r\n" "\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]; int i; read_boxname(boxname); len = 0; for (i = 0; i < 5; i++) switch ((i + os->first_ds) % 5) { case 0: if (os->dvbs2num) len += sprintf(cap + len, ",DVBS2-%u", os->dvbs2num); break; case 1: if (os->dvbtnum) len += sprintf(cap + len, ",DVBT-%u", os->dvbtnum); break; case 2: if (os->dvbt2num) len += sprintf(cap + len, ",DVBT2-%u", os->dvbt2num); break; case 3: if (os->dvbcnum) len += sprintf(cap + len, ",DVBC-%u", os->dvbcnum); break; case 4: if (os->dvbc2num) len += sprintf(cap + len, ",DVBC2-%u", os->dvbc2num); break; } 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\n" "Pragma: no-cache\r\n" "Cache-Control: no-cache\r\n" "Content-Type: application/x-javascript\r\n\r\n"; char httpjson[] = "HTTP/1.0 200 OK\r\nConnection: close\r\n" "Pragma: no-cache\r\n" "Cache-Control: no-cache\r\n" "Content-Type: application/json\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.DelsysMask = %u;\r\n", os->delsys_mask); sendstr(fd, "Octoserve.TunerList = new Array();\r\n"); for (i = 0; i < os->dvbfe_num; 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 = %u;\r\n", i, fe->type_orig); sendstr(fd, "Octoserve.TunerList[%d].Desc = \"%s\";\r\n", i, types); } else { sendstr(fd, "Octoserve.TunerList[%d] = false;\r\n", i); } } } void send_json_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, httpjson, sizeof(httpjson) - 1); sendstr(fd, "{\r\n"); sendstr(fd, "\"Version\":" OCTOSERVE_VERSION ",\r\n"); sendstr(fd, "\"BootID\":%u,\r\n", ss->bootid); sendstr(fd, "\"DeviceID\":%u,\r\n", ss->devid); sendstr(fd, "\"MAC\":\"%02x:%02x:%02x:%02x:%02x:%02x\",\r\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); sendstr(fd, "TunerList\":[\r\n"); for (i = 0; i < os->dvbfe_num; i++) { struct dvbfe *fe = &os->dvbfe[i]; if (i) sendstr(fd, ",\r\n"); sendstr(fd, "{\"Input\":\"%u\"", 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, ",\"Present\":true", i); sendstr(fd, ",\"Type\":%u", fe->type); sendstr(fd, ",\"Desc\":\"%s\"", types); } else { sendstr(fd, "\"Present\":false", i); } sendstr(fd, "}"); } sendstr(fd, "\r\n]\r\n}\r\n"); } 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 < os->dvbfe_num; 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); } } void send_json_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, httpjson, sizeof(httpjson) - 1); sendstr(fd, "{\"TunerList\":[\r\n"); for (i = 0; i < os->dvbfe_num; i++) { if (i) sendstr(fd, ",\r\n"); fe = &os->dvbfe[i]; sendstr(fd, "{\"Input\":\"%u\"", i); sendstr(fd, ",\"Status\":\"%s\"", fe->state ? "Active" : "Inactive"); sendstr(fd, ",\"Lock\":%s", fe->lock ? "true" : "false"); sendstr(fd, ",\"Strength\":%d", fe->strength); sendstr(fd, ",\"SNR\":%d", fe->snr); sendstr(fd, ",\"Quality\":%u", fe->quality); sendstr(fd, ",\"Level\":%u", fe->level); sendstr(fd, "}"); } sendstr(fd, "\r\n]}\r\n"); } 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_json_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, httpjson, sizeof(httpjson) - 1); sendstr(fd, "{\"TimeStamp\":%u,\r\n", (uint32_t) (tp.tv_sec * 1000 + tp.tv_nsec / 1000000)); sendstr(fd, "\"StreamList\":[\r\n"); for (i = 0; i < MAX_STREAM; i++) { uint32_t ctrl = ddreg(dd, 0x400 + i*0x20); if (i) sendstr(fd, ",\r\n"); sendstr(fd, "{\"Status\":\"%s\"", (ctrl & 1) ? "Active" : "Inactive"); sendstr(fd, ",\"Input\":%u", (ctrl >> 8) & 7); sendstr(fd, ",\"Packets\":%u", ddreg(dd, 0x41c + i*0x20)); sendstr(fd, ",\"Bytes\":%u", 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, ",\"Client\":\"%u.%u.%u.%u\"", mem[off], mem[off + 1], mem[off + 2], mem[off + 3]); } else sendstr(fd, ",\"Client\":\"\""); sendstr(fd, "}"); } sendstr(fd, "\r\n]\r\n}\r\n"); 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 < os->dvbfe_num; 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.json", buf, 20)) { send_json_serverinfo(ss); break; } else if (!strncasecmp("GET /serverinfo.js", buf, 18)) { send_serverinfo(ss); break; } else if (!strncasecmp("GET /streamstatus.json", buf, 22)) { send_json_streamstatus(ss); break; } else if (!strncasecmp("GET /streamstatus.js", buf, 20)) { send_streamstatus(ss); break; } else if (!strncasecmp("GET /tunerstatus.json", buf, 21)) { send_json_tunerstatus(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 || (NULL !=strstr(buf + i, ".."))) { 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); }