diff options
Diffstat (limited to 'src/input/input_http.c')
-rw-r--r-- | src/input/input_http.c | 164 |
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 } }; |