/* Generic support for headers. Copyright (C) 1997, 1998 Free Software Foundation, Inc. This file is part of GNU Wget. GNU Wget is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GNU Wget is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Wget; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #ifdef HAVE_STRING_H # include #else # include #endif #include "wget.h" #include "connect.h" #include "rbuf.h" #include "headers.h" /* This file contains the generic routines for work with headers. Currently they are used only by HTTP in http.c, but they can be used by anything that cares about RFC822-style headers. Header is defined in RFC2068, as quoted below. Note that this definition is not HTTP-specific -- it is virtually indistinguishable from the one given in RFC822 or RFC1036. message-header = field-name ":" [ field-value ] CRLF field-name = token field-value = *( field-content | LWS ) field-content = The public functions are header_get() and header_process(), which see. */ /* Get a header from read-buffer RBUF and return it in *HDR. As defined in RFC2068 and elsewhere, a header can be folded into multiple lines if the continuation line begins with a space or horizontal TAB. Also, this function will accept a header ending with just LF instead of CRLF. The header may be of arbitrary length; the function will allocate as much memory as necessary for it to fit. It need not contain a `:', thus you can use it to retrieve, say, HTTP status line. The trailing CRLF or LF are stripped from the header, and it is zero-terminated. #### Is this well-behaved? */ int header_get (struct rbuf *rbuf, char **hdr, enum header_get_flags flags) { int i; int bufsize = 80; *hdr = (char *)xmalloc (bufsize); for (i = 0; 1; i++) { int res; /* #### Use DO_REALLOC? */ if (i > bufsize - 1) *hdr = (char *)xrealloc (*hdr, (bufsize <<= 1)); res = RBUF_READCHAR (rbuf, *hdr + i); if (res == 1) { if ((*hdr)[i] == '\n') { if (!((flags & HG_NO_CONTINUATIONS) || i == 0 || (i == 1 && (*hdr)[0] == '\r'))) { char next; /* If the header is non-empty, we need to check if it continues on to the other line. We do that by peeking at the next character. */ res = rbuf_peek (rbuf, &next); if (res == 0) return HG_EOF; else if (res == -1) return HG_ERROR; /* If the next character is HT or SP, just continue. */ if (next == '\t' || next == ' ') continue; } /* The header ends. */ (*hdr)[i] = '\0'; /* Get rid of '\r'. */ if (i > 0 && (*hdr)[i - 1] == '\r') (*hdr)[i - 1] = '\0'; break; } } else if (res == 0) return HG_EOF; else return HG_ERROR; } DEBUGP (("%s\n", *hdr)); return HG_OK; } /* Check whether HEADER begins with NAME and, if yes, skip the `:' and the whitespace, and call PROCFUN with the arguments of HEADER's contents (after the `:' and space) and ARG. Otherwise, return 0. */ int header_process (const char *header, const char *name, int (*procfun) (const char *, void *), void *arg) { /* Check whether HEADER matches NAME. */ while (*name && (TOLOWER (*name) == TOLOWER (*header))) ++name, ++header; if (*name || *header++ != ':') return 0; header += skip_lws (header); return ((*procfun) (header, arg)); } /* Helper functions for use with header_process(). */ /* Extract a long integer from HEADER and store it to CLOSURE. If an error is encountered, return 0, else 1. */ int header_extract_number (const char *header, void *closure) { const char *p = header; long result; for (result = 0; ISDIGIT (*p); p++) result = 10 * result + (*p - '0'); /* Failure if no number present. */ if (p == header) return 0; /* Skip trailing whitespace. */ p += skip_lws (p); /* Indicate failure if trailing garbage is present. */ if (*p) return 0; *(long *)closure = result; return 1; } /* Strdup HEADER, and place the pointer to CLOSURE. */ int header_strdup (const char *header, void *closure) { *(char **)closure = xstrdup (header); return 1; } /* Write the value 1 into the integer pointed to by CLOSURE. */ int header_exists (const char *header, void *closure) { *(int *)closure = 1; return 1; } /* Skip LWS (linear white space), if present. Returns number of characters to skip. */ int skip_lws (const char *string) { const char *p = string; while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') ++p; return p - string; }