25 |
25 |
#include "http.h"
|
26 |
26 |
#include "tcp.h"
|
27 |
27 |
#include "webui.h"
|
|
28 |
#include "htsmsg_json.h"
|
28 |
29 |
#include "dvr/dvr.h"
|
29 |
30 |
#include "filebundle.h"
|
30 |
31 |
#include "streaming.h"
|
... | ... | |
1597 |
1598 |
return page_play_(hc, remain, opaque, URLAUTH_CODE);
|
1598 |
1599 |
}
|
1599 |
1600 |
|
|
1601 |
|
|
1602 |
|
|
1603 |
#if ENABLE_HDHOMERUN_SERVER
|
|
1604 |
/**
|
|
1605 |
* Dummy password verify callback for HDHomeRun. We need this to
|
|
1606 |
* ensure we can get the aa_auth information for the user in to the
|
|
1607 |
* access_t since it is not populated by an access_get_by_username.
|
|
1608 |
* Since the user is configured solely on the server, the "passwd" is
|
|
1609 |
* always valid.
|
|
1610 |
*
|
|
1611 |
* We have the username configured on the server (not the client)
|
|
1612 |
* since HDHomeRun clients usually do not allow entry of credentials.
|
|
1613 |
*
|
|
1614 |
* This is called via the "access_get" call which does IP checking
|
|
1615 |
* (configured via the Users/IP Blocking Records tab in expert
|
|
1616 |
* setting).
|
|
1617 |
*/
|
|
1618 |
static int
|
|
1619 |
hdhomerun_server_verify_callback(void *aux, const char *passwd)
|
|
1620 |
{
|
|
1621 |
return 1;
|
|
1622 |
}
|
|
1623 |
|
|
1624 |
|
|
1625 |
static const char *hdhomerun_get_server_name(void)
|
|
1626 |
{
|
|
1627 |
return config.server_name ?: "Tvheadend";
|
|
1628 |
}
|
|
1629 |
|
|
1630 |
/**
|
|
1631 |
* Our unique device id is calculated from our server's name
|
|
1632 |
* in the general config tab.
|
|
1633 |
*/
|
|
1634 |
static uint32_t hdhomerun_get_deviceid(void)
|
|
1635 |
{
|
|
1636 |
const char *server_name = hdhomerun_get_server_name();
|
|
1637 |
const uint32_t deviceid = tvh_crc32((const uint8_t*)server_name, strlen(server_name), 0);
|
|
1638 |
return deviceid;
|
|
1639 |
}
|
|
1640 |
|
|
1641 |
/// Get the model name, defaulting to a commonly used version.
|
|
1642 |
static const char *hdhomerun_get_model_name(void)
|
|
1643 |
{
|
|
1644 |
if (config.hdhomerun_server_model_name && !strempty(config.hdhomerun_server_model_name))
|
|
1645 |
return config.hdhomerun_server_model_name;
|
|
1646 |
else
|
|
1647 |
return "HDTC-2US";
|
|
1648 |
}
|
|
1649 |
|
|
1650 |
|
|
1651 |
/**
|
|
1652 |
* @param fail_log_reason Log this reason if permissions fail.
|
|
1653 |
* @return Permission for the verified user (caller owns the memory) or NULL.
|
|
1654 |
*/
|
|
1655 |
__attribute__((warn_unused_result))
|
|
1656 |
static access_t *hdhomerun_verify_user_permission(const http_connection_t *hc,
|
|
1657 |
const char *fail_log_reason)
|
|
1658 |
{
|
|
1659 |
/* Not explicitly enabled? Then all calls fail. */
|
|
1660 |
if (!config.hdhomerun_server_enable) {
|
|
1661 |
tvhwarn(LS_WEBUI, "hdhomerun server not enabled but received request [%s]",
|
|
1662 |
fail_log_reason?:"");
|
|
1663 |
return NULL;
|
|
1664 |
}
|
|
1665 |
|
|
1666 |
const char *hdhr_user = config.hdhomerun_server_username ?: "";
|
|
1667 |
access_t *perm = access_get(hc->hc_peer, hdhr_user, hdhomerun_server_verify_callback, NULL);
|
|
1668 |
|
|
1669 |
if (access_verify2(perm, ACCESS_STREAMING)) {
|
|
1670 |
/* Failed */
|
|
1671 |
tvhwarn(LS_WEBUI, "hdhomerun server received request but no streaming permission for user [%s] [%d] [%s]",
|
|
1672 |
hdhr_user ?: "<none>",
|
|
1673 |
perm? perm->aa_rights : 0,
|
|
1674 |
fail_log_reason?:"");
|
|
1675 |
access_destroy(perm);
|
|
1676 |
return NULL;
|
|
1677 |
} else {
|
|
1678 |
return perm;
|
|
1679 |
}
|
|
1680 |
}
|
|
1681 |
|
|
1682 |
|
|
1683 |
/**
|
|
1684 |
* Return the discovery information for HDHomeRun to give clients
|
|
1685 |
* details of how to access the lineup.
|
|
1686 |
*
|
|
1687 |
* Our HDHomeRun server implementation uses a server configured
|
|
1688 |
* username to determine access rather than passing it through the
|
|
1689 |
* request. This is because HDHomeRun clients are usually configured
|
|
1690 |
* with only an IP address and port number and do not offer any input
|
|
1691 |
* for credentials.
|
|
1692 |
*/
|
|
1693 |
static int
|
|
1694 |
hdhomerun_server_discover(http_connection_t *hc, const char *remain, void *opaque)
|
|
1695 |
{
|
|
1696 |
access_t *perm = hdhomerun_verify_user_permission(hc, "discover");
|
|
1697 |
if (!perm)
|
|
1698 |
return http_noaccess_code(hc);
|
|
1699 |
|
|
1700 |
char http_ip[128];
|
|
1701 |
htsbuf_queue_t *hq = &hc->hc_reply;
|
|
1702 |
const char *server_name = hdhomerun_get_server_name();
|
|
1703 |
const uint32_t deviceid = hdhomerun_get_deviceid();
|
|
1704 |
|
|
1705 |
tcp_get_str_from_ip(hc->hc_self, http_ip, sizeof(http_ip));
|
|
1706 |
|
|
1707 |
/* The contents below for the discovery message are based on tvhProxy */
|
|
1708 |
htsmsg_t *msg = htsmsg_create_map();
|
|
1709 |
htsmsg_add_str(msg, "FriendlyName", server_name);
|
|
1710 |
htsmsg_add_str(msg,"FirmwareVersion", tvheadend_version);
|
|
1711 |
// Currently hardcoded until we encounter a client that has a
|
|
1712 |
// problem.
|
|
1713 |
htsmsg_add_str(msg, "FirmwareName", "hdhomerun_atsc");
|
|
1714 |
/* We use same value for model name/number to avoid too many user
|
|
1715 |
* configuration options.
|
|
1716 |
*/
|
|
1717 |
htsmsg_add_str(msg, "ModelNumber", hdhomerun_get_model_name());
|
|
1718 |
htsmsg_add_str(msg, "Manufacturer", "Tvheadend");
|
|
1719 |
// Random string, but has to be fixed length.
|
|
1720 |
htsmsg_add_str(msg, "DeviceAuth", "3xw5UaJXhVShHEBoy76FuYQi");
|
|
1721 |
htsmsg_add_str_printf(msg, "BaseURL", "http://%s:%u", http_ip, tvheadend_webui_port);
|
|
1722 |
htsmsg_add_str_printf(msg, "DeviceID", "%08X", deviceid);
|
|
1723 |
htsmsg_add_str_printf(msg, "LineupURL", "http://%s:%u/lineup.json", http_ip, tvheadend_webui_port);
|
|
1724 |
|
|
1725 |
// If user has not explicitly set a count then we use a default.
|
|
1726 |
// The actual number of tuners is unknown since we allow multiplex
|
|
1727 |
// sharing and some channels may actually be iptv channels so not
|
|
1728 |
// use a tuner at all.
|
|
1729 |
htsmsg_add_u32(msg, "TunerCount", config.hdhomerun_server_tuner_count ?: 6);
|
|
1730 |
|
|
1731 |
char *json = htsmsg_json_serialize_to_str(msg, 1);
|
|
1732 |
htsbuf_append_str(hq, json);
|
|
1733 |
free(json);
|
|
1734 |
http_output_content(hc, "application/json");
|
|
1735 |
access_destroy(perm);
|
|
1736 |
return 0;
|
|
1737 |
}
|
|
1738 |
|
|
1739 |
|
|
1740 |
/**
|
|
1741 |
* Return the channel lineup for HDHomeRun
|
|
1742 |
*/
|
|
1743 |
static int
|
|
1744 |
hdhomerun_server_lineup(http_connection_t *hc, const char *remain, void *opaque)
|
|
1745 |
{
|
|
1746 |
access_t *perm = hdhomerun_verify_user_permission(hc, "lineup");
|
|
1747 |
if (!perm)
|
|
1748 |
return http_noaccess_code(hc);
|
|
1749 |
|
|
1750 |
htsbuf_queue_t *hq = &hc->hc_reply;
|
|
1751 |
channel_t *ch;
|
|
1752 |
const char *name;
|
|
1753 |
const char *blank;
|
|
1754 |
const char *chnum_str;
|
|
1755 |
const int use_auth = perm && perm->aa_auth && !strempty(perm->aa_auth);
|
|
1756 |
char buf1[128], chnum[32], ubuf[UUID_HEX_SIZE];
|
|
1757 |
char url[1024];
|
|
1758 |
char http_ip[128];
|
|
1759 |
/* We use the UI flags to determine if we should include channel
|
|
1760 |
* numbers/sources in the name. This can help distinguish channels
|
|
1761 |
* when you have multiple different sources of the same channel such
|
|
1762 |
* as satellite and aerial.
|
|
1763 |
*/
|
|
1764 |
const int flags =
|
|
1765 |
(config.chname_num ? CHANNEL_ENAME_NUMBERS : 0) |
|
|
1766 |
(config.chname_src ? CHANNEL_ENAME_SOURCES : 0);
|
|
1767 |
int is_first = 1;
|
|
1768 |
|
|
1769 |
tcp_get_str_from_ip(hc->hc_self, http_ip, sizeof(http_ip));
|
|
1770 |
blank = tvh_gettext_lang(perm->aa_lang_ui, channel_blank_name);
|
|
1771 |
htsbuf_append_str(hq, "[");
|
|
1772 |
tvh_mutex_lock(&global_lock);
|
|
1773 |
CHANNEL_FOREACH(ch) {
|
|
1774 |
if (!channel_access(ch, perm, 0) || !ch->ch_enabled)
|
|
1775 |
continue;
|
|
1776 |
if (!is_first)
|
|
1777 |
htsbuf_append_str(hq, ", \n");
|
|
1778 |
name = channel_get_ename(ch, buf1, sizeof(buf1), blank, flags);
|
|
1779 |
htsbuf_append_str(hq, "{ \"GuideName\" : ");
|
|
1780 |
htsbuf_append_and_escape_jsonstr(hq, name);
|
|
1781 |
htsbuf_append_str(hq, ", \"GuideNumber\" : ");
|
|
1782 |
/* channel_get_number_as_str returns NULL if no channel number! */
|
|
1783 |
chnum_str = channel_get_number_as_str(ch, chnum, sizeof(chnum));
|
|
1784 |
htsbuf_append_and_escape_jsonstr(hq, chnum_str ? chnum_str : "0");
|
|
1785 |
htsbuf_append_str(hq, ", \"URL\" : ");
|
|
1786 |
sprintf(url, "http://%s:%u/stream/channel/%s?profile=pass%s%s",
|
|
1787 |
http_ip,
|
|
1788 |
tvheadend_webui_port,
|
|
1789 |
channel_get_uuid(ch, ubuf),
|
|
1790 |
use_auth? "&auth=" : "",
|
|
1791 |
use_auth ? perm->aa_auth : "");
|
|
1792 |
htsbuf_append_and_escape_jsonstr(hq, url);
|
|
1793 |
htsbuf_append_str(hq, "}");
|
|
1794 |
is_first = 0;
|
|
1795 |
}
|
|
1796 |
tvh_mutex_unlock(&global_lock);
|
|
1797 |
htsbuf_append_str(hq, "]");
|
|
1798 |
http_output_content(hc, "application/json");
|
|
1799 |
access_destroy(perm);
|
|
1800 |
return 0;
|
|
1801 |
}
|
|
1802 |
|
|
1803 |
|
|
1804 |
static int
|
|
1805 |
hdhomerun_server_lineup_status(http_connection_t *hc, const char *remain, void *opaque)
|
|
1806 |
{
|
|
1807 |
access_t *perm = hdhomerun_verify_user_permission(hc, "lineup_status");
|
|
1808 |
if (!perm)
|
|
1809 |
return http_noaccess_code(hc);
|
|
1810 |
|
|
1811 |
htsbuf_queue_t *hq = &hc->hc_reply;
|
|
1812 |
/* The contents below for the discovery message are based on the forum. */
|
|
1813 |
htsbuf_append_str(hq, "{\"ScanInProgress\":0,\"ScanPossible\":0,\"Source\":\"Antenna\",\"SourceList\":[\"Antenna\"]}");
|
|
1814 |
http_output_content(hc, "application/json");
|
|
1815 |
access_destroy(perm);
|
|
1816 |
return 0;
|
|
1817 |
}
|
|
1818 |
|
|
1819 |
|
|
1820 |
|
|
1821 |
/** Some media players ignore the "scan not possible" and do a post to
|
|
1822 |
* this function with "?scan=start".
|
|
1823 |
*
|
|
1824 |
* We currently ignore this request and just return success.
|
|
1825 |
* This is because Tvheadend has separate scanning and mapping stages.
|
|
1826 |
*/
|
|
1827 |
static int
|
|
1828 |
hdhomerun_server_lineup_post(http_connection_t *hc, const char *remain, void *opaque)
|
|
1829 |
{
|
|
1830 |
access_t *perm = hdhomerun_verify_user_permission(hc, "lineup_status");
|
|
1831 |
if (!perm)
|
|
1832 |
return http_noaccess_code(hc);
|
|
1833 |
|
|
1834 |
// We can't send empty contents since the caller thinks empty (size
|
|
1835 |
// 0) is "unknown length" (for streaming data). So, we'll return an
|
|
1836 |
// empty json document.
|
|
1837 |
htsbuf_append_str(&hc->hc_reply, "{}");
|
|
1838 |
http_output_content(hc, "application/json");
|
|
1839 |
access_destroy(perm);
|
|
1840 |
return 0;
|
|
1841 |
}
|
|
1842 |
|
|
1843 |
/**
|
|
1844 |
* Needed for some clients. This contains much the same as discover,
|
|
1845 |
* but in xml format.
|
|
1846 |
*/
|
|
1847 |
static int
|
|
1848 |
hdhomerun_server_device_xml(http_connection_t *hc, const char *remain, void *opaque)
|
|
1849 |
{
|
|
1850 |
access_t *perm = hdhomerun_verify_user_permission(hc, "device.xml");
|
|
1851 |
if (!perm)
|
|
1852 |
return http_noaccess_code(hc);
|
|
1853 |
|
|
1854 |
const char *server_name = hdhomerun_get_server_name();
|
|
1855 |
const char *model_name = hdhomerun_get_model_name();
|
|
1856 |
/* Need to escape strings in xml */
|
|
1857 |
char server_name_escaped[128];
|
|
1858 |
char model_name_escaped[128];
|
|
1859 |
char http_ip[128];
|
|
1860 |
htsbuf_queue_t *hq = &hc->hc_reply;
|
|
1861 |
const uint32_t deviceid = hdhomerun_get_deviceid();
|
|
1862 |
|
|
1863 |
html_escape(server_name_escaped, server_name, sizeof(server_name_escaped));
|
|
1864 |
html_escape(model_name_escaped, model_name, sizeof(model_name_escaped));
|
|
1865 |
|
|
1866 |
tcp_get_str_from_ip(hc->hc_self, http_ip, sizeof(http_ip));
|
|
1867 |
htsbuf_qprintf(hq, "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
|
|
1868 |
"<specVersion>"
|
|
1869 |
"<major>1</major>"
|
|
1870 |
"<minor>0</minor>"
|
|
1871 |
"</specVersion>"
|
|
1872 |
"<URLBase>http://%s:%u</URLBase>"
|
|
1873 |
"<device>"
|
|
1874 |
"<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>"
|
|
1875 |
"<friendlyName>%s</friendlyName>"
|
|
1876 |
"<manufacturer>Tvheadend</manufacturer>"
|
|
1877 |
"<modelName>%s</modelName>"
|
|
1878 |
"<modelNumber>%s</modelNumber>"
|
|
1879 |
"<serialNumber></serialNumber>"
|
|
1880 |
/* Version 5 UUID (random) with top part as server id*/
|
|
1881 |
"<UDN>uuid:%8.8x-745e-5d9a-8903-4a02327a7e09</UDN>"
|
|
1882 |
"</device>"
|
|
1883 |
"</root>",
|
|
1884 |
http_ip, tvheadend_webui_port,
|
|
1885 |
server_name_escaped,
|
|
1886 |
// We'll use the same for model name and number to
|
|
1887 |
// avoid too much user configuration. Some clients
|
|
1888 |
// may use the model name to infer characteristics.
|
|
1889 |
model_name_escaped,
|
|
1890 |
model_name_escaped,
|
|
1891 |
deviceid);
|
|
1892 |
|
|
1893 |
http_output_content(hc, "application/xml");
|
|
1894 |
access_destroy(perm);
|
|
1895 |
return 0;
|
|
1896 |
}
|
|
1897 |
#endif /* ENABLE_HDHOMERUN_SERVER */
|
|
1898 |
|
1600 |
1899 |
/**
|
1601 |
1900 |
*
|
1602 |
1901 |
*/
|
... | ... | |
2134 |
2433 |
http_path_add("/satip_server", NULL, satip_server_http_page, ACCESS_ANONYMOUS);
|
2135 |
2434 |
#endif
|
2136 |
2435 |
|
|
2436 |
#if ENABLE_HDHOMERUN_SERVER
|
|
2437 |
/* These names are specified in https://info.hdhomerun.com/info/http_api */
|
|
2438 |
http_path_add("/discover.json", NULL, hdhomerun_server_discover, ACCESS_ANONYMOUS);
|
|
2439 |
http_path_add("/lineup.json", NULL, hdhomerun_server_lineup, ACCESS_ANONYMOUS);
|
|
2440 |
/* These names are not specified in the documents but are required to make Plex work. */
|
|
2441 |
http_path_add("/lineup_status.json", NULL, hdhomerun_server_lineup_status, ACCESS_ANONYMOUS);
|
|
2442 |
http_path_add("/lineup.post", NULL, hdhomerun_server_lineup_post, ACCESS_ANONYMOUS);
|
|
2443 |
http_path_add("/device.xml", NULL, hdhomerun_server_device_xml, ACCESS_ANONYMOUS);
|
|
2444 |
#endif
|
|
2445 |
|
2137 |
2446 |
http_path_add_modify("/play", NULL, page_play, ACCESS_ANONYMOUS, page_play_path_modify5);
|
2138 |
2447 |
http_path_add_modify("/play/ticket", NULL, page_play_ticket, ACCESS_ANONYMOUS, page_play_path_modify12);
|
2139 |
2448 |
http_path_add_modify("/play/auth", NULL, page_play_auth, ACCESS_ANONYMOUS, page_play_path_modify10);
|