http.c revision 60707
1139825Simp/*-
290643Sbenno * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav
390643Sbenno * All rights reserved.
490643Sbenno *
590643Sbenno * Redistribution and use in source and binary forms, with or without
690643Sbenno * modification, are permitted provided that the following conditions
790643Sbenno * are met:
890643Sbenno * 1. Redistributions of source code must retain the above copyright
990643Sbenno *    notice, this list of conditions and the following disclaimer
1090643Sbenno *    in this position and unchanged.
1190643Sbenno * 2. Redistributions in binary form must reproduce the above copyright
1290643Sbenno *    notice, this list of conditions and the following disclaimer in the
1390643Sbenno *    documentation and/or other materials provided with the distribution.
1490643Sbenno * 3. The name of the author may not be used to endorse or promote products
1590643Sbenno *    derived from this software without specific prior written permission
1690643Sbenno *
1790643Sbenno * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1890643Sbenno * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1990643Sbenno * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2090643Sbenno * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2190643Sbenno * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2290643Sbenno * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2390643Sbenno * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2490643Sbenno * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2590643Sbenno * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2690643Sbenno * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2790643Sbenno *
2890643Sbenno * $FreeBSD: head/lib/libfetch/http.c 60707 2000-05-19 09:45:42Z des $
2990643Sbenno */
3090643Sbenno
3190643Sbenno/*
3290643Sbenno * The base64 code in this file is based on code from MIT fetch, which
3390643Sbenno * has the following copyright and license:
3490643Sbenno *
3590643Sbenno *-
36139825Simp * Copyright 1997 Massachusetts Institute of Technology
3777957Sbenno *
3877957Sbenno * Permission to use, copy, modify, and distribute this software and
3977957Sbenno * its documentation for any purpose and without fee is hereby
4077957Sbenno * granted, provided that both the above copyright notice and this
4177957Sbenno * permission notice appear in all copies, that both the above
4277957Sbenno * copyright notice and this permission notice appear in all
4377957Sbenno * supporting documentation, and that the name of M.I.T. not be used
4477957Sbenno * in advertising or publicity pertaining to distribution of the
4577957Sbenno * software without specific, written prior permission.	 M.I.T. makes
4677957Sbenno * no representations about the suitability of this software for any
4777957Sbenno * purpose.  It is provided "as is" without express or implied
4877957Sbenno * warranty.
4977957Sbenno *
5077957Sbenno * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
5177957Sbenno * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
5277957Sbenno * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
5377957Sbenno * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
5477957Sbenno * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
5577957Sbenno * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
5677957Sbenno * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
5777957Sbenno * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
5877957Sbenno * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
5977957Sbenno * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
6077957Sbenno * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6177957Sbenno * SUCH DAMAGE. */
6277957Sbenno
6377957Sbenno#include <sys/param.h>
6477957Sbenno
6577957Sbenno#include <err.h>
6678880Sbenno#include <ctype.h>
6777957Sbenno#include <locale.h>
68139825Simp#include <netdb.h>
6977957Sbenno#include <stdarg.h>
7077957Sbenno#include <stdio.h>
7177957Sbenno#include <stdlib.h>
7277957Sbenno#include <string.h>
7377957Sbenno#include <time.h>
7477957Sbenno#include <unistd.h>
7577957Sbenno
7677957Sbenno#include "fetch.h"
7777957Sbenno#include "common.h"
7877957Sbenno#include "httperr.h"
7977957Sbenno
8077957Sbennoextern char *__progname;
8177957Sbenno
8277957Sbenno#define ENDL "\r\n"
8377957Sbenno
8477957Sbenno#define HTTP_OK		200
8577957Sbenno#define HTTP_PARTIAL	206
8677957Sbenno
8777957Sbennostruct cookie
8877957Sbenno{
8977957Sbenno    FILE *real_f;
9077957Sbenno#define ENC_NONE 0
9177957Sbenno#define ENC_CHUNKED 1
9277957Sbenno    int encoding;			/* 1 = chunked, 0 = none */
93113038Sobrien#define HTTPCTYPELEN 59
94113038Sobrien    char content_type[HTTPCTYPELEN+1];
9577957Sbenno    char *buf;
9690643Sbenno    int b_cur, eof;
9790643Sbenno    unsigned b_len, chunksize;
9890643Sbenno};
9990643Sbenno
10090643Sbenno/*
10190643Sbenno * Send a formatted line; optionally echo to terminal
10290643Sbenno */
10390643Sbennostatic int
10490643Sbenno_http_cmd(FILE *f, char *fmt, ...)
10590643Sbenno{
10690643Sbenno    va_list ap;
10790643Sbenno
10890643Sbenno    va_start(ap, fmt);
10990643Sbenno    vfprintf(f, fmt, ap);
11090643Sbenno#ifndef NDEBUG
11190643Sbenno    fprintf(stderr, "\033[1m>>> ");
11290643Sbenno    vfprintf(stderr, fmt, ap);
11390643Sbenno    fprintf(stderr, "\033[m");
11490643Sbenno#endif
11590643Sbenno    va_end(ap);
11690643Sbenno
117118239Speter    return 0; /* XXX */
118118239Speter}
11977957Sbenno
12080431Speter/*
12190643Sbenno * Fill the input buffer, do chunk decoding on the fly
12290643Sbenno */
12390643Sbennostatic char *
12490643Sbenno_http_fillbuf(struct cookie *c)
12577957Sbenno{
12690643Sbenno    char *ln;
12790643Sbenno    unsigned int len;
12877957Sbenno
12977957Sbenno    if (c->eof)
13090643Sbenno	return NULL;
13190643Sbenno
132152180Sgrehan    if (c->encoding == ENC_NONE) {
13377957Sbenno	c->buf = fgetln(c->real_f, &(c->b_len));
13477957Sbenno	c->b_cur = 0;
13577957Sbenno    } else if (c->encoding == ENC_CHUNKED) {
13677957Sbenno	if (c->chunksize == 0) {
13777957Sbenno	    ln = fgetln(c->real_f, &len);
13877957Sbenno	    if (len <= 2)
13977957Sbenno		return NULL;
14077957Sbenno	    DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): new chunk: "
14192847Sjeff			  "%*.*s\033[m\n", (int)len-2, (int)len-2, ln));
14277957Sbenno	    sscanf(ln, "%x", &(c->chunksize));
143125687Sgrehan	    if (!c->chunksize) {
14497346Sbenno		DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): "
14583730Smp			      "end of last chunk\033[m\n"));
14690643Sbenno		c->eof = 1;
14790643Sbenno		return NULL;
14890643Sbenno	    }
14977957Sbenno	    DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): "
150178628Smarcel			  "new chunk: %X\033[m\n", c->chunksize));
15190643Sbenno	}
152152180Sgrehan	c->buf = fgetln(c->real_f, &(c->b_len));
15377957Sbenno	if (c->b_len > c->chunksize)
154152180Sgrehan	    c->b_len = c->chunksize;
15577957Sbenno	c->chunksize -= c->b_len;
156152180Sgrehan	c->b_cur = 0;
157152180Sgrehan    }
15890643Sbenno    else return NULL; /* unknown encoding */
15977957Sbenno    return c->buf;
16090643Sbenno}
16190643Sbenno
16290643Sbenno/*
16390643Sbenno * Read function
164142416Sgrehan */
165142416Sgrehanstatic int
166142416Sgrehan_http_readfn(struct cookie *c, char *buf, int len)
167142416Sgrehan{
168142416Sgrehan    int l, pos = 0;
169142416Sgrehan    while (len) {
17092521Sbenno	/* empty buffer */
171142416Sgrehan	if (!c->buf || (c->b_cur == c->b_len))
17290643Sbenno	    if (!_http_fillbuf(c))
17390643Sbenno		break;
174142416Sgrehan
17590643Sbenno	l = c->b_len - c->b_cur;
17690643Sbenno	if (len < l) l = len;
17790643Sbenno	memcpy(buf + pos, c->buf + c->b_cur, l);
17890643Sbenno	c->b_cur += l;
17990643Sbenno	pos += l;
18090643Sbenno	len -= l;
18190643Sbenno    }
182152180Sgrehan
18390643Sbenno    if (ferror(c->real_f))
18490643Sbenno	return -1;
18590643Sbenno    else return pos;
18690643Sbenno}
18790643Sbenno
18890643Sbenno/*
18990643Sbenno * Write function
19077957Sbenno */
19190643Sbennostatic int
19290643Sbenno_http_writefn(struct cookie *c, const char *buf, int len)
19390643Sbenno{
19497346Sbenno    size_t r = fwrite(buf, 1, (size_t)len, c->real_f);
19597346Sbenno    return r ? r : -1;
196152180Sgrehan}
19797346Sbenno
198100319Sbenno/*
19977957Sbenno * Close function
20090643Sbenno */
20177957Sbennostatic int
20290643Sbenno_http_closefn(struct cookie *c)
203134535Salc{
204134535Salc    int r = fclose(c->real_f);
205152180Sgrehan    free(c);
206134535Salc    return (r == EOF) ? -1 : 0;
207183094Smarcel}
208183094Smarcel
209183094Smarcel/*
210134535Salc * Extract content type from cookie
21190643Sbenno */
21290643Sbennochar *
213152180SgrehanfetchContentType(FILE *f)
214152180Sgrehan{
215152180Sgrehan    /*
21677957Sbenno     * We have no way of making sure this really *is* one of our cookies,
21790643Sbenno     * so just check for a null pointer and hope for the best.
21890643Sbenno     */
21990643Sbenno    return f->_cookie ? (((struct cookie *)f->_cookie)->content_type) : NULL;
220152180Sgrehan}
221152180Sgrehan
222152180Sgrehan/*
223152180Sgrehan * Base64 encoding
224152180Sgrehan */
22577957Sbennoint
226152180Sgrehan_http_base64(char *dst, char *src, int l)
227152180Sgrehan{
22877957Sbenno    static const char base64[] =
22999037Sbenno	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
230152180Sgrehan	"abcdefghijklmnopqrstuvwxyz"
231152180Sgrehan	"0123456789+/";
23277957Sbenno    int t, r = 0;
23390643Sbenno
234152180Sgrehan    while (l >= 3) {
23577957Sbenno	t = (src[0] << 16) | (src[1] << 8) | src[2];
236152180Sgrehan	dst[0] = base64[(t >> 18) & 0x3f];
23777957Sbenno	dst[1] = base64[(t >> 12) & 0x3f];
23890643Sbenno	dst[2] = base64[(t >> 6) & 0x3f];
23990643Sbenno	dst[3] = base64[(t >> 0) & 0x3f];
24090643Sbenno	src += 3; l -= 3;
241152180Sgrehan	dst += 4; r += 4;
242152180Sgrehan    }
243152180Sgrehan
244152180Sgrehan    switch (l) {
245152180Sgrehan    case 2:
246152180Sgrehan	t = (src[0] << 16) | (src[1] << 8);
247152180Sgrehan	dst[0] = base64[(t >> 18) & 0x3f];
248152180Sgrehan	dst[1] = base64[(t >> 12) & 0x3f];
24990643Sbenno	dst[2] = base64[(t >> 6) & 0x3f];
250152180Sgrehan	dst[3] = '=';
251152180Sgrehan	dst += 4;
252152180Sgrehan	r += 4;
253152180Sgrehan	break;
254152180Sgrehan    case 1:
25590643Sbenno	t = src[0] << 16;
256152180Sgrehan	dst[0] = base64[(t >> 18) & 0x3f];
257152180Sgrehan	dst[1] = base64[(t >> 12) & 0x3f];
258152180Sgrehan	dst[2] = dst[3] = '=';
259152180Sgrehan	dst += 4;
260152180Sgrehan	r += 4;
261152180Sgrehan	break;
26277957Sbenno    case 0:
26390643Sbenno	break;
264152180Sgrehan    }
26590643Sbenno
266152180Sgrehan    *dst = 0;
26777957Sbenno    return r;
26890643Sbenno}
26990643Sbenno
27090643Sbenno/*
271152180Sgrehan * Encode username and password
27277957Sbenno */
27377957Sbennochar *
27490643Sbenno_http_auth(char *usr, char *pwd)
27577957Sbenno{
276152180Sgrehan    int len, lu, lp;
27790643Sbenno    char *str, *s;
278152180Sgrehan
279152180Sgrehan    lu = strlen(usr);
280152180Sgrehan    lp = strlen(pwd);
28190643Sbenno
28290643Sbenno    len = (lu * 4 + 2) / 3	/* user name, round up */
28390643Sbenno	+ 1			/* colon */
28490643Sbenno	+ (lp * 4 + 2) / 3	/* password, round up */
285159303Salc	+ 1;			/* null */
286159303Salc
287152180Sgrehan    if ((s = str = (char *)malloc(len)) == NULL)
288152180Sgrehan	return NULL;
289152180Sgrehan
290152180Sgrehan    s += _http_base64(s, usr, lu);
291152180Sgrehan    *s++ = ':';
29290643Sbenno    s += _http_base64(s, pwd, lp);
293152180Sgrehan    *s = 0;
294152180Sgrehan
295152180Sgrehan    return str;
296152180Sgrehan}
297152180Sgrehan
298152180Sgrehan/*
299152180Sgrehan * Connect to server or proxy
300152180Sgrehan */
301159303SalcFILE *
302159303Salc_http_connect(struct url *URL, char *flags)
303159627Sups{
304152180Sgrehan    int direct, sd = -1, verbose;
305152180Sgrehan    size_t len;
306152180Sgrehan    char *px;
307152180Sgrehan    FILE *f;
308152180Sgrehan
309152180Sgrehan    direct = (flags && strchr(flags, 'd'));
310152180Sgrehan    verbose = (flags && strchr(flags, 'v'));
311173708Salc
312152180Sgrehan    /* check port */
313152180Sgrehan    if (!URL->port) {
314152180Sgrehan	struct servent *se;
315152180Sgrehan
316152180Sgrehan	if (strcasecmp(URL->scheme, "ftp") == 0)
317152180Sgrehan	    if ((se = getservbyname("ftp", "tcp")) != NULL)
318152180Sgrehan		URL->port = ntohs(se->s_port);
319152180Sgrehan	    else
320160889Salc		URL->port = 21;
321152180Sgrehan	else
322152180Sgrehan	    if ((se = getservbyname("http", "tcp")) != NULL)
323152180Sgrehan		URL->port = ntohs(se->s_port);
324152180Sgrehan	    else
325152180Sgrehan		URL->port = 80;
326152180Sgrehan    }
327152180Sgrehan
328152180Sgrehan    /* attempt to connect to proxy server */
329152180Sgrehan    if (!direct && (px = getenv("HTTP_PROXY")) != NULL) {
330152180Sgrehan	char host[MAXHOSTNAMELEN];
331152180Sgrehan	int port = 0;
332164895Sgrehan
333152180Sgrehan	/* measure length */
334152180Sgrehan	len = strcspn(px, ":");
335152180Sgrehan
336152180Sgrehan	/* get port (XXX atoi is a little too tolerant perhaps?) */
337152180Sgrehan	if (px[len] == ':') {
338152180Sgrehan	    if (strspn(px+len+1, "0123456789") != strlen(px+len+1)
339152180Sgrehan		|| strlen(px+len+1) > 5) {
340159303Salc		/* XXX we should emit some kind of warning */
341152180Sgrehan	    }
342152180Sgrehan	    port = atoi(px+len+1);
343152180Sgrehan	    if (port < 1 || port > 65535) {
344152180Sgrehan		/* XXX we should emit some kind of warning */
345152180Sgrehan	    }
346152180Sgrehan	}
347152180Sgrehan	if (!port) {
348152180Sgrehan#if 0
349173708Salc	    /*
350152180Sgrehan	     * commented out, since there is currently no service name
351152180Sgrehan	     * for HTTP proxies
352152180Sgrehan	     */
353152180Sgrehan	    struct servent *se;
354152180Sgrehan
355152180Sgrehan	    if ((se = getservbyname("xxxx", "tcp")) != NULL)
356152180Sgrehan		port = ntohs(se->s_port);
357152180Sgrehan	    else
358160889Salc#endif
359152180Sgrehan		port = 3128;
360152180Sgrehan	}
361152180Sgrehan
362152180Sgrehan	/* get host name */
363152180Sgrehan	if (len >= MAXHOSTNAMELEN)
364152180Sgrehan	    len = MAXHOSTNAMELEN - 1;
365152180Sgrehan	strncpy(host, px, len);
366152180Sgrehan	host[len] = 0;
367152180Sgrehan
368152180Sgrehan	/* connect */
369152180Sgrehan	sd = _fetch_connect(host, port, verbose);
370152180Sgrehan    }
371152180Sgrehan
372164895Sgrehan    /* if no proxy is configured or could be contacted, try direct */
373152180Sgrehan    if (sd == -1) {
374152180Sgrehan	if (strcasecmp(URL->scheme, "ftp") == 0)
375152180Sgrehan	    goto ouch;
376152180Sgrehan	if ((sd = _fetch_connect(URL->host, URL->port, verbose)) == -1)
377152180Sgrehan	    goto ouch;
378152180Sgrehan    }
379152180Sgrehan
380152180Sgrehan    /* reopen as stream */
381152180Sgrehan    if ((f = fdopen(sd, "r+")) == NULL)
382152180Sgrehan	goto ouch;
383152180Sgrehan
384183094Smarcel    return f;
385183094Smarcel
386183094Smarcelouch:
387152180Sgrehan    if (sd >= 0)
388183094Smarcel	close(sd);
389183094Smarcel    _http_seterr(999); /* XXX do this properly RSN */
390183094Smarcel    return NULL;
391183094Smarcel}
392183094Smarcel
393183094Smarcel/*
394183094Smarcel * Send a HEAD or GET request
395183094Smarcel */
396183094Smarcelint
397183094Smarcel_http_request(FILE *f, char *op, struct url *URL, char *flags)
398183094Smarcel{
399183094Smarcel    int e, verbose;
400183094Smarcel    char *ln, *p;
401183094Smarcel    size_t len;
402183094Smarcel
403183094Smarcel    verbose = (flags && strchr(flags, 'v'));
404183094Smarcel
405183094Smarcel    /* send request (proxies require absolute form, so use that) */
406183094Smarcel    if (verbose)
407183094Smarcel	_fetch_info("requesting %s://%s:%d%s",
40890643Sbenno		    URL->scheme, URL->host, URL->port, URL->doc);
40990643Sbenno    _http_cmd(f, "%s %s://%s:%d%s HTTP/1.1" ENDL,
41077957Sbenno	      op, URL->scheme, URL->host, URL->port, URL->doc);
41190643Sbenno
41290643Sbenno    /* start sending headers away */
41377957Sbenno    if (URL->user[0] || URL->pwd[0]) {
41490643Sbenno	char *auth_str = _http_auth(URL->user, URL->pwd);
41590643Sbenno	if (!auth_str)
41690643Sbenno	    return 999; /* XXX wrong */
41790643Sbenno	_http_cmd(f, "Authorization: Basic %s" ENDL, auth_str);
41890643Sbenno	free(auth_str);
41990643Sbenno    }
42090643Sbenno    _http_cmd(f, "Host: %s:%d" ENDL, URL->host, URL->port);
421152180Sgrehan    _http_cmd(f, "User-Agent: %s " _LIBFETCH_VER ENDL, __progname);
42277957Sbenno    if (URL->offset)
42377957Sbenno	_http_cmd(f, "Range: bytes=%lld-" ENDL, URL->offset);
42490643Sbenno    _http_cmd(f, "Connection: close" ENDL ENDL);
42596250Sbenno
42677957Sbenno    /* get response */
42790643Sbenno    if ((ln = fgetln(f, &len)) == NULL)
42877957Sbenno	return 999;
42990643Sbenno    DEBUG(fprintf(stderr, "response: [\033[1m%*.*s\033[m]\n",
43090643Sbenno		  (int)len-2, (int)len-2, ln));
43196250Sbenno
43296250Sbenno    /* we can't use strchr() and friends since ln isn't NUL-terminated */
43396250Sbenno    p = ln;
43490643Sbenno    while ((p < ln + len) && !isspace(*p))
435152180Sgrehan	p++;
43690643Sbenno    while ((p < ln + len) && !isdigit(*p))
43790643Sbenno	p++;
43877957Sbenno    if (!isdigit(*p))
43977957Sbenno	return 999;
44090643Sbenno
44190643Sbenno    e = atoi(p);
44290643Sbenno    DEBUG(fprintf(stderr, "code:     [\033[1m%d\033[m]\n", e));
44390643Sbenno    return e;
44490643Sbenno}
44590643Sbenno
44690643Sbenno/*
44777957Sbenno * Check a header line
448152180Sgrehan */
44977957Sbennochar *
45090643Sbenno_http_match(char *str, char *hdr)
451159928Salc{
45290643Sbenno    while (*str && *hdr && tolower(*str++) == tolower(*hdr++))
45377957Sbenno	/* nothing */;
45477957Sbenno    if (*str || *hdr != ':')
45577957Sbenno	return NULL;
456152180Sgrehan    while (*hdr && isspace(*++hdr))
45777957Sbenno	/* nothing */;
45877957Sbenno    return hdr;
45990643Sbenno}
46077957Sbenno
46177957Sbenno/*
46290643Sbenno * Retrieve a file by HTTP
463152180Sgrehan */
46490643SbennoFILE *
46590643SbennofetchGetHTTP(struct url *URL, char *flags)
466159928Salc{
46790643Sbenno    int e, enc = ENC_NONE, i;
46890643Sbenno    struct cookie *c;
46990643Sbenno    char *ln, *p, *q;
47077957Sbenno    FILE *f, *cf;
471152180Sgrehan    size_t len;
47277957Sbenno    off_t pos = 0;
47390643Sbenno
47490643Sbenno    /* allocate cookie */
47590643Sbenno    if ((c = calloc(1, sizeof *c)) == NULL)
47690643Sbenno	return NULL;
47777957Sbenno
47877957Sbenno    /* connect */
47977957Sbenno    if ((f = _http_connect(URL, flags)) == NULL) {
480152180Sgrehan	free(c);
48177957Sbenno	return NULL;
48290643Sbenno    }
48390643Sbenno    c->real_f = f;
48490643Sbenno
48590643Sbenno    e = _http_request(f, "GET", URL, flags);
48677957Sbenno
48790643Sbenno    /* add code to handle redirects later */
488152180Sgrehan    if (e != (URL->offset ? HTTP_PARTIAL : HTTP_OK)) {
48990643Sbenno	_http_seterr(e);
490159928Salc	goto fouch;
491159928Salc    }
492159928Salc
49390643Sbenno    /* browse through header */
49490643Sbenno    while (1) {
49590643Sbenno	if ((ln = fgetln(f, &len)) == NULL)
49690643Sbenno	    goto fouch;
49790643Sbenno	if ((ln[0] == '\r') || (ln[0] == '\n'))
49890643Sbenno	    break;
49990643Sbenno	while (isspace(ln[len-1]))
50090643Sbenno	    --len;
50190643Sbenno	ln[len] = '\0'; /* XXX */
50277957Sbenno	DEBUG(fprintf(stderr, "header:	 [\033[1m%s\033[m]\n", ln));
50377957Sbenno	if ((p = _http_match("Transfer-Encoding", ln)) != NULL) {
50490643Sbenno	    for (q = p; *q && !isspace(*q); q++)
505152180Sgrehan		/* VOID */ ;
50677957Sbenno	    *q = 0;
50777957Sbenno	    if (strcasecmp(p, "chunked") == 0)
508159928Salc		enc = ENC_CHUNKED;
50990643Sbenno	    DEBUG(fprintf(stderr, "transfer encoding:  [\033[1m%s\033[m]\n", p));
51077957Sbenno	} else if ((p = _http_match("Content-Type", ln)) != NULL) {
51177957Sbenno	    for (i = 0; *p && i < HTTPCTYPELEN; p++, i++)
51290643Sbenno		    c->content_type[i] = *p;
513152180Sgrehan	    do c->content_type[i--] = 0; while (isspace(c->content_type[i]));
51477957Sbenno	    DEBUG(fprintf(stderr, "content type: [\033[1m%s\033[m]\n",
51577957Sbenno			  c->content_type));
516159928Salc	} else if ((p = _http_match("Content-Range", ln)) != NULL) {
517159928Salc	    if (strncasecmp(p, "bytes ", 6) != 0)
51890643Sbenno		goto fouch;
51990643Sbenno	    p += 6;
52090643Sbenno	    while (*p && isdigit(*p))
52190643Sbenno		pos = pos * 10 + (*p++ - '0');
522183094Smarcel	    /* XXX wouldn't hurt to be slightly more paranoid here */
52377957Sbenno	    DEBUG(fprintf(stderr, "content range: [\033[1m%lld-\033[m]\n", pos));
52477957Sbenno	    if (pos > URL->offset)
52590643Sbenno		goto fouch;
526152180Sgrehan	}
52777957Sbenno    }
52877957Sbenno
529159928Salc    /* only body remains */
53090643Sbenno    c->encoding = enc;
53190643Sbenno    cf = funopen(c,
53277957Sbenno		 (int (*)(void *, char *, int))_http_readfn,
53390643Sbenno		 (int (*)(void *, const char *, int))_http_writefn,
53490643Sbenno		 (fpos_t (*)(void *, fpos_t, int))NULL,
53590643Sbenno		 (int (*)(void *))_http_closefn);
53677957Sbenno    if (cf == NULL)
53790643Sbenno	goto fouch;
538183094Smarcel
53990643Sbenno    while (pos < URL->offset)
540183094Smarcel	if (fgetc(cf) == EOF)
541152180Sgrehan	    goto cfouch;
54290643Sbenno
54377957Sbenno    return cf;
54490643Sbenno
545152180Sgrehanfouch:
54690643Sbenno    fclose(f);
54790643Sbenno    free(c);
548159928Salc    _http_seterr(999); /* XXX do this properly RSN */
54990643Sbenno    return NULL;
55090643Sbennocfouch:
55177957Sbenno    fclose(cf);
55290643Sbenno    _http_seterr(999); /* XXX do this properly RSN */
55377957Sbenno    return NULL;
554183094Smarcel}
55577957Sbenno
55690643SbennoFILE *
55790643SbennofetchPutHTTP(struct url *URL, char *flags)
55890643Sbenno{
55990643Sbenno    warnx("fetchPutHTTP(): not implemented");
56077957Sbenno    return NULL;
561183094Smarcel}
56277957Sbenno
56390643Sbenno/*
56490643Sbenno * Get an HTTP document's metadata
56590643Sbenno */
566152180Sgrehanint
567152180SgrehanfetchStatHTTP(struct url *URL, struct url_stat *us, char *flags)
56877957Sbenno{
56977957Sbenno    int e;
57090643Sbenno    size_t len;
571152180Sgrehan    char *ln, *p;
57290643Sbenno    FILE *f;
57390643Sbenno
57490643Sbenno    us->size = -1;
57590643Sbenno    us->atime = us->mtime = 0;
57690643Sbenno
577152180Sgrehan    /* connect */
578152180Sgrehan    if ((f = _http_connect(URL, flags)) == NULL)
57990643Sbenno	return -1;
58090643Sbenno
58177957Sbenno    if ((e = _http_request(f, "HEAD", URL, flags)) != HTTP_OK) {
58290643Sbenno	_http_seterr(e);
58377957Sbenno	goto ouch;
58490643Sbenno    }
58590643Sbenno
58690643Sbenno    while (1) {
58790643Sbenno	if ((ln = fgetln(f, &len)) == NULL)
58890643Sbenno	    goto fouch;
58977957Sbenno	if ((ln[0] == '\r') || (ln[0] == '\n'))
59090643Sbenno	    break;
59190643Sbenno	while (isspace(ln[len-1]))
59277957Sbenno	    --len;
59390643Sbenno	ln[len] = '\0'; /* XXX */
59490643Sbenno	DEBUG(fprintf(stderr, "header:	 [\033[1m%s\033[m]\n", ln));
59590643Sbenno	if ((p = _http_match("Last-Modified", ln)) != NULL) {
59690643Sbenno	    struct tm tm;
59790643Sbenno	    char locale[64];
59890643Sbenno
59990643Sbenno	    strncpy(locale, setlocale(LC_TIME, NULL), sizeof locale);
60090643Sbenno	    setlocale(LC_TIME, "C");
60190643Sbenno	    strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm);
60277957Sbenno	    /* XXX should add support for date-2 and date-3 */
60390643Sbenno	    setlocale(LC_TIME, locale);
60490643Sbenno	    us->atime = us->mtime = timegm(&tm);
60590643Sbenno	    DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d "
60690643Sbenno			  "%02d:%02d:%02d\033[m]\n",
60790643Sbenno			  tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
60890643Sbenno			  tm.tm_hour, tm.tm_min, tm.tm_sec));
60990643Sbenno	} else if ((p = _http_match("Content-Length", ln)) != NULL) {
61090643Sbenno	    us->size = 0;
61190643Sbenno	    while (*p && isdigit(*p))
61290643Sbenno		us->size = us->size * 10 + (*p++ - '0');
61390643Sbenno	    DEBUG(fprintf(stderr, "content length: [\033[1m%lld\033[m]\n", us->size));
61490643Sbenno	}
61590643Sbenno    }
61690643Sbenno
61777957Sbenno    fclose(f);
61877957Sbenno    return 0;
61977957Sbenno ouch:
620183060Smarcel    _http_seterr(999); /* XXX do this properly RSN */
621178628Smarcel fouch:
622178628Smarcel    fclose(f);
623178628Smarcel    return -1;
624178628Smarcel}
625178628Smarcel
626183094Smarcel/*
627178628Smarcel * List a directory
628178628Smarcel */
629178628Smarcelstruct url_ent *
630178628SmarcelfetchListHTTP(struct url *url, char *flags)
631178628Smarcel{
632178628Smarcel    warnx("fetchListHTTP(): not implemented");
633178628Smarcel    return NULL;
634178628Smarcel}
635178629Smarcel