From: Guenter B. <gu...@us...> - 2002-10-25 23:14:00
|
Update of /cvsroot/xine/gnome-xine/src In directory usw-pr-cvs1:/tmp/cvs-serv21031/src Modified Files: Makefile.am http.c playlist.c Added Files: http.h Log Message: an asx parser based on thibaut's code, better http code based on input_http --- NEW FILE: http.h --- /* * Copyright (C) 2002 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 * * $Id: http.h,v 1.1 2002/10/25 23:13:54 guenter Exp $ */ #ifndef HAVE_HTTP_H #define HAVE_HTTP_H char *http_download (const char *url, int *file_size); #endif Index: Makefile.am =================================================================== RCS file: /cvsroot/xine/gnome-xine/src/Makefile.am,v retrieving revision 1.16 retrieving revision 1.17 diff -u -r1.16 -r1.17 --- Makefile.am 25 Oct 2002 18:24:09 -0000 1.16 +++ Makefile.am 25 Oct 2002 23:13:52 -0000 1.17 @@ -19,13 +19,16 @@ stream_info.c \ infobar.c \ settings.c \ - wizards.c + wizards.c \ + http.c \ + xmlparser.c\ + xmllexer.c gnome_xine_LDADD = @XINE_LIBS@ $(GNOMEUI_LIBS) $(GNOME_LIBDIR) $(INTLLIBS) noinst_HEADERS = gtkxine.h globals.h mediamarks.h preferences.h settings.h \ log_window.h actions.h stream_info.h playlist.h infobar.h \ - wizards.h + wizards.h http.h xmllexer.h xmlparser.h mostlyclean-generic: Index: http.c =================================================================== RCS file: /cvsroot/xine/gnome-xine/src/http.c,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- http.c 25 Oct 2002 18:24:10 -0000 1.1 +++ http.c 25 Oct 2002 23:13:53 -0000 1.2 @@ -36,6 +36,43 @@ #include <ctype.h> #include <errno.h> +#include "http.h" + +#define LOG + +#define BUFSIZE 1024 +#define DEFAULT_HTTP_PORT 80 + +typedef struct http_s http_t; + +struct http_s { + + int fh; + + off_t curpos; + off_t contentlength; + + char buf[BUFSIZE]; + char mrlbuf[BUFSIZE]; + char proxybuf[BUFSIZE]; + + char auth[BUFSIZE]; + char proxyauth[BUFSIZE]; + + char *user; + char *password; + char *host; + int port; + char *filename; + + char *proxyuser; + char *proxypassword; + char *proxyhost; + int proxyport; + +}; + + static int host_connect_attempt (struct in_addr ia, int port) { int s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); @@ -60,158 +97,471 @@ return s; } -int host_connect (const char *host, int port) { +static int http_host_connect_attempt (struct in_addr ia, int port) { + + int s; + struct sockaddr_in sin; + + s=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (s==-1) { + printf ("http: failed to open socket\n"); + return -1; + } + + sin.sin_family = AF_INET; + sin.sin_addr = ia; + sin.sin_port = htons(port); + + if (connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1 + && errno != EINPROGRESS) { + printf ("http: cannot connect to host\n"); + close(s); + return -1; + } + + return s; +} + +static int http_host_connect (const char *host, int port) { struct hostent *h; - int i, s; - + int i; + int s; + h=gethostbyname(host); if (h==NULL) { - printf ("http: unable to resolve '%s'.\n", host); + printf ("http: unable to resolve >%s<\n", host); return -1; } - for (i=0; h->h_addr_list[i]; i++) { + for(i=0; h->h_addr_list[i]; i++) { struct in_addr ia; - memcpy (&ia, h->h_addr_list[i],4); - s = host_connect_attempt(ia, port); + memcpy(&ia, h->h_addr_list[i], 4); + s = http_host_connect_attempt (ia, port); if(s != -1) return s; } - printf ("http: unable to connect to '%s'.\n", host); + + printf ("http: unable to connect to >%s<\n", host); return -1; } -static int http_connect (char *mrl, char **selected_host, - char **path, char **file) { - - int port=80; - char *url; - char *ports; - char *host; - char *hostend; +static int http_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; - /* - * parse mrl - */ + portcolon = strchr(at, ':'); + } else + portcolon = strchr(start, ':'); - if (strncmp (mrl, "http://", 7)) { - printf ("http: no http-style url\n"); - return -1; + 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; + + if (slash != 0) + { + *slash = '\0'; + + if (filename != NULL) + *filename = slash + 1; + } else + *filename = urlbuf + strlen(urlbuf); + + if (portcolon != NULL) + { + *portcolon = '\0'; + + if (port != NULL) + *port = atoi(portcolon + 1); } + + return 0; +} - url = strdup (mrl); - - /* extract hostname */ - - host = url + 7; +static int http_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; - printf ("http: extracting host name from '%s'\n", url); - hostend = strchr (host,'/'); - if (!hostend) { - printf ("http: failed to find hostend\n"); + 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; } - - *hostend = (char) 0; - - ports=strchr(host,':'); /* maybe they put a port here ?*/ - if (ports) { - *ports = 0; - ports++; - if (sscanf (ports,"%d",&port)!=1) { - printf ("http: failed to scan port in '%s'\n", ports); - port=80; + + 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; } - - *selected_host = host; - printf ("http: host=%s, port=%d\n", host, port); - /* extract path and file */ + dptr[0] = '\0'; + + free(tmp); + return 0; +} - *path = hostend+1; - *file = strrchr (*path, '/'); - if (!(*file)) - *file = *path; +static http_t *http_open (const char *mrl) { - /* - * try to connect - */ + http_t *this; + char *proxy; + int done,len,linenum; - printf("http: try to connect to %s on port %d \n", host, port); - return host_connect (host, port); -} + this = malloc (sizeof (http_t)); + strncpy (this->mrlbuf, mrl, BUFSIZE); -static void http_request (char* buf, char* host, char* file, int *len) { - char *ptr; - - bzero (buf,*len); - ptr=buf; - ptr+=sprintf(ptr,"GET %s HTTP/1.0\r\n",file); - ptr+=sprintf(ptr,"Accept: */*\r\n"); - ptr+=sprintf(ptr,"User-Agent: NSPlayer/7.0.0.1956\r\n"); - ptr+=sprintf(ptr,"Host: %s\r\n", host); - ptr+=sprintf(ptr,"Pragma: no-cache,rate=1.000000,stream-time=0,stream-offset=0:0,request-context=1,max-duration=0\r\n"); - ptr+=sprintf(ptr,"Pragma: xClientGUID=%s\r\n", "{33715801-BAB3-9D85-24E9-03B90328270A}"); + this->proxybuf[0] = '\0'; + proxy = getenv("http_proxy"); - ptr+=sprintf(ptr,"Connection: Keep-Alive\r\n\r\n"); + if (proxy != NULL) { + strncpy (this->proxybuf, proxy, BUFSIZE); + + if (http_parse_url (this->proxybuf, &this->proxyuser, + &this->proxypassword, &this->proxyhost, + &this->proxyport, NULL)) { + free (this); + return NULL; + } + + if (this->proxyport == 0) + this->proxyport = DEFAULT_HTTP_PORT; + + if (this->proxyuser != NULL) + if (http_basicauth (this->proxyuser, this->proxypassword, + this->proxyauth, BUFSIZE)) { + free (this); + return NULL; + } + } - *len =(int)ptr-(int)buf; -} + if (http_parse_url (this->mrlbuf, &this->user, &this->password, + &this->host, &this->port, &this->filename)) { + free (this); + return NULL; + } + + if (this->port == 0) + this->port = DEFAULT_HTTP_PORT; + + if (this->user != NULL) + if (http_basicauth (this->user, this->password, this->auth, BUFSIZE)) { + free (this); + return NULL; + } -static int http_read (int s, char *buf, int nlen) { +#ifdef LOG + { + char buf[256]; - int n, num_bytes; + sprintf (buf, "http: opening >/%s< on host >%s<", + this->filename, this->host); - num_bytes = 0; + if (proxy != NULL) + sprintf(buf, "%s via proxy >%s<", buf, this->proxyhost); + + sprintf(buf, "%s\n", buf); + + printf (buf); + } +#endif - while (num_bytes < nlen) { + if (proxy != NULL) + this->fh = http_host_connect (this->proxyhost, this->proxyport); + else + this->fh = http_host_connect (this->host, this->port); + + this->curpos = 0; + + if (this->fh == -1) { + free (this); + return NULL; + } - n = read (s, &buf[num_bytes], nlen - num_bytes); + if (proxy != NULL) + 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->proxyuser != NULL) + sprintf (this->buf + strlen(this->buf), "Proxy-Authorization: Basic %s\015\012", + this->proxyauth); + + if (this->user != NULL) + 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, "\015\012"); + + if (write (this->fh, this->buf, strlen(this->buf)) != strlen(this->buf)) { + printf ("http: couldn't send request\n"); + free (this); + return NULL ; + } - if (n <= 0) { +#ifdef LOG + printf ("http: request sent: >%s<\n", + this->buf); +#endif + + /* read and parse reply */ + done = 0; len = 0; linenum = 0; + this->contentlength = 0; + + while (!done) { + + if (read (this->fh, &this->buf[len], 1) <=0) { switch (errno) { case EAGAIN: printf ("http: EAGAIN\n"); continue; default: - printf ("http: read error\n"); - return num_bytes; + printf ("input_http: read error\n"); + free (this); + return NULL; } } - - num_bytes += n; + + 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, httpcode; + char httpstatus[BUFSIZE]; + + if (sscanf(this->buf, "HTTP/%d.%d %d %[^\015\012]", &httpver, &httpsub, + &httpcode, httpstatus) != 4) { + + printf ("http: invalid http answer\n"); + free (this); + return NULL; + } + + if (httpcode >= 300 && httpcode < 400) { + printf ("http: 3xx redirection not implemented: >%d %s<\n", + httpcode, httpstatus); + free (this); + return NULL; + } + if (httpcode < 200 || httpcode >= 300) { + printf ("http: http status not 2xx: >%d %s<\n", + httpcode, httpstatus); + free (this); + return NULL; + } + } else { + if (this->contentlength == 0) { + off_t contentlength; + + if (sscanf(this->buf, "Content-Length: %Ld", &contentlength) == 1) { + printf ("http: content length = %lld bytes\n", contentlength); + this->contentlength = contentlength; + } + } + + if (!strncasecmp(this->buf, "Location: ", 10)) { + printf ("http: Location redirection not implemented\n"); + free (this); + return NULL; + } + } + + if (len == -1) + done = 1; + else + len = 0; + } else + len ++; } - return num_bytes; + + return this; } -int http_download (char *url, char *buf, int maxlen) { - char req[2048]; - int s; /*socket*/ - char *host, *file, *path; - int len; +char *http_download (const char *url, int *file_size) { - s = http_connect (url, &host, &path, &file); - if (s<0) - return 0; + int total; + char *buf; + http_t *http; +#ifdef LOG + printf ("http: attempt to download >%s<\n", url); +#endif + http = http_open (url); + + if (!http) + return NULL; + +#ifdef LOG printf ("http: connect passed \n"); +#endif - len = sizeof (req); - http_request (req, host, path, &len); - write (s, req, len); - - len = http_read (s, buf, maxlen); - printf("asxparser: answer1=::%s:: \n %d byte received \n", buf, len); + buf = NULL; total = 0; + while (1) { + int n; + + buf = realloc (buf, total+4096); + + n = read (http->fh, &buf[total], 4096); + + if (n < 0) { + + switch (errno) { + case EAGAIN: + printf ("http: EAGAIN\n"); + continue; + default: + printf ("http: read error\n"); + + free(buf); + *file_size = 0; + return NULL; + } + } else if (n==0) { + break; + } + + total += n; +#ifdef LOG + printf ("http: got %d bytes of content\n", total); +#endif + } ; + +#ifdef LOG + printf ("http: got %d bytes of content\n%s", total, buf); +#endif + + close (http->fh); - close(s); + free (http); - return 1; + *file_size = total; + return buf; } Index: playlist.c =================================================================== RCS file: /cvsroot/xine/gnome-xine/src/playlist.c,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- playlist.c 25 Oct 2002 01:27:50 -0000 1.9 +++ playlist.c 25 Oct 2002 23:13:54 -0000 1.10 @@ -41,6 +41,9 @@ #include "globals.h" #include "gtkxine.h" #include "playlist.h" +#include "http.h" + +#include "xmlparser.h" static int is_visible; static GtkWidget *clist, *win; @@ -189,6 +192,9 @@ struct stat statb; int fd; + if (!strncasecmp (mrl, "http://", 7)) + return http_download (mrl, file_size); + if (stat (mrl, &statb) < 0) { printf ("playlist: cannot stat '%s'\n", mrl); return NULL; @@ -316,6 +322,163 @@ return retval; } +static int playlist_load_asx (const char *mrl) { + + char *contents; + int size, res, ret; + xml_node_t *xml_tree; + xml_node_t *current_node_l1; + xml_node_t *current_node_l2; + xml_property_t *property; + + ret = -1; + + if (! (contents = read_entire_file (mrl, &size)) ) + return ret; + + xml_parser_init (contents, size, XML_PARSER_CASE_INSENSITIVE); + res = xml_parser_build_tree (&xml_tree); + + if (res) + return ret; + + /* check ASX */ + if (strcmp(xml_tree->name, "ASX") == 0) { + printf("playlist:: ASX tag detected\n"); + + /* check version */ + property = xml_tree->props; + while ((property) && (strcmp(property->name, "VERSION"))) { + property = property->next; + } + if (property) { + printf("playlist: VERSION property detected\n"); + /* check version number */ + if (strcmp(property->value, "3.0") == 0) { + printf("playlist: VERSION 3.0 detected\n"); + + /* play each entry */ + current_node_l1 = xml_tree->child; + while (current_node_l1) { + /* entry */ + if (strcmp(current_node_l1->name, "ENTRY") == 0) { + printf("playlist: ENTRY detected\n"); + + /* play the first ref which is playable */ + current_node_l2 = current_node_l1->child; + while (current_node_l2) { + if (strcmp(current_node_l2->name, "REF") == 0) { + printf("playlist: REF detected\n"); + /* find href property */ + property = current_node_l2->props; + while ((property) && (strcmp(property->name, "HREF"))) { + property = property->next; + } + if (property) { + printf("playlist: HREF property detected\n"); + + printf ("playlist: found an mrl: %s\n", + property->value); + + if (ret >= 0) + playlist_add (property->value); + else + ret = playlist_add (property->value); + + /* jump to next entry */ + current_node_l2 = NULL; + +#if 0 /* FIXME */ + /* try to play REF */ + if (demux_asx_play(this, property->value) == 1) { + /* jump to next entry */ + printf("playlist: play next entry or entryref\n"); + current_node_l2 = NULL; + } else { + /* try next REF */ + printf("playlist: try next REF\n"); + } + +#endif + + } else { + printf("playlist: no HREF property\n"); + } /* if (property) */ + + } else { + printf("playlist: unknown tag detected\n"); + } /* end if (strcmp(current_node_l2->name, "REF") == 0) */ + if (current_node_l2) { + current_node_l2 = current_node_l2->next; + } + } /* end while */ + } else { + + /* entryref */ + if (strcmp(current_node_l1->name, "ENTRYREF") == 0) { + printf("playlist: ENTRYREF detected\n"); + + property = current_node_l1->props; + while ((property) && (strcmp(property->name, "HREF"))) { + property = property->next; + } + if (property) { + printf("playlist: HREF property detected\n"); + + printf ("playlist: found an mrl: %s\n", + property->value); + + if (ret >= 0) + playlist_add (property->value); + else + ret = playlist_add (property->value); + + /* jump to next entry */ + current_node_l2 = NULL; + +#if 0 /* FIXME */ + + /* try to play HREF */ + if (demux_asx_play(this, property->value) == 1) { + /* jump to next entry */ + printf("playlist: play next entry or entryref\n"); + current_node_l1 = NULL; + } else { + /* try next REF */ + printf("playlist: try next REF\n"); + } +#endif + } else { + printf("playlist: no HREF property\n"); + } /* if (property) */ + } else { + + /* title */ + if (strcmp(current_node_l1->name, "TITLE") == 0) { + printf("playlist: TITLE detected\n"); + /* change title */ + } else { + printf("playlist: unknown tag detected\n"); + } + } + } + current_node_l1 = current_node_l1->next; + } + + } else { + printf("playlist: bad VERSION\n"); + } + } else { + printf("playlist: can't find VERSION property\n"); + } + } else { + printf("playlist: no ASX tag\n"); + } + + + return ret; +} + int playlist_add (const char *mrl) { char *strs[2]; @@ -339,11 +502,7 @@ } else if (!strncasecmp (extension, ".asx", 4)) { - /* FIXME: implement ASX parser */ - - printf ("playlist: error, asx parser not implemented yet.\n"); - - return 0; + return playlist_load_asx (mrl); } else { @@ -366,7 +525,36 @@ strs[0] = pathname; } - } else + + } else if (!strncasecmp (mrl, "http://", 7)) { + + /* + * could still be a pls/asx playlist, but if so we + * need to download it first + */ + + char *extension; + + /* is it a playlist file? */ + + extension = strrchr(mrl, '.'); + + if (!strncasecmp (extension, ".pls", 4)) { + + return playlist_load_pls (mrl); + + } else if (!strncasecmp (extension, ".asx", 4)) { + + return playlist_load_asx (mrl); + + } else + strs[0] = strdup(mrl); + + } else if (!strncasecmp (mrl, "mmshttp://", 10)) { + + return playlist_load_asx (&mrl[3]); + + } else strs[0] = strdup(mrl); strs[1] = "-"; |