Logo Search packages:      
Sourcecode: kaffeine version File versions

http.c

/***************************************************************************
                          http.c - taken from the xine project
                          modified for use with c++
                             -------------------
    begin                : Fre Jan 23 2004
    revision             : $Revision: 1.6 $
    last modified        : $Date: 2005/02/02 10:08:25 $ by $Author: juergenk $
    copyright            : (C) 2004-2005 by J├╝rgen Kofler
    email                : kaffeine@gmx.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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.                                   *
 *                                                                         *
 ***************************************************************************/


/*
 * 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.c,v 1.7 2003/03/18 23:22:55 guenter Exp 
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

#include "http.h"

/*
#define LOG
*/

#define BUFSIZE                 1024
#define BUFSIZE_QUOTE          "1024"
#define DEFAULT_HTTP_PORT         80

typedef struct http_s http_t;

struct http_s {

  int              fh;

  off_t            curpos;
  off_t            contentlength;

  char             mime_type[BUFSIZE+1];

  char             buf[BUFSIZE+1];
  char             mrlbuf[BUFSIZE+1];
  char             proxybuf[BUFSIZE+1];

  char             auth[BUFSIZE+1];
  char             proxyauth[BUFSIZE+1];
  
  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);
  struct sockaddr_in sin;
  
  if (s==-1) {
    printf ("http: socket(): %s\n", strerror(errno));
    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: connect(): %s\n", strerror(errno));
    close(s);
    return -1;
  }   
      
  return s;
}
*/

static int http_host_connect_attempt (struct in_addr ia, int port) {

  int                s;
  struct sockaddr_in s_in;
      
  s=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

  if (s==-1) {
    printf ("http: failed to open socket\n");
    return -1;
  }

  s_in.sin_family = AF_INET;  
  s_in.sin_addr   = ia;
  s_in.sin_port   = htons(port);
      
  if (connect(s, (struct sockaddr *)&s_in, sizeof(s_in))==-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;
  int s;
      
  h=gethostbyname(host);
  if (h==NULL) {
    printf ("http: unable to resolve >%s<\n", host);
    return -1;
  }
      
  for(i=0; h->h_addr_list[i]; i++) {
    struct in_addr ia;
    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);
  return -1;
}

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;
    
    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;
  
  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_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 report_error (const char *url, const char *msg) {
  printf( "http: There was an error trying to download %s: %s\n", url, msg); 
}

static http_t *http_open (const char *mrl) {

  http_t   *this;
  char     *proxy;
  int       done,len,linenum;
  char      mime_type[BUFSIZE+1];

  this = malloc (sizeof (http_t));

  strncpy (this->mrlbuf, mrl, BUFSIZE);

  this->proxybuf[0] = '\0';
  proxy = getenv("http_proxy");
  
  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);
      report_error (mrl, "url parse error");
      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);
      report_error (mrl, "proxy auth error");
      return NULL;
      }
  }
  
  if (http_parse_url (this->mrlbuf, &this->user, &this->password,
                  &this->host, &this->port, &this->filename)) {
    free (this);
    report_error (mrl, "url parse error");
    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);
      report_error (mrl, "proxy auth error");
      return NULL;
    }

#ifdef LOG
  {
    char buf[256];

    sprintf (buf, "http: opening >/%s< on host >%s<",
           this->filename, this->host);

    if (proxy != NULL)
      sprintf(buf, "%s via proxy >%s<", buf, this->proxyhost);
    
    sprintf(buf, "%s\n", buf);

    printf (buf);
  }
#endif  

  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);
    report_error (mrl, "host connect error");
    return NULL;
  }

  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)) != (ssize_t)strlen(this->buf)) {
    free (this);
    report_error (mrl, "couldn't send request");
    return NULL ;
  }

#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:
      free (this);
      report_error (mrl, "read error");
      return NULL;
      }
    }

    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+1];
      
      if (sscanf(this->buf, "HTTP/%d.%d %d %[^\015\012]", &httpver, &httpsub,
               &httpcode, httpstatus) != 4)     {
        free (this);
        report_error (mrl, "invalid http answer");
        return NULL;
      }
      
      if (httpcode >= 300 && httpcode < 400) {
        printf ("http: 3xx redirection not implemented: >%d %s<\n",
              httpcode, httpstatus);
        free (this);
        report_error (mrl, "3xx redirection not implemented");
        return NULL;
      }
      if (httpcode < 200 || httpcode >= 300) {
        printf ("http: http status not 2xx: >%d %s<\n",
              httpcode, httpstatus);
        free (this);
        report_error (mrl, "http status not 2xx");
        return NULL;
      }
      } else {
      if (this->contentlength == 0) {
        intmax_t contentlength;
        
        if ( strlen(this->buf)<(BUFSIZE-20) && sscanf(this->buf, "Content-Length: %Ld", &contentlength) == 1) {
          printf ("http: content length = %Ld bytes\n", contentlength);
          this->contentlength = (off_t)contentlength;
        }
        }
        if ( sscanf(this->buf, "Content-Type: %" BUFSIZE_QUOTE "s", mime_type) == 1) {
        printf ("http: content type = '%s'\n", mime_type);
        strcpy(this->mime_type, mime_type);
      }     
      if (!strncasecmp(this->buf, "Location: ", 10)) {
              printf ("http: Location redirection not implemented\n");
        free (this);
        report_error (mrl, "location redirection not implemented");
        return NULL;
      }
      }
      
      if (len == -1)
      done = 1;
      else
      len = 0;
    } else
      len ++;
  }

  return this;
}



char *http_download (const char *url, int *file_size) {

  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
  
  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);

  free (http);

  *file_size = total;
  return buf;    
}


int http_peek( const char *url, int size, char *contents, char *mime_type ) {

  int     total;
/*  char   *buf; */
  http_t *http;

#ifdef LOG
  printf ("http: attempt to download %u bytes from >%s<\n", size, url);
#endif
  http = http_open (url);

  if (!http)
    return 0;

  strcpy(mime_type,http->mime_type);
  
#ifdef LOG
  printf ("http: connect passed \n");
#endif
  
  total=read (http->fh, contents, size);

  close (http->fh);
  free (http);

  return total;
}

Generated by  Doxygen 1.6.0   Back to index