/* 2004.02.01 first released source code for IOMP */ /* * Copyright (C) 2000-2003 the xine project * * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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 * * input plugin for http network streams * * $Id: input_http.c,v 1.2 2003/11/25 04:25:48 georgedon Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #ifndef WIN32 #include #include #include #endif /* WIN32 */ #include #include "xine_internal.h" #include "xineutils.h" #include "input_plugin.h" #include "net_buf_ctrl.h" #include "io_helper.h" /* #define LOG */ #define BUFSIZE 1024 #define DEFAULT_HTTP_PORT 80 typedef struct { input_plugin_t input_plugin; xine_stream_t *stream; int fh; char *mrl; nbc_t *nbc; off_t curpos; off_t contentlength; char buf[BUFSIZE]; char mrlbuf[BUFSIZE]; char mrlbuf2[BUFSIZE]; /* icecast */ char proxybuf[BUFSIZE]; char auth[BUFSIZE]; char proxyauth[BUFSIZE]; char *user; char *password; char *host; int port; char *filename; char preview[MAX_PREVIEW_SIZE]; off_t preview_size; /* ShoutCast */ int shoutcast_mode; int shoutcast_metaint; off_t shoutcast_pos; char *shoutcast_songtitle; /* scratch buffer for forward seeking */ char seek_buf[BUFSIZE]; } http_input_plugin_t; typedef struct { input_class_t input_class; xine_t *xine; config_values_t *config; char *proxyuser; char *proxypassword; char *proxyhost; int proxyport; } http_input_class_t; static void proxy_user_change_cb(void *data, xine_cfg_entry_t *cfg) { http_input_class_t *this = (http_input_class_t *) data; this->proxyuser = cfg->str_value; } static void proxy_password_change_cb(void *data, xine_cfg_entry_t *cfg) { http_input_class_t *this = (http_input_class_t *) data; this->proxypassword = cfg->str_value; } static void proxy_host_change_cb(void *data, xine_cfg_entry_t *cfg) { http_input_class_t *this = (http_input_class_t *) data; this->proxyhost = cfg->str_value; } static void proxy_port_change_cb(void *data, xine_cfg_entry_t *cfg) { http_input_class_t *this = (http_input_class_t *) data; this->proxyport = cfg->num_value; } static int http_plugin_parse_url (char *urlbuf, char **user, char **password, char** host, int *port, char **filename) { char *start = NULL; char *authcolon = NULL; char *at = NULL; char *portcolon = NULL; char *slash = NULL; if (user != NULL) *user = NULL; if (password != NULL) *password = NULL; if (host != NULL) *host = NULL; if (filename != NULL) *filename = NULL; if (port != NULL) *port = 0; start = strstr(urlbuf, "://"); if (start != NULL) start += 3; else start = urlbuf; at = strchr(start, '@'); slash = strchr(start, '/'); if (at != NULL && slash != NULL && at > slash) at = NULL; if (at != NULL) { authcolon = strchr(start, ':'); if(authcolon != NULL && authcolon > at) authcolon = NULL; portcolon = strchr(at, ':'); } else portcolon = strchr(start, ':'); if (portcolon != NULL && slash != NULL && portcolon > slash) portcolon = NULL; if (at != NULL) { *at = '\0'; if (user != NULL) *user = start; if (authcolon != NULL) { *authcolon = '\0'; if (password != NULL) *password = authcolon + 1; } if (host != NULL) *host = at + 1; } else if (host != NULL) *host = start; #ifdef ENABLE_IPV6 /* Add support for RFC 2732 */ { char *hostbracket, *hostendbracket; hostbracket = strchr(start, '['); if (hostbracket != NULL) { hostendbracket = strchr(hostbracket, ']'); if (hostendbracket != NULL) { *hostendbracket = '\0'; *host = (hostbracket + 1); /* Might have a trailing port */ if (*(hostendbracket+1) == ':') { portcolon = (hostendbracket + 1); } } } } #endif if (slash != 0) { *slash = '\0'; if (filename != NULL) *filename = slash + 1; } else { if (filename != NULL) *filename = urlbuf + strlen(urlbuf); } if (portcolon != NULL) { *portcolon = '\0'; if (port != NULL) *port = atoi(portcolon + 1); } return 0; } static int http_plugin_basicauth (const char *user, const char *password, char* dest, int len) { static char *enctable="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; char *tmp; char *sptr; char *dptr; int totlen; int enclen; int count; totlen = strlen (user) + 1; if(password != NULL) totlen += strlen (password); enclen = ((totlen + 2) / 3 ) * 4 + 1; if (len < enclen) return -1; tmp = malloc (sizeof(char) * (totlen + 1)); strcpy (tmp, user); strcat (tmp, ":"); if (password != NULL) strcat (tmp, password); count = strlen(tmp); sptr = tmp; dptr = dest; while (count >= 3) { dptr[0] = enctable[(sptr[0] & 0xFC) >> 2]; dptr[1] = enctable[((sptr[0] & 0x3) << 4) | ((sptr[1] & 0xF0) >> 4)]; dptr[2] = enctable[((sptr[1] & 0x0F) << 2) | ((sptr[2] & 0xC0) >> 6)]; dptr[3] = enctable[sptr[2] & 0x3F]; count -= 3; sptr += 3; dptr += 4; } if (count > 0) { dptr[0] = enctable[(sptr[0] & 0xFC) >> 2]; dptr[1] = enctable[(sptr[0] & 0x3) << 4]; dptr[2] = '='; if (count > 1) { dptr[1] = enctable[((sptr[0] & 0x3) << 4) | ((sptr[1] & 0xF0) >> 4)]; dptr[2] = enctable[(sptr[1] & 0x0F) << 2]; } dptr[3] = '='; dptr += 4; } dptr[0] = '\0'; free(tmp); return 0; } static void http_plugin_read_metainf (input_plugin_t *this_gen) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; char metadata_buf[255 * 16]; unsigned char len = 0; char *title_end; char *songtitle, *radio; xine_event_t uevent; xine_ui_data_t data; /* avoid recursion */ this->shoutcast_mode = 0; /* get the length of the metadata */ this_gen->read(this_gen, &len, 1); #ifdef LOG printf ("input_http: http_plugin_read_metainf: len=%d\n", len); #endif if (len > 0) { this_gen->read(this_gen, metadata_buf, len * 16); metadata_buf[len * 16] = '\0'; #ifdef LOG printf ("input_http: http_plugin_read_metainf: %s\n", metadata_buf); #endif /* Extract the title of the current song */ if ((songtitle = strstr(metadata_buf, "StreamTitle='"))) { songtitle += 13; /* skip "StreamTitle='" */ if ((title_end = strchr(songtitle, '\''))) { *title_end = '\0'; if ((!this->shoutcast_songtitle || (strcmp(songtitle, this->shoutcast_songtitle))) && (strlen(songtitle) > 0)) { #ifdef LOG printf ("input_http: http_plugin_read_metainf: songtitle: %s\n", songtitle); #endif if (this->shoutcast_songtitle) free(this->shoutcast_songtitle); this->shoutcast_songtitle = strdup(songtitle); if (this->stream->meta_info [XINE_META_INFO_TITLE]) free(this->stream->meta_info [XINE_META_INFO_TITLE]); this->stream->meta_info [XINE_META_INFO_TITLE] = strdup (songtitle); /* prepares the event */ radio = this->stream->meta_info [XINE_META_INFO_ALBUM]; if (radio) { strcpy(data.str, radio); /* WARNING: the data.str is char[256] */ strcat(data.str, " - "); data.str_len = strlen(radio) + 3 + strlen(songtitle) + 1; } else { data.str[0] = 0; data.str_len = strlen(songtitle) + 1; } strcat(data.str, songtitle); /* sends the event */ uevent.type = XINE_EVENT_UI_SET_TITLE; uevent.stream = this->stream; uevent.data = &data; uevent.data_length = sizeof(data); xine_event_send(this->stream, &uevent); } } } } this->shoutcast_mode = 1; this->shoutcast_pos = 0; } static off_t http_plugin_read (input_plugin_t *this_gen, char *buf, off_t nlen) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; off_t n, num_bytes; num_bytes = 0; if (this->shoutcast_mode && (this->shoutcast_pos == this->shoutcast_metaint)) { http_plugin_read_metainf(this_gen); } if (this->curpos < this->preview_size) { n = this->preview_size - this->curpos; if (n > (nlen - num_bytes)) n = nlen - num_bytes; #ifdef LOG printf ("input_http: %lld bytes from preview (which has %lld bytes)\n", n, this->preview_size); #endif if (this->shoutcast_mode) { if ((this->shoutcast_pos + n) >= this->shoutcast_metaint) { int i = this->shoutcast_metaint - this->shoutcast_pos; memcpy (&buf[num_bytes], &this->preview[this->curpos], i); this->shoutcast_pos += i; num_bytes += i; this->curpos += i; n -= i; http_plugin_read_metainf(this_gen); } this->shoutcast_pos += n; } memcpy (&buf[num_bytes], &this->preview[this->curpos], n); num_bytes += n; this->curpos += n; } n = nlen - num_bytes; if( n && this->shoutcast_mode) { if ((this->shoutcast_pos + n) >= this->shoutcast_metaint) { int i = this->shoutcast_metaint - this->shoutcast_pos; i = xio_tcp_read (this->stream, this->fh, &buf[num_bytes], i); if (i < 0) { if (!this->stream->demux_action_pending) xine_message (this->stream, XINE_MSG_READ_ERROR, this->host, NULL); xine_log (this->stream->xine, XINE_LOG_MSG, _("input_http: read error %d\n"), errno); return 0; } this->shoutcast_pos += i; num_bytes += i; this->curpos += i; n -= i; http_plugin_read_metainf(this_gen); } this->shoutcast_pos += n; } if( n ) { n = xio_tcp_read (this->stream, this->fh, &buf[num_bytes], n); /* read errors */ if (n < 0) { if (!this->stream->demux_action_pending) xine_message(this->stream, XINE_MSG_READ_ERROR, this->host, NULL); xine_log (this->stream->xine, XINE_LOG_MSG, _("input_http: read error %d\n"), errno); return 0; } num_bytes += n; this->curpos += n; } return num_bytes; } static int read_shoutcast_header(http_input_plugin_t *this) { int done, len, linenum; done = 0; len = 0; linenum = 0; while (!done) { /* printf ("input_http: read...\n"); */ if (http_plugin_read ((input_plugin_t*)this, &this->buf[len], 1) == 0) { return 1; } if (this->buf[len] == '\012') { this->buf[len] = '\0'; len--; if (len >= 0 && this->buf[len] == '\015') { this->buf[len] = '\0'; len--; } linenum++; #ifdef LOG printf ("input_http: shoutcast answer: >%s<\n", this->buf); #endif if (!strncasecmp(this->buf, "icy-name:", 9)) { this->stream->meta_info [XINE_META_INFO_ALBUM] = strdup (this->buf + 9 + (*(this->buf + 9) == ' ')); this->stream->meta_info [XINE_META_INFO_TITLE] = strdup (this->buf + 9 + (*(this->buf + 9) == ' ')); } if (!strncasecmp(this->buf, "icy-genre:", 10)) { this->stream->meta_info [XINE_META_INFO_GENRE] = strdup (this->buf + 10 + (*(this->buf + 10) == ' ')); } /* icy-notice1 is always the same */ if (!strncasecmp(this->buf, "icy-notice2:", 12)) { this->stream->meta_info [XINE_META_INFO_COMMENT] = strdup (this->buf + 12 + (*(this->buf + 12) == ' ')); } /* metadata interval (in byte) */ if (sscanf(this->buf, "icy-metaint:%d", &this->shoutcast_metaint) == 1) { #ifdef LOG printf("input_http: shoutcast_metaint: %d\n", this->shoutcast_metaint); #endif } if (len == -1) done = 1; else len = 0; } else len ++; } #ifdef LOG printf ("input_http: end of the shoutcast header\n"); #endif return 0; } static buf_element_t *http_plugin_read_block (input_plugin_t *this_gen, fifo_buffer_t *fifo, off_t todo) { off_t total_bytes; buf_element_t *buf = fifo->buffer_pool_alloc (fifo); buf->content = buf->mem; buf->type = BUF_DEMUX_BLOCK; total_bytes = http_plugin_read (this_gen, buf->content, todo); if (total_bytes != todo) { buf->free_buffer (buf); buf = NULL; } if (buf != NULL) buf->size = total_bytes; return buf; } static off_t http_plugin_get_length (input_plugin_t *this_gen) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; return this->contentlength; } static uint32_t http_plugin_get_capabilities (input_plugin_t *this_gen) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; uint32_t caps = INPUT_CAP_PREVIEW; /* Nullsoft asked to not allow saving streaming nsv files */ if (this->filename && !strncmp(this->filename + strlen(this->filename) - 4, ".nsv", 4)) caps |= INPUT_CAP_RIP_FORBIDDEN; return caps; } static uint32_t http_plugin_get_blocksize (input_plugin_t *this_gen) { return 0; } static off_t http_plugin_get_current_pos (input_plugin_t *this_gen){ http_input_plugin_t *this = (http_input_plugin_t *) this_gen; return this->curpos; } static off_t http_plugin_seek(input_plugin_t *this_gen, off_t offset, int origin) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; if ((origin == SEEK_CUR) && (offset >= 0)) { for (;((int)offset) - BUFSIZE > 0; offset -= BUFSIZE) { if( !this_gen->read (this_gen, this->seek_buf, BUFSIZE) ) return this->curpos; } this_gen->read (this_gen, this->seek_buf, offset); } if (origin == SEEK_SET) { if (offset < this->curpos) { if( this->curpos <= this->preview_size ) this->curpos = offset; else printf ("http: cannot seek back! (%lld > %lld)\n", this->curpos, offset); } else { offset -= this->curpos; for (;((int)offset) - BUFSIZE > 0; offset -= BUFSIZE) { if( !this_gen->read (this_gen, this->seek_buf, BUFSIZE) ) return this->curpos; } this_gen->read (this_gen, this->seek_buf, offset); } } return this->curpos; } static char* http_plugin_get_mrl (input_plugin_t *this_gen) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; return this->mrlbuf2; } static int http_plugin_get_optional_data (input_plugin_t *this_gen, void *data, int data_type) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; switch (data_type) { case INPUT_OPTIONAL_DATA_PREVIEW: memcpy (data, this->preview, this->preview_size); return this->preview_size; break; } return INPUT_OPTIONAL_UNSUPPORTED; } static void http_plugin_dispose (input_plugin_t *this_gen ) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; if (this->fh != -1) { close(this->fh); this->fh = -1; } if (this->nbc) { nbc_close (this->nbc); this->nbc = NULL; } free (this); } static void report_progress (xine_stream_t *stream, int p) { xine_event_t event; xine_progress_data_t prg; prg.description = _("Connecting HTTP server..."); prg.percent = p; event.type = XINE_EVENT_PROGRESS; event.data = &prg; event.data_length = sizeof (xine_progress_data_t); xine_event_send (stream, &event); } static int http_plugin_open (input_plugin_t *this_gen ) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; http_input_class_t *this_klass = (http_input_class_t *) this->input_plugin.input_class; int done,len,linenum; int shoutcast = 0, httpcode; int length; int res, progress; this->shoutcast_pos = 0; if (this_klass->proxyhost != NULL && strcmp (this_klass->proxyhost, "")) { if (this_klass->proxyport == 0) this_klass->proxyport = DEFAULT_HTTP_PORT; if ( (this_klass->proxyuser != NULL) && strcmp (this_klass->proxyuser, "") ) if (http_plugin_basicauth (this_klass->proxyuser, this_klass->proxypassword, this->proxyauth, BUFSIZE)) { xine_message(this->stream, XINE_MSG_CONNECTION_REFUSED, "proxy error", NULL); return 0; } } if (http_plugin_parse_url (this->mrlbuf, &this->user, &this->password, &this->host, &this->port, &this->filename)) { return 0; } if (this->port == 0) this->port = DEFAULT_HTTP_PORT; if ( (this->user != NULL) && strcmp (this_klass->proxyhost, "")) if (http_plugin_basicauth (this->user, this->password, this->auth, BUFSIZE)) { xine_message(this->stream, XINE_MSG_CONNECTION_REFUSED, "basic auth error", NULL); return 0; } #ifdef LOG { printf ("input_http: opening >/%s< on host >%s<", this->filename, this->host); if (this_klass->proxyhost != NULL) printf (" via proxy >%s<", this_klass->proxyhost); printf ("\n"); } #endif if ( (this_klass->proxyhost != NULL) && strcmp (this_klass->proxyhost, "") ) this->fh = xio_tcp_connect (this->stream, this_klass->proxyhost, this_klass->proxyport); else this->fh = xio_tcp_connect (this->stream, this->host, this->port); this->curpos = 0; if (this->fh == -1) { return 0; } /* connection timeout 20s */ progress = 0; do { report_progress(this->stream, progress); res = xio_select (this->stream, this->fh, XIO_WRITE_READY, 500); progress += 2; } while ((res == XIO_TIMEOUT) && (progress < 100)); if (res != XIO_READY) return 0; if ( (this_klass->proxyhost != NULL) && strcmp (this_klass->proxyhost, "") ) { if (this->port != DEFAULT_HTTP_PORT) { sprintf (this->buf, "GET http://%s:%d/%s HTTP/1.0\015\012", this->host, this->port, this->filename); } else { sprintf (this->buf, "GET http://%s/%s HTTP/1.0\015\012", this->host, this->filename); } } else { sprintf (this->buf, "GET /%s HTTP/1.0\015\012", this->filename); } if (this->port != DEFAULT_HTTP_PORT) sprintf (this->buf + strlen(this->buf), "Host: %s:%d\015\012", this->host, this->port); else sprintf (this->buf + strlen(this->buf), "Host: %s\015\012", this->host); if ( (this_klass->proxyuser != NULL) && strcmp (this_klass->proxyuser, "") ) sprintf (this->buf + strlen(this->buf), "Proxy-Authorization: Basic %s\015\012", this->proxyauth); if ( (this->user != NULL) && strcmp (this->user, "") ) sprintf (this->buf + strlen(this->buf), "Authorization: Basic %s\015\012", this->auth); sprintf (this->buf + strlen(this->buf), "User-Agent: xine/%s\015\012", VERSION); strcat (this->buf, "Accept: */*\015\012"); strcat (this->buf, "Icy-MetaData: 1\015\012"); strcat (this->buf, "\015\012"); length = strlen(this->buf); if (xio_tcp_write (this->stream, this->fh, this->buf, length) != length) { xine_message(this->stream, XINE_MSG_CONNECTION_REFUSED, "couldn't send request", NULL); printf ("input_http: couldn't send request\n"); return 0; } #ifdef LOG printf ("input_http: request sent: >%s<\n", this->buf); #endif /* read and parse reply */ done = 0; len = 0; linenum = 0; this->contentlength = 0; while (!done) { /* printf ("input_http: read...\n"); */ if (xio_tcp_read (this->stream, this->fh, &this->buf[len], 1) <= 0) { return 0; } if (this->buf[len] == '\012') { this->buf[len] = '\0'; len--; if (len >= 0 && this->buf[len] == '\015') { this->buf[len] = '\0'; len--; } linenum++; #ifdef LOG printf ("input_http: answer: >%s<\n", this->buf); #endif if (linenum == 1) { int httpver, httpsub; char httpstatus[BUFSIZE]; if (sscanf(this->buf, "HTTP/%d.%d %d %[^\015\012]", &httpver, &httpsub, &httpcode, httpstatus) != 4) { /* icecast ? */ if (sscanf(this->buf, "ICY %d OK", &httpcode) != 1) { xine_message(this->stream, XINE_MSG_CONNECTION_REFUSED, "invalid http answer", NULL); xine_log (this->stream->xine, XINE_LOG_MSG, _("input_http: invalid http answer\n")); return 0; } else { shoutcast = 1; done = 1; } } if (httpcode >= 300 && httpcode < 400) { xine_log (this->stream->xine, XINE_LOG_MSG, _("input_http: 3xx redirection: >%d %s<\n"), httpcode, httpstatus); } else if (httpcode < 200 || httpcode >= 300) { xine_message(this->stream, XINE_MSG_CONNECTION_REFUSED, "http status not 2xx: ", httpstatus, NULL); xine_log (this->stream->xine, XINE_LOG_MSG, _("input_http: http status not 2xx: >%d %s<\n"), httpcode, httpstatus); return 0; } } else { if (this->contentlength == 0) { off_t contentlength; if (sscanf(this->buf, "Content-Length: %Ld", &contentlength) == 1) { xine_log (this->stream->xine, XINE_LOG_MSG, _("input_http: content length = %Ld bytes\n"), contentlength); this->contentlength = contentlength; } } if (!strncasecmp(this->buf, "Location: ", 10)) { char *href = strdup (this->buf + 10); #ifdef LOG printf ("input_http: trying to open target of redirection: >%s<\n", href); #endif strncpy (this->mrlbuf, href, BUFSIZE); strncpy (this->mrlbuf2, href, BUFSIZE); return http_plugin_open(this_gen); } } if (len == -1) done = 1; else len = 0; } else len ++; } #ifdef LOG printf ("input_http: end of headers\n"); #endif /* * fill preview buffer */ this->preview_size = http_plugin_read (&this->input_plugin, this->preview, MAX_PREVIEW_SIZE); this->curpos = 0; /* Trivial shoutcast detection */ this->shoutcast_metaint = 0; this->shoutcast_songtitle = NULL; if (shoutcast || !strncasecmp(this->preview, "ICY", 3)) { this->mrlbuf2[0] = 'i'; this->mrlbuf2[1] = 'c'; this->mrlbuf2[2] = 'e'; this->mrlbuf2[3] = ' '; if (read_shoutcast_header(this)) { /* problem when reading shoutcast header */ xine_message(this->stream, XINE_MSG_CONNECTION_REFUSED, "can't read shoutcast header", NULL); printf ("can't read shoutcast header\n"); return 0; } this->shoutcast_mode = 1; this->shoutcast_pos = 0; } else { this->shoutcast_mode = 0; } return 1; } /* * http input plugin class */ static input_plugin_t *http_class_get_instance (input_class_t *cls_gen, xine_stream_t *stream, const char *mrl) { /* http_input_class_t *cls = (http_input_class_t *) cls_gen;*/ http_input_plugin_t *this; if (strncasecmp (mrl, "http://", 7)) { return NULL; } this = (http_input_plugin_t *) xine_xmalloc(sizeof(http_input_plugin_t)); strncpy (this->mrlbuf, mrl, BUFSIZE); strncpy (this->mrlbuf2, mrl, BUFSIZE); this->mrl = this->mrlbuf2; this->stream = stream; this->fh = -1; this->nbc = nbc_init (this->stream); this->input_plugin.open = http_plugin_open; this->input_plugin.get_capabilities = http_plugin_get_capabilities; this->input_plugin.read = http_plugin_read; this->input_plugin.read_block = http_plugin_read_block; this->input_plugin.seek = http_plugin_seek; this->input_plugin.get_current_pos = http_plugin_get_current_pos; this->input_plugin.get_length = http_plugin_get_length; this->input_plugin.get_blocksize = http_plugin_get_blocksize; this->input_plugin.get_mrl = http_plugin_get_mrl; this->input_plugin.get_optional_data = http_plugin_get_optional_data; this->input_plugin.dispose = http_plugin_dispose; this->input_plugin.input_class = cls_gen; return &this->input_plugin; } static char *http_class_get_description (input_class_t *this_gen) { return _("http input plugin"); } static 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; free (this); } static void *init_class (xine_t *xine, void *data) { http_input_class_t *this; config_values_t *config; this = (http_input_class_t *) xine_xmalloc (sizeof (http_input_class_t)); this->xine = xine; this->config = xine->config; 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.get_dir = NULL; this->input_class.get_autoplay_list = NULL; this->input_class.dispose = http_class_dispose; this->input_class.eject_media = NULL; this->proxyuser = config->register_string(config, "input.http_proxy_user", "", _("http proxy username"), NULL, 0, proxy_user_change_cb, (void *) this); this->proxypassword = config->register_string(config, "input.http_proxy_password", "", _("http proxy password"), NULL, 0, proxy_password_change_cb, (void *) this); this->proxyhost = config->register_string(config, "input.http_proxy_host", "", _("http proxy host"), NULL, 0, proxy_host_change_cb, (void *) this); this->proxyport = config->register_num(config, "input.http_proxy_port", DEFAULT_HTTP_PORT, _("http proxy port"), NULL, 0, proxy_port_change_cb, (void *) this); return this; } /* * exported plugin catalog entry */ plugin_info_t xine_plugin_info[] = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_INPUT, 13, "http", XINE_VERSION_CODE, NULL, init_class }, { PLUGIN_NONE, 0, "", 0, NULL, NULL } };