]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
satip server: allow to pass own satip.m3u file to clients (must be in the config...
authorJaroslav Kysela <perex@perex.cz>
Wed, 10 May 2017 14:54:24 +0000 (16:54 +0200)
committerJaroslav Kysela <perex@perex.cz>
Fri, 12 May 2017 19:45:02 +0000 (21:45 +0200)
docs/class/satip_server.md
src/htsp_server.c
src/imagecache.c
src/imagecache.h
src/satip/server.c
src/webui/webui.c
src/webui/webui.h

index 3b85b3895a805ea6692bb1cc1b414019f852de2b..6774968ebfa684f9f73f4d580fe8923ad5d0d07a 100644 (file)
@@ -59,4 +59,9 @@ Hopefully (and if everything went to plan) your client should have
 now detected Tvheadend as a SAT\>IP server. If not, restart or force 
 it to perform a service discovery.
 
+###Custom M3U Playlist
+
+You may put your custom m3u playlist which is advertised to the clients
+to your tvheadend's configuration directory - filename *satip.m3u*.
+
 ---
index 352b05d1a7b40cce6e31e20d386a1542abbd4f18..b1fe329d42400d93ebc42ebfcb495edd2f1f6927 100644 (file)
@@ -2705,7 +2705,7 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in)
 {
   const char *str, *s2;
   const char *filename = NULL;
-
+  char buf[PATH_MAX];
 
   if((str = htsmsg_get_str(in, "file")) == NULL)
     return htsp_error(htsp, N_("Invalid arguments"));
@@ -2731,7 +2731,9 @@ htsp_method_file_open(htsp_connection_t *htsp, htsmsg_t *in)
     return htsp_file_open(htsp, filename, 0, de);
 
   } else if ((s2 = tvh_strbegins(str, "imagecache/")) != NULL) {
-    int fd = imagecache_open(atoi(s2));
+    int fd = -1;
+    if (!imagecache_filename(atoi(s2), buf, sizeof(buf)))
+      fd = tvh_open(buf, O_RDONLY, 0);
     if (fd < 0)
       return htsp_error(htsp, N_("Failed to open image"));
     return htsp_file_open(htsp, str, fd, NULL);
index 25f0b51aacc34afa608b43b197ae60c9745ab52e..1af5970438443de3e6d0d8757fcf9a67dc578b2e 100644 (file)
@@ -642,11 +642,10 @@ imagecache_get_id ( const char *url )
  * Get data
  */
 int
-imagecache_open ( uint32_t id )
+imagecache_filename ( uint32_t id, char *name, size_t len )
 {
   imagecache_image_t skel, *i;
   char *fn;
-  int fd = -1;
 
   lock_assert(&global_lock);
 
@@ -659,7 +658,8 @@ imagecache_open ( uint32_t id )
   if (!strncasecmp(i->url, "file://", 7)) {
     fn = strdupa(i->url + 7);
     http_deescape(fn);
-    fd = open(fn, O_RDONLY);
+    strncpy(name, fn, len);
+    name[len-1] = '\0';
   }
 
   /* Remote file */
@@ -688,9 +688,10 @@ imagecache_open ( uint32_t id )
       if (e)
         return -1;
     }
-    fd = hts_settings_open_file(0, "imagecache/data/%d", i->id);
+    if (hts_settings_buildpath(name, len, "imagecache/data/%d", i->id))
+      return -1;
   }
 #endif
 
-  return fd;
+  return 0;
 }
index 4783f4de374b96ed6ca716abf4401f15f8ea1c0d..f4201bd9715ec2e8cdfea615ea6235d509e4a413 100644 (file)
@@ -44,6 +44,6 @@ void     imagecache_trigger  ( void );
 // Note: will return 0 if invalid (must serve original URL)
 uint32_t imagecache_get_id  ( const char *url );
 
-int      imagecache_open    ( uint32_t id );
+int      imagecache_filename ( uint32_t id, char *name, size_t len );
 
 #endif /* __IMAGE_CACHE_H__ */
index 9d503fe0dab0c1a0e7f613c828cb15e0e0894338..c00435b72bed777fa8504731b07cd5dac21941fb 100644 (file)
@@ -22,6 +22,7 @@
 #include "settings.h"
 #include "config.h"
 #include "input/mpegts/iptv/iptv_private.h"
+#include "webui/webui.h"
 #include "satip/server.h"
 
 #define UPNP_MAX_AGE 1800
@@ -115,7 +116,7 @@ satip_server_http_xml(http_connection_t *hc)
   htsbuf_queue_t q;
   mpegts_network_t *mn;
   int dvbt = 0, dvbs = 0, dvbc = 0, atsc = 0;
-  int srcs = 0, delim = 0, tuners = 0, i;
+  int srcs = 0, delim = 0, tuners = 0, i, satipm3u = 0;
   struct xml_type_xtab *p;
   http_arg_list_t args;
 
@@ -186,6 +187,9 @@ satip_server_http_xml(http_connection_t *hc)
   else
     snprintf(buf2, sizeof(buf2), " %s", satip_server_conf.satip_uuid  + 26);
 
+  if (!hts_settings_buildpath(buf, sizeof(buf), "satip.m3u"))
+    satipm3u = access(buf, R_OK) == 0;
+
   snprintf(buf, sizeof(buf), MSG,
            config_get_server_name(),
            buf2, tvheadend_version,
@@ -197,7 +201,9 @@ satip_server_http_xml(http_connection_t *hc)
            http_server_ip, http_server_port,
            devicelist ?: "",
            satip_server_conf.satip_nom3u ? "" :
-             "<satip:X_SATIPM3U xmlns:satip=\"urn:ses-com:satip\">/playlist/satip/channels</satip:X_SATIPM3U>\n");
+             (satipm3u ?
+               "<satip:X_SATIPM3U xmlns:satip=\"urn:ses-com:satip\">/satip_server/satip.m3u</satip:X_SATIPM3U>\n" :
+               "<satip:X_SATIPM3U xmlns:satip=\"urn:ses-com:satip\">/playlist/satip/channels</satip:X_SATIPM3U>\n"));
 
   free(devicelist);
 
@@ -220,12 +226,25 @@ satip_server_http_xml(http_connection_t *hc)
 #undef MSG
 }
 
+static int
+satip_server_satip_m3u(http_connection_t *hc)
+{
+  char path[PATH_MAX];
+
+  if (hts_settings_buildpath(path, sizeof(path), "satip.m3u"))
+    return HTTP_STATUS_SERVICE;
+
+  return http_serve_file(hc, path, 0, MIME_M3U, NULL, NULL, NULL);
+}
+
 int
 satip_server_http_page(http_connection_t *hc,
                        const char *remain, void *opaque)
 {
   if (strcmp(remain, "desc.xml") == 0)
     return satip_server_http_xml(hc);
+  if (strcmp(remain, "satip.m3u") == 0)
+    return satip_server_satip_m3u(hc);
   return 0;
 }
 
index 15fed7df8bcd0804be04ad95714d42c139972026..4f0421b21bc8ffcdaf7bf6120c8beb87810c6267 100644 (file)
@@ -61,9 +61,6 @@
 #include <sys/socket.h>
 #endif
 
-#define MIME_M3U "audio/x-mpegurl"
-#define MIME_E2 "application/x-e2-bouquet"
-
 typedef int (sortfcn_t)(const void *, const void *);
 
 enum {
@@ -1550,24 +1547,25 @@ page_play(http_connection_t *hc, const char *remain, void *opaque)
 }
 
 /**
- * Download a recorded file
+ * Send a file
  */
-static int
-page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
+int
+http_serve_file(http_connection_t *hc, const char *fname,
+                int fconv, const char *content,
+                int (*preop)(http_connection_t *hc, off_t file_start,
+                             size_t content_len, void *opaque),
+                void (*stats)(http_connection_t *hc, size_t len, void *opaque),
+                void *opaque)
 {
   int fd, ret;
   struct stat st;
-  const char *content = NULL, *range, *filename;
-  dvr_entry_t *de;
-  char *fname, *charset;
+  const char *range;
   char *basename;
   char *str, *str0;
   char range_buf[255];
   char *disposition = NULL;
   off_t content_len, chunk;
   intmax_t file_start, file_end;
-  void *tcp_id;
-  th_subscription_t *sub;
   htsbuf_queue_t q;
 #if defined(PLATFORM_LINUX)
   ssize_t r;
@@ -1575,53 +1573,26 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
   off_t r;
 #endif
   
-  if(remain == NULL)
-    return HTTP_STATUS_BAD_REQUEST;
-
-  if(hc->hc_access == NULL ||
-     (access_verify2(hc->hc_access, ACCESS_OR |
-                                    ACCESS_STREAMING |
-                                    ACCESS_ADVANCED_STREAMING |
-                                    ACCESS_RECORDER)))
-    return HTTP_STATUS_UNAUTHORIZED;
-
-  pthread_mutex_lock(&global_lock);
-  de = dvr_entry_find_by_uuid(remain);
-  if (de == NULL)
-    de = dvr_entry_find_by_id(atoi(remain));
-  if(de == NULL || (filename = dvr_get_filename(de)) == NULL) {
-    pthread_mutex_unlock(&global_lock);
-    return HTTP_STATUS_NOT_FOUND;
-  }
-  if(dvr_entry_verify(de, hc->hc_access, 1)) {
-    pthread_mutex_unlock(&global_lock);
-    return HTTP_STATUS_UNAUTHORIZED;
-  }
-
-  fname = tvh_strdupa(filename);
-  content = muxer_container_filename2mime(fname, 1);
-  charset = de->de_config ? de->de_config->dvr_charset_id : NULL;
-
-  pthread_mutex_unlock(&global_lock);
-
-  basename = strrchr(fname, '/');
-  if (basename) {
-    basename++; /* Skip '/' */
-    str0 = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1),
-                                basename, strlen(basename) * 3);
-    if (str0 == NULL)
-      return HTTP_STATUS_INTERNAL;
-    htsbuf_queue_init(&q, 0);
-    htsbuf_append_and_escape_url(&q, basename);
-    str = htsbuf_to_string(&q);
-    r = 50 + strlen(str0) + strlen(str);
-    disposition = alloca(r);
-    snprintf(disposition, r,
-             "attachment; filename=\"%s\"; filename*=UTF-8''%s",
-             str0, str);
-    htsbuf_queue_flush(&q);
-    free(str);
-    free(str0);
+  if (fconv) {
+    basename = strrchr(fname, '/');
+    if (basename) {
+      basename++; /* Skip '/' */
+      str0 = intlconv_utf8safestr(intlconv_charset_id("ASCII", 1, 1),
+                                  basename, strlen(basename) * 3);
+      if (str0 == NULL)
+        return HTTP_STATUS_INTERNAL;
+      htsbuf_queue_init(&q, 0);
+      htsbuf_append_and_escape_url(&q, basename);
+      str = htsbuf_to_string(&q);
+      r = 50 + strlen(str0) + strlen(str);
+      disposition = alloca(r);
+      snprintf(disposition, r,
+               "attachment; filename=\"%s\"; filename*=UTF-8''%s",
+               str0, str);
+      htsbuf_queue_flush(&q);
+      free(str);
+      free(str0);
+    }
   }
 
   fd = tvh_open(fname, O_RDONLY, 0);
@@ -1666,47 +1637,13 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
       return HTTP_STATUS_INTERNAL;
     }
 
-  pthread_mutex_lock(&global_lock);
-  tcp_id = http_stream_preop(hc);
-  sub = NULL;
-  if (tcp_id && !hc->hc_no_output && content_len > 64*1024) {
-    sub = subscription_create(NULL, 1, "HTTP",
-                              SUBSCRIPTION_NONE, NULL,
-                              hc->hc_peer_ipstr, hc->hc_username,
-                              http_arg_get(&hc->hc_args, "User-Agent"));
-    if (sub == NULL) {
-      http_stream_postop(tcp_id);
-      tcp_id = NULL;
-    } else {
-      str = intlconv_to_utf8safestr(charset, fname, strlen(fname) * 3);
-      if (str == NULL)
-        str = intlconv_to_utf8safestr(intlconv_charset_id("ASCII", 1, 1),
-                                      fname, strlen(fname) * 3);
-      if (str == NULL)
-        str = strdup("error");
-      basename = malloc(strlen(str) + 7 + 1);
-      strcpy(basename, "file://");
-      strcat(basename, str);
-      sub->ths_dvrfile = basename;
-      free(str);
-    }
-  }
-  /* Play count + 1 when write access */
-  if (!hc->hc_no_output && file_start <= 0 &&
-      !dvr_entry_verify(de, hc->hc_access, 0)) {
-    de = dvr_entry_find_by_uuid(remain);
-    if (de == NULL)
-      de = dvr_entry_find_by_id(atoi(remain));
-    if (de) {
-      de->de_playcount = de->de_playcount + 1;
-      dvr_entry_changed_notify(de);
+  if (preop) {
+    ret = preop(hc, file_start, content_len, opaque);
+    if (ret) {
+      close(fd);
+      return ret;
     }
   }
-  pthread_mutex_unlock(&global_lock);
-  if (tcp_id == NULL) {
-    close(fd);
-    return HTTP_STATUS_NOT_ALLOWED;
-  }
 
   pthread_mutex_lock(&hc->hc_fd_lock);
   http_send_header(hc, range ? HTTP_STATUS_PARTIAL_CONTENT : HTTP_STATUS_OK,
@@ -1716,7 +1653,7 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
   ret = 0;
   if(!hc->hc_no_output) {
     while(content_len > 0) {
-      chunk = MIN(1024 * (sub ? 128 : 1024 * 1024), content_len);
+      chunk = MIN(1024 * ((stats ? 128 : 1024) * 1024), content_len);
 #if defined(PLATFORM_LINUX)
       r = sendfile(hc->hc_fd, fd, NULL, chunk);
 #elif defined(PLATFORM_FREEBSD)
@@ -1730,19 +1667,133 @@ page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
         break;
       }
       content_len -= r;
-      if (sub) {
-        subscription_add_bytes_in(sub, r);
-        subscription_add_bytes_out(sub, r);
-      }
+      if (stats)
+        stats(hc, r, opaque);
     }
   }
   pthread_mutex_unlock(&hc->hc_fd_lock);
   close(fd);
 
+  return ret;
+}
+
+/**
+ * Download a recorded file
+ */
+typedef struct page_dvrfile_priv {
+  const char *uuid;
+  char *fname;
+  const char *charset;
+  const char *content;
+  void *tcp_id;
+  th_subscription_t *sub;
+} page_dvrfile_priv_t;
+
+static int
+page_dvrfile_preop(http_connection_t *hc, off_t file_start,
+                   size_t content_len, void *opaque)
+{
+  page_dvrfile_priv_t *priv = opaque;
+  char *str, *basename;
+  dvr_entry_t *de;
+
   pthread_mutex_lock(&global_lock);
-  if (sub)
-    subscription_unsubscribe(sub, UNSUBSCRIBE_FINAL);
-  http_stream_postop(tcp_id);
+  priv->tcp_id = http_stream_preop(hc);
+  priv->sub = NULL;
+  if (priv->tcp_id && !hc->hc_no_output && content_len > 64*1024) {
+    priv->sub = subscription_create(NULL, 1, "HTTP",
+                                    SUBSCRIPTION_NONE, NULL,
+                                    hc->hc_peer_ipstr, hc->hc_username,
+                                    http_arg_get(&hc->hc_args, "User-Agent"));
+    if (priv->sub == NULL) {
+      http_stream_postop(priv->tcp_id);
+      priv->tcp_id = NULL;
+    } else {
+      str = intlconv_to_utf8safestr(priv->charset, priv->fname, strlen(priv->fname) * 3);
+      if (str == NULL)
+        str = intlconv_to_utf8safestr(intlconv_charset_id("ASCII", 1, 1),
+                                      priv->fname, strlen(priv->fname) * 3);
+      if (str == NULL)
+        str = strdup("error");
+      basename = malloc(strlen(str) + 7 + 1);
+      strcpy(basename, "file://");
+      strcat(basename, str);
+      priv->sub->ths_dvrfile = basename;
+      free(str);
+    }
+  }
+  /* Play count + 1 when write access */
+  if (!hc->hc_no_output && file_start <= 0) {
+    de = dvr_entry_find_by_uuid(priv->uuid);
+    if (de == NULL)
+      de = dvr_entry_find_by_id(atoi(priv->uuid));
+    if (de && !dvr_entry_verify(de, hc->hc_access, 0)) {
+      de->de_playcount = de->de_playcount + 1;
+      dvr_entry_changed_notify(de);
+    }
+  }
+  pthread_mutex_unlock(&global_lock);
+  if (priv->tcp_id == NULL)
+    return HTTP_STATUS_NOT_ALLOWED;
+  return 0;
+}
+
+static void
+page_dvrfile_stats(http_connection_t *hc, size_t len, void *opaque)
+{
+  page_dvrfile_priv_t *priv = opaque;
+  if (priv->sub) {
+    subscription_add_bytes_in(priv->sub, len);
+    subscription_add_bytes_out(priv->sub, len);
+  }
+}
+
+static int
+page_dvrfile(http_connection_t *hc, const char *remain, void *opaque)
+{
+  int ret;
+  const char *filename;
+  char *basename;
+  dvr_entry_t *de;
+  page_dvrfile_priv_t priv;
+
+  if(remain == NULL)
+    return HTTP_STATUS_BAD_REQUEST;
+
+  if(hc->hc_access == NULL ||
+     (access_verify2(hc->hc_access, ACCESS_OR |
+                                    ACCESS_STREAMING |
+                                    ACCESS_ADVANCED_STREAMING |
+                                    ACCESS_RECORDER)))
+    return HTTP_STATUS_UNAUTHORIZED;
+
+  pthread_mutex_lock(&global_lock);
+  de = dvr_entry_find_by_uuid(remain);
+  if (de == NULL)
+    de = dvr_entry_find_by_id(atoi(remain));
+  if(de == NULL || (filename = dvr_get_filename(de)) == NULL) {
+    pthread_mutex_unlock(&global_lock);
+    return HTTP_STATUS_NOT_FOUND;
+  }
+  if(dvr_entry_verify(de, hc->hc_access, 1)) {
+    pthread_mutex_unlock(&global_lock);
+    return HTTP_STATUS_UNAUTHORIZED;
+  }
+
+  priv.uuid = remain;
+  priv.fname = tvh_strdupa(filename);
+  priv.content = muxer_container_filename2mime(priv.fname, 1);
+  priv.charset = de->de_config ? de->de_config->dvr_charset_id : NULL;
+
+  pthread_mutex_unlock(&global_lock);
+
+  ret = http_serve_file(hc, priv.fname, 1, priv.content,
+                        page_dvrfile_preop, page_dvrfile_stats, &priv);
+
+  pthread_mutex_lock(&global_lock);
+  if (priv.sub)
+    subscription_unsubscribe(priv.sub, UNSUBSCRIBE_FINAL);
+  http_stream_postop(priv.tcp_id);
   pthread_mutex_unlock(&global_lock);
   return ret;
 }
@@ -1757,10 +1808,8 @@ static int
 page_imagecache(http_connection_t *hc, const char *remain, void *opaque)
 {
   uint32_t id;
-  int fd;
-  char buf[8192];
-  struct stat st;
-  ssize_t c;
+  int r;
+  char fname[PATH_MAX];
 
   if(remain == NULL)
     return HTTP_STATUS_NOT_FOUND;
@@ -1780,31 +1829,13 @@ page_imagecache(http_connection_t *hc, const char *remain, void *opaque)
 
   /* Fetch details */
   pthread_mutex_lock(&global_lock);
-  fd = imagecache_open(id);
+  r = imagecache_filename(id, fname, sizeof(fname));
   pthread_mutex_unlock(&global_lock);
 
-  /* Check result */
-  if (fd < 0)
-    return HTTP_STATUS_NOT_FOUND;
-  if (fstat(fd, &st)) {
-    close(fd);
+  if (r)
     return HTTP_STATUS_NOT_FOUND;
-  }
 
-  pthread_mutex_lock(&hc->hc_fd_lock);
-  http_send_header(hc, 200, NULL, st.st_size, 0, NULL, 10, 0, NULL, NULL);
-
-  while (!hc->hc_no_output) {
-    c = read(fd, buf, sizeof(buf));
-    if (c <= 0)
-      break;
-    if (tvh_write(hc->hc_fd, buf, c))
-      break;
-  }
-  pthread_mutex_unlock(&hc->hc_fd_lock);
-  close(fd);
-
-  return 0;
+  return http_serve_file(hc, fname, 0, NULL, NULL, NULL, NULL);
 }
 
 /**
@@ -1814,7 +1845,7 @@ static void
 webui_static_content(const char *http_path, const char *source)
 {
   http_path_add(http_path, (void *)source, page_static_file,
-    ACCESS_WEB_INTERFACE);
+                ACCESS_WEB_INTERFACE);
 }
 
 
index fac1b973359edc30949a3b959e68bd2493661828..2ce29a71b8f39813ad86322b9e2199cdd0e15e6b 100644 (file)
@@ -23,6 +23,9 @@
 #include "idnode.h"
 #include "http.h"
 
+#define MIME_M3U "audio/x-mpegurl"
+#define MIME_E2 "application/x-e2-bouquet"
+
 void webui_init(int xspf);
 void webui_done(void);
 
@@ -33,6 +36,14 @@ void extjs_start(void);
 size_t html_escaped_len(const char *src);
 const char* html_escape(char *dst, const char *src, size_t len);
 
+int
+http_serve_file(http_connection_t *hc, const char *fname,
+                int fconv, const char *content,
+                int (*preop)(http_connection_t *hc, off_t file_start,
+                             size_t content_len, void *opaque),
+                void (*stats)(http_connection_t *hc, size_t len, void *opaque),
+                void *opaque);
+
 int page_static_file(http_connection_t *hc, const char *remain, void *opaque);
 int page_xmltv(http_connection_t *hc, const char *remain, void *opaque);
 int page_markdown(http_connection_t *hc, const char *remain, void *opaque);