/*
 *   httpget.c
 *
 *   Oliver Fromme  <oliver.fromme@heim3.tu-clausthal.de>
 *   Wed Apr  9 20:57:47 MET DST 1997
 */

#undef ALSA

#if !defined(WIN32) && !defined(GENERIC)

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <ctype.h>

#include "mpg123.h"

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif

void writestring (int fd, char *string)
{
	int result, bytes = strlen(string);

	while (bytes) {
		if ((result = write(fd, string, bytes)) < 0 && errno != EINTR) {
			perror ("write");
			exit (1);
		}
		else if (result == 0) {
			fprintf (stderr, "write: %s\n",
				"socket closed unexpectedly");
			exit (1);
		}
		string += result;
		bytes -= result;
	}
}

void readstring (char *string, int maxlen, FILE *f)
{
#if 0
	char *result;
#endif
	int pos = 0;

	while(1) {
		if( read(fileno(f),string+pos,1) == 1) {
			pos++;
			if(string[pos-1] == '\n') {
				string[pos] = 0;
				break;
			}
		}
		else if(errno != EINTR) {
			fprintf (stderr, "Error reading from socket or unexpected EOF.\n");
			exit(1);
		}
	}
#if 0
	do {
		result = fgets(string, maxlen, f);
	} while (!result  && errno == EINTR);
	if (!result) {
		fprintf (stderr, "Error reading from socket or unexpected EOF.\n");
		exit (1);
	}
#endif

}

void encode64 (char *source,char *destination)
{
  static char *Base64Digits =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  int n = 0;
  int ssiz=strlen(source);
  int i;

  for (i = 0 ; i < ssiz ; i += 3) {
    unsigned int buf;
    buf = ((unsigned char *)source)[i] << 16;
    if (i+1 < ssiz)
      buf |= ((unsigned char *)source)[i+1] << 8;
    if (i+2 < ssiz)
      buf |= ((unsigned char *)source)[i+2];

    destination[n++] = Base64Digits[(buf >> 18) % 64];
    destination[n++] = Base64Digits[(buf >> 12) % 64];
    if (i+1 < ssiz)
      destination[n++] = Base64Digits[(buf >> 6) % 64];
    else
      destination[n++] = '=';
    if (i+2 < ssiz)
      destination[n++] = Base64Digits[buf % 64];
    else
      destination[n++] = '=';
  }
  destination[n++] = 0;
}

/* VERY  simple auth-from-URL grabber */
int getauthfromURL(char *url,char *auth)
{
  char *pos;

  *auth = 0;

  if (!(strncasecmp(url, "http://", 7)))
    url += 7;

  if (!(strncasecmp(url, "ftp://", 6)))
    url += 6;

  if( (pos = strchr(url,'@')) ) {
    int i;
    for(i=0;i<pos-url;i++) {
      if( url[i] == '/' )
         return 0;
    }
    strncpy(auth,url,pos-url);
    auth[pos-url] = 0;
    strcpy(url,pos+1);
    return 1;
  }
  return 0;
}

static char *defaultportstr = "80";

char *url2hostport (char *url, char **hname, unsigned long *hip, unsigned char **port)
{
	char *h, *p;
	char *hostptr;
	char *r_hostptr;
	char *pathptr;
	char *portptr;
	char *p0;
	size_t stringlength;

	p = url;
	if (strncasecmp(p, "http://", 7) == 0)
		p += 7;

        if (strncasecmp(p, "ftp://", 6) == 0)
               p += 6;

	hostptr = p;
	while (*p && *p != '/')
		p++;
	pathptr = p;

	r_hostptr = --p;
	while (*p && hostptr < p && *p != ':' && *p != ']')
		p--;

	if (!*p || p < hostptr || *p != ':') {
		portptr = NULL;
	}
	else{
		portptr = p + 1;
		r_hostptr = p - 1;
	}
	if (*hostptr == '[' && *r_hostptr == ']') {
		hostptr++;
		r_hostptr--;
	}

	stringlength = r_hostptr - hostptr + 1;
	h = malloc(stringlength + 1); /* removed the strndup for better portability */
	if (h == NULL) {
		*hname = NULL;
		*port = NULL;
		return NULL;
	}
	strncpy(h, hostptr, stringlength);
	*(h+stringlength) = '\0';
	*hname = h;

	if (portptr) {
		stringlength = (pathptr - portptr);
		if(!stringlength) portptr = NULL;
	}
	if (portptr == NULL) {
		portptr = defaultportstr;
		stringlength = strlen(defaultportstr);
	}
	p0 = malloc(stringlength + 1);
	if (p0 == NULL) {
		free(h);
		*hname = NULL;
		*port = NULL;
		return NULL;
	}
	strncpy(p0, portptr, stringlength);
	*(p0 + stringlength) = '\0';

	for (p = p0; *p && isdigit((unsigned char) *p); p++) ;

	*p = '\0';
	*port = (unsigned char *) p0;

	return pathptr;
}

char *proxyurl = NULL;
unsigned long proxyip = 0;
unsigned char *proxyport;

#define ACCEPT_HEAD "Accept: audio/mpeg, audio/x-mpegurl, */*\r\n"

char *httpauth = NULL;
char httpauth1[256];

int http_open (const char *url)
{
	char *purl, *host, *request, *sptr;
	int linelength;
	unsigned long myip;
	unsigned char *myport;
	int sock;
	int relocate, numrelocs = 0;
	FILE *myfile;
#ifdef INET6
	struct addrinfo hints, *res, *res0;
	int error;
#else
	struct hostent *hp;
	struct sockaddr_in sin;
#endif

	host = NULL;
	proxyport = NULL;
	myport = NULL;
	if (!proxyip) {
		if (!proxyurl)
			if (!(proxyurl = getenv("MP3_HTTP_PROXY")))
				if (!(proxyurl = getenv("http_proxy")))
					proxyurl = getenv("HTTP_PROXY");
		if (proxyurl && proxyurl[0] && strcmp(proxyurl, "none")) {
			if (!(url2hostport(proxyurl, &host, &proxyip, &proxyport))) {
				fprintf (stderr, "Unknown proxy host \"%s\".\n",
					host ? host : "");
				exit (1);
			}
#if 0
			if (host)
				free (host);
#endif
		}
		else
			proxyip = INADDR_NONE;
	}


       if (proxyip == INADDR_NONE)
               if (strncasecmp(url, "ftp://", 6) == 0){
                       fprintf(stderr,"Downloading from ftp servers without PROXY not allowed\n");
                       exit(1);
               }

	
	if ((linelength = strlen(url)+200) < 1024)
		linelength = 1024;
	if (!(request = malloc(linelength)) || !(purl = malloc(1024))) {
		fprintf (stderr, "malloc() failed, out of memory.\n");
		exit (1);
	}
       /*
        * 2000-10-21:
        * We would like spaces to be automatically converted to %20's when
        * fetching via HTTP.
        * -- Martin Sjögren <md9ms@mdstud.chalmers.se>
        */
       if ((sptr = strchr(url, ' ')) == NULL) {
               strncpy (purl, url, 1023);
               purl[1023] = '\0';
       }
       else {
               int purllength = 0;
               char *urlptr = url;
               purl[0] = '\0';
               do {
                       purllength += sptr-urlptr + 3;
                       if (purllength >= 1023)
                               break;
                       strncat (purl, urlptr, sptr-urlptr);
                       /*purl[sptr-url] = '\0';*/
                       strcat (purl, "%20");
                       urlptr = sptr + 1;
               }
               while ((sptr = strchr (urlptr, ' ')) != NULL);
               strcat (purl, urlptr);
       }


        getauthfromURL(purl,httpauth1);

	do {
		strcpy (request, "GET ");
		if (proxyip != INADDR_NONE) {
                        if (strncasecmp(url, "http://", 7) != 0 && strncasecmp(url,"ftp://", 6) != 0)
				strcat (request, "http://");
			strcat (request, purl);
			myport = proxyport;
			myip = proxyip;
		}
		else {
			if (host) {
				free(host);
				host=NULL;
			}
			if (proxyport) {
				free(proxyport);
				proxyport=NULL;
			}
			if (!(sptr = url2hostport(purl, &host, &myip, &myport))) {
				fprintf (stderr, "Unknown host \"%s\".\n",
					host ? host : "");
				exit (1);
			}
			strcat (request, sptr);
		}
		sprintf (request + strlen(request),
			" HTTP/1.0\r\nUser-Agent: %s/%s\r\n",
			prgName, prgVersion);
		if (host) {
			sprintf(request + strlen(request),
				"Host: %s:%s\r\n", host, myport);
#if 0
			free (host);
#endif
		}
		strcat (request, ACCEPT_HEAD);

#ifdef INET6
		memset(&hints, 0, sizeof(hints));
		hints.ai_socktype = SOCK_STREAM;
		error = getaddrinfo(host, (char *)myport, &hints, &res0);
		if (error) {
			fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(error));
			exit(1);
		}

		sock = -1;
		for (res = res0; res; res = res->ai_next) {
			if ((sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) {
				continue;
			}
			if (connect(sock, res->ai_addr, res->ai_addrlen)) {
				close(sock);
				sock = -1;
				continue;
			}
			break;
		}

		freeaddrinfo(res0);
#else
		sock = -1;
		hp = gethostbyname(host);
		if (!hp)
			goto fail;
		if (hp->h_length != sizeof(sin.sin_addr))
			goto fail;
		sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (sock < 0)
			goto fail;
		memset(&sin, 0, sizeof(sin));
		sin.sin_family = AF_INET;
		/* sin.sin_len = sizeof(struct sockaddr_in); */
		memcpy(&sin.sin_addr, hp->h_addr, hp->h_length);
                sin.sin_port = htons(atoi( (char *) myport));
		if (connect(sock, (struct sockaddr *)&sin, sizeof(struct sockaddr_in) ) < 0) {
			close(sock);
			sock = -1;
		}
fail:
#endif

		if (sock < 0) {
			perror("socket");
			exit(1);
		}

		if (strlen(httpauth1) || httpauth) {
			char buf[1023];
			strcat (request,"Authorization: Basic ");
                        if(strlen(httpauth1))
                          encode64(httpauth1,buf);
                        else
			  encode64(httpauth,buf);
			strcat (request,buf);
			strcat (request,"\r\n");
		}
		strcat (request, "\r\n");

		writestring (sock, request);
		if (!(myfile = fdopen(sock, "rb"))) {
			perror ("fdopen");
			exit (1);
		};
		relocate = FALSE;
		purl[0] = '\0';
		readstring (request, linelength-1, myfile);
		if ((sptr = strchr(request, ' '))) {
			switch (sptr[1]) {
				case '3':
					relocate = TRUE;
				case '2':
					break;
				default:
					fprintf (stderr, "HTTP request failed: %s",
						sptr+1); /* '\n' is included */
					exit (1);
			}
		}
		do {
			readstring (request, linelength-1, myfile);
			if (!strncmp(request, "Location:", 9))
				strncpy (purl, request+10, 1023);
		} while (request[0] != '\r' && request[0] != '\n');
	} while (relocate && purl[0] && numrelocs++ < 5);
	if (relocate) {
		fprintf (stderr, "Too many HTTP relocations.\n");
		exit (1);
	}
	free (purl);
	free (request);
	free(host);
	free(proxyport);
	free(myport);

	return sock;
}

#else
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

extern int errno;

#include "mpg123.h"

void writestring (int fd, char *string)
{
}

void readstring (char *string, int maxlen, FILE *f)
{
}

char *url2hostport (char *url, char **hname, unsigned long *hip, unsigned int *port)
{
}

char *proxyurl = NULL;
unsigned long proxyip = 0;
unsigned int proxyport;

#define ACCEPT_HEAD "Accept: audio/mpeg, audio/x-mpegurl, */*\r\n"

int http_open (char *url)
{
}
#endif

/* EOF */

