1597 |
1597 |
return page_play_(hc, remain, opaque, URLAUTH_CODE);
|
1598 |
1598 |
}
|
1599 |
1599 |
|
|
1600 |
|
|
1601 |
|
|
1602 |
#if ENABLE_HDHOMERUN_SERVER
|
|
1603 |
/**
|
|
1604 |
* Dummy password verify callback for HDHomeRun. We need this to
|
|
1605 |
* ensure we can get the aa_auth information for the user in to the
|
|
1606 |
* access_t since it is not populated by an access_get_by_username.
|
|
1607 |
* Since the user is configured solely on the server, the "passwd" is
|
|
1608 |
* always valid.
|
|
1609 |
*
|
|
1610 |
* We have the username configured on the server (not the client)
|
|
1611 |
* since HDHomeRun clients usually do not allow entry of credentials.
|
|
1612 |
*
|
|
1613 |
* This is called via the "access_get" call which does IP checking
|
|
1614 |
* (configured via the Users/IP Blocking Records tab in expert
|
|
1615 |
* setting).
|
|
1616 |
*/
|
|
1617 |
static int
|
|
1618 |
hdhomerun_server_verify_callback(void *aux, const char *passwd)
|
|
1619 |
{
|
|
1620 |
return 1;
|
|
1621 |
}
|
|
1622 |
|
|
1623 |
|
|
1624 |
static const char *hdhomerun_get_server_name(void)
|
|
1625 |
{
|
|
1626 |
return config.server_name ?: "TVHeadend";
|
|
1627 |
}
|
|
1628 |
|
|
1629 |
/**
|
|
1630 |
* Our unique device id is calculated from our server's name
|
|
1631 |
* in the general config tab.
|
|
1632 |
*/
|
|
1633 |
static uint32_t hdhomerun_get_deviceid(void)
|
|
1634 |
{
|
|
1635 |
const char *server_name = hdhomerun_get_server_name();
|
|
1636 |
const uint32_t deviceid = tvh_crc32((const uint8_t*)server_name, strlen(server_name), 0);
|
|
1637 |
return deviceid;
|
|
1638 |
}
|
|
1639 |
|
|
1640 |
|
|
1641 |
/**
|
|
1642 |
* @param fail_log_reason Log this reason if permissions fail.
|
|
1643 |
* @return Permission for the verified user (caller owns the memory) or NULL.
|
|
1644 |
*/
|
|
1645 |
__attribute__((warn_unused_result))
|
|
1646 |
static access_t *hdhomerun_verify_user_permission(const http_connection_t *hc,
|
|
1647 |
const char *fail_log_reason)
|
|
1648 |
{
|
|
1649 |
/* Not explicitly enabled? Then all calls fail. */
|
|
1650 |
if (!config.hdhomerun_server_enable) {
|
|
1651 |
tvhwarn(LS_WEBUI, "hdhomerun server not enabled but received request [%s]",
|
|
1652 |
fail_log_reason?:"");
|
|
1653 |
return NULL;
|
|
1654 |
}
|
|
1655 |
|
|
1656 |
const char *hdhr_user = config.hdhomerun_server_username ?: "";
|
|
1657 |
access_t *perm = access_get(hc->hc_peer, hdhr_user, hdhomerun_server_verify_callback, NULL);
|
|
1658 |
|
|
1659 |
if (access_verify2(perm, ACCESS_STREAMING)) {
|
|
1660 |
/* Failed */
|
|
1661 |
tvhwarn(LS_WEBUI, "hdhomerun server received request but no streaming permission for user [%s] [%d] [%s]",
|
|
1662 |
hdhr_user ?: "<none>",
|
|
1663 |
perm? perm->aa_rights : 0,
|
|
1664 |
fail_log_reason?:"");
|
|
1665 |
access_destroy(perm);
|
|
1666 |
return NULL;
|
|
1667 |
} else {
|
|
1668 |
return perm;
|
|
1669 |
}
|
|
1670 |
}
|
|
1671 |
|
|
1672 |
|
|
1673 |
/**
|
|
1674 |
* Return the discovery information for HDHomeRun to give clients
|
|
1675 |
* details of how to access the lineup.
|
|
1676 |
*
|
|
1677 |
* Our HDHomeRun server implementation uses a server configured
|
|
1678 |
* username to determine access rather than passing it through the
|
|
1679 |
* request. This is because HDHomeRun clients are usually configured
|
|
1680 |
* with only an IP address and port number and do not offer any input
|
|
1681 |
* for credentials.
|
|
1682 |
*/
|
|
1683 |
static int
|
|
1684 |
hdhomerun_server_discover(http_connection_t *hc, const char *remain, void *opaque)
|
|
1685 |
{
|
|
1686 |
access_t *perm = hdhomerun_verify_user_permission(hc, "discover");
|
|
1687 |
if (!perm)
|
|
1688 |
return http_noaccess_code(hc);
|
|
1689 |
|
|
1690 |
char http_ip[128];
|
|
1691 |
htsbuf_queue_t *hq = &hc->hc_reply;
|
|
1692 |
const char *server_name = hdhomerun_get_server_name();
|
|
1693 |
const uint32_t deviceid = hdhomerun_get_deviceid();
|
|
1694 |
|
|
1695 |
tcp_get_str_from_ip(hc->hc_self, http_ip, sizeof(http_ip));
|
|
1696 |
|
|
1697 |
/* The contents below for the discovery message are based on tvhProxy */
|
|
1698 |
htsbuf_append_str(hq, "{ ");
|
|
1699 |
htsbuf_append_and_escape_jsonstr_nv(hq, "FriendlyName", server_name);
|
|
1700 |
htsbuf_qprintf(hq, ", " \
|
|
1701 |
"\"BaseURL\" : \"http://%s:%u\", " \
|
|
1702 |
"\"DeviceAuth\": \"3xw5UaJXhVShHEBoy76FuYQi\", " \
|
|
1703 |
"\"DeviceID\": \"%08X\", " \
|
|
1704 |
"\"FirmwareName\": \"hdhomerun_atsc\", " \
|
|
1705 |
"\"FirmwareVersion\": \"20200101\", " \
|
|
1706 |
"\"LineupURL\": \"http://%s:%u/lineup.json\", " \
|
|
1707 |
"\"Manufacturer\": \"Silicondust\", " \
|
|
1708 |
"\"ModelNumber\": \"HDTC-2US\", " \
|
|
1709 |
"\"TunerCount\": 6 " \
|
|
1710 |
"}",
|
|
1711 |
http_ip, tvheadend_webui_port,
|
|
1712 |
deviceid,
|
|
1713 |
http_ip, tvheadend_webui_port);
|
|
1714 |
http_output_content(hc, "application/json");
|
|
1715 |
access_destroy(perm);
|
|
1716 |
return 0;
|
|
1717 |
}
|
|
1718 |
|
|
1719 |
|
|
1720 |
/**
|
|
1721 |
* Return the channel lineup for HDHomeRun
|
|
1722 |
*/
|
|
1723 |
static int
|
|
1724 |
hdhomerun_server_lineup(http_connection_t *hc, const char *remain, void *opaque)
|
|
1725 |
{
|
|
1726 |
access_t *perm = hdhomerun_verify_user_permission(hc, "lineup");
|
|
1727 |
if (!perm)
|
|
1728 |
return http_noaccess_code(hc);
|
|
1729 |
|
|
1730 |
htsbuf_queue_t *hq = &hc->hc_reply;
|
|
1731 |
channel_t *ch;
|
|
1732 |
const char *name;
|
|
1733 |
const char *blank;
|
|
1734 |
const char *chnum_str;
|
|
1735 |
const int use_auth = perm && perm->aa_auth && !strempty(perm->aa_auth);
|
|
1736 |
char buf1[128], chnum[32], ubuf[UUID_HEX_SIZE];
|
|
1737 |
char url[1024];
|
|
1738 |
char http_ip[128];
|
|
1739 |
/* We use the UI flags to determine if we should include channel
|
|
1740 |
* numbers/sources in the name. This can help distinguish channels
|
|
1741 |
* when you have multiple different sources of the same channel such
|
|
1742 |
* as satellite and aerial.
|
|
1743 |
*/
|
|
1744 |
const int flags =
|
|
1745 |
(config.chname_num ? CHANNEL_ENAME_NUMBERS : 0) |
|
|
1746 |
(config.chname_src ? CHANNEL_ENAME_SOURCES : 0);
|
|
1747 |
int is_first = 1;
|
|
1748 |
|
|
1749 |
tcp_get_str_from_ip(hc->hc_self, http_ip, sizeof(http_ip));
|
|
1750 |
blank = tvh_gettext_lang(perm->aa_lang_ui, channel_blank_name);
|
|
1751 |
htsbuf_append_str(hq, "[");
|
|
1752 |
tvh_mutex_lock(&global_lock);
|
|
1753 |
CHANNEL_FOREACH(ch) {
|
|
1754 |
if (!channel_access(ch, perm, 0) || !ch->ch_enabled)
|
|
1755 |
continue;
|
|
1756 |
if (!is_first)
|
|
1757 |
htsbuf_append_str(hq, ", \n");
|
|
1758 |
name = channel_get_ename(ch, buf1, sizeof(buf1), blank, flags);
|
|
1759 |
htsbuf_append_str(hq, "{ \"GuideName\" : ");
|
|
1760 |
htsbuf_append_and_escape_jsonstr(hq, name);
|
|
1761 |
htsbuf_append_str(hq, ", \"GuideNumber\" : ");
|
|
1762 |
/* channel_get_number_as_str returns NULL if no channel number! */
|
|
1763 |
chnum_str = channel_get_number_as_str(ch, chnum, sizeof(chnum));
|
|
1764 |
htsbuf_append_and_escape_jsonstr(hq, chnum_str ? chnum_str : "0");
|
|
1765 |
htsbuf_append_str(hq, ", \"URL\" : ");
|
|
1766 |
sprintf(url, "http://%s:%u/stream/channel/%s?profile=pass%s%s",
|
|
1767 |
http_ip,
|
|
1768 |
tvheadend_webui_port,
|
|
1769 |
channel_get_uuid(ch, ubuf),
|
|
1770 |
use_auth? "&auth=" : "",
|
|
1771 |
use_auth ? perm->aa_auth : "");
|
|
1772 |
htsbuf_append_and_escape_jsonstr(hq, url);
|
|
1773 |
htsbuf_append_str(hq, "}");
|
|
1774 |
is_first = 0;
|
|
1775 |
}
|
|
1776 |
tvh_mutex_unlock(&global_lock);
|
|
1777 |
htsbuf_append_str(hq, "]");
|
|
1778 |
http_output_content(hc, "application/json");
|
|
1779 |
access_destroy(perm);
|
|
1780 |
return 0;
|
|
1781 |
}
|
|
1782 |
|
|
1783 |
|
|
1784 |
static int
|
|
1785 |
hdhomerun_server_lineup_status(http_connection_t *hc, const char *remain, void *opaque)
|
|
1786 |
{
|
|
1787 |
access_t *perm = hdhomerun_verify_user_permission(hc, "lineup_status");
|
|
1788 |
if (!perm)
|
|
1789 |
return http_noaccess_code(hc);
|
|
1790 |
|
|
1791 |
htsbuf_queue_t *hq = &hc->hc_reply;
|
|
1792 |
/* The contents below for the discovery message are based on the forum. */
|
|
1793 |
htsbuf_append_str(hq, "{\"ScanInProgress\":0,\"ScanPossible\":0,\"Source\":\"Antenna\",\"SourceList\":[\"Antenna\"]}");
|
|
1794 |
http_output_content(hc, "application/json");
|
|
1795 |
access_destroy(perm);
|
|
1796 |
return 0;
|
|
1797 |
}
|
|
1798 |
|
|
1799 |
|
|
1800 |
|
|
1801 |
/**
|
|
1802 |
* Needed for some clients. This contains much the same as discover,
|
|
1803 |
* but in xml format.
|
|
1804 |
*/
|
|
1805 |
static int
|
|
1806 |
hdhomerun_server_device_xml(http_connection_t *hc, const char *remain, void *opaque)
|
|
1807 |
{
|
|
1808 |
access_t *perm = hdhomerun_verify_user_permission(hc, "device.xml");
|
|
1809 |
if (!perm)
|
|
1810 |
return http_noaccess_code(hc);
|
|
1811 |
|
|
1812 |
const char *server_name = hdhomerun_get_server_name();
|
|
1813 |
char http_ip[128];
|
|
1814 |
htsbuf_queue_t *hq = &hc->hc_reply;
|
|
1815 |
const uint32_t deviceid = hdhomerun_get_deviceid();
|
|
1816 |
|
|
1817 |
tcp_get_str_from_ip(hc->hc_self, http_ip, sizeof(http_ip));
|
|
1818 |
htsbuf_qprintf(hq, "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
|
|
1819 |
"<specVersion>"
|
|
1820 |
"<major>1</major>"
|
|
1821 |
"<minor>0</minor>"
|
|
1822 |
"</specVersion>"
|
|
1823 |
"<URLBase>http://%s:%u</URLBase>"
|
|
1824 |
"<device>"
|
|
1825 |
"<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>"
|
|
1826 |
"<friendlyName>%s</friendlyName>"
|
|
1827 |
"<manufacturer>Silicondust</manufacturer>"
|
|
1828 |
"<modelName>HDTC-2US</modelName>"
|
|
1829 |
"<modelNumber>HDTC-2US</modelNumber>"
|
|
1830 |
"<serialNumber></serialNumber>"
|
|
1831 |
/* Version 5 UUID (random) with top part as server id*/
|
|
1832 |
"<UDN>uuid:%8.8x-745e-5d9a-8903-4a02327a7e09</UDN>"
|
|
1833 |
"</device>"
|
|
1834 |
"</root>",
|
|
1835 |
http_ip, tvheadend_webui_port,
|
|
1836 |
server_name,
|
|
1837 |
deviceid);
|
|
1838 |
|
|
1839 |
http_output_content(hc, "application/xml");
|
|
1840 |
access_destroy(perm);
|
|
1841 |
return 0;
|
|
1842 |
}
|
|
1843 |
#endif /* ENABLE_HDHOMERUN_SERVER */
|
|
1844 |
|
1600 |
1845 |
/**
|
1601 |
1846 |
*
|
1602 |
1847 |
*/
|
... | ... | |
2134 |
2379 |
http_path_add("/satip_server", NULL, satip_server_http_page, ACCESS_ANONYMOUS);
|
2135 |
2380 |
#endif
|
2136 |
2381 |
|
|
2382 |
#if ENABLE_HDHOMERUN_SERVER
|
|
2383 |
/* These names are specified in https://info.hdhomerun.com/info/http_api */
|
|
2384 |
http_path_add("/discover.json", NULL, hdhomerun_server_discover, ACCESS_ANONYMOUS);
|
|
2385 |
http_path_add("/lineup.json", NULL, hdhomerun_server_lineup, ACCESS_ANONYMOUS);
|
|
2386 |
/* These names are not specified in the documents but are required to make Plex work. */
|
|
2387 |
http_path_add("/lineup_status.json", NULL, hdhomerun_server_lineup_status, ACCESS_ANONYMOUS);
|
|
2388 |
http_path_add("/device.xml", NULL, hdhomerun_server_device_xml, ACCESS_ANONYMOUS);
|
|
2389 |
#endif
|
|
2390 |
|
2137 |
2391 |
http_path_add_modify("/play", NULL, page_play, ACCESS_ANONYMOUS, page_play_path_modify5);
|
2138 |
2392 |
http_path_add_modify("/play/ticket", NULL, page_play_ticket, ACCESS_ANONYMOUS, page_play_path_modify12);
|
2139 |
2393 |
http_path_add_modify("/play/auth", NULL, page_play_auth, ACCESS_ANONYMOUS, page_play_path_modify10);
|
2140 |
|
-
|