--------------------- PatchSet 1668 Date: 2005/09/12 15:40:19 Author: audrus Branch: squid3-icap Tag: (none) Log: *** empty log message *** Members: src/ICAPOptions.cc:1.1->1.1.2.1 src/ICAPOptions.h:1.1->1.1.2.1 --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAPOptions.cc Wed Feb 14 13:35:02 2007 @@ -0,0 +1,244 @@ +#include "ICAPOptions.h" +#include "TextException.h" + +ICAPOptionsHeaders::~ICAPOptionsHeaders() +{ + if (transfer_preview != NULL) + delete transfer_preview; + if (transfer_ignore != NULL) + delete transfer_ignore; + if (transfer_complete != NULL) + delete transfer_complete; +}; + +ICAPOptions::ICAPOptions() : default_transfer_type(TRANSFER_NONE), transfer_ext(NULL) +{ + memBufDefInit(&responseBuf); + start_time = current_time; +}; + +ICAPOptions::~ICAPOptions() +{ + memBufClean(&responseBuf); +}; + +/* find first CRLF */ +static int +isolateLine(const char **parse_start, const char **blk_start, const char **blk_end) +{ + int slen = strcspn(*parse_start, "\r\n"); + + if (!(*parse_start)[slen]) /* no CRLF found */ + return 0; + + *blk_start = *parse_start; + *blk_end = *blk_start + slen; + + while (**blk_end == '\r') /* CR */ + (*blk_end)++; + + if (**blk_end == '\n') /* LF */ + (*blk_end)++; + *parse_start = *blk_end; + return 1; +} + +int ICAPOptions::parseStatusLine(const char *start, const char *end) +{ + if (sscanf(responseBuf.content(), "ICAP/%d.%d", &version.major, &version.minor) != 2) { + debugs(93, 7, "ICAPOptions::statusLineParse: Invalid ICAP identifier"); + return 0; + } + if (!(start = strchr(start, ' '))) + return 0; + + status = atoi(++start); + return 1; +}; + +opt_transfer_type ICAPOptions::getTransferExt(const char *s) { + + opt_transfer_type t = TRANSFER_NONE; + + if (transfer_ext != NULL) { + List *data = transfer_ext; + while (data != NULL) { + if (*(data->element.ext) == *s) { + return data->element.type; + } + data = data->next; + } + } + return t; +} + +void ICAPOptions::insertTransferExt(const char *t, opt_transfer_type t_type) +{ + List **Tail; + opt_transfer_ext t_ext; + if (t == "*") { + setDefaultTransferExt(t_type); + return; + } + for (Tail = &transfer_ext; *Tail; Tail = &((*Tail)->next)) { + if (*(*Tail)->element.ext == *t) { + (*Tail)->element.type = t_type; + return; + } + } + t_ext.ext = xstrdup(t); + t_ext.type = t_type; + List *q = new List(t_ext); + *(Tail) = q; + +}; + +List *ICAPOptions::parseExtFileList(const char *start, const char *end, opt_transfer_type t_type) +{ + const String s = xstrndup(start, end - start - 1); + const char *item; + const char *pos = NULL; + char *fext = NULL; + int ilen; + String t = NULL; + + List **Tail; + List *H; + + for (Tail = &H; *Tail; Tail = &((*Tail)->next)) + + ; + while (strListGetItem(&s, ',', &item, &ilen, &pos)) { + fext = xstrndup(item, ilen + 1); + t = fext; + List *q = new List (t); + *(Tail) = q; + Tail = &q->next; + insertTransferExt(fext, t_type); + } + return H; +} + + +void ICAPOptions::parseHeader(const char *start, const char *end) +{ + const char *name_end = strchr(start, ':'); + const char *value_start; + + if (!name_end || name_end <= start || name_end > end) { + debugs(93, 5, "ICAPOptionsHeaders::parseHeader: invalid header item. skip it"); + return; + } + + const int name_len = name_end ? name_end - start : 0; + if (name_len > 65536) { + debugs(93, 5, "ICAPOptionsHeaders::parseHeader: unusual long header item. skip it"); + return; + } + + value_start = name_end + 1; /* skip ':' */ + /* skip white space */ + while (value_start < end && xisspace(*value_start)) + value_start++; + + if (!strncasecmp("Methods", start, 7)) { + headers->methods = xstrndup(value_start, end - value_start - 1); + } else if (!strncasecmp("Service", start, 7)) { + headers->service = xstrndup(value_start, end - value_start - 1); + } else if (!strncasecmp("ISTag", start, 5)) { + headers->istag = xstrndup(value_start, end - value_start - 1); + } else if (!strncasecmp("Encapsulated", start, 12)) { + headers->encapsulated = xstrndup(value_start, end - value_start - 1); + } else if (!strncasecmp("Opt-body-type", start, 13)) { + headers->opt_body_type = xstrndup(value_start, end - value_start - 1); + } else if (!strncasecmp("Max-Connections", start, 15)) { + headers->max_connections = atoi(value_start); + if (!headers->max_connections && !xisdigit(*value_start)) { + debug(93, 3) ("failed to parse an int header field near '%s'\n", value_start); + } + } else if (!strncasecmp("Options-TTL", start, 11)) { + headers->options_ttl = atoi(value_start); + if (!headers->options_ttl && !xisdigit(*value_start)) { + debug(93, 3) ("failed to parse an int header field near '%s'\n", value_start); + } + } else if (!strncasecmp("Date", start, 4)) { + headers->date = xstrndup(value_start, end - value_start - 1); + } else if (!strncasecmp("Service-ID", start, 10)) { + headers->service_id = xstrndup(value_start, end - value_start - 1); + } else if (!strncasecmp("Allow", start, 5)) { + headers->allow = xstrndup(value_start, end - value_start - 1); + } else if (!strncasecmp("Preview", start, 11)) { + headers->preview = atoi(value_start); + if (!headers->preview && !xisdigit(*value_start)) { + debug(93, 3) ("failed to parse an int header field near '%s'\n", value_start); + } + + } else if (!strncasecmp("Transfer-Preview", start, 16)) { + headers->transfer_preview = parseExtFileList(value_start, end, TRANSFER_PREVIEW); + } else if (!strncasecmp("Transfer-Ignore", start, 15)) { + headers->transfer_ignore = parseExtFileList(value_start, end, TRANSFER_IGNORE); + } else if (!strncasecmp("Transfer-Complete", start, 17)) { + headers->transfer_complete = parseExtFileList(value_start, end, TRANSFER_COMPLETE); + } +}; + + +int ICAPOptions::parseStep(int headers_len) +{ + + const char *parse_start = responseBuf.content(); + const char *blk_start, *blk_end; + const char **parse_end_ptr = &blk_end; + const char *headers_end = parse_start + headers_len - 1; + + Must(parse_start); + Must(state < parseDone); + + if (state == readyToParse) { + if (!isolateLine(&parse_start, &blk_start, &blk_end)) + return 0; + + if (!parseStatusLine(blk_start, blk_end)) + return 0; + + *parse_end_ptr = parse_start; + state = readyToParseHeaders; + } + + while (isolateLine(&parse_start, &blk_start, &blk_end)) { + if ((blk_end > headers_end) || (blk_start > headers_end) || (blk_start >= blk_end)) { + debugs(93, 3, "ICAPOptionsHeaders::parseStep: header limit exceeded."); + break; + } + parseHeader(blk_start, blk_end); + } + return 1; +}; + +bool ICAPOptions::parseResponse() +{ +// Check if the responseBuf is not empty + if (!responseBuf.hasContent()) { + state = parseDone; + headers->parsing = ICAPOptionsHeaders::psBadFormat; + debugs(93, 5, "ICAPOptions::parse: responseBuf is empty"); + return false; + } + + const size_t headers_len = headersEnd(responseBuf.content(), responseBuf.contentSize()); + + if (headers_len <= 0) { + debugs(93, 3, "ICAPOptions::parse: failed to find end of headers " << + "(: in '" << responseBuf.content() << "'"); + state = parseDone; + headers->parsing = ICAPOptionsHeaders::psBadFormat; + return false; + } + + const int res = parseStep(headers_len); + if (res) + return true; + else + return false; +}; + --- /dev/null Wed Feb 14 13:33:00 2007 +++ squid3/src/ICAPOptions.h Wed Feb 14 13:35:02 2007 @@ -0,0 +1,140 @@ + +/* + * $Id: ICAPOptions.h,v 1.1.2.1 2005/09/12 15:40:19 audrus Exp $ + * + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sinks; see the CREDITS file for full details. + * + * This program 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. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_ICAPOPTIONS_H +#define SQUID_ICAPOPTIONS_H + +#include "squid.h" +#include "List.h" +#include "ICAPServiceRep.h" + +typedef enum{ + TRANSFER_NONE, + TRANSFER_PREVIEW, + TRANSFER_IGNORE, + TRANSFER_COMPLETE +} opt_transfer_type; + +/* + * This structure could store key and value for file extension and transfer type + */ +typedef struct{ + char *ext; + opt_transfer_type type; +} opt_transfer_ext; + +/* + * The ICAP options class that encapsulates options supported by a given ICAP service. + * It includes all fields listed in Section "4.10.2 OPTIONS Response" of RFC 3507 + */ + +class ICAPOptionsHeaders +{ +public: +// Obligatory headers in OPTIONS response. + char *methods; + char *istag; + char *encapsulated; + +// The Opt_body_type MUST be included ONLY if an opt-body type is present + char *opt_body_type; + +// These fields MAY be included in OPTIONS response + char *service; + int max_connections; + int options_ttl; + char *date; + char *service_id; + char *allow; + int preview; + + List *transfer_preview; + List *transfer_ignore; + List *transfer_complete; + enum Parsing { psBadFormat, psObligNot, psGood } parsing; + +public: + ICAPOptionsHeaders() : max_connections(-1), options_ttl(-1), preview(-1), + transfer_preview(NULL), transfer_ignore(NULL), transfer_complete(NULL) {}; + ~ICAPOptionsHeaders(); +}; + +class ICAPOptions +{ + +public: + MemBuf responseBuf; + enum State { readyToParse, readyToParseStartLine, readyToParseHeaders, parseDone, parseDoneError} state; + struct timeval start_time; + struct { + unsigned int major; + unsigned int minor; + } version; + unsigned int status; + +// Default transfer type (Transfer-...: *) + opt_transfer_type default_transfer_type; + +// The list of pairs "file extension <-> transfer type" + List *transfer_ext; + + ICAPOptionsHeaders *headers; + ICAPServiceRep *service; + +public: + ICAPOptions(); + ~ICAPOptions(); + + void setDefaultTransferExt(opt_transfer_type t) { + default_transfer_type = t; + }; + + opt_transfer_type getDefaultTransferExt() + { + return default_transfer_type; + }; + opt_transfer_type getTransferExt(const char *); + + int parseStatusLine(const char *start, const char *end); + + List *parseExtFileList(const char *start, const char *end, opt_transfer_type); + void insertTransferExt(const char *, opt_transfer_type); + + void parseHeader(const char *start, const char *end); + int parseStep(int); + + bool parseResponse(); + +}; + +#endif /* SQUID_ICAPOPTIONS_H */