http.c revision 37573
1217355Sjkim/*- 2217355Sjkim * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav 3217355Sjkim * All rights reserved. 4217355Sjkim * 5217355Sjkim * Redistribution and use in source and binary forms, with or without 6217355Sjkim * modification, are permitted provided that the following conditions 7217355Sjkim * are met: 8245582Sjkim * 1. Redistributions of source code must retain the above copyright 9217355Sjkim * notice, this list of conditions and the following disclaimer 10217355Sjkim * in this position and unchanged. 11217355Sjkim * 2. Redistributions in binary form must reproduce the above copyright 12217355Sjkim * notice, this list of conditions and the following disclaimer in the 13217355Sjkim * documentation and/or other materials provided with the distribution. 14217355Sjkim * 3. The name of the author may not be used to endorse or promote products 15217355Sjkim * derived from this software without specific prior written permission 16217355Sjkim * 17217355Sjkim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18217355Sjkim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19217355Sjkim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20217355Sjkim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21217355Sjkim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22217355Sjkim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23217355Sjkim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24217355Sjkim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25217355Sjkim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26217355Sjkim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27217355Sjkim * 28217355Sjkim * $Id: http.c,v 1.1.1.1 1998/07/09 16:52:41 des Exp $ 29217355Sjkim */ 30217355Sjkim 31217355Sjkim#include <sys/param.h> 32217355Sjkim#include <sys/errno.h> 33217355Sjkim#include <sys/socket.h> 34217355Sjkim#include <sys/types.h> 35217355Sjkim 36217355Sjkim#include <netinet/in.h> 37217355Sjkim 38217355Sjkim#include <err.h> 39217355Sjkim#include <ctype.h> 40217355Sjkim#include <netdb.h> 41217355Sjkim#include <stdio.h> 42217355Sjkim#include <stdlib.h> 43217355Sjkim#include <string.h> 44217355Sjkim#include <unistd.h> 45217365Sjkim 46217355Sjkim#include "fetch.h" 47217355Sjkim#include "httperr.c" 48217355Sjkim 49217355Sjkim#ifndef NDEBUG 50217355Sjkim#define DEBUG(x) do x; while (0) 51217355Sjkim#else 52217355Sjkim#define DEBUG(x) do { } while (0) 53217355Sjkim#endif 54217355Sjkim 55217355Sjkimextern char *__progname; 56217355Sjkim 57217355Sjkimextern int fprint64(FILE *f, const unsigned char *buf); 58217355Sjkim 59217355Sjkim#define ENDL "\r\n" 60217355Sjkim 61217355Sjkimstruct cookie 62217355Sjkim{ 63217355Sjkim FILE *real_f; 64217355Sjkim#define ENC_NONE 0 65217355Sjkim#define ENC_CHUNKED 1 66217355Sjkim int encoding; /* 1 = chunked, 0 = none */ 67217355Sjkim#define HTTPCTYPELEN 59 68217355Sjkim char content_type[HTTPCTYPELEN+1]; 69217355Sjkim char *buf; 70217355Sjkim int b_cur, eof; 71217355Sjkim unsigned b_len, chunksize; 72217355Sjkim}; 73217355Sjkim 74217355Sjkimstatic const char * 75217355Sjkim_http_errstring(int e) 76217355Sjkim{ 77217355Sjkim struct httperr *p = _http_errlist; 78217355Sjkim 79217355Sjkim while ((p->num != -1) && (p->num != e)) 80217355Sjkim p++; 81217355Sjkim 82217355Sjkim return p->string; 83217355Sjkim} 84217355Sjkim 85217355Sjkimstatic char * 86217355Sjkim_http_fillbuf(struct cookie *c) 87217355Sjkim{ 88217355Sjkim char *ln; 89217355Sjkim unsigned int len; 90217355Sjkim 91217355Sjkim if (c->eof) 92217355Sjkim return NULL; 93217355Sjkim 94217355Sjkim if (c->encoding == ENC_NONE) { 95217355Sjkim c->buf = fgetln(c->real_f, &(c->b_len)); 96217355Sjkim c->b_cur = 0; 97217355Sjkim } else if (c->encoding == ENC_CHUNKED) { 98217355Sjkim if (c->chunksize == 0) { 99217355Sjkim ln = fgetln(c->real_f, &len); 100217355Sjkim DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): new chunk: " 101217355Sjkim "%*.*s\033[m\n", (int)len-2, (int)len-2, ln)); 102217355Sjkim sscanf(ln, "%x", &(c->chunksize)); 103217355Sjkim if (!c->chunksize) { 104217355Sjkim DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): " 105217355Sjkim "end of last chunk\033[m\n")); 106217355Sjkim c->eof = 1; 107217355Sjkim return NULL; 108217355Sjkim } 109217355Sjkim DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): " 110217355Sjkim "new chunk: %X\033[m\n", c->chunksize)); 111217355Sjkim } 112217355Sjkim c->buf = fgetln(c->real_f, &(c->b_len)); 113217355Sjkim if (c->b_len > c->chunksize) 114217355Sjkim c->b_len = c->chunksize; 115217355Sjkim c->chunksize -= c->b_len; 116217355Sjkim c->b_cur = 0; 117217355Sjkim } 118217355Sjkim else return NULL; /* unknown encoding */ 119217355Sjkim return c->buf; 120217355Sjkim} 121217355Sjkim 122217355Sjkimstatic int 123217355Sjkim_http_readfn(struct cookie *c, char *buf, int len) 124217355Sjkim{ 125217355Sjkim int l, pos = 0; 126217355Sjkim while (len) { 127217355Sjkim /* empty buffer */ 128217355Sjkim if (!c->buf || (c->b_cur == c->b_len)) 129217355Sjkim if (!_http_fillbuf(c)) 130217355Sjkim break; 131217355Sjkim 132217355Sjkim l = c->b_len - c->b_cur; 133217355Sjkim if (len < l) l = len; 134217355Sjkim memcpy(buf + pos, c->buf + c->b_cur, l); 135217355Sjkim c->b_cur += l; 136217355Sjkim pos += l; 137217355Sjkim len -= l; 138217355Sjkim } 139217355Sjkim 140217355Sjkim if (ferror(c->real_f)) 141217355Sjkim return -1; 142217355Sjkim else return pos; 143217355Sjkim} 144217355Sjkim 145217355Sjkimstatic int 146217355Sjkim_http_writefn(struct cookie *c, const char *buf, int len) 147217355Sjkim{ 148217355Sjkim size_t r = fwrite(buf, 1, (size_t)len, c->real_f); 149217355Sjkim return r ? r : -1; 150217355Sjkim} 151217355Sjkim 152217355Sjkimstatic int 153217355Sjkim_http_closefn(struct cookie *c) 154217355Sjkim{ 155217355Sjkim int r = fclose(c->real_f); 156217355Sjkim free(c); 157217355Sjkim return (r == EOF) ? -1 : 0; 158217355Sjkim} 159217355Sjkim 160217355Sjkimchar * 161217355SjkimfetchContentType(FILE *f) 162217355Sjkim{ 163217355Sjkim /* 164217355Sjkim * We have no way of making sure this really *is* one of our cookies, 165217355Sjkim * so just check for a null pointer and hope for the best. 166217355Sjkim */ 167217355Sjkim return f->_cookie ? (((struct cookie *)f->_cookie)->content_type) : NULL; 168217355Sjkim} 169217355Sjkim 170217355SjkimFILE * 171217355SjkimfetchGetHTTP(url_t *URL, char *flags) 172217355Sjkim{ 173217355Sjkim int sd = -1, err, i, enc = ENC_NONE; 174217355Sjkim struct cookie *c; 175217355Sjkim char *ln, *p, *q; 176217355Sjkim FILE *f, *cf; 177217355Sjkim size_t len; 178217355Sjkim 179217355Sjkim /* allocate cookie */ 180217355Sjkim if ((c = calloc(1, sizeof(struct cookie))) == NULL) 181217355Sjkim return NULL; 182217355Sjkim 183217355Sjkim /* check port */ 184217355Sjkim if (!URL->port) 185217355Sjkim URL->port = 80; /* default HTTP port */ 186217355Sjkim 187217355Sjkim /* attempt to connect to proxy server */ 188217355Sjkim if (getenv("HTTP_PROXY")) { 189217355Sjkim char *px, host[MAXHOSTNAMELEN]; 190217355Sjkim int port = 3128; /* XXX I think 3128 is default... check? */ 191217355Sjkim size_t len; 192217355Sjkim 193217355Sjkim /* measure length */ 194217355Sjkim px = getenv("HTTP_PROXY"); 195217355Sjkim len = strcspn(px, ":"); 196217355Sjkim 197217355Sjkim /* get port (atoi is a little too tolerant perhaps?) */ 198217355Sjkim if (px[len] == ':') 199217355Sjkim port = atoi(px+len+1); 200217355Sjkim 201217355Sjkim /* get host name */ 202217355Sjkim if (len >= MAXHOSTNAMELEN) 203217355Sjkim len = MAXHOSTNAMELEN - 1; 204217355Sjkim strncpy(host, px, len); 205217355Sjkim host[len] = 0; 206217355Sjkim 207217355Sjkim /* connect */ 208217355Sjkim sd = fetchConnect(host, port); 209217355Sjkim } 210217355Sjkim 211217355Sjkim /* if no proxy is configured or could be contacted, try direct */ 212217355Sjkim if (sd < 0) { 213217355Sjkim if ((sd = fetchConnect(URL->host, URL->port)) < 0) 214217355Sjkim goto ouch; 215217355Sjkim } 216217355Sjkim 217 /* reopen as stream */ 218 if ((f = fdopen(sd, "r+")) == NULL) 219 goto ouch; 220 c->real_f = f; 221 222 /* send request (proxies require absolute form, so use that) */ 223 fprintf(f, "GET http://%s:%d/%s HTTP/1.1" ENDL, 224 URL->host, URL->port, URL->doc); 225 226 /* start sending headers away */ 227 if (URL->user[0] || URL->pwd[0]) { 228 fprintf(f, "Authorization: Basic "); 229 fprint64(f, (const unsigned char *)URL->user); 230 fputc(':', f); 231 fprint64(f, (const unsigned char *)URL->pwd); 232 fputs(ENDL, f); 233 } 234 fprintf(f, "Host: %s:%d" ENDL, URL->host, URL->port); 235 fprintf(f, "User-Agent: %s " _LIBFETCH_VER ENDL, __progname); 236 fprintf(f, "Connection: close" ENDL ENDL); 237 238 /* get response */ 239 if ((ln = fgetln(f, &len)) == NULL) 240 goto fouch; 241 DEBUG(fprintf(stderr, "response: [\033[1m%*.*s\033[m]\n", 242 (int)len-2, (int)len-2, ln)); 243 244 /* we can't use strchr() and friends since ln isn't NUL-terminated */ 245 p = ln; 246 while ((p < ln + len) && !isspace(*p)) 247 p++; 248 while ((p < ln + len) && !isdigit(*p)) 249 p++; 250 if (!isdigit(*p)) 251 goto fouch; 252 err = atoi(p); 253 DEBUG(fprintf(stderr, "code: [\033[1m%d\033[m]\n", err)); 254 255 /* add code to handle redirects later */ 256 if (err != 200) { 257 fetchLastErrCode = err; 258 fetchLastErrText = _http_errstring(err); 259 goto fouch; 260 } 261 262 /* browse through header */ 263 while (1) { 264 if ((ln = fgetln(f, &len)) == NULL) 265 goto fouch; 266 if ((ln[0] == '\r') || (ln[0] == '\n')) 267 break; 268 DEBUG(fprintf(stderr, "header: [\033[1m%*.*s\033[m]\n", 269 (int)len-2, (int)len-2, ln)); 270#define XFERENC "Transfer-Encoding:" 271 if (strncasecmp(ln, XFERENC, sizeof(XFERENC)-1) == 0) { 272 p = ln + sizeof(XFERENC) - 1; 273 while ((p < ln + len) && isspace(*p)) 274 p++; 275 for (q = p; (q < ln + len) && !isspace(*q); q++) 276 /* VOID */ ; 277 *q = 0; 278 if (strcasecmp(p, "chunked") == 0) 279 enc = ENC_CHUNKED; 280 DEBUG(fprintf(stderr, "xferenc: [\033[1m%s\033[m]\n", p)); 281#undef XFERENC 282#define CONTTYPE "Content-Type:" 283 } else if (strncasecmp(ln, CONTTYPE, sizeof(CONTTYPE)-1) == 0) { 284 p = ln + sizeof(CONTTYPE) - 1; 285 while ((p < ln + len) && isspace(*p)) 286 p++; 287 for (i = 0; p < ln + len; p++) 288 if (i < HTTPCTYPELEN) 289 c->content_type[i++] = *p; 290 do c->content_type[i--] = 0; while (isspace(c->content_type[i])); 291 DEBUG(fprintf(stderr, "conttype: [\033[1m%s\033[m]\n", 292 c->content_type)); 293#undef CONTTYPE 294 } 295 } 296 297 /* only body remains */ 298 c->encoding = enc; 299 cf = funopen(c, 300 (int (*)(void *, char *, int))_http_readfn, 301 (int (*)(void *, const char *, int))_http_writefn, 302 (fpos_t (*)(void *, fpos_t, int))NULL, 303 (int (*)(void *))_http_closefn); 304 if (cf == NULL) 305 goto fouch; 306 return cf; 307 308ouch: 309 if (sd >= 0) 310 close(sd); 311 free(c); 312 return NULL; 313fouch: 314 fclose(f); 315 free(c); 316 return NULL; 317} 318 319FILE * 320fetchPutHTTP(url_t *URL, char *flags) 321{ 322 warnx("fetchPutHTTP(): not implemented"); 323 return NULL; 324} 325