/* $Id: http_req.c,v 1.6 2003/03/03 14:35:25 ossi Exp $ *
 *
 * puf 0.9  Copyright (C) 2000-2003 by Oswald Buddenhagen <puf@ossi.cjb.net>
 * based on puf 0.1.x (C) 1999,2000 by Anders Gavare <gavare@hotmail.com>
 *
 * You may modify and distribute this code under the terms of the GPL.
 * There is NO WARRANTY of any kind. See COPYING for details.
 *
 * http_req.c - compose and send http request message
 *
 */

#include "puf.h"

static char *user_agent;

void 
init_user_agent(void)
{
    struct utsname un;
    char http_agent[SHORTSTR];

    uname(&un);
    snprintf(http_agent, SHORTSTR, PACKAGE "/" VERSION " (%s %s; %s)",
	     un.sysname, un.release, un.machine);
    if (!(user_agent = strdup(http_agent)))
	die(2, "out of memory.");
}

/*  base64 encode - this is stolen from gnu wget  */
void 
encode_auth(char *p, char *s, int len)
{
    int i;
    static char tbl[64] = {
	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
	'w', 'x', 'y', 'z', '0', '1', '2', '3',
	'4', '5', '6', '7', '8', '9', '+', '/'
    };

    if (!s)
	return;

    for (i = 0; i < len; i += 3, s += 3) {
	*p++ = tbl[s[0] >> 2];
	*p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
	*p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
	*p++ = tbl[s[2] & 0x3f];
    }
    /* Pad the result if necessary...  */
    if (i == len + 1)
	*(p - 1) = '=';
    else if (i == len + 2)
	*(p - 1) = *(p - 2) = '=';
    /* ...and zero-terminate it.  */
    *p = '\0';
}


/*  Send http 'GET' command for a url:  */

int 
send_http_get(aurl_t *au)
{
    char buf[MAXBUFSIZE], tmp[SHORTSTR], *topo, *agnt;
    struct tm *a_tm;
    int i, pos = 0;
    u_int j;
/*    char http_ver; */

    /* oops: don't want to use this for now, as http/1.1 has too many rules.
    http_ver = ((au->file_off || max_url_bytes) && 
	        au->url->host->info->is_http11) ? '1' : '0';
    pos = sprintf(buf, "GET /%s HTTP/1.%c\r\nHost: %s:%i\r\n",
		  au->url->local_part, http_ver,
		  au->url->host->name, au->url->port); */
    /* using "Range:" with http/1.0 request should not work according to the 
       http/1.1 spec (if i understood it correctly), but in practice it 
       DOES work with the most common servers (in most cases) ... 
       ok: apache, iis, webstar, netscape
       bad: bestwww, enterpriseweb (did anybody hear about them? <g>) */

    sprintf(tmp, au->url->port == 80 ? "%s" : "%s:%hi",
	    au->url->host->name, au->url->port);

    if (au->proxy) {
	prx(NFO, "requesting http://%s/%s via http://%s:%hi/%s\n", 
	    tmp, au->url->local_part, 
	    au->proxy->host->name, au->proxy->port, au->proxy->cgi_path);
	pos = sprintf(buf, "GET http://%s/%s HTTP/1.0\r\nHost: %s\r\n",
		      tmp, au->url->local_part, tmp);
	if (au->proxy->have_auth)
	    pos += sprintf(buf + pos, "Proxy-Authorization: Basic %s\r\n",
			au->proxy->cgi_path + strlen(au->proxy->cgi_path) + 1);
    } else {
	prx(NFO, "requesting http://%s/%s\n", tmp, au->url->local_part);
	pos = sprintf(buf, "GET /%s HTTP/1.0\r\nHost: %s\r\n",
		      au->url->local_part, tmp);
    }

    if (au->url->parm->opt->user_agents.nents) {
	agent_t *ag;
	i = RND(au->url->parm->opt->uar_total);
	j = au->url->parm->opt->user_agents.nents;
	do {
	    ag = ((agent_t **)au->url->parm->opt->user_agents.ents)[--j];
	    if ((i -= ag->ratio) < 0)
		break;
	} while (j > 0);
	agnt = ag->agent;
    } else
	agnt = user_agent;
    if (agnt[0])
	pos += sprintf(buf + pos, "User-Agent: %s\r\n", agnt);

    if (au->file_off && au->url->host->info->is_http11) {
	/* oops: we cannot limit the file size, when the partial content 
	   is outdated. however, this is caught elsewhere. it just causes
	   somewhat more traffic. */
	pos += sprintf(buf + pos, au->url->parm->opt->max_bytes ? 
		                  "Range: bytes="SOFFT"-"SOFFT"\r\n" : 
		                  "Range: bytes="SOFFT"-\r\n", 
		       au->file_off, au->url->parm->opt->max_bytes - 1);
	topo = au->url->parm->opt->send_if_range ? "If-Range: %s\r\n" : 0;
    } else {
	if (au->url->parm->opt->max_bytes && au->url->host->info->is_http11)
	    pos += sprintf(buf + pos, "Range: bytes=0-"SOFFT"\r\n",
			   au->url->parm->opt->max_bytes - 1);
	topo = au->file_time ? "If-Modified-Since: %s\r\n" : 0;
    }

    if (topo) {
	a_tm = gmtime(&(au->file_time));
	strftime(tmp, SHORTSTR, "%a, %d %b %Y %T GMT", a_tm);
	pos += sprintf(buf + pos, topo, tmp);
    }

    if (au->url->parm->opt->send_referer && au->url->referer)
	pos += sprintf(buf + pos, "Referer: http://%s/%s\r\n",
		       au->url->referer->host->name, 
		       au->url->referer->local_part);

    if (au->url->parm->http_auth)
	pos +=
	    sprintf(buf + pos, "Authorization: Basic %s\r\n",
		    au->url->parm->http_auth);

    for (j = 0; j < au->url->parm->opt->aux_headers.nents; j++)
	pos += sprintf(buf + pos, "%s\r\n", 
		       ((char **)au->url->parm->opt->aux_headers.ents)[j]);

    dbge(HDR, ("---request begin---\n%.*s---request end---\n", pos, buf));

    pos += sprintf(buf + pos,
#ifdef HTTP_ACCEPT
		   HTTP_ACCEPT "\r\n"
#endif
		   "Connection: close\r\n\r\n");

    return write(au->socket, buf, pos);
}


syntax highlighted by Code2HTML, v. 0.9.1