summaryrefslogtreecommitdiff
path: root/src/input/input_http.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/input/input_http.c')
-rw-r--r--src/input/input_http.c164
1 files changed, 113 insertions, 51 deletions
diff --git a/src/input/input_http.c b/src/input/input_http.c
index 3adbc836b..c2836affe 100644
--- a/src/input/input_http.c
+++ b/src/input/input_http.c
@@ -15,11 +15,9 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
*
* input plugin for http network streams
- *
- * $Id: input_http.c,v 1.129 2007/03/17 16:47:16 dgp85 Exp $
*/
#ifdef HAVE_CONFIG_H
@@ -48,9 +46,9 @@
#define LOG
*/
-#include "xine_internal.h"
-#include "xineutils.h"
-#include "input_plugin.h"
+#include <xine/xine_internal.h>
+#include <xine/xineutils.h>
+#include <xine/input_plugin.h>
#include "net_buf_ctrl.h"
#include "http_helper.h"
@@ -64,13 +62,13 @@
#define TAG_ICY_NOTICE2 "icy-notice2:"
#define TAG_ICY_METAINT "icy-metaint:"
#define TAG_CONTENT_TYPE "Content-Type:"
+#define TAG_LASTFM_SERVER "Server: last.fm "
typedef struct {
input_plugin_t input_plugin;
xine_stream_t *stream;
- int fh;
char *mrl;
nbc_t *nbc;
@@ -79,30 +77,31 @@ typedef struct {
off_t contentlength;
char buf[BUFSIZE];
- char proxybuf[BUFSIZE];
- char auth[BUFSIZE];
- char proxyauth[BUFSIZE];
-
+ char preview[MAX_PREVIEW_SIZE];
+ off_t preview_size;
+
char *proto;
char *user;
char *password;
char *host;
- int port;
char *uri;
-
- char preview[MAX_PREVIEW_SIZE];
- off_t preview_size;
-
+ int port;
+
+ int fh;
+
+ /** Set to 1 if the stream is a NSV stream. */
+ int is_nsv:1;
+ /** Set to 1 if the stream comes from last.fm. */
+ int is_lastfm:1;
+ /** Set to 1 if the stream is ShoutCast. */
+ int shoutcast_mode:1;
+
/* ShoutCast */
- int shoutcast_mode;
int shoutcast_metaint;
off_t shoutcast_pos;
char *shoutcast_songtitle;
- /* NSV */
- int is_nsv;
-
/* scratch buffer for forward seeking */
char seek_buf[BUFSIZE];
@@ -117,13 +116,13 @@ typedef struct {
config_values_t *config;
char *proxyhost;
+ char *proxyhost_env;
int proxyport;
+ int proxyport_env;
+
char *proxyuser;
char *proxypassword;
char *noproxylist;
-
- char *proxyhost_env;
- int proxyport_env;
} http_input_class_t;
static void proxy_host_change_cb (void *this_gen, xine_cfg_entry_t *cfg) {
@@ -181,7 +180,7 @@ static int _x_use_proxy(http_input_class_t *this, const char *host) {
/* \177\0\0\1 is the *octal* representation of 127.0.0.1 */
if ( info->h_addrtype == AF_INET && !memcmp(info->h_addr_list[0], "\177\0\0\1", 4) ) {
lprintf("host '%s' is localhost\n", host);
- return 1;
+ return 0;
}
/* TODO: IPv6 check */
}
@@ -200,13 +199,13 @@ static int _x_use_proxy(http_input_class_t *this, const char *host) {
/* special-case domain beginning with '=' -> is a host name */
if (domain[0] == '=' && strcmp(target, domain + 1) == 0) {
lprintf("host '%s' is in no-proxy domain '%s'\n", target, domain);
- return 1;
+ return 0;
}
noprox_len = strlen(domain);
/* special-case host==domain, avoiding dot checks */
if (host_len == noprox_len && strcmp(target, domain) == 0) {
lprintf("host '%s' is in no-proxy domain '%s'\n", target, domain);
- return 1;
+ return 0;
}
/* check for host in domain, and require that (if matched) the domain
* name is preceded by a dot, either in the host or domain strings,
@@ -216,7 +215,7 @@ static int _x_use_proxy(http_input_class_t *this, const char *host) {
&& (domain[0] == '.' || target[host_len - noprox_len - 1] == '.')
&& strcmp(target + host_len - noprox_len, domain) == 0) {
lprintf("host '%s' is in no-proxy domain '%s'\n", target, domain);
- return 1;
+ return 0;
}
lprintf("host '%s' isn't in no-proxy domain '%s'\n", target, domain);
}
@@ -225,11 +224,11 @@ static int _x_use_proxy(http_input_class_t *this, const char *host) {
}
free(no_proxy);
- return 0;
+ return 1;
}
static int http_plugin_basicauth (const char *user, const char *password, char* dest, int len) {
- static char *enctable="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+ static const char enctable[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
char *tmp;
char *sptr;
char *dptr;
@@ -246,7 +245,7 @@ static int http_plugin_basicauth (const char *user, const char *password, char*
if (len < enclen)
return -1;
- tmp = malloc (sizeof(char) * (totlen + 1));
+ tmp = malloc (totlen + 1);
strcpy (tmp, user);
strcat (tmp, ":");
if (password != NULL)
@@ -383,6 +382,25 @@ static off_t http_plugin_read_int (http_input_plugin_t *this,
if (nlen < 0)
goto error;
+ /* Identify SYNC string for last.fm, this is limited to last.fm
+ * streaming servers to avoid hitting on tracks metadata for other
+ * servers.
+ */
+ if ( this->is_lastfm &&
+ memmem(&buf[read_bytes], nlen, "SYNC", 4) != NULL ) {
+ /* Tell frontend to update the UI */
+ const xine_event_t event = {
+ .type = XINE_EVENT_UI_CHANNELS_CHANGED,
+ .stream = this->stream,
+ .data = NULL,
+ .data_length = 0
+ };
+
+ lprintf("SYNC from last.fm server received\n");
+
+ xine_event_send(this->stream, &event);
+ }
+
this->shoutcast_pos += nlen;
}
@@ -402,8 +420,9 @@ error:
}
static off_t http_plugin_read (input_plugin_t *this_gen,
- char *buf, off_t nlen) {
+ void *buf_gen, off_t nlen) {
http_input_plugin_t *this = (http_input_plugin_t *) this_gen;
+ char *buf = (char *)buf_gen;
off_t n, num_bytes;
num_bytes = 0;
@@ -643,6 +662,9 @@ static int http_plugin_open (input_plugin_t *this_gen ) {
int buflen;
int use_proxy;
int proxyport;
+ int mpegurl_redirect = 0;
+ char auth[BUFSIZE];
+ char proxyauth[BUFSIZE];
use_proxy = this_class->proxyhost && strlen(this_class->proxyhost);
@@ -650,7 +672,7 @@ static int http_plugin_open (input_plugin_t *this_gen ) {
if (this_class->proxyuser && strlen(this_class->proxyuser)) {
if (http_plugin_basicauth (this_class->proxyuser,
this_class->proxypassword,
- this->proxyauth, BUFSIZE)) {
+ proxyauth, BUFSIZE)) {
_x_message(this->stream, XINE_MSG_CONNECTION_REFUSED, "proxy error", NULL);
return 0;
}
@@ -663,14 +685,13 @@ static int http_plugin_open (input_plugin_t *this_gen ) {
_x_message(this->stream, XINE_MSG_GENERAL_WARNING, "malformed url", NULL);
return 0;
}
- if (use_proxy && _x_use_proxy(this_class, this->host)) {
- use_proxy = 0;
- }
+ use_proxy = use_proxy && _x_use_proxy(this_class, this->host);
+
if (this->port == 0)
this->port = DEFAULT_HTTP_PORT;
if (this->user && strlen(this->user)) {
- if (http_plugin_basicauth (this->user, this->password, this->auth, BUFSIZE)) {
+ if (http_plugin_basicauth (this->user, this->password, auth, BUFSIZE)) {
_x_message(this->stream, XINE_MSG_CONNECTION_REFUSED, "basic auth error", NULL);
return -1;
}
@@ -753,12 +774,12 @@ static int http_plugin_open (input_plugin_t *this_gen ) {
buflen = strlen(this->buf);
if (this_class->proxyuser && strlen(this_class->proxyuser)) {
snprintf (this->buf + buflen, BUFSIZE - buflen,
- "Proxy-Authorization: Basic %s\015\012", this->proxyauth);
+ "Proxy-Authorization: Basic %s\015\012", proxyauth);
buflen = strlen(this->buf);
}
if (this->user && strlen(this->user)) {
snprintf (this->buf + buflen, BUFSIZE - buflen,
- "Authorization: Basic %s\015\012", this->auth);
+ "Authorization: Basic %s\015\012", auth);
buflen = strlen(this->buf);
}
@@ -830,6 +851,11 @@ static int http_plugin_open (input_plugin_t *this_gen ) {
_("input_http: http status not 2xx: >%d %s<\n"),
httpcode, httpstatus);
return -7;
+ } else if (httpcode == 401) {
+ xine_log (this->stream->xine, XINE_LOG_MSG,
+ _("input_http: http status not 2xx: >%d %s<\n"),
+ httpcode, httpstatus);
+ /* don't return - there may be a WWW-Authenticate header... */
} else if (httpcode == 403) {
_x_message(this->stream, XINE_MSG_PERMISSION_ERROR, this->mrl, NULL);
xine_log (this->stream->xine, XINE_LOG_MSG,
@@ -867,6 +893,19 @@ static int http_plugin_open (input_plugin_t *this_gen ) {
return http_plugin_open(this_gen);
}
+ if (!strncasecmp (this->buf, "WWW-Authenticate: ", 18))
+ strcpy (this->preview, this->buf + 18);
+
+ {
+ static const char mpegurl_ct_str[] = "Content-Type: audio/x-mpegurl";
+ static const size_t mpegurl_ct_size = sizeof(mpegurl_ct_str)-1;
+ if (!strncasecmp(this->buf, mpegurl_ct_str, mpegurl_ct_size)) {
+ lprintf("Opening an audio/x-mpegurl file, late redirect.");
+
+ mpegurl_redirect = 1;
+ }
+ }
+
/* Icecast / ShoutCast Stuff */
if (!strncasecmp(this->buf, TAG_ICY_NAME, sizeof(TAG_ICY_NAME) - 1)) {
_x_meta_info_set(this->stream, XINE_META_INFO_ALBUM,
@@ -908,6 +947,10 @@ static int http_plugin_open (input_plugin_t *this_gen ) {
this->is_nsv = 1;
}
}
+ if ( !strncasecmp(this->buf, TAG_LASTFM_SERVER, sizeof(TAG_LASTFM_SERVER)-1) ) {
+ lprintf("last.fm streaming server detected\n");
+ this->is_lastfm = 1;
+ }
}
if (len == -1)
@@ -926,6 +969,34 @@ static int http_plugin_open (input_plugin_t *this_gen ) {
lprintf ("end of headers\n");
+ if (httpcode == 401)
+ _x_message(this->stream, XINE_MSG_AUTHENTICATION_NEEDED,
+ this->mrl, *this->preview ? this->preview : NULL, NULL);
+
+ if ( mpegurl_redirect ) {
+ char buf[4096] = { 0, };
+ char *newline = NULL;
+
+ http_plugin_read_int(this, buf, 4095);
+ newline = strstr(buf, "\r\n");
+
+ /* If the newline can't be found, either the 4K buffer is too small, or
+ * more likely something is fuzzy.
+ */
+ if ( newline ) {
+ char *href;
+
+ *newline = '\0';
+
+ lprintf("mpegurl pointing to %s\n", buf);
+
+ href = _x_canonicalise_url (this->mrl, buf);
+ free(this->mrl);
+ this->mrl = href;
+ return http_plugin_open(this_gen);
+ }
+ }
+
/*
* fill preview buffer
*/
@@ -992,19 +1063,10 @@ static input_plugin_t *http_class_get_instance (input_class_t *cls_gen, xine_str
return &this->input_plugin;
}
-static const char *http_class_get_description (input_class_t *this_gen) {
- return _("http input plugin");
-}
-
-static const char *http_class_get_identifier (input_class_t *this_gen) {
- return "http";
-}
-
static void http_class_dispose (input_class_t *this_gen) {
http_input_class_t *this = (http_input_class_t *) this_gen;
- if(this->proxyhost_env)
- free(this->proxyhost_env);
+ free(this->proxyhost_env);
free (this);
}
@@ -1021,8 +1083,8 @@ static void *init_class (xine_t *xine, void *data) {
config = xine->config;
this->input_class.get_instance = http_class_get_instance;
- this->input_class.get_identifier = http_class_get_identifier;
- this->input_class.get_description = http_class_get_description;
+ this->input_class.identifier = "http";
+ this->input_class.description = N_("http input plugin");
this->input_class.get_dir = NULL;
this->input_class.get_autoplay_list = NULL;
this->input_class.dispose = http_class_dispose;
@@ -1094,6 +1156,6 @@ static void *init_class (xine_t *xine, void *data) {
const plugin_info_t xine_plugin_info[] EXPORTED = {
/* type, API, "name", version, special_info, init_function */
- { PLUGIN_INPUT | PLUGIN_MUST_PRELOAD, 17, "http", XINE_VERSION_CODE, NULL, init_class },
+ { PLUGIN_INPUT | PLUGIN_MUST_PRELOAD, 18, "http", XINE_VERSION_CODE, NULL, init_class },
{ PLUGIN_NONE, 0, "", 0, NULL, NULL }
};