note option

This patch adds note option to squid.conf. It is similar to 
adaptation_meta but is applied after all adaptation and before logging.
Values of key/value pair notes can be logged using %{key}note macros.

    note key value acl ...
    logformat myFormat ... %{key}note ...

This option may be initially used to log custom information about the master
transaction. For example, an admin may configure Squid to log which "user group"
the transaction belongs to, where "user group" will be determined based on a
set of ACLs and not [just] authentication information.

From user point of view, adaptation_header sets/implies meta (i.e.,
setting adaptation_meta is sufficient to be able to log it using
%note) but the note option itself (if any) is evaluated later,
so it has no effect on ICAP headers.

This is a Measurement Factory project
=== modified file 'src/AccessLogEntry.h'
--- src/AccessLogEntry.h	2012-09-21 14:57:30 +0000
+++ src/AccessLogEntry.h	2012-10-22 08:03:05 +0000
@@ -15,49 +15,51 @@
  *  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.
  *
  * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
  */
 #ifndef SQUID_HTTPACCESSLOGENTRY_H
 #define SQUID_HTTPACCESSLOGENTRY_H
 
 #include "anyp/PortCfg.h"
 #include "comm/Connection.h"
+#include "HttpHeader.h"
 #include "HttpVersion.h"
 #include "HttpRequestMethod.h"
 #include "HierarchyLogEntry.h"
 #include "icp_opcode.h"
 #include "ip/Address.h"
 #include "HttpRequestMethod.h"
 #if ICAP_CLIENT
 #include "adaptation/icap/Elements.h"
 #endif
+#include "Notes.h"
 #include "RefCount.h"
 #if USE_SSL
 #include "ssl/gadgets.h"
 #endif
 
 /* forward decls */
 class HttpReply;
 class HttpRequest;
 class CustomLog;
 
 class AccessLogEntry: public RefCountable
 {
 
 public:
     typedef RefCount<AccessLogEntry> Pointer;
 
     AccessLogEntry() : url(NULL), tcpClient(), reply(NULL), request(NULL),
             adapted_request(NULL) {}
     ~AccessLogEntry();
 
@@ -212,40 +214,42 @@
         /// image of the last ICAP response header or eCAP meta received
         char *last_meta;
     } adapt;
 #endif
 
     // Why is this a sub-class and not a set of real "private:" fields?
     // It looks like its duplicating HTTPRequestMethod anyway!
     // TODO: shuffle this to the relevant protocol section OR replace with request->method
     class Private
     {
 
     public:
         Private() : method_str(NULL) {}
 
         const char *method_str;
     } _private;
     HierarchyLogEntry hier;
     HttpReply *reply;
     HttpRequest *request; //< virgin HTTP request
     HttpRequest *adapted_request; //< HTTP request after adaptation and redirection
+    /// key:value pairs set by note and adaptation_meta
+    NotePairs notes;
 
 #if ICAP_CLIENT
     /** \brief This subclass holds log info for ICAP part of request
      *  \todo Inner class declarations should be moved outside
      */
     class IcapLogEntry
     {
     public:
         IcapLogEntry():bodyBytesRead(-1),request(NULL),reply(NULL),outcome(Adaptation::Icap::xoUnknown),trTime(0),ioTime(0),resStatus(HTTP_STATUS_NONE) {}
 
         Ip::Address hostAddr; ///< ICAP server IP address
         String serviceName;        ///< ICAP service name
         String reqUri;             ///< ICAP Request-URI
         Adaptation::Icap::ICAP::Method reqMethod; ///< ICAP request method
         int64_t bytesSent;       ///< number of bytes sent to ICAP server so far
         int64_t bytesRead;       ///< number of bytes read from ICAP server so far
         /**
          * number of ICAP body bytes read from ICAP server or -1 for no encapsulated
          * message data in ICAP reply (eg 204 responses)
          */

=== modified file 'src/HttpHeader.h'
--- src/HttpHeader.h	2012-09-25 16:38:36 +0000
+++ src/HttpHeader.h	2012-10-22 07:56:13 +0000
@@ -158,40 +158,41 @@
     ftDate_1123,
     ftETag,
     ftPCc,
     ftPContRange,
     ftPRange,
     ftPSc,
     ftDate_1123_or_ETag
 } field_type;
 
 /** Possible owners of http header */
 typedef enum {
     hoNone =0,
 #if USE_HTCP
     hoHtcpReply,
 #endif
     hoRequest,
     hoReply,
 #if USE_SSL
     hoErrorDetail,
 #endif
+    hoNote,
     hoEnd
 } http_hdr_owner_type;
 
 // currently a POD
 class HttpHeaderFieldAttrs
 {
 public:
     const char *name;
     http_hdr_type id;
     field_type type;
 };
 
 /** Iteration for headers; use HttpHeaderPos as opaque type, do not interpret */
 typedef ssize_t HttpHeaderPos;
 
 /* use this and only this to initialize HttpHeaderPos */
 #define HttpHeaderInitPos (-1)
 
 class HttpHeaderEntry
 {

=== modified file 'src/HttpHeaderTools.cc'
--- src/HttpHeaderTools.cc	2012-09-21 13:27:44 +0000
+++ src/HttpHeaderTools.cc	2012-10-21 17:06:00 +0000
@@ -14,46 +14,48 @@
  *  incorporates software developed and/or copyrighted by other
  *  sources; 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.
  *
  */
 
 #include "squid.h"
+#include "globals.h"
 #include "acl/FilledChecklist.h"
 #include "acl/Gadgets.h"
 #include "client_side_request.h"
 #include "client_side.h"
 #include "comm/Connection.h"
 #include "compat/strtoll.h"
+#include "ConfigParser.h"
 #include "fde.h"
 #include "HttpHdrContRange.h"
 #include "HttpHeader.h"
 #include "HttpHeaderFieldInfo.h"
 #include "HttpHeaderTools.h"
 #include "HttpRequest.h"
 #include "MemBuf.h"
 #include "SquidConfig.h"
 #include "Store.h"
 #include "StrList.h"
 
 #if USE_SSL
 #include "ssl/support.h"
 #endif
 
 #include <algorithm>
 #include <string>
 #if HAVE_ERRNO_H
 #include <errno.h>
 #endif

=== modified file 'src/Makefile.am'
--- src/Makefile.am	2012-10-13 21:31:34 +0000
+++ src/Makefile.am	2012-10-21 22:09:02 +0000
@@ -423,40 +423,42 @@
 	SquidList.h \
 	SquidList.cc \
 	lookup_t.h \
 	main.cc \
 	Mem.h \
 	mem.cc \
 	mem_node.cc \
 	mem_node.h \
 	Mem.h \
 	MemBuf.cc \
 	MemObject.cc \
 	MemObject.h \
 	mime.h \
 	mime.cc \
 	mime_header.h \
 	mime_header.cc \
 	multicast.h \
 	multicast.cc \
 	neighbors.h \
 	neighbors.cc \
+	Notes.cc \
+	Notes.h \
 	Packer.cc \
 	Packer.h \
 	Parsing.cc \
 	Parsing.h \
 	$(XPROF_STATS_SOURCE) \
 	pconn.cc \
 	pconn.h \
 	PeerDigest.h \
 	peer_digest.cc \
 	peer_proxy_negotiate_auth.h \
 	peer_proxy_negotiate_auth.cc \
 	peer_select.cc \
 	peer_sourcehash.h \
 	peer_sourcehash.cc \
 	peer_userhash.h \
 	peer_userhash.cc \
 	PeerSelectState.h \
 	PingData.h \
 	protos.h \
 	redirect.h \
@@ -1100,40 +1102,41 @@
 #tests_testX_SOURCES=\
 #	tests/testX.h \
 #	tests/testX.cc \
 #	tests/testMain.cc \
 #	X.h \
 #	X.cc
 #nodist_tests_testX_SOURCES=\
 #	$(TESTSOURCES)
 #tests_testX_LDFLAGS = $(LIBADD_DL)
 #tests_testX_LDADD=\
 #	$(SQUID_CPPUNIT_LIBS) \
 #	$(SQUID_CPPUNIT_LA) \
 #	$(COMPAT_LIB) \
 #tests_testX_DEPENDENCIES= $(SQUID_CPPUNIT_LA)
 
 
 # - add other component .(h|cc) files needed to link and run tests
 tests_testHttpReply_SOURCES=\
 	cbdata.cc \
 	cbdata.h \
+	ConfigParser.cc \
 	ETag.cc \
 	fatal.h \
 	tests/stub_fatal.cc \
 	HttpBody.h \
 	HttpBody.cc \
 	HttpHeaderFieldStat.h \
 	HttpHdrCc.h \
 	HttpHdrCc.cc \
 	HttpHdrCc.cci \
 	HttpHdrContRange.cc \
 	HttpHdrContRange.h \
 	HttpHdrRange.cc \
 	HttpHdrSc.cc \
 	HttpHdrSc.h \
 	HttpHdrScTarget.cc \
 	HttpHdrScTarget.h \
 	HttpHeader.h \
 	HttpHeader.cc \
 	HttpHeaderMask.h \
 	HttpHeaderFieldInfo.h \
@@ -1472,40 +1475,42 @@
 	icp_v3.cc \
 	$(IPC_SOURCE) \
 	ipcache.cc \
 	int.h \
 	int.cc \
 	internal.h \
 	internal.cc \
 	SquidList.h \
 	SquidList.cc \
 	multicast.h \
 	multicast.cc \
 	mem_node.cc \
 	MemBuf.cc \
 	MemObject.cc \
 	mime.h \
 	mime.cc \
 	mime_header.h \
 	mime_header.cc \
 	neighbors.h \
 	neighbors.cc \
+	Notes.cc \
+	Notes.h \
 	Packer.cc \
 	Parsing.cc \
 	pconn.cc \
 	peer_digest.cc \
 	peer_proxy_negotiate_auth.h \
 	peer_proxy_negotiate_auth.cc \
 	peer_select.cc \
 	peer_sourcehash.h \
 	peer_sourcehash.cc \
 	peer_userhash.h \
 	peer_userhash.cc \
 	redirect.h \
 	redirect.cc \
 	refresh.h \
 	refresh.cc \
 	RemovalPolicy.cc \
 	Server.cc \
 	$(SNMP_SOURCE) \
 	SquidMath.h \
 	SquidMath.cc \
@@ -1882,40 +1887,42 @@
 	ipcache.cc \
 	int.h \
 	int.cc \
 	internal.h \
 	internal.cc \
 	SquidList.h \
 	SquidList.cc \
 	Mem.h \
 	mem.cc \
 	mem_node.cc \
 	MemBuf.cc \
 	MemObject.cc \
 	mime.h \
 	mime.cc \
 	mime_header.h \
 	mime_header.cc \
 	multicast.h \
 	multicast.cc \
 	neighbors.h \
 	neighbors.cc \
+	Notes.cc \
+	Notes.h \
 	Packer.cc \
 	Parsing.cc \
 	pconn.cc \
 	peer_digest.cc \
 	peer_proxy_negotiate_auth.h \
 	peer_proxy_negotiate_auth.cc \
 	peer_select.cc \
 	peer_sourcehash.h \
 	peer_sourcehash.cc \
 	peer_userhash.h \
 	peer_userhash.cc \
 	redirect.h \
 	redirect.cc \
 	refresh.h \
 	refresh.cc \
 	RemovalPolicy.cc \
 	Server.cc \
 	StrList.h \
 	StrList.cc \
 	$(SNMP_SOURCE) \
@@ -2124,40 +2131,42 @@
 	ipcache.cc \
 	int.h \
 	int.cc \
 	internal.h \
 	internal.cc \
 	SquidList.h \
 	SquidList.cc \
 	MemBuf.cc \
 	MemObject.cc \
 	Mem.h \
 	mem.cc \
 	mem_node.cc \
 	mime.h \
 	mime.cc \
 	mime_header.h \
 	mime_header.cc \
 	multicast.h \
 	multicast.cc \
 	neighbors.h \
 	neighbors.cc \
+	Notes.cc \
+	Notes.h \
 	Packer.cc \
 	Parsing.cc \
 	pconn.cc \
 	peer_digest.cc \
 	peer_proxy_negotiate_auth.h \
 	peer_proxy_negotiate_auth.cc \
 	peer_select.cc \
 	peer_sourcehash.h \
 	peer_sourcehash.cc \
 	peer_userhash.h \
 	peer_userhash.cc \
 	RemovalPolicy.cc \
 	redirect.h \
 	redirect.cc \
 	refresh.h \
 	refresh.cc \
 	Server.cc \
 	$(SNMP_SOURCE) \
 	SquidMath.h \
 	SquidMath.cc \
@@ -2363,40 +2372,42 @@
 	int.cc \
 	internal.h \
 	internal.cc \
 	$(IPC_SOURCE) \
 	ipcache.cc \
 	SquidList.h \
 	SquidList.cc \
 	MemBuf.cc \
 	MemObject.cc \
 	Mem.h \
 	mem.cc \
 	mem_node.cc \
 	mime.h \
 	mime.cc \
 	mime_header.h \
 	mime_header.cc \
 	multicast.h \
 	multicast.cc \
 	neighbors.h \
 	neighbors.cc \
+	Notes.cc \
+	Notes.h \
 	Packer.cc \
 	Parsing.cc \
 	peer_digest.cc \
 	peer_proxy_negotiate_auth.h \
 	peer_proxy_negotiate_auth.cc \
 	peer_select.cc \
 	peer_sourcehash.h \
 	peer_sourcehash.cc \
 	peer_userhash.h \
 	peer_userhash.cc \
 	pconn.cc \
 	redirect.h \
 	redirect.cc \
 	refresh.h \
 	refresh.cc \
 	RemovalPolicy.cc \
 	Server.cc \
 	$(SNMP_SOURCE) \
 	SquidMath.h \
 	SquidMath.cc \
@@ -2643,40 +2654,42 @@
 	icp_v3.cc \
 	$(IPC_SOURCE) \
 	ipcache.cc \
 	int.h \
 	int.cc \
 	internal.h \
 	internal.cc \
 	SquidList.h \
 	SquidList.cc \
 	multicast.h \
 	multicast.cc \
 	mem_node.cc \
 	MemBuf.cc \
 	MemObject.cc \
 	mime.h \
 	mime.cc \
 	mime_header.h \
 	mime_header.cc \
 	neighbors.h \
 	neighbors.cc \
+	Notes.cc \
+	Notes.h \
 	Packer.cc \
 	Parsing.cc \
 	pconn.cc \
 	peer_digest.cc \
 	peer_proxy_negotiate_auth.h \
 	peer_proxy_negotiate_auth.cc \
 	peer_select.cc \
 	peer_sourcehash.h \
 	peer_sourcehash.cc \
 	peer_userhash.h \
 	peer_userhash.cc \
 	redirect.h \
 	redirect.cc \
 	refresh.h \
 	refresh.cc \
 	RemovalPolicy.cc \
 	Server.cc \
 	$(SNMP_SOURCE) \
 	SquidMath.h \
 	SquidMath.cc \
@@ -3732,40 +3745,42 @@
 	ipcache.cc \
 	int.h \
 	int.cc \
 	internal.h \
 	internal.cc \
 	SquidList.h \
 	SquidList.cc \
 	multicast.h \
 	multicast.cc \
 	Mem.h \
 	mem.cc \
 	mem_node.cc \
 	MemBuf.cc \
 	MemObject.cc \
 	mime.h \
 	mime.cc \
 	mime_header.h \
 	mime_header.cc \
 	neighbors.h \
 	neighbors.cc \
+	Notes.cc \
+	Notes.h \
 	Packer.cc \
 	Parsing.cc \
 	pconn.cc \
 	peer_digest.cc \
 	peer_proxy_negotiate_auth.h \
 	peer_proxy_negotiate_auth.cc \
 	peer_select.cc \
 	peer_sourcehash.h \
 	peer_sourcehash.cc \
 	peer_userhash.h \
 	peer_userhash.cc \
 	redirect.h \
 	redirect.cc \
 	refresh.h \
 	refresh.cc \
 	RemovalPolicy.cc \
 	Server.cc \
 	$(SNMP_SOURCE) \
 	SquidMath.h \
 	SquidMath.cc \

=== added file 'src/Notes.cc'
--- src/Notes.cc	1970-01-01 00:00:00 +0000
+++ src/Notes.cc	2012-10-21 21:49:02 +0000
@@ -0,0 +1,131 @@
+/*
+ * 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
+ *  sources; 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.
+ *
+ */
+
+#include "squid.h"
+#include "globals.h"
+#include "acl/FilledChecklist.h"
+#include "acl/Gadgets.h"
+#include "ConfigParser.h"
+#include "HttpRequest.h"
+#include "HttpReply.h"
+#include "SquidConfig.h"
+#include "Store.h"
+
+#include <algorithm>
+#include <string>
+
+Note::Value::~Value()
+{
+    aclDestroyAclList(&aclList);
+}
+
+Note::Value::Pointer
+Note::addValue(const String &value)
+{
+    Value::Pointer v = new Value(value);
+    values.push_back(v);
+    return v;
+}
+
+const char *
+Note::match(HttpRequest *request, HttpReply *reply)
+{
+
+    typedef Values::iterator VLI;
+    ACLFilledChecklist ch(NULL, request, NULL);
+    if (reply)
+        ch.reply = HTTPMSGLOCK(reply);
+
+    for (VLI i = values.begin(); i != values.end(); ++i ) {
+        const int ret= ch.fastCheck((*i)->aclList);
+        debugs(93, 5, HERE << "Check for header name: " << key << ": " << (*i)->value
+               <<", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
+        if (ret == ACCESS_ALLOWED)
+            return (*i)->value.termedBuf();
+    }
+    return NULL;
+}
+
+Note::Pointer
+Notes::add(const String &noteKey)
+{
+    typedef Notes::NotesList::iterator AMLI;
+    for (AMLI i = notes.begin(); i != notes.end(); ++i) {
+        if ((*i)->key == noteKey)
+            return (*i);
+    }
+
+    Note::Pointer note = new Note(noteKey);
+    notes.push_back(note);
+    return note;
+}
+
+Note::Pointer
+Notes::parse(ConfigParser &parser)
+{
+    String key, value;
+    ConfigParser::ParseString(&key);
+    ConfigParser::ParseQuotedString(&value);
+    Note::Pointer note = add(key);
+    Note::Value::Pointer noteValue = note->addValue(value);
+    aclParseAclList(parser, &noteValue->aclList);
+
+    if (blacklisted) {
+        for (int i = 0; blacklisted[i] != NULL; ++i) {
+            if (note->key.caseCmp(blacklisted[i]) == 0) {
+                fatalf("%s:%d: meta key \"%s\" is a reserved %s name",
+                       cfg_filename, config_lineno, note->key.termedBuf(),
+                       descr ? descr : "");
+            }
+        }
+    }
+
+    return note;
+}
+
+void
+Notes::dump(StoreEntry *entry, const char *key)
+{
+    typedef Notes::NotesList::iterator AMLI;
+    for (AMLI m = notes.begin(); m != notes.end(); ++m) {
+        typedef Note::Values::iterator VLI;
+        for (VLI v =(*m)->values.begin(); v != (*m)->values.end(); ++v ) {
+            storeAppendPrintf(entry, "%s " SQUIDSTRINGPH " %s",
+                              key, SQUIDSTRINGPRINT((*m)->key), ConfigParser::QuoteString((*v)->value));
+            dump_acl_list(entry, (*v)->aclList);
+            storeAppendPrintf(entry, "\n");
+        }
+    }
+}
+
+void
+Notes::clean()
+{
+    notes.clean();
+}

=== added file 'src/Notes.h'
--- src/Notes.h	1970-01-01 00:00:00 +0000
+++ src/Notes.h	2012-10-22 08:01:54 +0000
@@ -0,0 +1,103 @@
+#ifndef SQUID_NOTES_H
+#define SQUID_NOTES_H
+
+#include "HttpHeader.h"
+#include "HttpHeaderTools.h"
+#include "typedefs.h"
+
+#if HAVE_STRING
+#include <string>
+#endif
+
+
+class HttpRequest;
+class HttpReply;
+
+/**
+ * Used to store notes. The notes are custom key:value pairs
+ * ICAP request headers or ECAP options used to pass
+ * custom transaction-state related meta information to squid
+ * internal subsystems or to addaptation services.
+ */
+class Note: public RefCountable
+{
+public:
+    typedef RefCount<Note> Pointer;
+    /// Stores a value for the note.
+    class Value: public RefCountable
+    {
+    public:
+        typedef RefCount<Value> Pointer;
+        String value; ///< a note value
+        ACLList *aclList; ///< The access list used to determine if this value is valid for a request
+        explicit Value(const String &aVal) : value(aVal), aclList(NULL) {}
+        ~Value();
+    };
+    typedef Vector<Value::Pointer> Values;
+
+    explicit Note(const String &aKey): key(aKey) {}
+
+    /**
+     * Adds a value to the note and returns a  pointer to the
+     * related Value object.
+     */
+    Value::Pointer addValue(const String &value);
+
+    /**
+     * Walks through the  possible values list of the note and selects
+     * the first value which matches the given HttpRequest and HttpReply
+     * or NULL if none matches.
+     */
+    const char *match(HttpRequest *request, HttpReply *reply);
+    String key; ///< The note key
+    Values values; ///< The possible values list for the note
+};
+
+class ConfigParser;
+/**
+ * Used to store a notes list.
+ */
+class Notes {
+public:
+    typedef Vector<Note::Pointer> NotesList;
+    typedef NotesList::iterator iterator; ///< iterates over the notes list
+
+    Notes(const char *aDescr, const char **metasBlacklist): descr(aDescr), blacklisted(metasBlacklist) {}
+    Notes(): descr(NULL), blacklisted(NULL) {}
+    ~Notes() { notes.clean(); }
+    /**
+     * Parse a notes line and returns a pointer to the
+     * parsed Note object.
+     */
+    Note::Pointer parse(ConfigParser &parser);
+    /**
+     * Dump the notes list to the given StoreEntry object.
+     */
+    void dump(StoreEntry *entry, const char *name);
+    void clean(); /// clean the notes list
+
+    /// points to the first argument
+    iterator begin() { return notes.begin(); }
+    /// points to the end of list
+    iterator end() { return notes.end(); }
+    /// return true if the notes list is empty
+    bool empty() { return notes.empty(); }
+
+    NotesList notes; ///< The Note::Pointer objects array list
+    const char *descr; ///< A short description for notes list
+    const char **blacklisted; ///< Null terminated list of blacklisted note keys
+private:
+    /**
+     * Adds a note to the notes list and returns a pointer to the
+     * related Note object. If the note key already exists in list,
+     * returns a pointer to the existing object.
+     */
+    Note::Pointer add(const String &noteKey);
+};
+
+class NotePairs : public HttpHeader {
+public:
+    NotePairs() : HttpHeader(hoNote) {}
+};
+
+#endif

=== modified file 'src/SquidConfig.h'
--- src/SquidConfig.h	2012-10-09 23:15:44 +0000
+++ src/SquidConfig.h	2012-10-21 21:36:28 +0000
@@ -19,40 +19,41 @@
  *  (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.
  *
  */
 
 #include "acl/AclAddress.h"
 #include "ClientDelayConfig.h"
 #include "DelayConfig.h"
 #include "HelperChildConfig.h"
 #include "HttpHeaderTools.h"
 #include "icmp/IcmpConfig.h"
 #include "ip/Address.h"
+#include "Notes.h"
 #include "RefCount.h"
 #include "YesNoNone.h"
 
 #if USE_SSL
 #include <openssl/ssl.h>
 class sslproxy_cert_sign;
 class sslproxy_cert_adapt;
 #endif
 
 class acl_access;
 class AclSizeLimit;
 class AclDenyInfoList;
 namespace Mgr
 {
 class ActionPasswordList;
 } // namespace Mgr
 class CustomLog;
 class CpuAffinityMap;
 class external_acl;
 class HeaderManglers;
@@ -459,40 +460,42 @@
     int max_open_disk_fds;
     int uri_whitespace;
     AclSizeLimit *rangeOffsetLimit;
 #if MULTICAST_MISS_STREAM
 
     struct {
 
         Ip::Address addr;
         int ttl;
         unsigned short port;
         char *encode_key;
     } mcast_miss;
 #endif
 
     /// request_header_access and request_header_replace
     HeaderManglers *request_header_access;
     /// reply_header_access and reply_header_replace
     HeaderManglers *reply_header_access;
     ///request_header_add access list
     HeaderWithAclList *request_header_add;
+    ///note
+    Notes notes;
     char *coredump_dir;
     char *chroot_dir;
 #if USE_CACHE_DIGESTS
 
     struct {
         int bits_per_entry;
         time_t rebuild_period;
         time_t rewrite_period;
         size_t swapout_chunk_size;
         int rebuild_chunk_percentage;
     } digest;
 #endif
 #if USE_SSL
 
     struct {
         int unclean_shutdown;
         char *ssl_engine;
     } SSL;
 #endif
 

=== modified file 'src/adaptation/Config.cc'
--- src/adaptation/Config.cc	2012-09-23 09:04:21 +0000
+++ src/adaptation/Config.cc	2012-10-21 14:35:35 +0000
@@ -32,87 +32,58 @@
 #include "acl/FilledChecklist.h"
 #include "acl/Gadgets.h"
 #include "adaptation/AccessRule.h"
 #include "adaptation/Config.h"
 #include "adaptation/History.h"
 #include "adaptation/Service.h"
 #include "adaptation/ServiceGroups.h"
 #include "Array.h"
 #include "ConfigParser.h"
 #include "globals.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "Store.h"
 
 bool Adaptation::Config::Enabled = false;
 char *Adaptation::Config::masterx_shared_name = NULL;
 int Adaptation::Config::service_iteration_limit = 16;
 int Adaptation::Config::send_client_ip = false;
 int Adaptation::Config::send_username = false;
 int Adaptation::Config::use_indirect_client = true;
-Adaptation::Config::MetaHeaders Adaptation::Config::metaHeaders;
-
-Adaptation::Config::MetaHeader::Value::~Value()
-{
-    aclDestroyAclList(&aclList);
-}
-
-Adaptation::Config::MetaHeader::Value::Pointer
-Adaptation::Config::MetaHeader::addValue(const String &value)
-{
-    Value::Pointer v = new Value(value);
-    values.push_back(v);
-    return v;
-}
-
-const char *
-Adaptation::Config::MetaHeader::match(HttpRequest *request, HttpReply *reply)
-{
-
-    typedef Values::iterator VLI;
-    ACLFilledChecklist ch(NULL, request, NULL);
-    if (reply)
-        ch.reply = HTTPMSGLOCK(reply);
-
-    for (VLI i = values.begin(); i != values.end(); ++i ) {
-        const int ret= ch.fastCheck((*i)->aclList);
-        debugs(93, 5, HERE << "Check for header name: " << name << ": " << (*i)->value
-               <<", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
-        if (ret == ACCESS_ALLOWED)
-            return (*i)->value.termedBuf();
-    }
-    return NULL;
-}
-
-Adaptation::Config::MetaHeader::Pointer
-Adaptation::Config::addMetaHeader(const String &headerName)
-{
-    typedef MetaHeaders::iterator AMLI;
-    for (AMLI i = metaHeaders.begin(); i != metaHeaders.end(); ++i) {
-        if ((*i)->name == headerName)
-            return (*i);
-    }
-
-    MetaHeader::Pointer meta = new MetaHeader(headerName);
-    metaHeaders.push_back(meta);
-    return meta;
-}
+const char *metasBlacklist[] = {
+    "Methods",
+    "Service",
+    "ISTag",
+    "Encapsulated",
+    "Opt-body-type",
+    "Max-Connections",
+    "Options-TTL",
+    "Date",
+    "Service-ID",
+    "Allow",
+    "Preview",
+    "Transfer-Preview",
+    "Transfer-Ignore",
+    "Transfer-Complete",
+    NULL
+};
+Notes Adaptation::Config::metaHeaders("ICAP header", metasBlacklist);
 
 Adaptation::ServiceConfig*
 Adaptation::Config::newServiceConfig() const
 {
     return new ServiceConfig();
 }
 
 void
 Adaptation::Config::removeService(const String& service)
 {
     removeRule(service);
     const Groups& groups = AllGroups();
     for (unsigned int i = 0; i < groups.size(); ) {
         const ServiceGroupPointer group = groups[i];
         const ServiceGroup::Store& services = group->services;
         typedef ServiceGroup::Store::const_iterator SGSI;
         for (SGSI it = services.begin(); it != services.end(); ++it) {
             if (*it == service) {
                 group->removedServices.push_back(service);
                 group->services.prune(service);
@@ -163,42 +134,40 @@
 void
 Adaptation::Config::parseService()
 {
     ServiceConfigPointer cfg = newServiceConfig();
     if (!cfg->parse()) {
         fatalf("%s:%d: malformed adaptation service configuration",
                cfg_filename, config_lineno);
     }
     serviceConfigs.push_back(cfg);
 }
 
 void
 Adaptation::Config::freeService()
 {
     FreeAccess();
     FreeServiceGroups();
 
     DetachServices();
 
     serviceConfigs.clean();
-
-    FreeMetaHeader();
 }
 
 void
 Adaptation::Config::dumpService(StoreEntry *entry, const char *name) const
 {
     typedef Services::iterator SCI;
     for (SCI i = AllServices().begin(); i != AllServices().end(); ++i) {
         const ServiceConfig &cfg = (*i)->cfg();
         storeAppendPrintf(entry, "%s " SQUIDSTRINGPH "_%s %s %d " SQUIDSTRINGPH "\n",
                           name,
                           SQUIDSTRINGPRINT(cfg.key),
                           cfg.methodStr(), cfg.vectPointStr(), cfg.bypass,
                           SQUIDSTRINGPRINT(cfg.uri));
     }
 }
 
 bool
 Adaptation::Config::finalize()
 {
     if (!onoff) {
@@ -239,98 +208,40 @@
 {
     typedef typename Collection::iterator CI;
     for (CI i = collection.begin(); i != collection.end(); ++i)
         (*i)->finalize();
 
     debugs(93,2, HERE << "Initialized " << collection.size() << ' ' << label);
 }
 
 void
 Adaptation::Config::Finalize(bool enabled)
 {
     Enabled = enabled;
     debugs(93, DBG_IMPORTANT, "Adaptation support is " << (Enabled ? "on" : "off."));
 
     FinalizeEach(AllServices(), "message adaptation services");
     FinalizeEach(AllGroups(), "message adaptation service groups");
     FinalizeEach(AllRules(), "message adaptation access rules");
 }
 
 void
-Adaptation::Config::ParseMetaHeader(ConfigParser &parser)
-{
-    String name, value;
-    const char *warnFor[] = {
-        "Methods",
-        "Service",
-        "ISTag",
-        "Encapsulated",
-        "Opt-body-type",
-        "Max-Connections",
-        "Options-TTL",
-        "Date",
-        "Service-ID",
-        "Allow",
-        "Preview",
-        "Transfer-Preview",
-        "Transfer-Ignore",
-        "Transfer-Complete",
-        NULL
-    };
-    ConfigParser::ParseString(&name);
-    ConfigParser::ParseQuotedString(&value);
-
-    // TODO: Find a way to move this check to ICAP
-    for (int i = 0; warnFor[i] != NULL; ++i) {
-        if (name.caseCmp(warnFor[i]) == 0) {
-            fatalf("%s:%d: meta name \"%s\" is a reserved ICAP header name",
-                   cfg_filename, config_lineno, name.termedBuf());
-        }
-    }
-
-    MetaHeader::Pointer meta = addMetaHeader(name);
-    MetaHeader::Value::Pointer headValue = meta->addValue(value);
-    aclParseAclList(parser, &headValue->aclList);
-}
-
-void
-Adaptation::Config::DumpMetaHeader(StoreEntry *entry, const char *name)
-{
-    typedef MetaHeaders::iterator AMLI;
-    for (AMLI m = metaHeaders.begin(); m != metaHeaders.end(); ++m) {
-        typedef MetaHeader::Values::iterator VLI;
-        for (VLI v =(*m)->values.begin(); v != (*m)->values.end(); ++v ) {
-            storeAppendPrintf(entry, "%s " SQUIDSTRINGPH " %s",
-                              name, SQUIDSTRINGPRINT((*m)->name), ConfigParser::QuoteString((*v)->value));
-            dump_acl_list(entry, (*v)->aclList);
-            storeAppendPrintf(entry, "\n");
-        }
-    }
-}
-
-void
-Adaptation::Config::FreeMetaHeader()
-{
-    metaHeaders.clean();
-}
-
-void
 Adaptation::Config::ParseServiceSet()
 {
     Adaptation::Config::ParseServiceGroup(new ServiceSet);
 }
 
 void
 Adaptation::Config::ParseServiceChain()
 {
     Adaptation::Config::ParseServiceGroup(new ServiceChain);
 }
 
 void
 Adaptation::Config::ParseServiceGroup(ServiceGroupPointer g)
 {
     assert(g != NULL);
     g->parse();
     AllGroups().push_back(g);
 }
 
 void

=== modified file 'src/adaptation/Config.h'
--- src/adaptation/Config.h	2012-08-14 11:53:07 +0000
+++ src/adaptation/Config.h	2012-10-21 21:39:04 +0000
@@ -1,119 +1,71 @@
 #ifndef SQUID_ADAPTATION__CONFIG_H
 #define SQUID_ADAPTATION__CONFIG_H
 
 #include "event.h"
 #include "acl/Gadgets.h"
 #include "base/AsyncCall.h"
 #include "adaptation/forward.h"
 #include "adaptation/Elements.h"
+#include "Notes.h"
 #include "SquidString.h"
 
 class acl_access;
 class ConfigParser;
 class HttpRequest;
 class HttpReply;
 
 namespace Adaptation
 {
 
 class Config
 {
 public:
     static void Finalize(bool enable);
 
     static void ParseServiceSet(void);
     static void ParseServiceChain(void);
-    static void ParseMetaHeader(ConfigParser &parser);
-    static void FreeMetaHeader();
-    static void DumpMetaHeader(StoreEntry *, const char *);
 
     static void ParseAccess(ConfigParser &parser);
     static void FreeAccess(void);
     static void DumpAccess(StoreEntry *, const char *);
 
     friend class AccessCheck;
 
 public:
     static bool Enabled; // true if at least one adaptation mechanism is
 
     // these are global squid.conf options, documented elsewhere
     static char *masterx_shared_name; // global TODO: do we need TheConfig?
     static int service_iteration_limit;
     static int send_client_ip;
     static int send_username;
     static int use_indirect_client;
 
     // Options below are accessed via Icap::TheConfig or Ecap::TheConfig
     // TODO: move ICAP-specific options to Icap::Config and add TheConfig
     int onoff;
     int service_failure_limit;
     time_t oldest_service_failure;
     int service_revival_delay;
 
-    /**
-     * Used to store meta headers. The meta headers are custom
-     * ICAP request headers or ECAP options used to pass custom
-     * transaction-state related meta information to a service.
-     */
-    class MetaHeader: public RefCountable
-    {
-    public:
-        typedef RefCount<MetaHeader> Pointer;
-        /// Stores a value for the meta header.
-        class Value: public RefCountable
-        {
-        public:
-            typedef RefCount<Value> Pointer;
-            String value; ///< a header value
-            ACLList *aclList; ///< The access list used to determine if this value is valid for a request
-            explicit Value(const String &aVal) : value(aVal), aclList(NULL) {}
-            ~Value();
-        };
-        typedef Vector<Value::Pointer> Values;
-
-        explicit MetaHeader(const String &aName): name(aName) {}
-
-        /**
-         * Adds a value to the meta header and returns a  pointer to the
-         * related Value object.
-         */
-        Value::Pointer addValue(const String &value);
-
-        /**
-         * Walks through the  possible values list of the  meta and selects
-         * the first value which matches the given HttpRequest and HttpReply
-         * or NULL if none matches.
-         */
-        const char *match(HttpRequest *request, HttpReply *reply);
-        String name; ///< The meta header name
-        Values values; ///< The possible values list for the meta header
-    };
-    typedef Vector<MetaHeader::Pointer> MetaHeaders;
-    static MetaHeaders metaHeaders; ///< The list of configured meta headers
-
-    /**
-     * Adds a header to the meta headers list and returns a pointer to the
-     * related metaHeaders object. If the header name already exists in list,
-     * returns a pointer to the existing object.
-     */
-    static MetaHeader::Pointer addMetaHeader(const String &header);
+    static Notes metaHeaders; ///< The list of configured meta headers
 
     typedef Vector<ServiceConfigPointer> ServiceConfigs;
     ServiceConfigs serviceConfigs;
 
     Config();
     virtual ~Config();
 
     void parseService(void);
     void freeService(void);
     void dumpService(StoreEntry *, const char *) const;
     ServicePointer findService(const String&);
 
     /**
      * Creates and starts the adaptation services. In the case the adaptation
      * mechanism is disabled then removes any reference to the services from
      * access rules and service groups, and returns false.
      * \return true if the services are ready and running, false otherwise
      */
     virtual bool finalize();
 

=== modified file 'src/adaptation/History.h'
--- src/adaptation/History.h	2012-08-28 13:00:30 +0000
+++ src/adaptation/History.h	2012-10-22 09:05:24 +0000
@@ -1,26 +1,27 @@
 #ifndef SQUID_ADAPT_HISTORY_H
 #define SQUID_ADAPT_HISTORY_H
 
 #include "adaptation/DynamicGroupCfg.h"
 #include "Array.h"
 #include "HttpHeader.h"
+#include "Notes.h"
 #include "RefCount.h"
 #include "SquidString.h"
 
 namespace Adaptation
 {
 
 /// collects information about adaptations related to a master transaction
 class History: public RefCountable
 {
 public:
     typedef RefCount<Adaptation::History> Pointer;
 
     History();
 
     /// record the start of a xact, return xact history ID
     int recordXactStart(const String &serviceId, const timeval &when, bool retrying);
 
     /// record the end of a xact identified by its history ID
     void recordXactFinish(int hid);
 
@@ -33,40 +34,43 @@
     /// sets or resets a cross-transactional database record
     void updateXxRecord(const char *name, const String &value);
 
     /// returns true and fills the record fields iff there is a db record
     bool getXxRecord(String &name, String &value) const;
 
     /// sets or resets next services for the Adaptation::Iterator to notice
     void updateNextServices(const String &services);
 
     /// returns true, fills the value, and resets iff next services were set
     bool extractNextServices(String &value);
 
     /// store the last meta header fields received from the adaptation service
     void recordMeta(const HttpHeader *lm);
 
 public:
     /// Last received meta header (REQMOD or RESPMOD, whichever comes last).
     HttpHeader lastMeta;
     /// All REQMOD and RESPMOD meta headers merged. Last field wins conflicts.
     HttpHeader allMeta;
+    /// key:value pairs set by adaptation_meta, to be added to
+    /// AccessLogEntry::notes when ALE becomes available
+    NotePairs metaHeaders;
 
     /// sets future services for the Adaptation::AccessCheck to notice
     void setFutureServices(const DynamicGroupCfg &services);
 
     /// returns true, fills the value, and resets iff future services were set
     bool extractFutureServices(DynamicGroupCfg &services);
 
 private:
     /// single Xaction stats (i.e., a historical record entry)
     class Entry
     {
     public:
         Entry(const String &serviceId, const timeval &when);
         Entry(); // required by Vector<>
 
         void stop(); ///< updates stats on transaction end
         int rptm(); ///< returns response time [msec], calculates it if needed
 
         String service; ///< adaptation service ID
         timeval start; ///< when the xaction was started

=== modified file 'src/adaptation/ecap/XactionRep.cc'
--- src/adaptation/ecap/XactionRep.cc	2012-08-28 13:00:30 +0000
+++ src/adaptation/ecap/XactionRep.cc	2012-10-22 09:08:40 +0000
@@ -160,88 +160,98 @@
     if (name.known()) { // must check to avoid empty names matching unset cfg
         Adaptation::History::Pointer ah = request->adaptHistory(false);
         if (ah != NULL) {
             String name, value;
             if (ah->getXxRecord(name, value))
                 return libecap::Area::FromTempBuffer(value.rawBuf(), value.size());
         }
     }
     return libecap::Area();
 }
 
 const libecap::Area
 Adaptation::Ecap::XactionRep::metaValue(const libecap::Name &name) const
 {
     HttpRequest *request = dynamic_cast<HttpRequest*>(theCauseRep ?
                            theCauseRep->raw().header : theVirginRep.raw().header);
     Must(request);
     HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header);
 
     if (name.known()) { // must check to avoid empty names matching unset cfg
-        typedef Adaptation::Config::MetaHeaders::iterator ACAMLI;
+        typedef Notes::iterator ACAMLI;
         for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) {
-            if (name == (*i)->name.termedBuf()) {
+            if (name == (*i)->key.termedBuf()) {
                 if (const char *value = (*i)->match(request, reply))
                     return libecap::Area::FromTempString(value);
                 else
                     return libecap::Area();
             }
         }
     }
 
     return libecap::Area();
 }
 
 void
 Adaptation::Ecap::XactionRep::visitEachMetaHeader(libecap::NamedValueVisitor &visitor) const
 {
     HttpRequest *request = dynamic_cast<HttpRequest*>(theCauseRep ?
                            theCauseRep->raw().header : theVirginRep.raw().header);
     Must(request);
     HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header);
 
-    typedef Adaptation::Config::MetaHeaders::iterator ACAMLI;
+    typedef Notes::iterator ACAMLI;
     for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) {
         const char *v = (*i)->match(request, reply);
         if (v) {
-            const libecap::Name name((*i)->name.termedBuf());
+            const libecap::Name name((*i)->key.termedBuf());
             const libecap::Area value = libecap::Area::FromTempString(v);
             visitor.visit(name, value);
         }
     }
 }
 
 void
 Adaptation::Ecap::XactionRep::start()
 {
     Must(theMaster);
 
     if (!theVirginRep.raw().body_pipe)
         makingVb = opNever; // there is nothing to deliver
 
-    const HttpRequest *request = dynamic_cast<const HttpRequest*> (theCauseRep ?
+    HttpRequest *request = dynamic_cast<HttpRequest*> (theCauseRep ?
                                  theCauseRep->raw().header : theVirginRep.raw().header);
     Must(request);
+
+    HttpReply *reply = dynamic_cast<HttpReply*>(theVirginRep.raw().header);
+
     Adaptation::History::Pointer ah = request->adaptLogHistory();
     if (ah != NULL) {
         // retrying=false because ecap never retries transactions
         adaptHistoryId = ah->recordXactStart(service().cfg().key, current_time, false);
+        typedef Notes::iterator ACAMLI;
+        for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) {
+            const char *v = (*i)->match(request, reply);
+            if (v && !ah->metaHeaders.hasByNameListMember((*i)->key.termedBuf(), v, ',')) {
+                ah->metaHeaders.addEntry(new HttpHeaderEntry(HDR_OTHER, (*i)->key.termedBuf(), v));
+            }
+        }
     }
 
     theMaster->start();
 }
 
 void
 Adaptation::Ecap::XactionRep::swanSong()
 {
     // clear body_pipes, if any
     // this code does not maintain proxying* and canAccessVb states; should it?
 
     if (theAnswerRep != NULL) {
         BodyPipe::Pointer body_pipe = answer().body_pipe;
         if (body_pipe != NULL) {
             Must(body_pipe->stillProducing(this));
             stopProducingFor(body_pipe, false);
         }
     }
 
     BodyPipe::Pointer &body_pipe = theVirginRep.raw().body_pipe;

=== modified file 'src/adaptation/icap/ModXact.cc'
--- src/adaptation/icap/ModXact.cc	2012-08-31 16:57:39 +0000
+++ src/adaptation/icap/ModXact.cc	2012-10-21 16:50:14 +0000
@@ -1403,50 +1403,54 @@
     }
 
     makeAllowHeader(buf);
 
     if (TheConfig.send_client_ip && request) {
         Ip::Address client_addr;
 #if FOLLOW_X_FORWARDED_FOR
         if (TheConfig.use_indirect_client) {
             client_addr = request->indirect_client_addr;
         } else
 #endif
             client_addr = request->client_addr;
         if (!client_addr.IsAnyAddr() && !client_addr.IsNoAddr())
             buf.Printf("X-Client-IP: %s\r\n", client_addr.NtoA(ntoabuf,MAX_IPSTRLEN));
     }
 
     if (TheConfig.send_username && request)
         makeUsernameHeader(request, buf);
 
     // Adaptation::Config::metaHeaders
-    typedef Adaptation::Config::MetaHeaders::iterator ACAMLI;
+    typedef Notes::iterator ACAMLI;
     for (ACAMLI i = Adaptation::Config::metaHeaders.begin(); i != Adaptation::Config::metaHeaders.end(); ++i) {
         HttpRequest *r = virgin.cause ?
                          virgin.cause : dynamic_cast<HttpRequest*>(virgin.header);
         Must(r);
 
         HttpReply *reply = dynamic_cast<HttpReply*>(virgin.header);
 
-        if (const char *value = (*i)->match(r, reply))
-            buf.Printf("%s: %s\r\n", (*i)->name.termedBuf(), value);
+        if (const char *value = (*i)->match(r, reply)) {
+            buf.Printf("%s: %s\r\n", (*i)->key.termedBuf(), value);
+            Adaptation::History::Pointer ah = request->adaptHistory(false);
+            if (ah != NULL && !ah->metaHeaders.hasByNameListMember((*i)->key.termedBuf(), value, ','))
+                ah->metaHeaders.addEntry(new HttpHeaderEntry(HDR_OTHER, (*i)->key.termedBuf(), value));
+        }
     }
 
     // fprintf(stderr, "%s\n", buf.content());
 
     buf.append(ICAP::crlf, 2); // terminate ICAP header
 
     // fill icapRequest for logging
     Must(icapRequest->parseCharBuf(buf.content(), buf.contentSize()));
 
     // start ICAP request body with encapsulated HTTP headers
     buf.append(httpBuf.content(), httpBuf.contentSize());
 
     httpBuf.clean();
 }
 
 // decides which Allow values to write and updates the request buffer
 void Adaptation::Icap::ModXact::makeAllowHeader(MemBuf &buf)
 {
     const bool allow204in = preview.enabled(); // TODO: add shouldAllow204in()
     const bool allow204out = state.allowedPostview204 = shouldAllow204();

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2012-10-20 08:01:32 +0000
+++ src/cache_cf.cc	2012-10-21 14:36:37 +0000
@@ -117,43 +117,40 @@
 #include <sys/types.h>
 #endif
 #if HAVE_PWD_H
 #include <pwd.h>
 #endif
 #if HAVE_GRP_H
 #include <grp.h>
 #endif
 #if HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
 
 #if USE_SSL
 #include "ssl/gadgets.h"
 #endif
 
 #if USE_ADAPTATION
 static void parse_adaptation_service_set_type();
 static void parse_adaptation_service_chain_type();
 static void parse_adaptation_access_type();
-static void parse_adaptation_meta_type(Adaptation::Config::MetaHeaders *);
-static void dump_adaptation_meta_type(StoreEntry *, const char *, Adaptation::Config::MetaHeaders &);
-static void free_adaptation_meta_type(Adaptation::Config::MetaHeaders *);
 #endif
 
 #if ICAP_CLIENT
 static void parse_icap_service_type(Adaptation::Icap::Config *);
 static void dump_icap_service_type(StoreEntry *, const char *, const Adaptation::Icap::Config &);
 static void free_icap_service_type(Adaptation::Icap::Config *);
 static void parse_icap_class_type();
 static void parse_icap_access_type();
 
 static void parse_icap_service_failure_limit(Adaptation::Icap::Config *);
 static void dump_icap_service_failure_limit(StoreEntry *, const char *, const Adaptation::Icap::Config &);
 static void free_icap_service_failure_limit(Adaptation::Icap::Config *);
 #endif
 
 #if USE_ECAP
 static void parse_ecap_service_type(Adaptation::Ecap::Config *);
 static void dump_ecap_service_type(StoreEntry *, const char *, const Adaptation::Ecap::Config &);
 static void free_ecap_service_type(Adaptation::Ecap::Config *);
 #endif
 
@@ -202,40 +199,43 @@
 #endif
 #if !USE_DNSHELPER
 static void parseBytesLineSigned(ssize_t * bptr, const char *units);
 #endif
 static size_t parseBytesUnits(const char *unit);
 static void free_all(void);
 void requirePathnameExists(const char *name, const char *path);
 static OBJH dump_config;
 #if USE_HTTP_VIOLATIONS
 static void free_HeaderManglers(HeaderManglers **pm);
 static void dump_http_header_access(StoreEntry * entry, const char *name, const HeaderManglers *manglers);
 static void parse_http_header_access(HeaderManglers **manglers);
 #define free_http_header_access free_HeaderManglers
 static void dump_http_header_replace(StoreEntry * entry, const char *name, const HeaderManglers *manglers);
 static void parse_http_header_replace(HeaderManglers **manglers);
 #define free_http_header_replace free_HeaderManglers
 #endif
 static void dump_HeaderWithAclList(StoreEntry * entry, const char *name, HeaderWithAclList *headers);
 static void parse_HeaderWithAclList(HeaderWithAclList **header);
 static void free_HeaderWithAclList(HeaderWithAclList **header);
+static void parse_note(Notes *);
+static void dump_note(StoreEntry *, const char *, Notes &);
+static void free_note(Notes *);
 static void parse_denyinfo(AclDenyInfoList ** var);
 static void dump_denyinfo(StoreEntry * entry, const char *name, AclDenyInfoList * var);
 static void free_denyinfo(AclDenyInfoList ** var);
 
 #if USE_WCCPv2
 static void parse_IpAddress_list(Ip::Address_list **);
 static void dump_IpAddress_list(StoreEntry *, const char *, const Ip::Address_list *);
 static void free_IpAddress_list(Ip::Address_list **);
 #if CURRENTLY_UNUSED
 static int check_null_IpAddress_list(const Ip::Address_list *);
 #endif /* CURRENTLY_UNUSED */
 #endif /* USE_WCCPv2 */
 
 static void parsePortCfg(AnyP::PortCfg **, const char *protocol);
 #define parse_PortCfg(l) parsePortCfg((l), token)
 static void dump_PortCfg(StoreEntry *, const char *, const AnyP::PortCfg *);
 static void free_PortCfg(AnyP::PortCfg **);
 
 #if USE_SSL
 static void parse_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign);
@@ -4195,58 +4195,40 @@
 
 #if USE_ADAPTATION
 
 static void
 parse_adaptation_service_set_type()
 {
     Adaptation::Config::ParseServiceSet();
 }
 
 static void
 parse_adaptation_service_chain_type()
 {
     Adaptation::Config::ParseServiceChain();
 }
 
 static void
 parse_adaptation_access_type()
 {
     Adaptation::Config::ParseAccess(LegacyParser);
 }
-
-static void
-parse_adaptation_meta_type(Adaptation::Config::MetaHeaders *)
-{
-    Adaptation::Config::ParseMetaHeader(LegacyParser);
-}
-
-static void
-dump_adaptation_meta_type(StoreEntry *entry, const char *name, Adaptation::Config::MetaHeaders &)
-{
-    Adaptation::Config::DumpMetaHeader(entry, name);
-}
-
-static void
-free_adaptation_meta_type(Adaptation::Config::MetaHeaders *)
-{
-    // Nothing to do, it is released inside Adaptation::Config::freeService()
-}
 #endif /* USE_ADAPTATION */
 
 #if ICAP_CLIENT
 
 static void
 parse_icap_service_type(Adaptation::Icap::Config * cfg)
 {
     cfg->parseService();
 }
 
 static void
 free_icap_service_type(Adaptation::Icap::Config * cfg)
 {
     cfg->freeService();
 }
 
 static void
 dump_icap_service_type(StoreEntry * entry, const char *name, const Adaptation::Icap::Config &cfg)
 {
     cfg.dumpService(entry, name);
@@ -4644,20 +4626,36 @@
     (*headers)->push_back(hwa);
 }
 
 static void free_HeaderWithAclList(HeaderWithAclList **header)
 {
     if (!(*header))
         return;
 
     for (HeaderWithAclList::iterator hwa = (*header)->begin(); hwa != (*header)->end(); ++hwa) {
         if (hwa->aclList)
             aclDestroyAclList(&hwa->aclList);
 
         if (hwa->valueFormat) {
             delete hwa->valueFormat;
             hwa->valueFormat = NULL;
         }
     }
     delete *header;
     *header = NULL;
 }
+
+static void parse_note(Notes *notes)
+{
+    assert(notes);
+    notes->parse(LegacyParser);
+}
+
+static void dump_note(StoreEntry *entry, const char *name, Notes &notes)
+{
+    notes.dump(entry, name);
+}
+
+static void free_note(Notes *notes)
+{
+    notes->clean();
+}

=== modified file 'src/cf.data.depend'
--- src/cf.data.depend	2012-07-18 16:21:47 +0000
+++ src/cf.data.depend	2012-10-21 14:16:54 +0000
@@ -18,52 +18,52 @@
 debug
 delay_pool_access	acl	delay_class
 delay_pool_class	delay_pools
 delay_pool_count
 delay_pool_rates	delay_class
 client_delay_pool_access	acl
 client_delay_pool_count
 client_delay_pool_rates
 denyinfo		acl
 eol
 externalAclHelper	auth_param
 HelperChildConfig
 hostdomain		cache_peer
 hostdomaintype		cache_peer
 http_header_access	acl
 http_header_replace
 HeaderWithAclList	acl
 adaptation_access_type	adaptation_service_set adaptation_service_chain acl icap_service icap_class
 adaptation_service_set_type	icap_service ecap_service
 adaptation_service_chain_type	icap_service ecap_service
-adaptation_meta_type	acl
 icap_access_type	icap_class acl
 icap_class_type		icap_service
 icap_service_type
 icap_service_failure_limit
 ecap_service_type
 int
 kb_int64_t
 kb_size_t
 logformat
 YesNoNone
 memcachemode
+note			acl
 obsolete
 onoff
 peer
 peer_access		cache_peer acl
 PortCfg
 QosConfig
 refreshpattern
 removalpolicy
 size_t
 IpAddress_list
 string
 string
 time_msec
 time_t
 tristate
 uri_whitespace
 u_short
 wccp2_method
 wccp2_amethod
 wccp2_service

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2012-10-20 08:01:32 +0000
+++ src/cf.data.pre	2012-10-22 09:32:01 +0000
@@ -3256,40 +3256,44 @@
 		[	output in squid text log format as used by log_mime_hdrs
 		#	output in URL quoted format
 		'	output as-is
 
 		-	left aligned
 
 		width	minimum and/or maximum field width:
 			    [width_min][.width_max]
 			When minimum starts with 0, the field is zero-padded.
 			String values exceeding maximum width are truncated.
 
 		{arg}	argument such as header name etc
 
 	Format codes:
 
 		%	a literal % character
 		sn	Unique sequence number per log line entry
 		err_code    The ID of an error response served by Squid or
 				a similar internal error identifier.
 		err_detail  Additional err_code-dependent error information.
+		note	The meta header specified by the argument. Also
+			logs the adaptation meta headers set by the
+			adaptation_meta configuration parameter.
+			If no argument given all meta headers logged.
 
 	Connection related format codes:
 
 		>a	Client source IP address
 		>A	Client FQDN
 		>p	Client source port
 		>eui	Client source EUI (MAC address, EUI-48 or EUI-64 identifier)
 		>la	Local IP address the client connected to
 		>lp	Local port number the client connected to
 
 		la	Local listening IP address the client connection was connected to.
 		lp	Local listening port number the client connection was connected to.
 
 		<a	Server IP address of the last server or peer connection
 		<A	Server FQDN or peer name
 		<p	Server port number of the last server or peer connection
 		<la	Local IP address of the last server or peer connection
 		<lp     Local port number of the last server or peer connection
 
 	Time related format codes:
@@ -4904,40 +4908,56 @@
 	Field-value is either a token or a quoted string. If quoted
 	string format is used, then the surrounding quotes are removed
 	while escape sequences and %macros are processed.
 
 	In theory, all of the logformat codes can be used as %macros.
 	However, unlike logging (which happens at the very end of
 	transaction lifetime), the transaction may not yet have enough
 	information to expand a macro when the new header value is needed.
 	And some information may already be available to Squid but not yet
 	committed where the macro expansion code can access it (report
 	such instances!). The macro will be expanded into a single dash
 	('-') in such cases. Not all macros have been tested.
 
 	One or more Squid ACLs may be specified to restrict header
 	injection to matching requests. As always in squid.conf, all
 	ACLs in an option ACL list must be satisfied for the insertion
 	to happen. The request_header_add option supports fast ACLs
 	only.
 DOC_END
 
+NAME: note
+TYPE: note
+LOC: Config.notes
+DEFAULT: none
+DOC_START
+	This option used to log custom information about the master
+	transaction. For example, an admin may configure Squid to log
+	which "user group" the transaction belongs to, where "user group"
+	will be determined based on a set of ACLs and not [just]
+	authentication information.
+	Values of key/value pairs can be logged using %{key}note macros:
+
+	    note key value acl ...
+	    logformat myFormat ... %{key}note ...
+DOC_END
+
 NAME: relaxed_header_parser
 COMMENT: on|off|warn
 TYPE: tristate
 LOC: Config.onoff.relaxed_header_parser
 DEFAULT: on
 DOC_START
 	In the default "on" setting Squid accepts certain forms
 	of non-compliant HTTP messages where it is unambiguous
 	what the sending application intended even if the message
 	is not correctly formatted. The messages is then normalized
 	to the correct form when forwarded by Squid.
 
 	If set to "warn" then a warning will be emitted in cache.log
 	each time such HTTP error is encountered.
 
 	If set to "off" then such HTTP errors will cause the request
 	or response to be rejected.
 DOC_END
 
 COMMENT_START
@@ -7382,72 +7402,78 @@
 	An ICAP REQMOD or RESPMOD transaction may set an entry in the 
 	shared table by returning an ICAP header field with a name 
 	specified in adaptation_masterx_shared_names.
 
 	An eCAP REQMOD or RESPMOD transaction may set an entry in the
 	shared table by implementing the libecap::visitEachOption() API
 	to provide an option with a name specified in
 	adaptation_masterx_shared_names.
 
 	Squid will store and forward the set entry to subsequent adaptation
 	transactions within the same master transaction scope.
 
 	Only one shared entry name is supported at this time.
 
 Example:
 # share authentication information among ICAP services
 adaptation_masterx_shared_names X-Subscriber-ID
 DOC_END
 
 NAME: adaptation_meta
-TYPE: adaptation_meta_type
+TYPE: note
 IFDEF: USE_ADAPTATION
 LOC: Adaptation::Config::metaHeaders
 DEFAULT: none
 DOC_START
 	This option allows Squid administrator to add custom ICAP request
 	headers or eCAP options to Squid ICAP requests or eCAP transactions.
 	Use it to pass custom authentication tokens and other
 	transaction-state related meta information to an ICAP/eCAP service.
 	
 	The addition of a meta header is ACL-driven:
 		adaptation_meta name value [!]aclname ...
 	
 	Processing for a given header name stops after the first ACL list match.
 	Thus, it is impossible to add two headers with the same name. If no ACL
 	lists match for a given header name, no such header is added. For 
 	example:
 	
 		# do not debug transactions except for those that need debugging
 		adaptation_meta X-Debug 1 needs_debugging
 	
 		# log all transactions except for those that must remain secret
 		adaptation_meta X-Log 1 !keep_secret
 	
 		# mark transactions from users in the "G 1" group
 		adaptation_meta X-Authenticated-Groups "G 1" authed_as_G1
 	
 	The "value" parameter may be a regular squid.conf token or a "double
 	quoted string". Within the quoted string, use backslash (\) to escape
 	any character, which is currently only useful for escaping backslashes
 	and double quotes. For example,
 	    "this string has one backslash (\\) and two \"quotes\""
+
+	Used adaptation_meta header values may be logged via %note
+	logformat code. If multiple adaptation_meta headers with the same name
+	are used during master transaction lifetime, the header values are
+	logged in the order they were used and duplicate values are ignored
+	(only the first repeated value will be logged).
 DOC_END
 
 NAME: icap_retry
 TYPE: acl_access
 IFDEF: ICAP_CLIENT
 LOC: Adaptation::Icap::TheConfig.repeat
 DEFAULT_IF_NONE: deny all
 DOC_START
 	This ACL determines which retriable ICAP transactions are
 	retried. Transactions that received a complete ICAP response
 	and did not have to consume or produce HTTP bodies to receive
 	that response are usually retriable.
 
 	icap_retry allow|deny [!]aclname ...
 
 	Squid automatically retries some ICAP I/O timeouts and errors
 	due to persistent connection race conditions.
 
 	See also: icap_retry_limit
 DOC_END

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2012-10-05 07:26:35 +0000
+++ src/client_side.cc	2012-10-21 16:53:58 +0000
@@ -581,40 +581,41 @@
         //This is the request after adaptation or redirection
         aLogEntry->headers.adapted_request = xstrdup(mb.buf);
 
         // the virgin request is saved to aLogEntry->request
         if (aLogEntry->request) {
             packerClean(&p);
             mb.reset();
             packerToMemInit(&p, &mb);
             aLogEntry->request->header.packInto(&p);
             aLogEntry->headers.request = xstrdup(mb.buf);
         }
 
 #if USE_ADAPTATION
         const Adaptation::History::Pointer ah = request->adaptLogHistory();
         if (ah != NULL) {
             packerClean(&p);
             mb.reset();
             packerToMemInit(&p, &mb);
             ah->lastMeta.packInto(&p);
             aLogEntry->adapt.last_meta = xstrdup(mb.buf);
+            aLogEntry->notes.append(&ah->metaHeaders);
         }
 #endif
 
         packerClean(&p);
         mb.clean();
     }
 
 #if ICAP_CLIENT
     const Adaptation::Icap::History::Pointer ih = request->icapHistory();
     if (ih != NULL)
         aLogEntry->icap.processingTime = ih->processingTime();
 #endif
 
     aLogEntry->http.method = request->method;
     aLogEntry->http.version = request->http_ver;
     aLogEntry->hier = request->hier;
     if (request->content_length > 0) // negative when no body or unknown length
         aLogEntry->cache.requestSize += request->content_length;
     aLogEntry->cache.extuser = request->extacl_user.termedBuf();
 
@@ -676,40 +677,49 @@
     al->cache.code = logType;
 
     al->cache.msec = tvSubMsec(start_time, current_time);
 
     if (request)
         prepareLogWithRequestDetails(request, al);
 
     if (getConn() != NULL && getConn()->clientConnection != NULL && getConn()->clientConnection->rfc931[0])
         al->cache.rfc931 = getConn()->clientConnection->rfc931;
 
 #if USE_SSL && 0
 
     /* This is broken. Fails if the connection has been closed. Needs
      * to snarf the ssl details some place earlier..
      */
     if (getConn() != NULL)
         al->cache.ssluser = sslGetUserEmail(fd_table[getConn()->fd].ssl);
 
 #endif
 
+    /*Add meta headers*/
+    typedef Notes::iterator ACAMLI;
+    for (ACAMLI i = Config.notes.begin(); i != Config.notes.end(); ++i) {
+        if (const char *value = (*i)->match(request, al->reply)) {
+            al->notes.addEntry(new HttpHeaderEntry(HDR_OTHER, (*i)->key.termedBuf(), value));
+            debugs(33, 3, HERE << (*i)->key.termedBuf() << " " << value);
+        }
+    }
+
     ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.log, this);
 
     if (al->reply)
         checklist->reply = HTTPMSGLOCK(al->reply);
 
     if (!Config.accessList.log || checklist->fastCheck() == ACCESS_ALLOWED) {
         if (request)
             al->adapted_request = HTTPMSGLOCK(request);
         accessLogLog(al, checklist);
         updateCounters();
 
         if (getConn() != NULL && getConn()->clientConnection != NULL)
             clientdbUpdate(getConn()->clientConnection->remote, logType, AnyP::PROTO_HTTP, out.size);
     }
 
     delete checklist;
 }
 
 void
 ClientHttpRequest::freeResources()

=== modified file 'src/format/ByteCode.h'
--- src/format/ByteCode.h	2012-07-18 16:21:47 +0000
+++ src/format/ByteCode.h	2012-10-22 09:36:16 +0000
@@ -179,37 +179,38 @@
     LFT_ICAP_REQ_HEADER,
     LFT_ICAP_REQ_HEADER_ELEM,
     LFT_ICAP_REQ_ALL_HEADERS,
 
     LFT_ICAP_REP_HEADER,
     LFT_ICAP_REP_HEADER_ELEM,
     LFT_ICAP_REP_ALL_HEADERS,
 
     LFT_ICAP_TR_RESPONSE_TIME,
     LFT_ICAP_IO_TIME,
     LFT_ICAP_OUTCOME,
     LFT_ICAP_STATUS_CODE,
 #endif
 
 #if USE_SSL
     LFT_SSL_BUMP_MODE,
     LFT_SSL_USER_CERT_SUBJECT,
     LFT_SSL_USER_CERT_ISSUER,
 #endif
 
+    LFT_NOTE,
     LFT_PERCENT			/* special string cases for escaped chars */
 } ByteCode_t;
 
 /// Quoting style for a format output.
 enum Quoting {
     LOG_QUOTE_NONE = 0,
     LOG_QUOTE_QUOTES,
     LOG_QUOTE_MIMEBLOB,
     LOG_QUOTE_URL,
     LOG_QUOTE_RAW
 };
 
 extern const char *log_tags[];
 
 } // namespace Format
 
 #endif /* _SQUID_FMT_BYTECODE_H */

=== modified file 'src/format/Format.cc'
--- src/format/Format.cc	2012-09-19 17:16:56 +0000
+++ src/format/Format.cc	2012-10-22 09:36:03 +0000
@@ -1023,40 +1023,57 @@
         }
 
         case LFT_SSL_USER_CERT_SUBJECT:
             if (X509 *cert = al->cache.sslClientCert.get()) {
                 if (X509_NAME *subject = X509_get_subject_name(cert)) {
                     X509_NAME_oneline(subject, tmp, sizeof(tmp));
                     out = tmp;
                 }
             }
             break;
 
         case LFT_SSL_USER_CERT_ISSUER:
             if (X509 *cert = al->cache.sslClientCert.get()) {
                 if (X509_NAME *issuer = X509_get_issuer_name(cert)) {
                     X509_NAME_oneline(issuer, tmp, sizeof(tmp));
                     out = tmp;
                 }
             }
             break;
 #endif
+        case LFT_NOTE:
+            if (fmt->data.string) {
+                sb = al->notes.getByName(fmt->data.string);                
+                out = sb.termedBuf();
+                quote = 1;
+            } else {
+                HttpHeaderPos pos = HttpHeaderInitPos;
+                while (const HttpHeaderEntry *e = al->notes.getEntry(&pos)) {
+                    sb.append(e->name);
+                    sb.append(": ");
+                    sb.append(e->value);
+                    sb.append("\r\n");
+                }
+                out = sb.termedBuf();
+                quote = 1;
+            }
+            break;
 
         case LFT_PERCENT:
             out = "%";
 
             break;
         }
 
         if (dooff) {
             snprintf(tmp, sizeof(tmp), "%0*" PRId64, fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outoff);
             out = tmp;
 
         } else if (doint) {
             snprintf(tmp, sizeof(tmp), "%0*ld", fmt->zero && fmt->widthMin >= 0 ? fmt->widthMin : 0, outint);
             out = tmp;
         }
 
         if (out && *out) {
             if (quote || fmt->quote != LOG_QUOTE_NONE) {
                 char *newout = NULL;
                 int newfree = 0;

=== modified file 'src/format/Token.cc'
--- src/format/Token.cc	2012-10-07 05:00:02 +0000
+++ src/format/Token.cc	2012-10-22 09:36:03 +0000
@@ -133,40 +133,41 @@
     {"<sH", LFT_REPLY_HIGHOFFSET},
     {"<sS", LFT_REPLY_OBJECTSIZE},
     /*{ "<sl", LFT_REPLY_SIZE_LINE }, * /   / * the reply line (protocol, code, text) */
     {"<sh", LFT_REPLY_SIZE_HEADERS },
     /*{ "<sb", LFT_REPLY_SIZE_BODY }, */
     /*{ "<sB", LFT_REPLY_SIZE_BODY_NO_TE }, */
 
     {"et", LFT_TAG},
     {"st", LFT_IO_SIZE_TOTAL},
     {"ea", LFT_EXT_LOG},
     {"sn", LFT_SEQUENCE_NUMBER},
 
     {NULL, LFT_NONE}		/* this must be last */
 };
 
 /// Miscellaneous >2 byte tokens
 static TokenTableEntry TokenTableMisc[] = {
     {">eui", LFT_CLIENT_EUI},
     {"err_code", LFT_SQUID_ERROR },
     {"err_detail", LFT_SQUID_ERROR_DETAIL },
+    {"note", LFT_NOTE },
     {NULL, LFT_NONE}		/* this must be last */
 };
 
 #if USE_ADAPTATION
 static TokenTableEntry TokenTableAdapt[] = {
     {"all_trs", LFT_ADAPTATION_ALL_XACT_TIMES},
     {"sum_trs", LFT_ADAPTATION_SUM_XACT_TIMES},
     {"<last_h", LFT_ADAPTATION_LAST_HEADER},
     {NULL, LFT_NONE}           /* this must be last */
 };
 #endif
 
 #if ICAP_CLIENT
 /// ICAP (icap::) tokens
 static TokenTableEntry TokenTableIcap[] = {
     {"tt", LFT_ICAP_TOTAL_TIME},
     {"<last_h", LFT_ADAPTATION_LAST_HEADER}, // deprecated
 
     {"<A",  LFT_ICAP_ADDR},
     {"<service_name",  LFT_ICAP_SERV_NAME},

=== modified file 'src/log/access_log.cc'
--- src/log/access_log.cc	2012-09-06 13:29:14 +0000
+++ src/log/access_log.cc	2012-10-22 09:36:03 +0000
@@ -16,41 +16,45 @@
  *
  *  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.
  *
  */
 
 #include "squid.h"
 #include "AccessLogEntry.h"
 #include "acl/Checklist.h"
+#if USE_ADAPTATION
+#include "adaptation/Config.h"
+#endif
 #include "CachePeer.h"
+#include "errorpage.h"
 #include "err_detail_type.h"
 #include "errorpage.h"
 #include "errorpage.h"
 #include "format/Token.h"
 #include "globals.h"
 #include "hier_code.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "log/access_log.h"
 #include "log/Config.h"
 #include "log/CustomLog.h"
 #include "log/File.h"
 #include "log/Formats.h"
 #include "MemBuf.h"
 #include "mgr/Registration.h"
 #include "rfc1738.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
 #include "Store.h"
 
@@ -309,41 +313,42 @@
     Log::TheConfig.hasAdaptToken = false;
 #endif
 #if ICAP_CLIENT
     Log::TheConfig.hasIcapToken = false;
 #endif
 
     for (log = Config.Log.accesslogs; log; log = log->next) {
         if (log->type == Log::Format::CLF_NONE)
             continue;
 
         log->logfile = logfileOpen(log->filename, MAX_URL << 2, 1);
 
         LogfileStatus = LOG_ENABLE;
 
 #if USE_ADAPTATION
         for (Format::Token * curr_token = (log->logFormat?log->logFormat->format:NULL); curr_token; curr_token = curr_token->next) {
             if (curr_token->type == Format::LFT_ADAPTATION_SUM_XACT_TIMES ||
                     curr_token->type == Format::LFT_ADAPTATION_ALL_XACT_TIMES ||
                     curr_token->type == Format::LFT_ADAPTATION_LAST_HEADER ||
                     curr_token->type == Format::LFT_ADAPTATION_LAST_HEADER_ELEM ||
-                    curr_token->type == Format::LFT_ADAPTATION_LAST_ALL_HEADERS) {
+                    curr_token->type == Format::LFT_ADAPTATION_LAST_ALL_HEADERS||
+                    (curr_token->type == Format::LFT_NOTE && !Adaptation::Config::metaHeaders.empty())) {
                 Log::TheConfig.hasAdaptToken = true;
             }
 #if ICAP_CLIENT
             if (curr_token->type == Format::LFT_ICAP_TOTAL_TIME) {
                 Log::TheConfig.hasIcapToken = true;
             }
 #endif
         }
 #endif
     }
 
 #if HEADERS_LOG
 
     headerslog = logfileOpen("/usr/local/squid/logs/headers.log", 512);
 
     assert(NULL != headerslog);
 
 #endif
 #if MULTICAST_MISS_STREAM
 

=== modified file 'src/tests/stub_cache_cf.cc'
--- src/tests/stub_cache_cf.cc	2012-09-17 15:12:01 +0000
+++ src/tests/stub_cache_cf.cc	2012-10-21 14:35:35 +0000
@@ -31,22 +31,24 @@
  */
 
 #include "squid.h"
 #include "acl/Acl.h"
 #include "ConfigParser.h"
 #include "wordlist.h"
 #include "YesNoNone.h"
 
 #define STUB_API "cache_cf.cc"
 #include "tests/STUB.h"
 
 void self_destruct(void) STUB
 void parse_int(int *var) STUB
 void parse_onoff(int *var) STUB
 void parse_eol(char *volatile *var) STUB
 void parse_wordlist(wordlist ** list) STUB
 void requirePathnameExists(const char *name, const char *path) STUB_NOP
 void parse_time_t(time_t * var) STUB
 char * strtokFile(void) STUB_RETVAL(NULL)
 void ConfigParser::ParseUShort(unsigned short *var) STUB
+void ConfigParser::ParseString(String*) STUB
 void dump_acl_access(StoreEntry * entry, const char *name, acl_access * head) STUB
+void dump_acl_list(StoreEntry*, ACLList*) STUB
 YesNoNone::operator void*() const { STUB_NOP; return NULL; }

=== modified file 'src/tests/testHttpReply.cc'
--- src/tests/testHttpReply.cc	2012-09-04 09:10:20 +0000
+++ src/tests/testHttpReply.cc	2012-10-21 14:35:35 +0000
@@ -6,46 +6,40 @@
 #include "HttpHeader.h"
 #include "HttpReply.h"
 #include "Mem.h"
 #include "mime_header.h"
 #include "SquidConfig.h"
 
 CPPUNIT_TEST_SUITE_REGISTRATION( testHttpReply );
 
 class SquidConfig Config;
 
 /* stub functions to link successfully */
 
 #include "MemObject.h"
 int64_t
 MemObject::endOffset() const
 {
     return 0;
 }
 
 #include "ConfigParser.h"
-void
-ConfigParser::destruct()
-{
-// CALLED as shutdown no-op
-//    fatal("ConfigParser::destruct. Not implemented.");
-}
 
 void
 eventAdd(const char *name, EVH * func, void *arg, double when, int, bool cbdata)
 {
 // CALLED as setUp no-op
 //    fatal("eventAdd. Not implemented.");
 }
 
 /* end */
 
 void
 testHttpReply::setUp()
 {
     Mem::Init();
     httpHeaderInitModule();
 }
 
 void
 testHttpReply::testSanityCheckFirstLine()
 {


