Deleted Added
full compact
http.c (62965) http.c (63012)
1/*-
1/*-
2 * Copyright (c) 1998 Dag-Erling Co�dan Sm�rgrav
2 * Copyright (c) 2000 Dag-Erling Co�dan Sm�rgrav
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer
10 * in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer
10 * in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $FreeBSD: head/lib/libfetch/http.c 62965 2000-07-11 18:12:41Z des $
28 * $FreeBSD: head/lib/libfetch/http.c 63012 2000-07-12 10:39:56Z des $
29 */
30
29 */
30
31/*
32 * The base64 code in this file is based on code from MIT fetch, which
33 * has the following copyright and license:
34 *
35 *-
36 * Copyright 1997 Massachusetts Institute of Technology
37 *
38 * Permission to use, copy, modify, and distribute this software and
39 * its documentation for any purpose and without fee is hereby
40 * granted, provided that both the above copyright notice and this
41 * permission notice appear in all copies, that both the above
42 * copyright notice and this permission notice appear in all
43 * supporting documentation, and that the name of M.I.T. not be used
44 * in advertising or publicity pertaining to distribution of the
45 * software without specific, written prior permission. M.I.T. makes
46 * no representations about the suitability of this software for any
47 * purpose. It is provided "as is" without express or implied
48 * warranty.
49 *
50 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
51 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
52 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
53 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
54 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
55 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
56 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
57 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
58 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
59 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
60 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE. */
62
63#include <sys/param.h>
64#include <sys/socket.h>
65
31#include <sys/param.h>
32#include <sys/socket.h>
33
66#include <err.h>
67#include <ctype.h>
34#include <ctype.h>
35#include <err.h>
36#include <errno.h>
68#include <locale.h>
69#include <netdb.h>
70#include <stdarg.h>
71#include <stdio.h>
72#include <stdlib.h>
73#include <string.h>
74#include <time.h>
75#include <unistd.h>
76
77#include "fetch.h"
78#include "common.h"
79#include "httperr.h"
80
37#include <locale.h>
38#include <netdb.h>
39#include <stdarg.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <time.h>
44#include <unistd.h>
45
46#include "fetch.h"
47#include "common.h"
48#include "httperr.h"
49
81extern char *__progname;
50extern char *__progname; /* XXX not portable */
82
51
83#define ENDL "\r\n"
52/* Maximum number of redirects to follow */
53#define MAX_REDIRECT 5
84
54
85#define HTTP_OK 200
86#define HTTP_PARTIAL 206
87#define HTTP_MOVED 302
55/* Symbolic names for reply codes we care about */
56#define HTTP_OK 200
57#define HTTP_PARTIAL 206
58#define HTTP_MOVED_PERM 301
59#define HTTP_MOVED_TEMP 302
60#define HTTP_SEE_OTHER 303
61#define HTTP_NEED_AUTH 401
62#define HTTP_NEED_PROXY_AUTH 403
63#define HTTP_PROTOCOL_ERROR 999
88
64
65#define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \
66 || (xyz) == HTTP_MOVED_TEMP \
67 || (xyz) == HTTP_SEE_OTHER)
68
69
70
71/*****************************************************************************
72 * I/O functions for decoding chunked streams
73 */
74
89struct cookie
90{
75struct cookie
76{
91 FILE *real_f;
92#define ENC_NONE 0
93#define ENC_CHUNKED 1
94 int encoding; /* 1 = chunked, 0 = none */
95#define HTTPCTYPELEN 59
96 char content_type[HTTPCTYPELEN+1];
97 char *buf;
98 int b_cur, eof;
99 unsigned b_len, chunksize;
77 int fd;
78 char *buf;
79 size_t b_size;
80 size_t b_len;
81 int b_pos;
82 int eof;
83 int error;
84 long chunksize;
85#ifdef DEBUG
86 long total;
87#endif
100};
101
102/*
88};
89
90/*
103 * Send a formatted line; optionally echo to terminal
91 * Get next chunk header
104 */
105static int
92 */
93static int
106_http_cmd(FILE *f, char *fmt, ...)
94_http_new_chunk(struct cookie *c)
107{
95{
108 va_list ap;
109
110 va_start(ap, fmt);
111 vfprintf(f, fmt, ap);
112 DEBUG(fprintf(stderr, "\033[1m>>> "));
113 DEBUG(vfprintf(stderr, fmt, ap));
114 DEBUG(fprintf(stderr, "\033[m"));
115 va_end(ap);
96 char *p;
116
97
117 return 0; /* XXX */
98 if (_fetch_getln(c->fd, &c->buf, &c->b_size, &c->b_len) == -1)
99 return -1;
100
101 if (c->b_len < 2 || !ishexnumber(*c->buf))
102 return -1;
103
104 for (p = c->buf; !isspace(*p) && *p != ';' && p < c->buf + c->b_len; ++p)
105 if (!ishexnumber(*p))
106 return -1;
107 else if (isdigit(*p))
108 c->chunksize = c->chunksize * 16 + *p - '0';
109 else
110 c->chunksize = c->chunksize * 16 + 10 + tolower(*p) - 'a';
111
112#ifdef DEBUG
113 c->total += c->chunksize;
114 if (c->chunksize == 0)
115 fprintf(stderr, "\033[1m_http_fillbuf(): "
116 "end of last chunk\033[m\n");
117 else
118 fprintf(stderr, "\033[1m_http_fillbuf(): "
119 "new chunk: %ld (%ld)\033[m\n", c->chunksize, c->total);
120#endif
121
122 return c->chunksize;
118}
119
120/*
121 * Fill the input buffer, do chunk decoding on the fly
122 */
123}
124
125/*
126 * Fill the input buffer, do chunk decoding on the fly
127 */
123static char *
128static int
124_http_fillbuf(struct cookie *c)
125{
129_http_fillbuf(struct cookie *c)
130{
126 char *ln;
127 unsigned int len;
128
131 if (c->error)
132 return -1;
129 if (c->eof)
133 if (c->eof)
130 return NULL;
131
132 if (c->encoding == ENC_NONE) {
133 c->buf = fgetln(c->real_f, &(c->b_len));
134 c->b_cur = 0;
135 } else if (c->encoding == ENC_CHUNKED) {
136 if (c->chunksize == 0) {
137 ln = fgetln(c->real_f, &len);
138 if (len <= 2)
139 return NULL;
140 DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): new chunk: "
141 "%*.*s\033[m\n", (int)len-2, (int)len-2, ln));
142 sscanf(ln, "%x", &(c->chunksize));
143 if (!c->chunksize) {
144 DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): "
145 "end of last chunk\033[m\n"));
146 c->eof = 1;
147 return NULL;
148 }
149 DEBUG(fprintf(stderr, "\033[1m_http_fillbuf(): "
150 "new chunk: %X\033[m\n", c->chunksize));
134 return 0;
135
136 if (c->chunksize == 0) {
137 switch (_http_new_chunk(c)) {
138 case -1:
139 c->error = 1;
140 return -1;
141 case 0:
142 c->eof = 1;
143 return 0;
151 }
144 }
152 c->buf = fgetln(c->real_f, &(c->b_len));
153 if (c->b_len > c->chunksize)
154 c->b_len = c->chunksize;
155 c->chunksize -= c->b_len;
156 c->b_cur = 0;
157 }
145 }
158 else return NULL; /* unknown encoding */
159 return c->buf;
146
147 if (c->b_size < c->chunksize) {
148 char *tmp;
149
150 if ((tmp = realloc(c->buf, c->chunksize)) == NULL)
151 return -1;
152 c->buf = tmp;
153 c->b_size = c->chunksize;
154 }
155
156 if ((c->b_len = read(c->fd, c->buf, c->chunksize)) == -1)
157 return -1;
158 c->chunksize -= c->b_len;
159
160 if (c->chunksize == 0) {
161 char endl[2];
162 read(c->fd, endl, 2);
163 }
164
165 c->b_pos = 0;
166
167 return c->b_len;
160}
161
162/*
163 * Read function
164 */
165static int
168}
169
170/*
171 * Read function
172 */
173static int
166_http_readfn(struct cookie *c, char *buf, int len)
174_http_readfn(void *v, char *buf, int len)
167{
175{
168 int l, pos = 0;
169 while (len) {
176 struct cookie *c = (struct cookie *)v;
177 int l, pos;
178
179 if (c->error)
180 return -1;
181 if (c->eof)
182 return 0;
183
184 for (pos = 0; len > 0; pos += l, len -= l) {
170 /* empty buffer */
185 /* empty buffer */
171 if (!c->buf || (c->b_cur == c->b_len))
172 if (!_http_fillbuf(c))
186 if (!c->buf || c->b_pos == c->b_len)
187 if (_http_fillbuf(c) < 1)
173 break;
188 break;
174
175 l = c->b_len - c->b_cur;
176 if (len < l) l = len;
177 memcpy(buf + pos, c->buf + c->b_cur, l);
178 c->b_cur += l;
179 pos += l;
180 len -= l;
189 l = c->b_len - c->b_pos;
190 if (len < l)
191 l = len;
192 bcopy(c->buf + c->b_pos, buf + pos, l);
193 c->b_pos += l;
181 }
194 }
182
183 if (ferror(c->real_f))
195
196 if (!pos && c->error)
184 return -1;
197 return -1;
185 else return pos;
198 return pos;
186}
187
188/*
189 * Write function
190 */
191static int
199}
200
201/*
202 * Write function
203 */
204static int
192_http_writefn(struct cookie *c, const char *buf, int len)
205_http_writefn(void *v, const char *buf, int len)
193{
206{
194 size_t r = fwrite(buf, 1, (size_t)len, c->real_f);
195 return r ? r : -1;
207 struct cookie *c = (struct cookie *)v;
208
209 return write(c->fd, buf, len);
196}
197
198/*
199 * Close function
200 */
201static int
210}
211
212/*
213 * Close function
214 */
215static int
202_http_closefn(struct cookie *c)
216_http_closefn(void *v)
203{
217{
204 int r = fclose(c->real_f);
218 struct cookie *c = (struct cookie *)v;
219 int r;
220
221 r = close(c->fd);
222 if (c->buf)
223 free(c->buf);
205 free(c);
224 free(c);
206 return (r == EOF) ? -1 : 0;
225 return r;
207}
208
209/*
226}
227
228/*
210 * Extract content type from cookie
229 * Wrap a file descriptor up
211 */
230 */
212char *
213fetchContentType(FILE *f)
231static FILE *
232_http_funopen(int fd)
214{
233{
234 struct cookie *c;
235 FILE *f;
236
237 if ((c = calloc(1, sizeof *c)) == NULL) {
238 _fetch_syserr();
239 return NULL;
240 }
241 c->fd = fd;
242 if (!(f = funopen(c, _http_readfn, _http_writefn, NULL, _http_closefn))) {
243 _fetch_syserr();
244 free(c);
245 return NULL;
246 }
247 return f;
248}
249
250
251/*****************************************************************************
252 * Helper functions for talking to the server and parsing its replies
253 */
254
255/* Header types */
256typedef enum {
257 hdr_syserror = -2,
258 hdr_error = -1,
259 hdr_end = 0,
260 hdr_unknown = 1,
261 hdr_content_length,
262 hdr_content_range,
263 hdr_last_modified,
264 hdr_location,
265 hdr_transfer_encoding
266} hdr;
267
268/* Names of interesting headers */
269static struct {
270 hdr num;
271 char *name;
272} hdr_names[] = {
273 { hdr_content_length, "Content-Length" },
274 { hdr_content_range, "Content-Range" },
275 { hdr_last_modified, "Last-Modified" },
276 { hdr_location, "Location" },
277 { hdr_transfer_encoding, "Transfer-Encoding" },
278 { hdr_unknown, NULL },
279};
280
281static char *reply_buf;
282static size_t reply_size;
283static size_t reply_length;
284
285/*
286 * Send a formatted line; optionally echo to terminal
287 */
288static int
289_http_cmd(int fd, char *fmt, ...)
290{
291 va_list ap;
292 size_t len;
293 char *msg;
294 int r;
295
296 va_start(ap, fmt);
297 len = vasprintf(&msg, fmt, ap);
298 va_end(ap);
299
300 if (msg == NULL) {
301 errno = ENOMEM;
302 _fetch_syserr();
303 return -1;
304 }
305
306 r = _fetch_putln(fd, msg, len);
307 free(msg);
308
309 if (r == -1) {
310 _fetch_syserr();
311 return -1;
312 }
313
314 return 0;
315}
316
317/*
318 * Get and parse status line
319 */
320static int
321_http_get_reply(int fd)
322{
323 if (_fetch_getln(fd, &reply_buf, &reply_size, &reply_length) == -1)
324 return -1;
215 /*
325 /*
216 * We have no way of making sure this really *is* one of our cookies,
217 * so just check for a null pointer and hope for the best.
326 * A valid status line looks like "HTTP/m.n xyz reason" where m
327 * and n are the major and minor protocol version numbers and xyz
328 * is the reply code.
329 * We grok HTTP 1.0 and 1.1, so m must be 1 and n must be 0 or 1.
330 * We don't care about the reason phrase.
218 */
331 */
219 return f->_cookie ? (((struct cookie *)f->_cookie)->content_type) : NULL;
332 if (strncmp(reply_buf, "HTTP/1.", 7) != 0
333 || (reply_buf[7] != '0' && reply_buf[7] != '1') || reply_buf[8] != ' '
334 || !isdigit(reply_buf[9])
335 || !isdigit(reply_buf[10])
336 || !isdigit(reply_buf[11]))
337 return HTTP_PROTOCOL_ERROR;
338
339 return ((reply_buf[9] - '0') * 100
340 + (reply_buf[10] - '0') * 10
341 + (reply_buf[11] - '0'));
220}
221
222/*
342}
343
344/*
345 * Check a header; if the type matches the given string, return a
346 * pointer to the beginning of the value.
347 */
348static char *
349_http_match(char *str, char *hdr)
350{
351 while (*str && *hdr && tolower(*str++) == tolower(*hdr++))
352 /* nothing */;
353 if (*str || *hdr != ':')
354 return NULL;
355 while (*hdr && isspace(*++hdr))
356 /* nothing */;
357 return hdr;
358}
359
360/*
361 * Get the next header and return the appropriate symbolic code.
362 */
363static hdr
364_http_next_header(int fd, char **p)
365{
366 int i;
367
368 if (_fetch_getln(fd, &reply_buf, &reply_size, &reply_length) == -1)
369 return hdr_syserror;
370 while (reply_length && isspace(reply_buf[reply_length-1]))
371 reply_length--;
372 reply_buf[reply_length] = 0;
373 if (reply_length == 0)
374 return hdr_end;
375 /*
376 * We could check for malformed headers but we don't really care.
377 * A valid header starts with a token immediately followed by a
378 * colon; a token is any sequence of non-control, non-whitespace
379 * characters except "()<>@,;:\\\"{}".
380 */
381 for (i = 0; hdr_names[i].num != hdr_unknown; i++)
382 if ((*p = _http_match(hdr_names[i].name, reply_buf)) != NULL)
383 return hdr_names[i].num;
384 return hdr_unknown;
385}
386
387/*
388 * Parse a last-modified header
389 */
390static time_t
391_http_parse_mtime(char *p)
392{
393 char locale[64];
394 struct tm tm;
395
396 strncpy(locale, setlocale(LC_TIME, NULL), sizeof locale);
397 setlocale(LC_TIME, "C");
398 strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm);
399 /* XXX should add support for date-2 and date-3 */
400 setlocale(LC_TIME, locale);
401 DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d "
402 "%02d:%02d:%02d\033[m]\n",
403 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
404 tm.tm_hour, tm.tm_min, tm.tm_sec));
405 return timegm(&tm);
406}
407
408/*
409 * Parse a content-length header
410 */
411static off_t
412_http_parse_length(char *p)
413{
414 off_t len;
415
416 for (len = 0; *p && isdigit(*p); ++p)
417 len = len * 10 + (*p - '0');
418 DEBUG(fprintf(stderr, "content length: [\033[1m%lld\033[m]\n", len));
419 return len;
420}
421
422/*
423 * Parse a content-range header
424 */
425static off_t
426_http_parse_range(char *p)
427{
428 off_t off;
429
430 if (strncasecmp(p, "bytes ", 6) != 0)
431 return -1;
432 for (p += 6, off = 0; *p && isdigit(*p); ++p)
433 off = off * 10 + *p - '0';
434 if (*p != '-')
435 return -1;
436 DEBUG(fprintf(stderr, "content range: [\033[1m%lld-\033[m]\n", off));
437 return off;
438}
439
440
441/*****************************************************************************
442 * Helper functions for authorization
443 */
444
445/*
223 * Base64 encoding
224 */
225static char *
226_http_base64(char *src)
227{
228 static const char base64[] =
229 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
230 "abcdefghijklmnopqrstuvwxyz"

--- 43 unchanged lines hidden (view full) ---

274 *dst = 0;
275 return str;
276}
277
278/*
279 * Encode username and password
280 */
281static int
446 * Base64 encoding
447 */
448static char *
449_http_base64(char *src)
450{
451 static const char base64[] =
452 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
453 "abcdefghijklmnopqrstuvwxyz"

--- 43 unchanged lines hidden (view full) ---

497 *dst = 0;
498 return str;
499}
500
501/*
502 * Encode username and password
503 */
504static int
282_http_basic_auth(FILE *f, char *hdr, char *usr, char *pwd)
505_http_basic_auth(int fd, char *hdr, char *usr, char *pwd)
283{
284 char *upw, *auth;
285 int r;
286
287 if (asprintf(&upw, "%s:%s", usr, pwd) == -1)
288 return -1;
289 auth = _http_base64(upw);
290 free(upw);
291 if (auth == NULL)
292 return -1;
506{
507 char *upw, *auth;
508 int r;
509
510 if (asprintf(&upw, "%s:%s", usr, pwd) == -1)
511 return -1;
512 auth = _http_base64(upw);
513 free(upw);
514 if (auth == NULL)
515 return -1;
293 r = _http_cmd(f, "%s: Basic %s" ENDL, hdr, auth);
516 r = _http_cmd(fd, "%s: Basic %s", hdr, auth);
294 free(auth);
295 return r;
296}
297
298/*
299 * Send an authorization header
300 */
301static int
517 free(auth);
518 return r;
519}
520
521/*
522 * Send an authorization header
523 */
524static int
302_http_authorize(FILE *f, char *hdr, char *p)
525_http_authorize(int fd, char *hdr, char *p)
303{
304 /* basic authorization */
305 if (strncasecmp(p, "basic:", 6) == 0) {
306 char *user, *pwd, *str;
307 int r;
308
309 /* skip realm */
310 for (p += 6; *p && *p != ':'; ++p)
311 /* nothing */ ;
312 if (!*p || strchr(++p, ':') == NULL)
313 return -1;
314 if ((str = strdup(p)) == NULL)
315 return -1; /* XXX */
316 user = str;
317 pwd = strchr(str, ':');
318 *pwd++ = '\0';
526{
527 /* basic authorization */
528 if (strncasecmp(p, "basic:", 6) == 0) {
529 char *user, *pwd, *str;
530 int r;
531
532 /* skip realm */
533 for (p += 6; *p && *p != ':'; ++p)
534 /* nothing */ ;
535 if (!*p || strchr(++p, ':') == NULL)
536 return -1;
537 if ((str = strdup(p)) == NULL)
538 return -1; /* XXX */
539 user = str;
540 pwd = strchr(str, ':');
541 *pwd++ = '\0';
319 r = _http_basic_auth(f, hdr, user, pwd);
542 r = _http_basic_auth(fd, hdr, user, pwd);
320 free(str);
321 return r;
322 }
323 return -1;
324}
325
543 free(str);
544 return r;
545 }
546 return -1;
547}
548
549
550/*****************************************************************************
551 * Helper functions for connecting to a server or proxy
552 */
553
326/*
554/*
327 * Connect to server or proxy
555 * Connect to the specified HTTP proxy server.
328 */
556 */
329static FILE *
330_http_connect(struct url *URL, char *flags, int *proxy)
557static int
558_http_proxy_connect(char *proxy, int af, int verbose)
331{
559{
332 int direct, sd = -1, verbose;
560 char *hostname, *p;
561 int fd, port;
562
563 /* get hostname */
564 hostname = NULL;
333#ifdef INET6
565#ifdef INET6
334 int af = AF_UNSPEC;
335#else
336 int af = AF_INET;
566 /* host part can be an IPv6 address enclosed in square brackets */
567 if (*proxy == '[') {
568 if ((p = strchr(proxy, ']')) == NULL) {
569 /* no terminating bracket */
570 /* XXX should set an error code */
571 goto ouch;
572 }
573 if (p[1] != '\0' && p[1] != ':') {
574 /* garbage after address */
575 /* XXX should set an error code */
576 goto ouch;
577 }
578 if ((hostname = malloc(p - proxy)) == NULL) {
579 errno = ENOMEM;
580 _fetch_syserr();
581 goto ouch;
582 }
583 strncpy(hostname, proxy + 1, p - proxy - 1);
584 hostname[p - proxy - 1] = '\0';
585 ++p;
586 } else {
587#endif /* INET6 */
588 if ((p = strchr(proxy, ':')) == NULL)
589 p = strchr(proxy, '\0');
590 if ((hostname = malloc(p - proxy + 1)) == NULL) {
591 errno = ENOMEM;
592 _fetch_syserr();
593 goto ouch;
594 }
595 strncpy(hostname, proxy, p - proxy);
596 hostname[p - proxy] = '\0';
597#ifdef INET6
598 }
599#endif /* INET6 */
600 DEBUG(fprintf(stderr, "proxy name: [%s]\n", hostname));
601
602 /* get port number */
603 port = 0;
604 if (*p == ':') {
605 ++p;
606 if (strspn(p, "0123456789") != strlen(p) || strlen(p) > 5) {
607 /* port number is non-numeric or too long */
608 /* XXX should set an error code */
609 goto ouch;
610 }
611 port = atoi(p);
612 if (port < 1 || port > 65535) {
613 /* port number is out of range */
614 /* XXX should set an error code */
615 goto ouch;
616 }
617 }
618
619 if (!port) {
620#if 0
621 /*
622 * commented out, since there is currently no service name
623 * for HTTP proxies
624 */
625 struct servent *se;
626
627 if ((se = getservbyname("xxxx", "tcp")) != NULL)
628 port = ntohs(se->s_port);
629 else
337#endif
630#endif
338 size_t len;
339 char *px;
340 FILE *f;
631 port = 3128;
632 }
633 DEBUG(fprintf(stderr, "proxy port: %d\n", port));
634
635 /* connect */
636 if ((fd = _fetch_connect(hostname, port, af, verbose)) == -1)
637 _fetch_syserr();
638 return fd;
341
639
640 ouch:
641 if (hostname)
642 free(hostname);
643 return -1;
644}
645
646/*
647 * Connect to the correct HTTP server or proxy.
648 */
649static int
650_http_connect(struct url *URL, int *proxy, char *flags)
651{
652 int direct, verbose;
653 int af, fd;
654 char *p;
655
656#ifdef INET6
657 af = AF_UNSPEC;
658#else
659 af = AF_INET;
660#endif
661
342 direct = (flags && strchr(flags, 'd'));
343 verbose = (flags && strchr(flags, 'v'));
662 direct = (flags && strchr(flags, 'd'));
663 verbose = (flags && strchr(flags, 'v'));
344 if ((flags && strchr(flags, '4')))
664 if (flags && strchr(flags, '4'))
345 af = AF_INET;
665 af = AF_INET;
346 else if ((flags && strchr(flags, '6')))
666 else if (flags && strchr(flags, '6'))
347 af = AF_INET6;
348
349 /* check port */
350 if (!URL->port) {
351 struct servent *se;
352
667 af = AF_INET6;
668
669 /* check port */
670 if (!URL->port) {
671 struct servent *se;
672
673 /* Scheme can be ftp if we're using a proxy */
353 if (strcasecmp(URL->scheme, "ftp") == 0)
354 if ((se = getservbyname("ftp", "tcp")) != NULL)
355 URL->port = ntohs(se->s_port);
356 else
357 URL->port = 21;
358 else
359 if ((se = getservbyname("http", "tcp")) != NULL)
360 URL->port = ntohs(se->s_port);
361 else
362 URL->port = 80;
363 }
364
674 if (strcasecmp(URL->scheme, "ftp") == 0)
675 if ((se = getservbyname("ftp", "tcp")) != NULL)
676 URL->port = ntohs(se->s_port);
677 else
678 URL->port = 21;
679 else
680 if ((se = getservbyname("http", "tcp")) != NULL)
681 URL->port = ntohs(se->s_port);
682 else
683 URL->port = 80;
684 }
685
365 /* attempt to connect to proxy server */
366 if (!direct && (px = getenv("HTTP_PROXY")) != NULL) {
367 char host[MAXHOSTNAMELEN];
368 int port = 0;
369
370 /* measure length */
371#ifdef INET6
372 if (px[0] != '[' ||
373 (len = strcspn(px, "]")) >= strlen(px) ||
374 (px[++len] != '\0' && px[len] != ':'))
375#endif
376 len = strcspn(px, ":");
377
378 /* get port (XXX atoi is a little too tolerant perhaps?) */
379 if (px[len] == ':') {
380 if (strspn(px+len+1, "0123456789") != strlen(px+len+1)
381 || strlen(px+len+1) > 5) {
382 /* XXX we should emit some kind of warning */
383 }
384 port = atoi(px+len+1);
385 if (port < 1 || port > 65535) {
386 /* XXX we should emit some kind of warning */
387 }
686 if (!direct && (p = getenv("HTTP_PROXY")) != NULL) {
687 /* attempt to connect to proxy server */
688 if ((fd = _http_proxy_connect(p, af, verbose)) == -1)
689 return -1;
690 *proxy = 1;
691 } else {
692 /* if no proxy is configured, try direct */
693 if (strcasecmp(URL->scheme, "ftp") == 0) {
694 /* can't talk http to an ftp server */
695 /* XXX should set an error code */
696 return -1;
388 }
697 }
389 if (!port) {
390#if 0
391 /*
392 * commented out, since there is currently no service name
393 * for HTTP proxies
394 */
395 struct servent *se;
396
397 if ((se = getservbyname("xxxx", "tcp")) != NULL)
398 port = ntohs(se->s_port);
399 else
400#endif
401 port = 3128;
402 }
403
404 /* get host name */
405#ifdef INET6
406 if (len > 1 && px[0] == '[' && px[len - 1] == ']') {
407 px++;
408 len -= 2;
409 }
410#endif
411 if (len >= MAXHOSTNAMELEN)
412 len = MAXHOSTNAMELEN - 1;
413 strncpy(host, px, len);
414 host[len] = 0;
415
416 /* connect */
417 sd = _fetch_connect(host, port, af, verbose);
698 if ((fd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1)
699 /* _fetch_connect() has already set an error code */
700 return -1;
701 *proxy = 0;
418 }
419
702 }
703
420 /* if no proxy is configured or could be contacted, try direct */
421 *proxy = (sd != -1);
422 if (!*proxy) {
423 if (strcasecmp(URL->scheme, "ftp") == 0)
424 goto ouch;
425 if ((sd = _fetch_connect(URL->host, URL->port, af, verbose)) == -1)
426 goto ouch;
427 }
428
429 /* reopen as stream */
430 if ((f = fdopen(sd, "r+")) == NULL)
431 goto ouch;
432
433 return f;
434
435ouch:
436 if (sd >= 0)
437 close(sd);
438 _http_seterr(999); /* XXX do this properly RSN */
439 return NULL;
704 return fd;
440}
441
705}
706
442/*
443 * Check a header line
707
708/*****************************************************************************
709 * Core
444 */
710 */
445static char *
446_http_match(char *str, char *hdr)
447{
448 while (*str && *hdr && tolower(*str++) == tolower(*hdr++))
449 /* nothing */;
450 if (*str || *hdr != ':')
451 return NULL;
452 while (*hdr && isspace(*++hdr))
453 /* nothing */;
454 return hdr;
455}
456
457/*
711
712/*
458 * Send a HEAD or GET request
713 * Send a request and process the reply
459 */
714 */
460static int
461_http_request(FILE *f, char *op, struct url *URL, char *flags, int proxy)
715static FILE *
716_http_request(struct url *URL, char *op, struct url_stat *us, char *flags)
462{
717{
463 int e, verbose;
464 char *ln, *p;
465 size_t len;
718 struct url *url, *new;
719 int chunked, need_auth, noredirect, proxy, verbose;
720 int code, fd, i, n;
721 off_t offset;
722 char *p;
723 FILE *f;
724 hdr h;
466 char *host;
467#ifdef INET6
468 char hbuf[MAXHOSTNAMELEN + 1];
469#endif
725 char *host;
726#ifdef INET6
727 char hbuf[MAXHOSTNAMELEN + 1];
728#endif
470
729
730 noredirect = (flags && strchr(flags, 'A'));
471 verbose = (flags && strchr(flags, 'v'));
472
731 verbose = (flags && strchr(flags, 'v'));
732
473 host = URL->host;
733 n = noredirect ? 1 : MAX_REDIRECT;
734
735 us->size = -1;
736 us->atime = us->mtime = 0;
737 chunked = 0;
738 offset = 0;
739 fd = -1;
740 code = HTTP_PROTOCOL_ERROR; /* just to appease a compiler warning */
741
742 for (url = URL, i = 0; i < n; ++i) {
743 need_auth = 0;
744 retry:
745 /* connect to server or proxy */
746 if ((fd = _http_connect(url, &proxy, flags)) == -1)
747 goto ouch;
748
749 host = url->host;
474#ifdef INET6
750#ifdef INET6
475 if (strchr(URL->host, ':')) {
476 snprintf(hbuf, sizeof(hbuf), "[%s]", URL->host);
477 host = hbuf;
478 }
751 if (strchr(url->host, ':')) {
752 snprintf(hbuf, sizeof(hbuf), "[%s]", url->host);
753 host = hbuf;
754 }
479#endif
755#endif
480
481 /* send request (proxies require absolute form) */
482 if (verbose)
483 _fetch_info("requesting %s://%s:%d%s",
484 URL->scheme, host, URL->port, URL->doc);
485 if (proxy)
486 _http_cmd(f, "%s %s://%s:%d%s HTTP/1.1" ENDL,
487 op, URL->scheme, host, URL->port, URL->doc);
488 else
489 _http_cmd(f, "%s %s HTTP/1.1" ENDL, op, URL->doc);
490
756
491 /* start sending headers away */
492 if (URL->user[0] || URL->pwd[0])
493 _http_basic_auth(f, "Authorization",
494 URL->user ? URL->user : "",
495 URL->pwd ? URL->pwd : "");
496 else if ((p = getenv("HTTP_AUTH")) != NULL)
497 _http_authorize(f, "Authorization", p);
498 if (proxy && (p = getenv("HTTP_PROXY_AUTH")) != NULL)
499 _http_authorize(f, "Proxy-Authorization", p);
500 _http_cmd(f, "Host: %s:%d" ENDL, host, URL->port);
501 _http_cmd(f, "User-Agent: %s " _LIBFETCH_VER ENDL, __progname);
502 if (URL->offset)
503 _http_cmd(f, "Range: bytes=%lld-" ENDL, URL->offset);
504 _http_cmd(f, "Connection: close" ENDL ENDL);
757 /* send request */
758 if (verbose)
759 _fetch_info("requesting %s://%s:%d%s",
760 url->scheme, host, url->port, url->doc);
761 if (proxy) {
762 _http_cmd(fd, "%s %s://%s:%d%s HTTP/1.1",
763 op, url->scheme, host, url->port, url->doc);
764 } else {
765 _http_cmd(fd, "%s %s HTTP/1.1",
766 op, url->doc);
767 }
505
768
506 /* get response */
507 if ((ln = fgetln(f, &len)) == NULL)
508 return 999;
509 DEBUG(fprintf(stderr, "response: [\033[1m%*.*s\033[m]\n",
510 (int)len-2, (int)len-2, ln));
511
512 /* we can't use strchr() and friends since ln isn't NUL-terminated */
513 p = ln;
514 while ((p < ln + len) && !isspace(*p))
515 p++;
516 while ((p < ln + len) && !isdigit(*p))
517 p++;
518 if (!isdigit(*p))
519 return 999;
520
521 e = atoi(p);
522 DEBUG(fprintf(stderr, "code: [\033[1m%d\033[m]\n", e));
523 return e;
524}
769 /* proxy authorization */
770 if (proxy && (p = getenv("HTTP_PROXY_AUTH")) != NULL)
771 _http_authorize(fd, "Proxy-Authorization", p);
772
773 /* server authorization */
774 if (need_auth) {
775 if (*url->user || *url->pwd)
776 _http_basic_auth(fd, "Authorization",
777 url->user ? url->user : "",
778 url->pwd ? url->pwd : "");
779 else if ((p = getenv("HTTP_AUTH")) != NULL)
780 _http_authorize(fd, "Authorization", p);
781 else {
782 _http_seterr(HTTP_NEED_AUTH);
783 goto ouch;
784 }
785 }
525
786
526/*
527 * Retrieve a file by HTTP
528 */
529FILE *
530fetchGetHTTP(struct url *URL, char *flags)
531{
532 int e, enc = ENC_NONE, i, noredirect, proxy;
533 struct cookie *c;
534 char *ln, *p, *q;
535 FILE *f, *cf;
536 size_t len;
537 off_t pos = 0;
787 /* other headers */
788 _http_cmd(fd, "Host: %s:%d", host, url->port);
789 _http_cmd(fd, "User-Agent: %s " _LIBFETCH_VER, __progname);
790 if (URL->offset)
791 _http_cmd(fd, "Range: bytes=%lld-", url->offset);
792 _http_cmd(fd, "Connection: close");
793 _http_cmd(fd, "");
538
794
539 noredirect = (flags && strchr(flags, 'A'));
540
541 /* allocate cookie */
542 if ((c = calloc(1, sizeof *c)) == NULL)
543 return NULL;
795 /* get reply */
796 switch ((code = _http_get_reply(fd))) {
797 case HTTP_OK:
798 case HTTP_PARTIAL:
799 /* fine */
800 break;
801 case HTTP_MOVED_PERM:
802 case HTTP_MOVED_TEMP:
803 /*
804 * Not so fine, but we still have to read the headers to
805 * get the new location.
806 */
807 break;
808 case HTTP_NEED_AUTH:
809 if (need_auth) {
810 /*
811 * We already sent out authorization code, so there's
812 * nothing more we can do.
813 */
814 _http_seterr(code);
815 goto ouch;
816 }
817 /* try again, but send the password this time */
818 if (verbose)
819 _fetch_info("server requires authorization");
820 need_auth = 1;
821 close(fd);
822 goto retry;
823 case HTTP_NEED_PROXY_AUTH:
824 /*
825 * If we're talking to a proxy, we already sent our proxy
826 * authorization code, so there's nothing more we can do.
827 */
828 _http_seterr(code);
829 goto ouch;
830 case HTTP_PROTOCOL_ERROR:
831 /* fall through */
832 case -1:
833 _fetch_syserr();
834 goto ouch;
835 default:
836 _http_seterr(code);
837 goto ouch;
838 }
839
840 /* get headers */
841 do {
842 switch ((h = _http_next_header(fd, &p))) {
843 case hdr_syserror:
844 _fetch_syserr();
845 goto ouch;
846 case hdr_error:
847 _http_seterr(HTTP_PROTOCOL_ERROR);
848 goto ouch;
849 case hdr_content_length:
850 us->size = _http_parse_length(p);
851 break;
852 case hdr_content_range:
853 offset = _http_parse_range(p);
854 break;
855 case hdr_last_modified:
856 us->atime = us->mtime = _http_parse_mtime(p);
857 break;
858 case hdr_location:
859 if (!HTTP_REDIRECT(code))
860 break;
861 if (verbose)
862 _fetch_info("%d redirect to %s", code, p);
863 if ((new = fetchParseURL(p)) == NULL)
864 /* invalid location */
865 goto ouch;
866 if (!*new->user && !*new->pwd) {
867 strcpy(new->user, url->user);
868 strcpy(new->pwd, url->pwd);
869 }
870 new->offset = url->offset;
871 new->length = url->length;
872 close(fd);
873 us->size = -1;
874 us->atime = us->mtime = 0;
875 chunked = 0;
876 offset = 0;
877 fd = -1;
878 if (url != URL)
879 fetchFreeURL(url);
880 url = new;
881 continue;
882 case hdr_transfer_encoding:
883 /* XXX weak test*/
884 chunked = (strcasecmp(p, "chunked") == 0);
885 break;
886 case hdr_end:
887 /* fall through */
888 case hdr_unknown:
889 /* ignore */
890 break;
891 }
892 } while (h > hdr_end);
544
893
545 /* connect */
546 if ((f = _http_connect(URL, flags, &proxy)) == NULL) {
547 free(c);
548 return NULL;
894 if (code == HTTP_OK || code == HTTP_PARTIAL)
895 break;
549 }
896 }
550 c->real_f = f;
551
897
552 e = _http_request(f, "GET", URL, flags, proxy);
553 if (e != (URL->offset ? HTTP_PARTIAL : HTTP_OK)
554 && (e != HTTP_MOVED || noredirect)) {
555 _http_seterr(e);
556 free(c);
557 fclose(f);
558 return NULL;
898 /* no success */
899 if (fd == -1) {
900 _http_seterr(code);
901 goto ouch;
559 }
560
902 }
903
561 /* browse through header */
562 while (1) {
563 if ((ln = fgetln(f, &len)) == NULL)
564 goto fouch;
565 if ((ln[0] == '\r') || (ln[0] == '\n'))
566 break;
567 while (isspace(ln[len-1]))
568 --len;
569 ln[len] = '\0'; /* XXX */
570 DEBUG(fprintf(stderr, "header: [\033[1m%s\033[m]\n", ln));
571 if ((p = _http_match("Location", ln)) != NULL) {
572 struct url *url;
573
574 for (q = p; *q && !isspace(*q); q++)
575 /* VOID */ ;
576 *q = 0;
577 if ((url = fetchParseURL(p)) == NULL)
578 goto fouch;
579 url->offset = URL->offset;
580 url->length = URL->length;
581 DEBUG(fprintf(stderr, "location: [\033[1m%s\033[m]\n", p));
582 cf = fetchGetHTTP(url, flags);
583 fetchFreeURL(url);
904 /* wrap it up in a FILE */
905 if ((f = chunked ? _http_funopen(fd) : fdopen(fd, "r")) == NULL) {
906 _fetch_syserr();
907 goto ouch;
908 }
909
910 while (offset++ < url->offset)
911 if (fgetc(f) == EOF) {
912 _fetch_syserr();
584 fclose(f);
913 fclose(f);
585 return cf;
586 } else if ((p = _http_match("Transfer-Encoding", ln)) != NULL) {
587 for (q = p; *q && !isspace(*q); q++)
588 /* VOID */ ;
589 *q = 0;
590 if (strcasecmp(p, "chunked") == 0)
591 enc = ENC_CHUNKED;
592 DEBUG(fprintf(stderr, "transfer encoding: [\033[1m%s\033[m]\n", p));
593 } else if ((p = _http_match("Content-Type", ln)) != NULL) {
594 for (i = 0; *p && i < HTTPCTYPELEN; p++, i++)
595 c->content_type[i] = *p;
596 do c->content_type[i--] = 0; while (isspace(c->content_type[i]));
597 DEBUG(fprintf(stderr, "content type: [\033[1m%s\033[m]\n",
598 c->content_type));
599 } else if ((p = _http_match("Content-Range", ln)) != NULL) {
600 if (strncasecmp(p, "bytes ", 6) != 0)
601 goto fouch;
602 p += 6;
603 while (*p && isdigit(*p))
604 pos = pos * 10 + (*p++ - '0');
605 /* XXX wouldn't hurt to be slightly more paranoid here */
606 DEBUG(fprintf(stderr, "content range: [\033[1m%lld-\033[m]\n", pos));
607 if (pos > URL->offset)
608 goto fouch;
914 f = NULL;
609 }
915 }
610 }
916
917 if (url != URL)
918 fetchFreeURL(url);
919
920 return f;
611
921
612 /* only body remains */
613 c->encoding = enc;
614 cf = funopen(c,
615 (int (*)(void *, char *, int))_http_readfn,
616 (int (*)(void *, const char *, int))_http_writefn,
617 (fpos_t (*)(void *, fpos_t, int))NULL,
618 (int (*)(void *))_http_closefn);
619 if (cf == NULL)
620 goto fouch;
922 ouch:
923 if (url != URL)
924 fetchFreeURL(url);
925 if (fd != -1)
926 close(fd);
927 return NULL;
928}
621
929
622 while (pos < URL->offset)
623 if (fgetc(cf) == EOF)
624 goto cfouch;
625
626 return cf;
930
931/*****************************************************************************
932 * Entry points
933 */
934
935/*
936 * Retrieve a file by HTTP
937 */
938FILE *
939fetchGetHTTP(struct url *URL, char *flags)
940{
941 struct url_stat us;
627
942
628fouch:
629 fclose(f);
630 free(c);
631 _http_seterr(999); /* XXX do this properly RSN */
632 return NULL;
633cfouch:
634 fclose(cf);
635 _http_seterr(999); /* XXX do this properly RSN */
636 return NULL;
943 return _http_request(URL, "GET", &us, flags);
637}
638
639FILE *
640fetchPutHTTP(struct url *URL, char *flags)
641{
642 warnx("fetchPutHTTP(): not implemented");
643 return NULL;
644}
645
646/*
647 * Get an HTTP document's metadata
648 */
649int
650fetchStatHTTP(struct url *URL, struct url_stat *us, char *flags)
651{
944}
945
946FILE *
947fetchPutHTTP(struct url *URL, char *flags)
948{
949 warnx("fetchPutHTTP(): not implemented");
950 return NULL;
951}
952
953/*
954 * Get an HTTP document's metadata
955 */
956int
957fetchStatHTTP(struct url *URL, struct url_stat *us, char *flags)
958{
652 int e, noredirect, proxy;
653 size_t len;
654 char *ln, *p, *q;
655 FILE *f;
959 FILE *f;
656
657 noredirect = (flags && strchr(flags, 'A'));
658
960
659 us->size = -1;
660 us->atime = us->mtime = 0;
661
662 /* connect */
663 if ((f = _http_connect(URL, flags, &proxy)) == NULL)
961 if ((f = _http_request(URL, "HEAD", us, flags)) == NULL)
664 return -1;
962 return -1;
665
666 e = _http_request(f, "HEAD", URL, flags, proxy);
667 if (e != HTTP_OK && (e != HTTP_MOVED || noredirect)) {
668 _http_seterr(e);
669 fclose(f);
670 return -1;
671 }
672
673 while (1) {
674 if ((ln = fgetln(f, &len)) == NULL)
675 goto fouch;
676 if ((ln[0] == '\r') || (ln[0] == '\n'))
677 break;
678 while (isspace(ln[len-1]))
679 --len;
680 ln[len] = '\0'; /* XXX */
681 DEBUG(fprintf(stderr, "header: [\033[1m%s\033[m]\n", ln));
682 if ((p = _http_match("Location", ln)) != NULL) {
683 struct url *url;
684
685 for (q = p; *q && !isspace(*q); q++)
686 /* VOID */ ;
687 *q = 0;
688 if ((url = fetchParseURL(p)) == NULL)
689 goto ouch;
690 url->offset = URL->offset;
691 url->length = URL->length;
692 DEBUG(fprintf(stderr, "location: [\033[1m%s\033[m]\n", p));
693 e = fetchStatHTTP(url, us, flags);
694 fetchFreeURL(url);
695 fclose(f);
696 return e;
697 } else if ((p = _http_match("Last-Modified", ln)) != NULL) {
698 struct tm tm;
699 char locale[64];
700
701 strncpy(locale, setlocale(LC_TIME, NULL), sizeof locale);
702 setlocale(LC_TIME, "C");
703 strptime(p, "%a, %d %b %Y %H:%M:%S GMT", &tm);
704 /* XXX should add support for date-2 and date-3 */
705 setlocale(LC_TIME, locale);
706 us->atime = us->mtime = timegm(&tm);
707 DEBUG(fprintf(stderr, "last modified: [\033[1m%04d-%02d-%02d "
708 "%02d:%02d:%02d\033[m]\n",
709 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
710 tm.tm_hour, tm.tm_min, tm.tm_sec));
711 } else if ((p = _http_match("Content-Length", ln)) != NULL) {
712 us->size = 0;
713 while (*p && isdigit(*p))
714 us->size = us->size * 10 + (*p++ - '0');
715 DEBUG(fprintf(stderr, "content length: [\033[1m%lld\033[m]\n", us->size));
716 }
717 }
718
719 fclose(f);
720 return 0;
963 fclose(f);
964 return 0;
721 ouch:
722 _http_seterr(999); /* XXX do this properly RSN */
723 fouch:
724 fclose(f);
725 return -1;
726}
727
728/*
729 * List a directory
730 */
731struct url_ent *
732fetchListHTTP(struct url *url, char *flags)
733{
734 warnx("fetchListHTTP(): not implemented");
735 return NULL;
736}
965}
966
967/*
968 * List a directory
969 */
970struct url_ent *
971fetchListHTTP(struct url *url, char *flags)
972{
973 warnx("fetchListHTTP(): not implemented");
974 return NULL;
975}