2 * Copyright (c) 2000-2014 Dag-Erling Smørgrav
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 * $FreeBSD: head/lib/libfetch/http.c 261284 2014-01-30 08:37:23Z des $
32 * The following copyright applies to the base64 code:
35 * Copyright 1997 Massachusetts Institute of Technology
37 * Permission to use, copy, modify, and distribute this software and
38 * its documentation for any purpose and without fee is hereby
39 * granted, provided that both the above copyright notice and this
40 * permission notice appear in all copies, that both the above
41 * copyright notice and this permission notice appear in all
42 * supporting documentation, and that the name of M.I.T. not be used
43 * in advertising or publicity pertaining to distribution of the
44 * software without specific, written prior permission. M.I.T. makes
45 * no representations about the suitability of this software for any
46 * purpose. It is provided "as is" without express or implied
49 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
50 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
51 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
52 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
53 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
54 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
55 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
56 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
57 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
58 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
59 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 #include <sys/param.h>
64 #include <sys/socket.h>
80 #include <openssl/md5.h>
81 #define MD5Init(c) MD5_Init(c)
82 #define MD5Update(c, data, len) MD5_Update(c, data, len)
83 #define MD5Final(md, c) MD5_Final(md, c)
88 #include <netinet/in.h>
89 #include <netinet/tcp.h>
95 /* Maximum number of redirects to follow */
96 #define MAX_REDIRECT 20
98 /* Symbolic names for reply codes we care about */
100 #define HTTP_PARTIAL 206
101 #define HTTP_MOVED_PERM 301
102 #define HTTP_MOVED_TEMP 302
103 #define HTTP_SEE_OTHER 303
104 #define HTTP_NOT_MODIFIED 304
105 #define HTTP_USE_PROXY 305
106 #define HTTP_TEMP_REDIRECT 307
107 #define HTTP_PERM_REDIRECT 308
108 #define HTTP_NEED_AUTH 401
109 #define HTTP_NEED_PROXY_AUTH 407
110 #define HTTP_BAD_RANGE 416
111 #define HTTP_PROTOCOL_ERROR 999
113 #define HTTP_REDIRECT(xyz) ((xyz) == HTTP_MOVED_PERM \
114 || (xyz) == HTTP_MOVED_TEMP \
115 || (xyz) == HTTP_TEMP_REDIRECT \
116 || (xyz) == HTTP_USE_PROXY \
117 || (xyz) == HTTP_SEE_OTHER)
119 #define HTTP_ERROR(xyz) ((xyz) > 400 && (xyz) < 599)
122 /*****************************************************************************
123 * I/O functions for decoding chunked streams
128 conn_t
*conn
; /* connection */
129 int chunked
; /* chunked mode */
130 char *buf
; /* chunk buffer */
131 size_t bufsize
; /* size of chunk buffer */
132 ssize_t buflen
; /* amount of data currently in buffer */
133 int bufpos
; /* current read offset in buffer */
134 int eof
; /* end-of-file flag */
135 int error
; /* error flag */
136 size_t chunksize
; /* remaining size of current chunk */
143 * Get next chunk header
146 http_new_chunk(struct httpio
*io
)
150 if (fetch_getln(io
->conn
) == -1)
153 if (io
->conn
->buflen
< 2 || !isxdigit((unsigned char)*io
->conn
->buf
))
156 for (p
= io
->conn
->buf
; *p
&& !isspace((unsigned char)*p
); ++p
) {
159 if (!isxdigit((unsigned char)*p
))
161 if (isdigit((unsigned char)*p
)) {
162 io
->chunksize
= io
->chunksize
* 16 +
165 io
->chunksize
= io
->chunksize
* 16 +
166 10 + tolower((unsigned char)*p
) - 'a';
172 io
->total
+= io
->chunksize
;
173 if (io
->chunksize
== 0)
174 fprintf(stderr
, "%s(): end of last chunk\n", __func__
);
176 fprintf(stderr
, "%s(): new chunk: %lu (%lu)\n",
177 __func__
, (unsigned long)io
->chunksize
,
178 (unsigned long)io
->total
);
182 return (io
->chunksize
);
186 * Grow the input buffer to at least len bytes
189 http_growbuf(struct httpio
*io
, size_t len
)
193 if (io
->bufsize
>= len
)
196 if ((tmp
= realloc(io
->buf
, len
)) == NULL
)
204 * Fill the input buffer, do chunk decoding on the fly
207 http_fillbuf(struct httpio
*io
, size_t len
)
217 if (io
->chunked
== 0) {
218 if (http_growbuf(io
, len
) == -1)
220 if ((nbytes
= fetch_read(io
->conn
, io
->buf
, len
)) == -1) {
229 if (io
->chunksize
== 0) {
230 switch (http_new_chunk(io
)) {
240 if (len
> io
->chunksize
)
242 if (http_growbuf(io
, len
) == -1)
244 if ((nbytes
= fetch_read(io
->conn
, io
->buf
, len
)) == -1) {
249 io
->chunksize
-= io
->buflen
;
251 if (io
->chunksize
== 0) {
252 if (fetch_read(io
->conn
, &ch
, 1) != 1 || ch
!= '\r' ||
253 fetch_read(io
->conn
, &ch
, 1) != 1 || ch
!= '\n')
266 http_readfn(void *v
, char *buf
, int len
)
268 struct httpio
*io
= (struct httpio
*)v
;
277 if (!io
->buf
|| io
->bufpos
== io
->buflen
) {
278 if ((rlen
= http_fillbuf(io
, len
)) < 0) {
279 if ((errno
= io
->error
) == EINTR
)
282 } else if (rlen
== 0) {
287 rlen
= io
->buflen
- io
->bufpos
;
290 memcpy(buf
, io
->buf
+ io
->bufpos
, rlen
);
299 http_writefn(void *v
, const char *buf
, int len
)
301 struct httpio
*io
= (struct httpio
*)v
;
303 return (fetch_write(io
->conn
, buf
, len
));
310 http_closefn(void *v
)
312 struct httpio
*io
= (struct httpio
*)v
;
315 r
= fetch_close(io
->conn
);
323 * Wrap a file descriptor up
326 http_funopen(conn_t
*conn
, int chunked
)
331 if ((io
= calloc(1, sizeof(*io
))) == NULL
) {
336 io
->chunked
= chunked
;
337 f
= funopen(io
, http_readfn
, http_writefn
, NULL
, http_closefn
);
347 /*****************************************************************************
348 * Helper functions for talking to the server and parsing its replies
361 hdr_transfer_encoding
,
362 hdr_www_authenticate
,
363 hdr_proxy_authenticate
,
366 /* Names of interesting headers */
371 { hdr_content_length
, "Content-Length" },
372 { hdr_content_range
, "Content-Range" },
373 { hdr_last_modified
, "Last-Modified" },
374 { hdr_location
, "Location" },
375 { hdr_transfer_encoding
, "Transfer-Encoding" },
376 { hdr_www_authenticate
, "WWW-Authenticate" },
377 { hdr_proxy_authenticate
, "Proxy-Authenticate" },
378 { hdr_unknown
, NULL
},
382 * Send a formatted line; optionally echo to terminal
385 http_cmd(conn_t
*conn
, const char *fmt
, ...)
393 len
= vasprintf(&msg
, fmt
, ap
);
402 r
= fetch_putln(conn
, msg
, len
);
414 * Get and parse status line
417 http_get_reply(conn_t
*conn
)
421 if (fetch_getln(conn
) == -1)
424 * A valid status line looks like "HTTP/m.n xyz reason" where m
425 * and n are the major and minor protocol version numbers and xyz
427 * Unfortunately, there are servers out there (NCSA 1.5.1, to name
428 * just one) that do not send a version number, so we can't rely
429 * on finding one, but if we do, insist on it being 1.0 or 1.1.
430 * We don't care about the reason phrase.
432 if (strncmp(conn
->buf
, "HTTP", 4) != 0)
433 return (HTTP_PROTOCOL_ERROR
);
436 if (p
[1] != '1' || p
[2] != '.' || (p
[3] != '0' && p
[3] != '1'))
437 return (HTTP_PROTOCOL_ERROR
);
441 !isdigit((unsigned char)p
[1]) ||
442 !isdigit((unsigned char)p
[2]) ||
443 !isdigit((unsigned char)p
[3]))
444 return (HTTP_PROTOCOL_ERROR
);
446 conn
->err
= (p
[1] - '0') * 100 + (p
[2] - '0') * 10 + (p
[3] - '0');
451 * Check a header; if the type matches the given string, return a pointer
452 * to the beginning of the value.
455 http_match(const char *str
, const char *hdr
)
457 while (*str
&& *hdr
&&
458 tolower((unsigned char)*str
++) == tolower((unsigned char)*hdr
++))
460 if (*str
|| *hdr
!= ':')
462 while (*hdr
&& isspace((unsigned char)*++hdr
))
469 * Get the next header and return the appropriate symbolic code. We
470 * need to read one line ahead for checking for a continuation line
471 * belonging to the current header (continuation lines start with
474 * We get called with a fresh line already in the conn buffer, either
475 * from the previous http_next_header() invocation, or, the first
476 * time, from a fetch_getln() performed by our caller.
478 * This stops when we encounter an empty line (we dont read beyond the header
481 * Note that the "headerbuf" is just a place to return the result. Its
482 * contents are not used for the next call. This means that no cleanup
483 * is needed when ie doing another connection, just call the cleanup when
484 * fully done to deallocate memory.
487 /* Limit the max number of continuation lines to some reasonable value */
488 #define HTTP_MAX_CONT_LINES 10
490 /* Place into which to build a header from one or several lines */
492 char *buf
; /* buffer */
493 size_t bufsize
; /* buffer size */
494 size_t buflen
; /* length of buffer contents */
498 init_http_headerbuf(http_headerbuf_t
*buf
)
506 clean_http_headerbuf(http_headerbuf_t
*buf
)
510 init_http_headerbuf(buf
);
513 /* Remove whitespace at the end of the buffer */
515 http_conn_trimright(conn_t
*conn
)
517 while (conn
->buflen
&&
518 isspace((unsigned char)conn
->buf
[conn
->buflen
- 1]))
520 conn
->buf
[conn
->buflen
] = '\0';
524 http_next_header(conn_t
*conn
, http_headerbuf_t
*hbuf
, const char **p
)
529 * Have to do the stripping here because of the first line. So
530 * it's done twice for the subsequent lines. No big deal
532 http_conn_trimright(conn
);
533 if (conn
->buflen
== 0)
536 /* Copy the line to the headerbuf */
537 if (hbuf
->bufsize
< conn
->buflen
+ 1) {
538 if ((hbuf
->buf
= realloc(hbuf
->buf
, conn
->buflen
+ 1)) == NULL
)
539 return (hdr_syserror
);
540 hbuf
->bufsize
= conn
->buflen
+ 1;
542 strcpy(hbuf
->buf
, conn
->buf
);
543 hbuf
->buflen
= conn
->buflen
;
546 * Fetch possible continuation lines. Stop at 1st non-continuation
547 * and leave it in the conn buffer
549 for (i
= 0; i
< HTTP_MAX_CONT_LINES
; i
++) {
550 if (fetch_getln(conn
) == -1)
551 return (hdr_syserror
);
554 * Note: we carry on the idea from the previous version
555 * that a pure whitespace line is equivalent to an empty
556 * one (so it's not continuation and will be handled when
557 * we are called next)
559 http_conn_trimright(conn
);
560 if (conn
->buf
[0] != ' ' && conn
->buf
[0] != "\t"[0])
563 /* Got a continuation line. Concatenate to previous */
564 len
= hbuf
->buflen
+ conn
->buflen
;
565 if (hbuf
->bufsize
< len
+ 1) {
567 if ((hbuf
->buf
= realloc(hbuf
->buf
, len
+ 1)) == NULL
)
568 return (hdr_syserror
);
569 hbuf
->bufsize
= len
+ 1;
571 strcpy(hbuf
->buf
+ hbuf
->buflen
, conn
->buf
);
572 hbuf
->buflen
+= conn
->buflen
;
576 * We could check for malformed headers but we don't really care.
577 * A valid header starts with a token immediately followed by a
578 * colon; a token is any sequence of non-control, non-whitespace
579 * characters except "()<>@,;:\\\"{}".
581 for (i
= 0; hdr_names
[i
].num
!= hdr_unknown
; i
++)
582 if ((*p
= http_match(hdr_names
[i
].name
, hbuf
->buf
)) != NULL
)
583 return (hdr_names
[i
].num
);
585 return (hdr_unknown
);
588 /**************************
589 * [Proxy-]Authenticate header parsing
593 * Read doublequote-delimited string into output buffer obuf (allocated
594 * by caller, whose responsibility it is to ensure that it's big enough)
595 * cp points to the first char after the initial '"'
597 * Returns pointer to the first char after the terminating double quote, or
601 http_parse_headerstring(const char *cp
, char *obuf
)
605 case 0: /* Unterminated string */
608 case '"': /* Ending quote */
623 /* Http auth challenge schemes */
624 typedef enum {HTTPAS_UNKNOWN
, HTTPAS_BASIC
,HTTPAS_DIGEST
} http_auth_schemes_t
;
626 /* Data holder for a Basic or Digest challenge. */
628 http_auth_schemes_t scheme
;
635 int nc
; /* Nonce count */
636 } http_auth_challenge_t
;
639 init_http_auth_challenge(http_auth_challenge_t
*b
)
641 b
->scheme
= HTTPAS_UNKNOWN
;
642 b
->realm
= b
->qop
= b
->nonce
= b
->opaque
= b
->algo
= NULL
;
643 b
->stale
= b
->nc
= 0;
647 clean_http_auth_challenge(http_auth_challenge_t
*b
)
659 init_http_auth_challenge(b
);
662 /* Data holder for an array of challenges offered in an http response. */
663 #define MAX_CHALLENGES 10
665 http_auth_challenge_t
*challenges
[MAX_CHALLENGES
];
666 int count
; /* Number of parsed challenges in the array */
667 int valid
; /* We did parse an authenticate header */
668 } http_auth_challenges_t
;
671 init_http_auth_challenges(http_auth_challenges_t
*cs
)
674 for (i
= 0; i
< MAX_CHALLENGES
; i
++)
675 cs
->challenges
[i
] = NULL
;
676 cs
->count
= cs
->valid
= 0;
680 clean_http_auth_challenges(http_auth_challenges_t
*cs
)
683 /* We rely on non-zero pointers being allocated, not on the count */
684 for (i
= 0; i
< MAX_CHALLENGES
; i
++) {
685 if (cs
->challenges
[i
] != NULL
) {
686 clean_http_auth_challenge(cs
->challenges
[i
]);
687 free(cs
->challenges
[i
]);
690 init_http_auth_challenges(cs
);
694 * Enumeration for lexical elements. Separators will be returned as their own
697 typedef enum {HTTPHL_WORD
=256, HTTPHL_STRING
=257, HTTPHL_END
=258,
698 HTTPHL_ERROR
= 259} http_header_lex_t
;
701 * Determine what kind of token comes next and return possible value
702 * in buf, which is supposed to have been allocated big enough by
703 * caller. Advance input pointer and return element type.
706 http_header_lex(const char **cpp
, char *buf
)
709 /* Eat initial whitespace */
710 *cpp
+= strspn(*cpp
, " \t");
715 if (**cpp
== ',' || **cpp
== '=')
716 return (*((*cpp
)++));
720 *cpp
= http_parse_headerstring(++*cpp
, buf
);
722 return (HTTPHL_ERROR
);
723 return (HTTPHL_STRING
);
726 /* Read other token, until separator or whitespace */
727 l
= strcspn(*cpp
, " \t,=");
728 memcpy(buf
, *cpp
, l
);
731 return (HTTPHL_WORD
);
735 * Read challenges from http xxx-authenticate header and accumulate them
736 * in the challenges list structure.
738 * Headers with multiple challenges are specified by rfc2617, but
739 * servers (ie: squid) often send them in separate headers instead,
740 * which in turn is forbidden by the http spec (multiple headers with
741 * the same name are only allowed for pure comma-separated lists, see
744 * We support both approaches anyway
747 http_parse_authenticate(const char *cp
, http_auth_challenges_t
*cs
)
750 http_header_lex_t lex
;
751 char *key
= malloc(strlen(cp
) + 1);
752 char *value
= malloc(strlen(cp
) + 1);
753 char *buf
= malloc(strlen(cp
) + 1);
755 if (key
== NULL
|| value
== NULL
|| buf
== NULL
) {
760 /* In any case we've seen the header and we set the valid bit */
763 /* Need word first */
764 lex
= http_header_lex(&cp
, key
);
765 if (lex
!= HTTPHL_WORD
)
768 /* Loop on challenges */
769 for (; cs
->count
< MAX_CHALLENGES
; cs
->count
++) {
770 cs
->challenges
[cs
->count
] =
771 malloc(sizeof(http_auth_challenge_t
));
772 if (cs
->challenges
[cs
->count
] == NULL
) {
776 init_http_auth_challenge(cs
->challenges
[cs
->count
]);
777 if (!strcasecmp(key
, "basic")) {
778 cs
->challenges
[cs
->count
]->scheme
= HTTPAS_BASIC
;
779 } else if (!strcasecmp(key
, "digest")) {
780 cs
->challenges
[cs
->count
]->scheme
= HTTPAS_DIGEST
;
782 cs
->challenges
[cs
->count
]->scheme
= HTTPAS_UNKNOWN
;
784 * Continue parsing as basic or digest may
785 * follow, and the syntax is the same for
786 * all. We'll just ignore this one when
787 * looking at the list
791 /* Loop on attributes */
794 lex
= http_header_lex(&cp
, key
);
795 if (lex
!= HTTPHL_WORD
)
799 lex
= http_header_lex(&cp
, buf
);
804 lex
= http_header_lex(&cp
, value
);
805 if (lex
!= HTTPHL_WORD
&& lex
!= HTTPHL_STRING
)
808 if (!strcasecmp(key
, "realm"))
809 cs
->challenges
[cs
->count
]->realm
=
811 else if (!strcasecmp(key
, "qop"))
812 cs
->challenges
[cs
->count
]->qop
=
814 else if (!strcasecmp(key
, "nonce"))
815 cs
->challenges
[cs
->count
]->nonce
=
817 else if (!strcasecmp(key
, "opaque"))
818 cs
->challenges
[cs
->count
]->opaque
=
820 else if (!strcasecmp(key
, "algorithm"))
821 cs
->challenges
[cs
->count
]->algo
=
823 else if (!strcasecmp(key
, "stale"))
824 cs
->challenges
[cs
->count
]->stale
=
825 strcasecmp(value
, "no");
826 /* Else ignore unknown attributes */
828 /* Comma or Next challenge or End */
829 lex
= http_header_lex(&cp
, key
);
831 * If we get a word here, this is the beginning of the
832 * next challenge. Break the attributes loop
834 if (lex
== HTTPHL_WORD
)
837 if (lex
== HTTPHL_END
) {
838 /* End while looking for ',' is normal exit */
843 /* Anything else is an error */
847 } /* End attributes loop */
848 } /* End challenge loop */
851 * Challenges max count exceeded. This really can't happen
852 * with normal data, something's fishy -> error
867 * Parse a last-modified header
870 http_parse_mtime(const char *p
, time_t *mtime
)
875 strncpy(locale
, setlocale(LC_TIME
, NULL
), sizeof(locale
));
876 setlocale(LC_TIME
, "C");
877 r
= strptime(p
, "%a, %d %b %Y %H:%M:%S GMT", &tm
);
878 /* XXX should add support for date-2 and date-3 */
879 setlocale(LC_TIME
, locale
);
882 DEBUG(fprintf(stderr
, "last modified: [%04d-%02d-%02d "
884 tm
.tm_year
+ 1900, tm
.tm_mon
+ 1, tm
.tm_mday
,
885 tm
.tm_hour
, tm
.tm_min
, tm
.tm_sec
));
886 *mtime
= timegm(&tm
);
891 * Parse a content-length header
894 http_parse_length(const char *p
, off_t
*length
)
898 for (len
= 0; *p
&& isdigit((unsigned char)*p
); ++p
)
899 len
= len
* 10 + (*p
- '0');
902 DEBUG(fprintf(stderr
, "content length: [%lld]\n",
909 * Parse a content-range header
912 http_parse_range(const char *p
, off_t
*offset
, off_t
*length
, off_t
*size
)
914 off_t first
, last
, len
;
916 if (strncasecmp(p
, "bytes ", 6) != 0)
923 for (first
= 0; *p
&& isdigit((unsigned char)*p
); ++p
)
924 first
= first
* 10 + *p
- '0';
927 for (last
= 0, ++p
; *p
&& isdigit((unsigned char)*p
); ++p
)
928 last
= last
* 10 + *p
- '0';
930 if (first
> last
|| *p
!= '/')
932 for (len
= 0, ++p
; *p
&& isdigit((unsigned char)*p
); ++p
)
933 len
= len
* 10 + *p
- '0';
934 if (*p
|| len
< last
- first
+ 1)
937 DEBUG(fprintf(stderr
, "content range: [*/%lld]\n",
941 DEBUG(fprintf(stderr
, "content range: [%lld-%lld/%lld]\n",
942 (long long)first
, (long long)last
, (long long)len
));
943 *length
= last
- first
+ 1;
951 /*****************************************************************************
952 * Helper functions for authorization
959 http_base64(const char *src
)
961 static const char base64
[] =
962 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
963 "abcdefghijklmnopqrstuvwxyz"
970 if ((str
= malloc(((l
+ 2) / 3) * 4 + 1)) == NULL
)
976 t
= (src
[0] << 16) | (src
[1] << 8) | src
[2];
977 dst
[0] = base64
[(t
>> 18) & 0x3f];
978 dst
[1] = base64
[(t
>> 12) & 0x3f];
979 dst
[2] = base64
[(t
>> 6) & 0x3f];
980 dst
[3] = base64
[(t
>> 0) & 0x3f];
987 t
= (src
[0] << 16) | (src
[1] << 8);
988 dst
[0] = base64
[(t
>> 18) & 0x3f];
989 dst
[1] = base64
[(t
>> 12) & 0x3f];
990 dst
[2] = base64
[(t
>> 6) & 0x3f];
997 dst
[0] = base64
[(t
>> 18) & 0x3f];
998 dst
[1] = base64
[(t
>> 12) & 0x3f];
999 dst
[2] = dst
[3] = '=';
1013 * Extract authorization parameters from environment value.
1014 * The value is like scheme:realm:user:pass
1021 } http_auth_params_t
;
1024 init_http_auth_params(http_auth_params_t
*s
)
1026 s
->scheme
= s
->realm
= s
->user
= s
->password
= NULL
;
1030 clean_http_auth_params(http_auth_params_t
*s
)
1040 init_http_auth_params(s
);
1044 http_authfromenv(const char *p
, http_auth_params_t
*parms
)
1048 char *str
= strdup(p
);
1056 if ((ve
= strchr(v
, ':')) == NULL
)
1060 if ((parms
->scheme
= strdup(v
)) == NULL
) {
1066 if ((ve
= strchr(v
, ':')) == NULL
)
1070 if ((parms
->realm
= strdup(v
)) == NULL
) {
1076 if ((ve
= strchr(v
, ':')) == NULL
)
1080 if ((parms
->user
= strdup(v
)) == NULL
) {
1087 if ((parms
->password
= strdup(v
)) == NULL
) {
1094 clean_http_auth_params(parms
);
1102 * Digest response: the code to compute the digest is taken from the
1103 * sample implementation in RFC2616
1109 typedef char HASH
[HASHLEN
];
1110 #define HASHHEXLEN 32
1111 typedef char HASHHEX
[HASHHEXLEN
+1];
1113 static const char *hexchars
= "0123456789abcdef";
1115 CvtHex(IN HASH Bin
, OUT HASHHEX Hex
)
1120 for (i
= 0; i
< HASHLEN
; i
++) {
1121 j
= (Bin
[i
] >> 4) & 0xf;
1122 Hex
[i
*2] = hexchars
[j
];
1124 Hex
[i
*2+1] = hexchars
[j
];
1126 Hex
[HASHHEXLEN
] = '\0';
1129 /* calculate H(A1) as per spec */
1133 IN
char * pszUserName
,
1135 IN
char * pszPassword
,
1137 IN
char * pszCNonce
,
1138 OUT HASHHEX SessionKey
1145 MD5Update(&Md5Ctx
, pszUserName
, strlen(pszUserName
));
1146 MD5Update(&Md5Ctx
, ":", 1);
1147 MD5Update(&Md5Ctx
, pszRealm
, strlen(pszRealm
));
1148 MD5Update(&Md5Ctx
, ":", 1);
1149 MD5Update(&Md5Ctx
, pszPassword
, strlen(pszPassword
));
1150 MD5Final(HA1
, &Md5Ctx
);
1151 if (strcasecmp(pszAlg
, "md5-sess") == 0) {
1154 MD5Update(&Md5Ctx
, HA1
, HASHLEN
);
1155 MD5Update(&Md5Ctx
, ":", 1);
1156 MD5Update(&Md5Ctx
, pszNonce
, strlen(pszNonce
));
1157 MD5Update(&Md5Ctx
, ":", 1);
1158 MD5Update(&Md5Ctx
, pszCNonce
, strlen(pszCNonce
));
1159 MD5Final(HA1
, &Md5Ctx
);
1161 CvtHex(HA1
, SessionKey
);
1164 /* calculate request-digest/response-digest as per HTTP Digest spec */
1167 IN HASHHEX HA1
, /* H(A1) */
1168 IN
char * pszNonce
, /* nonce from server */
1169 IN
char * pszNonceCount
, /* 8 hex digits */
1170 IN
char * pszCNonce
, /* client nonce */
1171 IN
char * pszQop
, /* qop-value: "", "auth", "auth-int" */
1172 IN
char * pszMethod
, /* method from the request */
1173 IN
char * pszDigestUri
, /* requested URL */
1174 IN HASHHEX HEntity
, /* H(entity body) if qop="auth-int" */
1175 OUT HASHHEX Response
/* request-digest or response-digest */
1178 /* DEBUG(fprintf(stderr,
1179 "Calc: HA1[%s] Nonce[%s] qop[%s] method[%s] URI[%s]\n",
1180 HA1, pszNonce, pszQop, pszMethod, pszDigestUri));*/
1188 MD5Update(&Md5Ctx
, pszMethod
, strlen(pszMethod
));
1189 MD5Update(&Md5Ctx
, ":", 1);
1190 MD5Update(&Md5Ctx
, pszDigestUri
, strlen(pszDigestUri
));
1191 if (strcasecmp(pszQop
, "auth-int") == 0) {
1192 MD5Update(&Md5Ctx
, ":", 1);
1193 MD5Update(&Md5Ctx
, HEntity
, HASHHEXLEN
);
1195 MD5Final(HA2
, &Md5Ctx
);
1196 CvtHex(HA2
, HA2Hex
);
1198 // calculate response
1200 MD5Update(&Md5Ctx
, HA1
, HASHHEXLEN
);
1201 MD5Update(&Md5Ctx
, ":", 1);
1202 MD5Update(&Md5Ctx
, pszNonce
, strlen(pszNonce
));
1203 MD5Update(&Md5Ctx
, ":", 1);
1205 MD5Update(&Md5Ctx
, pszNonceCount
, strlen(pszNonceCount
));
1206 MD5Update(&Md5Ctx
, ":", 1);
1207 MD5Update(&Md5Ctx
, pszCNonce
, strlen(pszCNonce
));
1208 MD5Update(&Md5Ctx
, ":", 1);
1209 MD5Update(&Md5Ctx
, pszQop
, strlen(pszQop
));
1210 MD5Update(&Md5Ctx
, ":", 1);
1212 MD5Update(&Md5Ctx
, HA2Hex
, HASHHEXLEN
);
1213 MD5Final(RespHash
, &Md5Ctx
);
1214 CvtHex(RespHash
, Response
);
1218 * Generate/Send a Digest authorization header
1219 * This looks like: [Proxy-]Authorization: credentials
1221 * credentials = "Digest" digest-response
1222 * digest-response = 1#( username | realm | nonce | digest-uri
1223 * | response | [ algorithm ] | [cnonce] |
1224 * [opaque] | [message-qop] |
1225 * [nonce-count] | [auth-param] )
1226 * username = "username" "=" username-value
1227 * username-value = quoted-string
1228 * digest-uri = "uri" "=" digest-uri-value
1229 * digest-uri-value = request-uri ; As specified by HTTP/1.1
1230 * message-qop = "qop" "=" qop-value
1231 * cnonce = "cnonce" "=" cnonce-value
1232 * cnonce-value = nonce-value
1233 * nonce-count = "nc" "=" nc-value
1235 * response = "response" "=" request-digest
1236 * request-digest = <"> 32LHEX <">
1239 http_digest_auth(conn_t
*conn
, const char *hdr
, http_auth_challenge_t
*c
,
1240 http_auth_params_t
*parms
, struct url
*url
)
1243 char noncecount
[10];
1245 char *options
= NULL
;
1247 if (!c
->realm
|| !c
->nonce
) {
1248 DEBUG(fprintf(stderr
, "realm/nonce not set in challenge\n"));
1252 c
->algo
= strdup("");
1254 if (asprintf(&options
, "%s%s%s%s",
1255 *c
->algo
? ",algorithm=" : "", c
->algo
,
1256 c
->opaque
? ",opaque=" : "", c
->opaque
?c
->opaque
:"")== -1)
1260 c
->qop
= strdup("");
1265 sprintf(noncecount
, "%08x", c
->nc
);
1266 /* We don't try very hard with the cnonce ... */
1267 sprintf(cnonce
, "%x%lx", getpid(), (unsigned long)time(0));
1271 DigestCalcHA1(c
->algo
, parms
->user
, c
->realm
,
1272 parms
->password
, c
->nonce
, cnonce
, HA1
);
1273 DEBUG(fprintf(stderr
, "HA1: [%s]\n", HA1
));
1275 DigestCalcResponse(HA1
, c
->nonce
, noncecount
, cnonce
, c
->qop
,
1276 "GET", url
->doc
, "", digest
);
1279 r
= http_cmd(conn
, "%s: Digest username=\"%s\",realm=\"%s\","
1280 "nonce=\"%s\",uri=\"%s\",response=\"%s\","
1281 "qop=\"auth\", cnonce=\"%s\", nc=%s%s",
1282 hdr
, parms
->user
, c
->realm
,
1283 c
->nonce
, url
->doc
, digest
,
1284 cnonce
, noncecount
, options
);
1286 r
= http_cmd(conn
, "%s: Digest username=\"%s\",realm=\"%s\","
1287 "nonce=\"%s\",uri=\"%s\",response=\"%s\"%s",
1288 hdr
, parms
->user
, c
->realm
,
1289 c
->nonce
, url
->doc
, digest
, options
);
1297 * Encode username and password
1300 http_basic_auth(conn_t
*conn
, const char *hdr
, const char *usr
, const char *pwd
)
1305 DEBUG(fprintf(stderr
, "basic: usr: [%s]\n", usr
));
1306 DEBUG(fprintf(stderr
, "basic: pwd: [%s]\n", pwd
));
1307 if (asprintf(&upw
, "%s:%s", usr
, pwd
) == -1)
1309 auth
= http_base64(upw
);
1313 r
= http_cmd(conn
, "%s: Basic %s", hdr
, auth
);
1319 * Chose the challenge to answer and call the appropriate routine to
1320 * produce the header.
1323 http_authorize(conn_t
*conn
, const char *hdr
, http_auth_challenges_t
*cs
,
1324 http_auth_params_t
*parms
, struct url
*url
)
1326 http_auth_challenge_t
*digest
= NULL
;
1329 /* If user or pass are null we're not happy */
1330 if (!parms
->user
|| !parms
->password
) {
1331 DEBUG(fprintf(stderr
, "NULL usr or pass\n"));
1335 /* Look for a Digest */
1336 for (i
= 0; i
< cs
->count
; i
++) {
1337 if (cs
->challenges
[i
]->scheme
== HTTPAS_DIGEST
)
1338 digest
= cs
->challenges
[i
];
1341 /* Error if "Digest" was specified and there is no Digest challenge */
1342 if (!digest
&& (parms
->scheme
&&
1343 !strcasecmp(parms
->scheme
, "digest"))) {
1344 DEBUG(fprintf(stderr
,
1345 "Digest auth in env, not supported by peer\n"));
1349 * If "basic" was specified in the environment, or there is no Digest
1350 * challenge, do the basic thing. Don't need a challenge for this,
1351 * so no need to check basic!=NULL
1353 if (!digest
|| (parms
->scheme
&& !strcasecmp(parms
->scheme
,"basic")))
1354 return (http_basic_auth(conn
,hdr
,parms
->user
,parms
->password
));
1356 /* Else, prefer digest. We just checked that it's not NULL */
1357 return (http_digest_auth(conn
, hdr
, digest
, parms
, url
));
1360 /*****************************************************************************
1361 * Helper functions for connecting to a server or proxy
1365 * Connect to the correct HTTP server or proxy.
1368 http_connect(struct url
*URL
, struct url
*purl
, const char *flags
)
1381 verbose
= CHECK_FLAG('v');
1382 if (CHECK_FLAG('4'))
1385 else if (CHECK_FLAG('6'))
1389 curl
= (purl
!= NULL
) ? purl
: URL
;
1391 if ((conn
= fetch_connect(curl
->host
, curl
->port
, af
, verbose
)) == NULL
)
1392 /* fetch_connect() has already set an error code */
1394 if (strcasecmp(URL
->scheme
, SCHEME_HTTPS
) == 0 && purl
) {
1395 http_cmd(conn
, "CONNECT %s:%d HTTP/1.1",
1396 URL
->host
, URL
->port
);
1397 http_cmd(conn
, "Host: %s:%d",
1398 URL
->host
, URL
->port
);
1400 if (http_get_reply(conn
) != HTTP_OK
) {
1404 http_get_reply(conn
);
1406 if (strcasecmp(URL
->scheme
, SCHEME_HTTPS
) == 0 &&
1407 fetch_ssl(conn
, URL
, verbose
) == -1) {
1416 setsockopt(conn
->sd
, IPPROTO_TCP
, TCP_NOPUSH
, &val
, sizeof(val
));
1422 http_get_proxy(struct url
* url
, const char *flags
)
1427 if (flags
!= NULL
&& strchr(flags
, 'd') != NULL
)
1429 if (fetch_no_proxy_match(url
->host
))
1431 if (((p
= getenv("HTTP_PROXY")) || (p
= getenv("http_proxy"))) &&
1432 *p
&& (purl
= fetchParseURL(p
))) {
1434 strcpy(purl
->scheme
, SCHEME_HTTP
);
1436 purl
->port
= fetch_default_proxy_port(purl
->scheme
);
1437 if (strcasecmp(purl
->scheme
, SCHEME_HTTP
) == 0)
1445 http_print_html(FILE *out
, FILE *in
)
1452 while ((line
= fgetln(in
, &len
)) != NULL
) {
1453 while (len
&& isspace((unsigned char)line
[len
- 1]))
1455 for (p
= q
= line
; q
< line
+ len
; ++q
) {
1456 if (comment
&& *q
== '-') {
1457 if (q
+ 2 < line
+ len
&&
1458 strcmp(q
, "-->") == 0) {
1462 } else if (tag
&& !comment
&& *q
== '>') {
1465 } else if (!tag
&& *q
== '<') {
1467 fwrite(p
, q
- p
, 1, out
);
1469 if (q
+ 3 < line
+ len
&&
1470 strcmp(q
, "<!--") == 0) {
1477 fwrite(p
, q
- p
, 1, out
);
1483 /*****************************************************************************
1488 * Send a request and process the reply
1490 * XXX This function is way too long, the do..while loop should be split
1491 * XXX off into a separate function.
1494 http_request(struct url
*URL
, const char *op
, struct url_stat
*us
,
1495 struct url
*purl
, const char *flags
)
1498 char hbuf
[MAXHOSTNAMELEN
+ 7], *host
;
1500 struct url
*url
, *new;
1501 int chunked
, direct
, ims
, noredirect
, verbose
;
1503 off_t offset
, clength
, length
, size
;
1508 struct tm
*timestruct
;
1509 http_headerbuf_t headerbuf
;
1510 http_auth_challenges_t server_challenges
;
1511 http_auth_challenges_t proxy_challenges
;
1513 /* The following calls don't allocate anything */
1514 init_http_headerbuf(&headerbuf
);
1515 init_http_auth_challenges(&server_challenges
);
1516 init_http_auth_challenges(&proxy_challenges
);
1518 direct
= CHECK_FLAG('d');
1519 noredirect
= CHECK_FLAG('A');
1520 verbose
= CHECK_FLAG('v');
1521 ims
= CHECK_FLAG('i');
1523 if (direct
&& purl
) {
1528 /* try the provided URL first */
1534 e
= HTTP_PROTOCOL_ERROR
;
1546 url
->port
= fetch_default_port(url
->scheme
);
1548 /* were we redirected to an FTP URL? */
1549 if (purl
== NULL
&& strcmp(url
->scheme
, SCHEME_FTP
) == 0) {
1550 if (strcmp(op
, "GET") == 0)
1551 return (ftp_request(url
, "RETR", us
, purl
, flags
));
1552 else if (strcmp(op
, "HEAD") == 0)
1553 return (ftp_request(url
, "STAT", us
, purl
, flags
));
1556 /* connect to server or proxy */
1557 if ((conn
= http_connect(url
, purl
, flags
)) == NULL
)
1562 if (strchr(url
->host
, ':')) {
1563 snprintf(hbuf
, sizeof(hbuf
), "[%s]", url
->host
);
1567 if (url
->port
!= fetch_default_port(url
->scheme
)) {
1572 snprintf(hbuf
+ strlen(hbuf
),
1573 sizeof(hbuf
) - strlen(hbuf
), ":%d", url
->port
);
1578 fetch_info("requesting %s://%s%s",
1579 url
->scheme
, host
, url
->doc
);
1580 if (purl
&& strcasecmp(URL
->scheme
, SCHEME_HTTPS
) != 0) {
1581 http_cmd(conn
, "%s %s://%s%s HTTP/1.1",
1582 op
, url
->scheme
, host
, url
->doc
);
1584 http_cmd(conn
, "%s %s HTTP/1.1",
1588 if (ims
&& url
->ims_time
) {
1589 timestruct
= gmtime((time_t *)&url
->ims_time
);
1590 (void)strftime(timebuf
, 80, "%a, %d %b %Y %T GMT",
1593 fetch_info("If-Modified-Since: %s", timebuf
);
1594 http_cmd(conn
, "If-Modified-Since: %s", timebuf
);
1597 http_cmd(conn
, "Host: %s", host
);
1600 * Proxy authorization: we only send auth after we received
1601 * a 407 error. We do not first try basic anyway (changed
1602 * when support was added for digest-auth)
1604 if (purl
&& proxy_challenges
.valid
) {
1605 http_auth_params_t aparams
;
1606 init_http_auth_params(&aparams
);
1607 if (*purl
->user
|| *purl
->pwd
) {
1608 aparams
.user
= purl
->user
?
1609 strdup(purl
->user
) : strdup("");
1610 aparams
.password
= purl
->pwd
?
1611 strdup(purl
->pwd
) : strdup("");
1612 } else if ((p
= getenv("HTTP_PROXY_AUTH")) != NULL
&&
1614 if (http_authfromenv(p
, &aparams
) < 0) {
1615 http_seterr(HTTP_NEED_PROXY_AUTH
);
1619 http_authorize(conn
, "Proxy-Authorization",
1620 &proxy_challenges
, &aparams
, url
);
1621 clean_http_auth_params(&aparams
);
1625 * Server authorization: we never send "a priori"
1626 * Basic auth, which used to be done if user/pass were
1627 * set in the url. This would be weird because we'd send the
1628 * password in the clear even if Digest is finally to be
1629 * used (it would have made more sense for the
1630 * pre-digest version to do this when Basic was specified
1631 * in the environment)
1633 if (server_challenges
.valid
) {
1634 http_auth_params_t aparams
;
1635 init_http_auth_params(&aparams
);
1636 if (*url
->user
|| *url
->pwd
) {
1637 aparams
.user
= url
->user
?
1638 strdup(url
->user
) : strdup("");
1639 aparams
.password
= url
->pwd
?
1640 strdup(url
->pwd
) : strdup("");
1641 } else if ((p
= getenv("HTTP_AUTH")) != NULL
&&
1643 if (http_authfromenv(p
, &aparams
) < 0) {
1644 http_seterr(HTTP_NEED_AUTH
);
1647 } else if (fetchAuthMethod
&&
1648 fetchAuthMethod(url
) == 0) {
1649 aparams
.user
= url
->user
?
1650 strdup(url
->user
) : strdup("");
1651 aparams
.password
= url
->pwd
?
1652 strdup(url
->pwd
) : strdup("");
1654 http_seterr(HTTP_NEED_AUTH
);
1657 http_authorize(conn
, "Authorization",
1658 &server_challenges
, &aparams
, url
);
1659 clean_http_auth_params(&aparams
);
1663 if ((p
= getenv("HTTP_ACCEPT")) != NULL
) {
1665 http_cmd(conn
, "Accept: %s", p
);
1667 http_cmd(conn
, "Accept: */*");
1669 if ((p
= getenv("HTTP_REFERER")) != NULL
&& *p
!= '\0') {
1670 if (strcasecmp(p
, "auto") == 0)
1671 http_cmd(conn
, "Referer: %s://%s%s",
1672 url
->scheme
, host
, url
->doc
);
1674 http_cmd(conn
, "Referer: %s", p
);
1676 if ((p
= getenv("HTTP_USER_AGENT")) != NULL
&& *p
!= '\0')
1677 http_cmd(conn
, "User-Agent: %s", p
);
1679 http_cmd(conn
, "User-Agent: %s " _LIBFETCH_VER
, getprogname());
1680 if (url
->offset
> 0)
1681 http_cmd(conn
, "Range: bytes=%lld-", (long long)url
->offset
);
1682 http_cmd(conn
, "Connection: close");
1686 * Force the queued request to be dispatched. Normally, one
1687 * would do this with shutdown(2) but squid proxies can be
1688 * configured to disallow such half-closed connections. To
1689 * be compatible with such configurations, fiddle with socket
1690 * options to force the pending data to be written.
1693 setsockopt(conn
->sd
, IPPROTO_TCP
, TCP_NOPUSH
, &val
,
1696 setsockopt(conn
->sd
, IPPROTO_TCP
, TCP_NODELAY
, &val
,
1700 switch (http_get_reply(conn
)) {
1703 case HTTP_NOT_MODIFIED
:
1706 case HTTP_MOVED_PERM
:
1707 case HTTP_MOVED_TEMP
:
1708 case HTTP_SEE_OTHER
:
1709 case HTTP_USE_PROXY
:
1711 * Not so fine, but we still have to read the
1712 * headers to get the new location.
1715 case HTTP_NEED_AUTH
:
1716 if (server_challenges
.valid
) {
1718 * We already sent out authorization code,
1719 * so there's nothing more we can do.
1721 http_seterr(conn
->err
);
1724 /* try again, but send the password this time */
1726 fetch_info("server requires authorization");
1728 case HTTP_NEED_PROXY_AUTH
:
1729 if (proxy_challenges
.valid
) {
1731 * We already sent our proxy
1732 * authorization code, so there's
1733 * nothing more we can do. */
1734 http_seterr(conn
->err
);
1737 /* try again, but send the password this time */
1739 fetch_info("proxy requires authorization");
1741 case HTTP_BAD_RANGE
:
1743 * This can happen if we ask for 0 bytes because
1744 * we already have the whole file. Consider this
1745 * a success for now, and check sizes later.
1748 case HTTP_PROTOCOL_ERROR
:
1754 http_seterr(conn
->err
);
1757 /* fall through so we can get the full error message */
1760 /* get headers. http_next_header expects one line readahead */
1761 if (fetch_getln(conn
) == -1) {
1766 switch ((h
= http_next_header(conn
, &headerbuf
, &p
))) {
1771 http_seterr(HTTP_PROTOCOL_ERROR
);
1773 case hdr_content_length
:
1774 http_parse_length(p
, &clength
);
1776 case hdr_content_range
:
1777 http_parse_range(p
, &offset
, &length
, &size
);
1779 case hdr_last_modified
:
1780 http_parse_mtime(p
, &mtime
);
1783 if (!HTTP_REDIRECT(conn
->err
))
1786 * if the A flag is set, we don't follow
1787 * temporary redirects.
1790 conn
->err
!= HTTP_MOVED_PERM
&&
1791 conn
->err
!= HTTP_PERM_REDIRECT
&&
1792 conn
->err
!= HTTP_USE_PROXY
) {
1799 fetch_info("%d redirect to %s", conn
->err
, p
);
1802 new = fetchMakeURL(url
->scheme
, url
->host
, url
->port
, p
,
1803 url
->user
, url
->pwd
);
1805 new = fetchParseURL(p
);
1807 /* XXX should set an error code */
1808 DEBUG(fprintf(stderr
, "failed to parse new URL\n"));
1812 /* Only copy credentials if the host matches */
1813 if (!strcmp(new->host
, url
->host
) && !*new->user
&& !*new->pwd
) {
1814 strcpy(new->user
, url
->user
);
1815 strcpy(new->pwd
, url
->pwd
);
1817 new->offset
= url
->offset
;
1818 new->length
= url
->length
;
1820 case hdr_transfer_encoding
:
1822 chunked
= (strcasecmp(p
, "chunked") == 0);
1824 case hdr_www_authenticate
:
1825 if (conn
->err
!= HTTP_NEED_AUTH
)
1827 if (http_parse_authenticate(p
, &server_challenges
) == 0)
1830 case hdr_proxy_authenticate
:
1831 if (conn
->err
!= HTTP_NEED_PROXY_AUTH
)
1833 if (http_parse_authenticate(p
, &proxy_challenges
) == 0)
1842 } while (h
> hdr_end
);
1844 /* we need to provide authentication */
1845 if (conn
->err
== HTTP_NEED_AUTH
||
1846 conn
->err
== HTTP_NEED_PROXY_AUTH
) {
1848 if ((conn
->err
== HTTP_NEED_AUTH
&&
1849 !server_challenges
.valid
) ||
1850 (conn
->err
== HTTP_NEED_PROXY_AUTH
&&
1851 !proxy_challenges
.valid
)) {
1852 /* 401/7 but no www/proxy-authenticate ?? */
1853 DEBUG(fprintf(stderr
, "401/7 and no auth header\n"));
1861 /* requested range not satisfiable */
1862 if (conn
->err
== HTTP_BAD_RANGE
) {
1863 if (url
->offset
== size
&& url
->length
== 0) {
1864 /* asked for 0 bytes; fake it */
1865 offset
= url
->offset
;
1867 conn
->err
= HTTP_OK
;
1870 http_seterr(conn
->err
);
1875 /* we have a hit or an error */
1876 if (conn
->err
== HTTP_OK
1877 || conn
->err
== HTTP_NOT_MODIFIED
1878 || conn
->err
== HTTP_PARTIAL
1879 || HTTP_ERROR(conn
->err
))
1882 /* all other cases: we got a redirect */
1884 clean_http_auth_challenges(&server_challenges
);
1888 DEBUG(fprintf(stderr
, "redirect with no new location\n"));
1896 /* we failed, or ran out of retries */
1902 DEBUG(fprintf(stderr
, "offset %lld, length %lld,"
1903 " size %lld, clength %lld\n",
1904 (long long)offset
, (long long)length
,
1905 (long long)size
, (long long)clength
));
1907 if (conn
->err
== HTTP_NOT_MODIFIED
) {
1908 http_seterr(HTTP_NOT_MODIFIED
);
1912 /* check for inconsistencies */
1913 if (clength
!= -1 && length
!= -1 && clength
!= length
) {
1914 http_seterr(HTTP_PROTOCOL_ERROR
);
1920 length
= offset
+ clength
;
1921 if (length
!= -1 && size
!= -1 && length
!= size
) {
1922 http_seterr(HTTP_PROTOCOL_ERROR
);
1931 us
->atime
= us
->mtime
= mtime
;
1935 if (URL
->offset
> 0 && offset
> URL
->offset
) {
1936 http_seterr(HTTP_PROTOCOL_ERROR
);
1940 /* report back real offset and size */
1941 URL
->offset
= offset
;
1942 URL
->length
= clength
;
1944 /* wrap it up in a FILE */
1945 if ((f
= http_funopen(conn
, chunked
)) == NULL
) {
1955 if (HTTP_ERROR(conn
->err
)) {
1956 http_print_html(stderr
, f
);
1960 clean_http_headerbuf(&headerbuf
);
1961 clean_http_auth_challenges(&server_challenges
);
1962 clean_http_auth_challenges(&proxy_challenges
);
1972 clean_http_headerbuf(&headerbuf
);
1973 clean_http_auth_challenges(&server_challenges
);
1974 clean_http_auth_challenges(&proxy_challenges
);
1979 /*****************************************************************************
1984 * Retrieve and stat a file by HTTP
1987 fetchXGetHTTP(struct url
*URL
, struct url_stat
*us
, const char *flags
)
1989 return (http_request(URL
, "GET", us
, http_get_proxy(URL
, flags
), flags
));
1993 * Retrieve a file by HTTP
1996 fetchGetHTTP(struct url
*URL
, const char *flags
)
1998 return (fetchXGetHTTP(URL
, NULL
, flags
));
2002 * Store a file by HTTP
2005 fetchPutHTTP(struct url
*URL __unused
, const char *flags __unused
)
2007 warnx("fetchPutHTTP(): not implemented");
2012 * Get an HTTP document's metadata
2015 fetchStatHTTP(struct url
*URL
, struct url_stat
*us
, const char *flags
)
2019 f
= http_request(URL
, "HEAD", us
, http_get_proxy(URL
, flags
), flags
);
2030 fetchListHTTP(struct url
*url __unused
, const char *flags __unused
)
2032 warnx("fetchListHTTP(): not implemented");