# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: rousskov@measurement-factory.com-20130513233223-\
#   6d58cyjzs2k66k0g
# target_branch: http://www.squid-cache.org/bzr/squid3/trunk
# testament_sha1: b16b61dd04ec7cfbda49393b1c793ecc3d4e5aa1
# timestamp: 2013-05-13 17:34:11 -0600
# base_revision_id: squid3@treenet.co.nz-20130513163633-\
#   rsh0w1qy0xqojqt9
# 
# Begin patch
=== modified file 'src/AclRegs.cc'
--- src/AclRegs.cc	2013-01-27 17:35:07 +0000
+++ src/AclRegs.cc	2013-05-13 22:48:23 +0000
@@ -5,7 +5,8 @@
     does not get linked in, because nobody is using these classes by name.
 */
 
-#include "acl/Acl.h"
+#include "acl/AllOf.h"
+#include "acl/AnyOf.h"
 #if USE_SQUID_EUI
 #include "acl/Arp.h"
 #include "acl/Eui64.h"
@@ -181,3 +182,9 @@
 
 ACL::Prototype ACLTag::RegistryProtoype(&ACLTag::RegistryEntry_, "tag");
 ACLStrategised<const char *> ACLTag::RegistryEntry_(new ACLStringData, ACLTagStrategy::Instance(), "tag");
+
+ACL::Prototype Acl::AnyOf::RegistryProtoype(&Acl::AnyOf::RegistryEntry_, "any-of");
+Acl::AnyOf Acl::AnyOf::RegistryEntry_;
+
+ACL::Prototype Acl::AllOf::RegistryProtoype(&Acl::AllOf::RegistryEntry_, "all-of");
+Acl::AllOf Acl::AllOf::RegistryEntry_;

=== modified file 'src/CachePeer.h'
--- src/CachePeer.h	2013-01-25 16:01:16 +0000
+++ src/CachePeer.h	2013-05-13 22:48:23 +0000
@@ -29,6 +29,7 @@
  *
  */
 
+#include "acl/forward.h"
 #include "enums.h"
 #include "icp_opcode.h"
 #include "ip/Address.h"
@@ -40,7 +41,6 @@
 #include <openssl/ssl.h>
 #endif
 
-class acl_access;
 class CachePeerDomainList;
 class NeighborTypeDomainList;
 class PeerDigest;

=== modified file 'src/ClientDelayConfig.cc'
--- src/ClientDelayConfig.cc	2012-08-14 11:53:07 +0000
+++ src/ClientDelayConfig.cc	2013-05-13 22:48:23 +0000
@@ -83,7 +83,7 @@
     }
 
     --pool;
-    aclParseAccessLine(parser, &pools[pool].access);
+    aclParseAccessLine("client_delay_access", parser, &pools[pool].access);
 }
 
 void ClientDelayConfig::clean()

=== modified file 'src/ClientDelayConfig.h'
--- src/ClientDelayConfig.h	2013-05-04 11:50:26 +0000
+++ src/ClientDelayConfig.h	2013-05-13 23:32:23 +0000
@@ -1,10 +1,10 @@
 #ifndef SQUID_CLIENTDELAYCONFIG_H
 #define SQUID_CLIENTDELAYCONFIG_H
 
+#include "acl/forward.h"
 #include "base/Vector.h"
 
 class StoreEntry;
-class acl_access;
 class ConfigParser;
 
 /// \ingroup DelayPoolsAPI

=== modified file 'src/DelayConfig.cc'
--- src/DelayConfig.cc	2012-09-01 14:38:36 +0000
+++ src/DelayConfig.cc	2013-05-13 22:48:23 +0000
@@ -113,7 +113,7 @@
     }
 
     --pool;
-    aclParseAccessLine(parser, &DelayPools::delay_data[pool].access);
+    aclParseAccessLine("delay_access", parser, &DelayPools::delay_data[pool].access);
 }
 
 void

=== modified file 'src/DelayPool.h'
--- src/DelayPool.h	2012-09-01 14:38:36 +0000
+++ src/DelayPool.h	2013-05-13 22:48:23 +0000
@@ -39,13 +39,12 @@
 
 #if USE_DELAY_POOLS
 #include "CompositePoolNode.h"
+#include "acl/forward.h"
 
 class StoreEntry;
 
 class CommonPool;
 
-class acl_access;
-
 /// \ingroup DelayPoolsAPI
 class DelayPool
 {

=== modified file 'src/HttpHeaderTools.h'
--- src/HttpHeaderTools.h	2013-02-17 09:31:18 +0000
+++ src/HttpHeaderTools.h	2013-05-13 22:48:23 +0000
@@ -1,6 +1,7 @@
 #ifndef SQUID_HTTPHEADERTOOLS_H
 #define SQUID_HTTPHEADERTOOLS_H
 
+#include "acl/forward.h"
 #include "format/Format.h"
 #include "HttpHeader.h"
 #include "typedefs.h"
@@ -15,8 +16,6 @@
 #include <string>
 #endif
 
-class acl_access;
-class ACLList;
 class HeaderWithAcl;
 class HttpHeader;
 class HttpHeaderFieldInfo;

=== modified file 'src/Notes.cc'
--- src/Notes.cc	2013-04-30 00:13:26 +0000
+++ src/Notes.cc	2013-05-13 23:32:23 +0000
@@ -96,7 +96,11 @@
     ConfigParser::ParseQuotedString(&value);
     Note::Pointer note = add(key);
     Note::Value::Pointer noteValue = note->addValue(value);
-    aclParseAclList(parser, &noteValue->aclList);
+
+    String label(key);
+    label.append('=');
+    label.append(value);
+    aclParseAclList(parser, &noteValue->aclList, label.termedBuf());
 
     if (blacklisted) {
         for (int i = 0; blacklisted[i] != NULL; ++i) {

=== modified file 'src/Notes.h'
--- src/Notes.h	2013-05-04 11:50:26 +0000
+++ src/Notes.h	2013-05-13 23:32:23 +0000
@@ -1,6 +1,7 @@
 #ifndef SQUID_NOTES_H
 #define SQUID_NOTES_H
 
+#include "acl/forward.h"
 #include "base/Vector.h"
 #include "base/RefCount.h"
 #include "CbDataList.h"
@@ -14,7 +15,6 @@
 
 class HttpRequest;
 class HttpReply;
-class ACLList;
 
 /**
  * Used to store a note configuration. The notes are custom key:value

=== modified file 'src/SquidConfig.h'
--- src/SquidConfig.h	2013-05-13 03:57:03 +0000
+++ src/SquidConfig.h	2013-05-13 23:32:23 +0000
@@ -29,7 +29,7 @@
  *
  */
 
-#include "acl/AclAddress.h"
+#include "acl/forward.h"
 #include "base/RefCount.h"
 #include "ClientDelayConfig.h"
 #include "DelayConfig.h"
@@ -46,9 +46,6 @@
 class sslproxy_cert_adapt;
 #endif
 
-class acl_access;
-class AclSizeLimit;
-class AclDenyInfoList;
 namespace Mgr
 {
 class ActionPasswordList;

=== modified file 'src/acl/Acl.cc'
--- src/acl/Acl.cc	2013-05-05 08:38:06 +0000
+++ src/acl/Acl.cc	2013-05-13 23:32:23 +0000
@@ -1,6 +1,5 @@
 /*
  * DEBUG: section 28    Access Control
- * AUTHOR: Duane Wessels
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
  * ----------------------------------------------------------
@@ -38,6 +37,7 @@
 #include "Debug.h"
 #include "dlink.h"
 #include "globals.h"
+#include "profiler/Profiler.h"
 #include "SquidConfig.h"
 
 const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END};
@@ -138,7 +138,8 @@
 
 ACL::ACL() :
         cfgline(NULL),
-        next(NULL)
+        next(NULL),
+        registered(false)
 {
     *name = 0;
 }
@@ -148,6 +149,46 @@
     return true;
 }
 
+bool
+ACL::matches(ACLChecklist *checklist) const
+{
+    PROF_start(ACL_matches);
+    debugs(28, 5, "checking " << name);
+
+    // XXX: AclMatchedName does not contain a matched ACL name when the acl
+    // does not match. It contains the last (usually leaf) ACL name checked
+    // (or is NULL if no ACLs were checked).
+    AclMatchedName = name;
+
+    int result = 0;
+    if (!checklist->hasRequest() && requiresRequest()) {
+        debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
+               "context without an HTTP request. Assuming mismatch.");
+    } else if (!checklist->hasReply() && requiresReply()) {
+        debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
+               "context without an HTTP response. Assuming mismatch.");
+    } else {
+        // have to cast because old match() API is missing const
+        result = const_cast<ACL*>(this)->match(checklist);
+    }
+
+    const char *extra = checklist->asyncInProgress() ? " async" : "";
+    debugs(28, 3, "checked: " << name << " = " << result << extra);
+    PROF_stop(ACL_matches);
+    return result == 1; // true for match; false for everything else
+}
+
+void
+ACL::context(const char *aName, const char *aCfgLine)
+{
+    name[0] = '\0';
+    if (aName)
+        xstrncpy(name, aName, ACL_NAME_SZ-1);
+    safe_free(cfgline);
+    if (aCfgLine)
+        cfgline = xstrdup(aCfgLine);
+}
+
 void
 ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
 {
@@ -216,8 +257,7 @@
     if ((A = FindByName(aclname)) == NULL) {
         debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
         A = ACL::Factory(theType);
-        xstrncpy(A->name, aclname, ACL_NAME_SZ);
-        A->cfgline = xstrdup(config_input_line);
+        A->context(aclname, config_input_line);
         new_acl = 1;
     } else {
         if (strcmp (A->typeString(),theType) ) {
@@ -259,6 +299,9 @@
     }
 
     /* append */
+    assert(head && *head == Config.aclList);
+    A->registered = true;
+
     while (*head)
         head = &(*head)->next;
 
@@ -271,18 +314,6 @@
     return false;
 }
 
-ACLList::ACLList() : op (1), _acl (NULL), next (NULL)
-{}
-
-void
-ACLList::negated(bool isNegated)
-{
-    if (isNegated)
-        op = 0;
-    else
-        op = 1;
-}
-
 /* ACL result caching routines */
 
 int
@@ -359,49 +390,6 @@
     return false;
 }
 
-int
-ACL::checklistMatches(ACLChecklist *checklist)
-{
-    int rv;
-
-    if (!checklist->hasRequest() && requiresRequest()) {
-        debugs(28, DBG_IMPORTANT, "ACL::checklistMatches WARNING: '" << name << "' ACL is used but there is no HTTP request -- not matching.");
-        return 0;
-    }
-
-    if (!checklist->hasReply() && requiresReply()) {
-        debugs(28, DBG_IMPORTANT, "ACL::checklistMatches WARNING: '" << name << "' ACL is used but there is no HTTP reply -- not matching.");
-        return 0;
-    }
-
-    debugs(28, 3, "ACL::checklistMatches: checking '" << name << "'");
-    rv= match(checklist);
-    debugs(28, 3, "ACL::ChecklistMatches: result for '" << name << "' is " << rv);
-    return rv;
-}
-
-bool
-ACLList::matches (ACLChecklist *checklist) const
-{
-    assert (_acl);
-    // XXX: AclMatchedName does not contain a matched ACL name when the acl
-    // does not match (or contains stale name if no ACLs are checked). In
-    // either case, we get misleading debugging and possibly incorrect error
-    // messages. Unfortunately, deny_info's "when none http_access
-    // lines match" exception essentially requires this mess.
-    // TODO: Rework by using an acl-free deny_info for the no-match cases?
-    AclMatchedName = _acl->name;
-    debugs(28, 3, "ACLList::matches: checking " << (op ? null_string : "!") << _acl->name);
-
-    if (_acl->checklistMatches(checklist) != op) {
-        debugs(28, 4, "ACLList::matches: result is false");
-        return false;
-    }
-
-    debugs(28, 4, "ACLList::matches: result is true");
-    return true;
-}
-
 /*********************/
 /* Destroy functions */
 /*********************/
@@ -410,26 +398,9 @@
 {
     debugs(28, 3, "ACL::~ACL: '" << cfgline << "'");
     safe_free(cfgline);
-}
-
-/* to be split into separate files in the future */
-
-CBDATA_CLASS_INIT(acl_access);
-
-void *
-acl_access::operator new (size_t)
-{
-    CBDATA_INIT_TYPE(acl_access);
-    acl_access *result = cbdataAlloc(acl_access);
-    return result;
-}
-
-void
-acl_access::operator delete (void *address)
-{
-    acl_access *t = static_cast<acl_access *>(address);
-    cbdataFree(t);
-}
+    AclMatchedName = NULL; // in case it was pointing to our name
+}
+
 
 ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {}
 

=== modified file 'src/acl/Acl.h'
--- src/acl/Acl.h	2013-05-05 08:38:06 +0000
+++ src/acl/Acl.h	2013-05-13 23:32:23 +0000
@@ -33,6 +33,7 @@
 #ifndef SQUID_ACL_H
 #define SQUID_ACL_H
 
+#include "acl/forward.h"
 #include "base/Vector.h"
 #include "cbdata.h"
 #include "defines.h"
@@ -47,8 +48,6 @@
 #endif
 
 class ConfigParser;
-class ACLChecklist;
-class ACLList;
 
 typedef char ACLFlag;
 // ACLData Flags
@@ -90,6 +89,10 @@
     static const ACLFlag NoFlags[1]; ///< An empty flags list
 };
 
+
+/// A configurable condition. A node in the ACL expression tree.
+/// Can evaluate itself in FilledChecklist context.
+/// Does not change during evaluation. 
 /// \ingroup ACLAPI
 class ACL
 {
@@ -106,17 +109,25 @@
     ACL();
     explicit ACL(const ACLFlag flgs[]) : cfgline(NULL), next(NULL), flags(flgs) { memset(name, '\0', sizeof(name)); }
     virtual ~ACL();
+
+    /// sets user-specified ACL name and squid.conf context
+    void context(const char *name, const char *configuration);
+
+    /// Orchestrates matching checklist against the ACL using match(),
+    /// after checking preconditions and while providing debugging.
+    /// Returns true if and only if there was a successful match.
+    /// Updates the checklist state on match, async, and failure.
+    bool matches(ACLChecklist *checklist) const;
+
     virtual ACL *clone()const = 0;
+
+    /// parses node represenation in squid.conf; dies on failures
     virtual void parse() = 0;
     virtual char const *typeString() const = 0;
     virtual bool isProxyAuth() const;
-    virtual bool requiresRequest() const;
-    virtual bool requiresReply() const;
-    virtual int match(ACLChecklist * checklist) = 0;
     virtual wordlist *dump() const = 0;
     virtual bool empty () const = 0;
     virtual bool valid () const;
-    int checklistMatches(ACLChecklist *);
 
     int cacheMatchAcl(dlink_list * cache, ACLChecklist *);
     virtual int matchForCache(ACLChecklist *checklist);
@@ -127,6 +138,7 @@
     char *cfgline;
     ACL *next;
     ACLFlags flags; ///< The list of given ACL flags
+    bool registered; ///< added to Config.aclList and can be reused via by FindByName()
 
 public:
 
@@ -151,6 +163,15 @@
         typedef Vector<Prototype const*>::const_iterator const_iterator;
         void registerMe();
     };
+
+private:
+    /// Matches the actual data in checklist against this ACL.
+    virtual int match(ACLChecklist *checklist) = 0; // XXX: missing const
+
+    /// whether our (i.e. shallow) match() requires checklist to have a request
+    virtual bool requiresRequest() const;
+    /// whether our (i.e. shallow) match() requires checklist to have a reply
+    virtual bool requiresReply() const;
 };
 
 /// \ingroup ACLAPI
@@ -211,39 +232,6 @@
 }
 
 /// \ingroup ACLAPI
-class acl_access
-{
-
-public:
-    void *operator new(size_t);
-    void operator delete(void *);
-    allow_t allow;
-    ACLList *aclList;
-    char *cfgline;
-    acl_access *next;
-
-private:
-    CBDATA_CLASS(acl_access);
-};
-
-/// \ingroup ACLAPI
-class ACLList
-{
-
-public:
-    MEMPROXY_CLASS(ACLList);
-
-    ACLList();
-    void negated(bool isNegated);
-    bool matches (ACLChecklist *)const;
-    int op;
-    ACL *_acl;
-    ACLList *next;
-};
-
-MEMPROXY_CLASS_INLINE(ACLList);
-
-/// \ingroup ACLAPI
 class acl_proxy_auth_match_cache
 {
 

=== modified file 'src/acl/AclNameList.h'
--- src/acl/AclNameList.h	2012-09-21 13:27:44 +0000
+++ src/acl/AclNameList.h	2013-05-13 22:48:23 +0000
@@ -29,7 +29,7 @@
  *
  */
 
-#include "defines.h"
+#include "acl/forward.h"
 
 /// list of name-based ACLs. Currently a POD.
 class AclNameList

=== modified file 'src/acl/AclSizeLimit.h'
--- src/acl/AclSizeLimit.h	2012-10-04 00:23:44 +0000
+++ src/acl/AclSizeLimit.h	2013-05-13 22:48:23 +0000
@@ -29,7 +29,8 @@
  *
  */
 
-class ACLList;
+#include "acl/forward.h"
+
 /// representation of a class of Size-limit ACLs
 // a POD. TODO: convert to new ACL framework
 class AclSizeLimit

=== added file 'src/acl/AllOf.cc'
--- src/acl/AllOf.cc	1970-01-01 00:00:00 +0000
+++ src/acl/AllOf.cc	2013-05-13 22:48:23 +0000
@@ -0,0 +1,83 @@
+#include "squid.h"
+#include "acl/AllOf.h"
+#include "acl/Checklist.h"
+#include "acl/BoolOps.h"
+#include "globals.h"
+#include "MemBuf.h"
+
+
+char const *
+Acl::AllOf::typeString() const
+{
+    return "all-of";
+}
+
+ACL *
+Acl::AllOf::clone() const
+{
+    return new AllOf;
+}
+
+wordlist*
+Acl::AllOf::dump() const
+{
+    return empty() ? NULL : nodes.front()->dump();
+}
+
+int
+Acl::AllOf::doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const
+{
+    assert(start == nodes.begin()); // we only have one node
+
+    // avoid dereferencing invalid start
+    if (empty())
+        return 1; // not 0 because in math empty product equals identity
+
+    if (checklist->matchChild(this, start, *start))
+        return 1; // match
+
+    return checklist->keepMatching() ? 0 : -1;
+}
+
+// called once per "acl name all-of name1 name2 ...." line
+void
+Acl::AllOf::parse()
+{
+    Acl::InnerNode *whole = NULL;
+    ACL *oldNode = empty() ? NULL : nodes.front();
+
+    // optimization: this logic reduces subtree hight (number of tree levels)
+    if (Acl::OrNode *oldWhole = dynamic_cast<Acl::OrNode*>(oldNode)) {
+        // this acl saw multiple lines before; add another one to the old node
+        whole = oldWhole;
+    } else if (oldNode) {
+        // this acl saw a single line before; create a new OR inner node
+
+        MemBuf wholeCtx;
+        wholeCtx.init();
+        wholeCtx.Printf("(%s lines)", name);
+        wholeCtx.terminate();
+
+        Acl::OrNode *newWhole = new Acl::OrNode;
+        newWhole->context(wholeCtx.content(), oldNode->cfgline);
+        newWhole->add(oldNode); // old (i.e. first) line
+        nodes.front() = whole = newWhole;
+    } else {
+        // this is the first line for this acl; just use it as is
+        whole = this;
+    }
+
+    assert(whole);
+    const int lineId = whole->childrenCount() + 1;
+
+    MemBuf lineCtx;
+    lineCtx.init();
+    lineCtx.Printf("(%s line #%d)", name, lineId);
+    lineCtx.terminate();
+    
+    Acl::AndNode *line = new AndNode;
+    line->context(lineCtx.content(), config_input_line);
+    line->lineParse();
+
+    whole->add(line);
+}

=== added file 'src/acl/AllOf.h'
--- src/acl/AllOf.h	1970-01-01 00:00:00 +0000
+++ src/acl/AllOf.h	2013-05-13 22:48:23 +0000
@@ -0,0 +1,33 @@
+#ifndef SQUID_ACL_ALL_OF_H
+#define SQUID_ACL_ALL_OF_H
+
+#include "acl/InnerNode.h"
+
+namespace Acl {
+
+/// Configurable all-of ACL. Each ACL line is a conjuction of ACLs.
+/// Uses AndNode and OrNode to handle squid.conf configuration where multiple
+/// acl all-of lines are always ORed together.
+class AllOf: public Acl::InnerNode
+{
+public:
+    MEMPROXY_CLASS(AllOf);
+
+    /* ACL API */
+    virtual char const *typeString() const;
+    virtual ACL *clone() const;
+    virtual void parse();
+    virtual wordlist *dump() const;
+
+private:
+    /* Acl::InnerNode API */
+    virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const;
+
+    static Prototype RegistryProtoype;
+    static AllOf RegistryEntry_;
+};
+MEMPROXY_CLASS_INLINE(Acl::AllOf);
+
+} // namespace Acl
+
+#endif /* SQUID_ACL_ALL_OF_H */

=== added file 'src/acl/AnyOf.cc'
--- src/acl/AnyOf.cc	1970-01-01 00:00:00 +0000
+++ src/acl/AnyOf.cc	2013-05-13 22:48:23 +0000
@@ -0,0 +1,23 @@
+#include "squid.h"
+#include "acl/AnyOf.h"
+
+char const *
+Acl::AnyOf::typeString() const
+{
+    return "any-of";
+}
+
+ACL *
+Acl::AnyOf::clone() const
+{
+    return new AnyOf;
+}
+
+// called once per "acl name any-of name1 name2 ...." line
+// but since multiple lines are ORed, the line boundary does not matter,
+// so we flatten the tree into one line/level here to minimize overheads
+void
+Acl::AnyOf::parse()
+{
+    lineParse();
+}

=== added file 'src/acl/AnyOf.h'
--- src/acl/AnyOf.h	1970-01-01 00:00:00 +0000
+++ src/acl/AnyOf.h	2013-05-13 22:48:23 +0000
@@ -0,0 +1,27 @@
+#ifndef SQUID_ACL_ANY_OF_H
+#define SQUID_ACL_ANY_OF_H
+
+#include "acl/BoolOps.h"
+
+namespace Acl {
+
+/// Configurable any-of ACL. Each ACL line is a disjuction of ACLs.
+class AnyOf: public Acl::OrNode
+{
+public:
+    MEMPROXY_CLASS(AnyOf);
+
+    /* ACL API */
+    virtual char const *typeString() const;
+    virtual ACL *clone() const;
+    virtual void parse();
+
+private:
+    static Prototype RegistryProtoype;
+    static AnyOf RegistryEntry_;
+};
+MEMPROXY_CLASS_INLINE(Acl::AnyOf);
+
+} // namespace Acl
+
+#endif /* SQUID_ACL_ANY_OF_H */

=== modified file 'src/acl/Asn.cc'
--- src/acl/Asn.cc	2013-03-18 04:55:51 +0000
+++ src/acl/Asn.cc	2013-05-13 22:48:23 +0000
@@ -39,7 +39,7 @@
 #include "acl/DestinationIp.h"
 #include "acl/SourceAsn.h"
 #include "cache_cf.h"
-#include "forward.h"
+#include "src/forward.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "ipcache.h"
@@ -638,14 +638,13 @@
         /* No entry in cache, lookup not attempted */
         /* XXX FIXME: allow accessing the acl name here */
         debugs(28, 3, "asnMatchAcl: Can't yet compare '" << "unknown" /*name*/ << "' ACL for '" << checklist->request->GetHost() << "'");
-        checklist->changeState (DestinationIPLookup::Instance());
-    } else {
+        if (checklist->goAsync(DestinationIPLookup::Instance()))
+            return -1;
+        // else fall through to noaddr match, hiding the lookup failure (XXX)
+    }
         Ip::Address noaddr;
         noaddr.SetNoAddr();
         return data->match(noaddr);
-    }
-
-    return 0;
 }
 
 ACLDestinationASNStrategy *

=== added file 'src/acl/BoolOps.cc'
--- src/acl/BoolOps.cc	1970-01-01 00:00:00 +0000
+++ src/acl/BoolOps.cc	2013-05-13 22:51:02 +0000
@@ -0,0 +1,139 @@
+#include "squid.h"
+#include "acl/BoolOps.h"
+#include "acl/Checklist.h"
+#include "Debug.h"
+#include "wordlist.h"
+
+
+/* Acl::NotNode */
+
+Acl::NotNode::NotNode(ACL *acl)
+{
+    assert(acl);
+    name[0] = '!';
+    strncat(&name[1], acl->name, sizeof(name)-1-1);
+    add(acl);
+}
+
+void
+Acl::NotNode::parse()
+{
+    // Not implemented: by the time an upper level parser discovers
+    // an '!' operator, there is nothing left for us to parse.
+    assert(false);    
+}
+
+int
+Acl::NotNode::doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const
+{
+    assert(start == nodes.begin()); // we only have one node
+
+    if (checklist->matchChild(this, start, *start))
+        return 0; // converting match into mismatch
+
+    if (!checklist->keepMatching())
+        return -1; // suspend on async calls and stop on failures
+
+    return 1; // converting mismatch into match
+}
+
+char const *
+Acl::NotNode::typeString() const
+{
+    return "!";
+}
+
+ACL *
+Acl::NotNode::clone() const
+{
+    // Not implemented: we are not a named ACL type in squid.conf so nobody
+    // should try to create a NotNode instance by ACL type name (which is
+    // what clone() API is for -- it does not really clone anything).
+    assert(false);
+    return NULL;
+}
+
+wordlist*
+Acl::NotNode::dump() const
+{
+    wordlist *text = NULL;
+    wordlistAdd(&text, name);
+    return text;
+}
+
+
+/* Acl::AndNode */
+
+char const *
+Acl::AndNode::typeString() const
+{
+    return "and";
+}
+
+ACL *
+Acl::AndNode::clone() const
+{
+    return new AndNode;
+}
+
+int
+Acl::AndNode::doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const
+{
+    // find the first node that does not match
+    for (Nodes::const_iterator i = start; i != nodes.end(); ++i) {
+        if (!checklist->matchChild(this, i, *i))
+            return checklist->keepMatching() ? 0 : -1;
+    }
+
+    // one and not zero on empty because in math empty product equals identity
+    return 1; // no mismatches found (i.e., all kids matched)
+}
+
+void
+Acl::AndNode::parse()
+{
+    // Not implemented: AndNode cannot be configured directly. See Acl::AllOf.
+    assert(false);    
+}
+
+
+/* Acl::OrNode */
+
+char const *
+Acl::OrNode::typeString() const
+{
+    return "any-of";
+}
+
+ACL *
+Acl::OrNode::clone() const
+{
+    return new OrNode;
+}
+
+int
+Acl::OrNode::doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const
+{
+    lastMatch_ = nodes.end();
+
+    // find the first node that matches, but stop if things go wrong
+    for (Nodes::const_iterator i = start; i != nodes.end(); ++i) {
+        if (checklist->matchChild(this, i, *i)) {
+            lastMatch_ = i;
+            return 1;
+        }
+
+        if (!checklist->keepMatching())
+            return -1; // suspend on async calls and stop on failures
+    }
+
+    // zero and not one on empty because in math empty sum equals zero
+    return 0; // all nodes mismatched
+}
+
+void
+Acl::OrNode::parse()
+{
+    // Not implemented: OrNode cannot be configured directly. See Acl::AnyOf.
+    assert(false);
+}

=== added file 'src/acl/BoolOps.h'
--- src/acl/BoolOps.h	1970-01-01 00:00:00 +0000
+++ src/acl/BoolOps.h	2013-05-13 22:51:02 +0000
@@ -0,0 +1,75 @@
+#ifndef SQUID_ACL_LOGIC_H
+#define SQUID_ACL_LOGIC_H
+
+#include "acl/InnerNode.h"
+
+/* ACLs defined here are used internally to construct an ACL expression tree.
+ * They cannot be specified directly in squid.conf because squid.conf ACLs are
+ * more complex than (and are implemented using) these operator-like classes.*/
+
+namespace Acl {
+
+/// Implements the "not" or "!" operator.
+class NotNode: public InnerNode
+{
+public:
+    MEMPROXY_CLASS(NotNode);
+
+    explicit NotNode(ACL *acl);
+
+private:
+    /* ACL API */
+    virtual char const *typeString() const;
+    virtual ACL *clone() const;
+    virtual void parse();
+    virtual wordlist *dump() const;
+
+    /* Acl::InnerNode API */
+    virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const;
+};
+MEMPROXY_CLASS_INLINE(Acl::NotNode);
+
+
+/// An inner ACL expression tree node representing a boolean conjuction (AND)
+/// operator applied to a list of child tree nodes.
+/// For example, conditions expressed on a single http_access line are ORed.
+class AndNode: public InnerNode
+{
+public:
+    MEMPROXY_CLASS(AndNode);
+
+    /* ACL API */
+    virtual char const *typeString() const;
+    virtual ACL *clone() const;
+    virtual void parse();
+
+private:
+    virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const;
+};
+MEMPROXY_CLASS_INLINE(Acl::AndNode);
+
+/// An inner ACL expression tree node representing a boolean disjuction (OR)
+/// operator applied to a list of child tree nodes.
+/// For example, conditions expressed by multiple http_access lines are ORed.
+class OrNode: public InnerNode
+{
+public:
+    MEMPROXY_CLASS(OrNode);
+
+    /* ACL API */
+    virtual char const *typeString() const;
+    virtual ACL *clone() const;
+    virtual void parse();
+
+protected:
+    mutable Nodes::const_iterator lastMatch_;
+
+private:
+    virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const;
+};
+MEMPROXY_CLASS_INLINE(Acl::OrNode);
+
+
+} // namespace Acl
+
+#endif /* SQUID_ACL_LOGIC_H */

=== modified file 'src/acl/Checklist.cc'
--- src/acl/Checklist.cc	2012-11-15 07:35:32 +0000
+++ src/acl/Checklist.cc	2013-05-13 22:48:23 +0000
@@ -1,66 +1,24 @@
 /*
  * DEBUG: section 28    Access Control
- * AUTHOR: Duane Wessels
- *
- * 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.
- *
- * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
  */
 
 #include "squid.h"
 #include "acl/Checklist.h"
+#include "acl/Tree.h"
 #include "Debug.h"
 #include "profiler/Profiler.h"
 
-void
-ACLChecklist::matchNonBlocking()
+/// common parts of nonBlockingCheck() and resumeNonBlockingCheck()
+bool
+ACLChecklist::prepNonBlocking()
 {
-    if (checking())
-        return;
+    assert(accessList);
 
     if (callerGone()) {
         checkCallback(ACCESS_DUNNO); // the answer does not really matter
-        return;
-    }
-
-    /** The ACL List should NEVER be NULL when calling this method.
-     * Always caller should check for NULL and handle appropriate to its needs first.
-     * We cannot select a sensible default for all callers here. */
-    if (accessList == NULL) {
-        debugs(28, DBG_CRITICAL, "SECURITY ERROR: ACL " << this << " checked with nothing to match against!!");
-        checkCallback(ACCESS_DUNNO);
-        return;
-    }
-
-    allow_t lastSeenKeyword = ACCESS_DUNNO;
-    /* NOTE: This holds a cbdata reference to the current access_list
-     * entry, not the whole list.
-     */
-    while (accessList != NULL) {
+        return false;
+    }
+
         /** \par
          * If the _acl_access is no longer valid (i.e. its been
          * freed because of a reconfigure), then bail with ACCESS_DUNNO.
@@ -70,73 +28,36 @@
             cbdataReferenceDone(accessList);
             debugs(28, 4, "ACLChecklist::check: " << this << " accessList is invalid");
             checkCallback(ACCESS_DUNNO);
-            return;
-        }
-
-        checking (true);
-        checkAccessList();
-        checking (false);
-
-        if (asyncInProgress()) {
-            return;
-        }
-
-        if (finished()) {
+            return false;
+        }
+
+        // If doNonBlocking() was called for a finished() checklist to call
+        // the callbacks, then do not try to match again. XXX: resumeNonBlockingCheck() should check for this instead.
+        if (!finished())
+            return true;
+
             /** \par
              * Either the request is allowed, denied, requires authentication.
              */
-            debugs(28, 3, "ACLChecklist::check: " << this << " match found, calling back with " << currentAnswer());
+            debugs(28, 3, this << " calling back with " << currentAnswer());
             cbdataReferenceDone(accessList); /* A */
             checkCallback(currentAnswer());
             /* From here on in, this may be invalid */
-            return;
-        }
-
-        lastSeenKeyword = accessList->allow;
-
-        /*
-         * Reference the next access entry
-         */
-        const acl_access *A = accessList;
-
-        assert (A);
-
-        accessList = cbdataReference(A->next);
-
-        cbdataReferenceDone(A);
-    }
-
-    calcImplicitAnswer(lastSeenKeyword);
+            return false;
+}
+
+void
+ACLChecklist::completeNonBlocking()
+{
+    assert(!asyncInProgress());
+
+    if (!finished())
+        calcImplicitAnswer();
+
+    cbdataReferenceDone(accessList);
     checkCallback(currentAnswer());
 }
 
-bool
-ACLChecklist::asyncNeeded() const
-{
-    return state_ != NullState::Instance();
-}
-
-bool
-ACLChecklist::asyncInProgress() const
-{
-    return async_;
-}
-
-void
-ACLChecklist::asyncInProgress(bool const newAsync)
-{
-    assert (!finished() && !(asyncInProgress() && newAsync));
-    async_ = newAsync;
-    debugs(28, 3, "ACLChecklist::asyncInProgress: " << this <<
-           " async set to " << async_);
-}
-
-bool
-ACLChecklist::finished() const
-{
-    return finished_;
-}
-
 void
 ACLChecklist::markFinished(const allow_t &finalAnswer, const char *reason)
 {
@@ -151,25 +72,77 @@
 ACLChecklist::preCheck(const char *what)
 {
     debugs(28, 3, HERE << this << " checking " << what);
+    AclMatchedName = NULL;
     finished_ = false;
 }
 
-void
-ACLChecklist::checkAccessList()
+bool
+ACLChecklist::matchChild(const Acl::InnerNode *current, Acl::Nodes::const_iterator pos, const ACL *child)
 {
-    debugs(28, 3, HERE << this << " checking '" << accessList->cfgline << "'");
-    /* does the current AND clause match */
-    if (matchAclList(accessList->aclList, false))
-        markFinished(accessList->allow, "first matching rule won");
-
-    // If we are not finished() here, the caller must distinguish between
-    // slow async calls and pure rule mismatches using asyncInProgress().
+    assert(current && child);
+
+    // Remember the curernt tree location to prevent "async loop" cases where
+    // the same child node wants to go async more than once.
+    matchLoc_ = Breadcrumb(current, pos);
+
+    // if there are any breadcrumbs left, then follow them on the way down
+    bool result = false;
+    if (matchPath.empty()) {
+        result = child->matches(this);
+    } else {
+        const Breadcrumb top(matchPath.top());
+        assert(child == top.parent);
+        matchPath.pop();
+        result = top.parent->resumeMatchingAt(this, top.position);
+    }
+
+    if (asyncInProgress()) {
+        // We get here for node N that called goAsync() and then, as the call
+        // stack unwinds, for the nodes higher in the ACL tree that led to N.
+        matchPath.push(Breadcrumb(current, pos));
+    } else {
+        asyncLoc_.clear();
+    }
+
+    matchLoc_.clear();
+    return result;
 }
 
-void
-ACLChecklist::checkForAsync()
+bool
+ACLChecklist::goAsync(AsyncState *state)
 {
-    asyncState()->checkForAsync(this);
+    assert(state);
+    assert(!asyncInProgress());
+    assert(matchLoc_.parent);
+
+    // TODO: add a once-in-a-while WARNING about fast directive using slow ACL?
+    if (!asyncCaller_) {
+        debugs(28, 2, this << " a fast-only directive uses a slow ACL!");
+        return false;
+    }
+
+    // TODO: add a once-in-a-while WARNING about async loops?
+    if (matchLoc_ == asyncLoc_) {
+        debugs(28, 2, this << " a slow ACL resumes by going async again!");
+        return false;
+    }
+
+    asyncLoc_ = matchLoc_; // prevent async loops
+
+    asyncStage_ = asyncStarting;
+    changeState(state);
+    state->checkForAsync(this); // this is supposed to go async
+
+    // Did AsyncState object actually go async? If not, tell the caller.
+    if (asyncStage_ != asyncStarting) {
+        assert(asyncStage_ == asyncFailed);
+        asyncStage_ = asyncNone; // sanity restored
+        return false;
+    }
+ 
+    // yes, we must pause until the async callback calls resumeNonBlockingCheck
+    asyncStage_ = asyncRunning;
+    return true;
 }
 
 // ACLFilledChecklist overwrites this to unclock something before we
@@ -190,138 +163,15 @@
     delete this;
 }
 
-/// An ACLChecklist::matchNodes() wrapper to simplify profiling.
-bool
-ACLChecklist::matchAclList(const ACLList * head, bool const fast)
-{
-    // TODO: remove by using object con/destruction-based PROF_* macros.
-    PROF_start(aclMatchAclList);
-    const bool result = matchNodes(head, fast);
-    PROF_stop(aclMatchAclList);
-    return result;
-}
-
-/** Returns true if and only if there was a match. If false is returned:
-    finished() indicates an error or exception of some kind, while
-    !finished() means there was a mismatch or an allowed slow async call.
-    If async calls are allowed (i.e. 'fast' was false), then those last
-    two cases can be distinguished using asyncInProgress().
-*/
-bool
-ACLChecklist::matchNodes(const ACLList * head, bool const fast)
-{
-    assert(!finished());
-
-    for (const ACLList *node = head; node; node = node->next) {
-
-        const NodeMatchingResult resultBeforeAsync = matchNode(*node, fast);
-
-        if (resultBeforeAsync == nmrMatch)
-            continue;
-
-        if (resultBeforeAsync == nmrMismatch || resultBeforeAsync == nmrFinished)
-            return false;
-
-        assert(resultBeforeAsync == nmrNeedsAsync);
-
-        // Ideally, this should be inside match() itself, but that requires
-        // prohibiting slow ACLs in options that do not support them.
-        // TODO: rename to maybeStartAsync()?
-        checkForAsync();
-
-        // Some match() code claims that an async lookup is needed, but then
-        // fails to start an async lookup when given a chance. We catch such
-        // cases here and call matchNode() again, hoping that some cached data
-        // prevents us from going async again.
-        // This is inefficient and ugly, but fixing all match() code, including
-        // the code it calls, such as ipcache_nbgethostbyname(), takes time.
-        if (!asyncInProgress()) { // failed to start an async operation
-
-            if (finished()) {
-                debugs(28, 3, HERE << this << " finished after failing to go async: " << currentAnswer());
-                return false; // an exceptional case
-            }
-
-            const NodeMatchingResult resultAfterAsync = matchNode(*node, true);
-            // the second call disables slow checks so we cannot go async again
-            assert(resultAfterAsync != nmrNeedsAsync);
-            if (resultAfterAsync == nmrMatch)
-                continue;
-
-            assert(resultAfterAsync == nmrMismatch || resultAfterAsync == nmrFinished);
-            return false;
-        }
-
-        assert(!finished()); // async operation is truly asynchronous
-        debugs(28, 3, HERE << this << " awaiting async operation");
-        return false;
-    }
-
-    debugs(28, 3, HERE << this << " success: all ACLs matched");
-    return true;
-}
-
-/// Check whether a single ACL matches, returning NodeMatchingResult
-ACLChecklist::NodeMatchingResult
-ACLChecklist::matchNode(const ACLList &node, bool const fast)
-{
-    const bool nodeMatched = node.matches(this);
-    const bool needsAsync = asyncNeeded();
-    const bool matchFinished = finished();
-
-    debugs(28, 3, HERE << this <<
-           " matched=" << nodeMatched <<
-           " async=" << needsAsync <<
-           " finished=" << matchFinished);
-
-    /* There are eight possible outcomes of the matches() call based on
-       (matched, async, finished) permutations. We support these four:
-       matched,!async,!finished: a match (must check next rule node)
-       !matched,!async,!finished: a mismatch (whole rule fails to match)
-       !matched,!async,finished: error or special condition (propagate)
-       !matched,async,!finished: ACL needs to make an async call (pause)
-     */
-
-    if (nodeMatched) {
-        // matches() should return false in all special cases
-        assert(!needsAsync && !matchFinished);
-        return nmrMatch;
-    }
-
-    if (matchFinished) {
-        // we cannot be done and need an async call at the same time
-        assert(!needsAsync);
-        debugs(28, 3, HERE << this << " exception: " << currentAnswer());
-        return nmrFinished;
-    }
-
-    if (!needsAsync) {
-        debugs(28, 3, HERE << this << " simple mismatch");
-        return nmrMismatch;
-    }
-
-    /* we need an async call */
-
-    if (fast) {
-        changeState(NullState::Instance()); // disable async checks
-        markFinished(ACCESS_DUNNO, "async required but prohibited");
-        debugs(28, 3, HERE << this << " DUNNO because cannot async");
-        return nmrFinished;
-    }
-
-    debugs(28, 3, HERE << this << " going async");
-    return nmrNeedsAsync;
-}
-
 ACLChecklist::ACLChecklist() :
         accessList (NULL),
         callback (NULL),
         callback_data (NULL),
-        async_(false),
+        asyncCaller_(false),
         finished_(false),
         allow_(ACCESS_DENIED),
-        state_(NullState::Instance()),
-        checking_(false)
+        asyncStage_(asyncNone),
+        state_(NullState::Instance())
 {
 }
 
@@ -334,12 +184,6 @@
     debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
 }
 
-void
-ACLChecklist::AsyncState::changeState (ACLChecklist *checklist, AsyncState *newState) const
-{
-    checklist->changeState(newState);
-}
-
 ACLChecklist::NullState *
 ACLChecklist::NullState::Instance()
 {
@@ -348,7 +192,9 @@
 
 void
 ACLChecklist::NullState::checkForAsync(ACLChecklist *) const
-{}
+{
+    assert(false); // or the Checklist will never get out of the async state
+}
 
 ACLChecklist::NullState ACLChecklist::NullState::_instance;
 
@@ -381,21 +227,91 @@
     preCheck("slow rules");
     callback = callback_;
     callback_data = cbdataReference(callback_data_);
-    matchNonBlocking();
+    asyncCaller_ = true;
+
+    /** The ACL List should NEVER be NULL when calling this method.
+     * Always caller should check for NULL and handle appropriate to its needs first.
+     * We cannot select a sensible default for all callers here. */
+    if (accessList == NULL) {
+        debugs(28, DBG_CRITICAL, "SECURITY ERROR: ACL " << this << " checked with nothing to match against!!");
+        checkCallback(ACCESS_DUNNO);
+        return;
+    }
+
+    if (prepNonBlocking()) {
+        matchAndFinish(); // calls markFinished() on success
+        if (!asyncInProgress())
+            completeNonBlocking();
+    } // else checkCallback() has been called
+}
+
+void
+ACLChecklist::resumeNonBlockingCheck(AsyncState *state)
+{
+    assert(asyncState() == state);
+    changeState(NullState::Instance());
+
+    if (asyncStage_ == asyncStarting) { // oops, we did not really go async
+        asyncStage_ = asyncFailed; // goAsync() checks for that
+        // Do not fall through to resume checks from the async callback. Let
+        // the still-pending(!) goAsync() notice and notify its caller instead.
+        return;
+    }
+    assert(asyncStage_ == asyncRunning);
+    asyncStage_ = asyncNone;
+
+    assert(!matchPath.empty());
+
+    if (!prepNonBlocking())
+        return; // checkCallback() has been called
+
+    matchAndFinish();
+
+    if (asyncInProgress())
+        assert(!matchPath.empty()); // we have breadcrumbs to resume matching
+    else
+        completeNonBlocking();
+}
+
+/// performs (or resumes) an ACL tree match and, if successful, sets the action
+void
+ACLChecklist::matchAndFinish() {
+    bool result = false;
+    if (matchPath.empty()) {
+        result = accessList->matches(this);
+    } else {
+        const Breadcrumb top(matchPath.top());
+        matchPath.pop();
+        result = top.parent->resumeMatchingAt(this, top.position);
+    }
+    
+    if (result) // the entire tree matched
+        markFinished(accessList->winningAction(), "match");
 }
 
 allow_t const &
-ACLChecklist::fastCheck(const ACLList * list)
+ACLChecklist::fastCheck(const Acl::Tree * list)
 {
     PROF_start(aclCheckFast);
 
     preCheck("fast ACLs");
+    asyncCaller_ = false;
+
+    // This call is not compatible with a pre-set accessList because we cannot
+    // tell whether this Checklist is used by some other concurent call, which
+    // is not supported.
+    assert(!accessList);
+    accessList = list;
 
     // assume DENY/ALLOW on mis/matches due to not having acl_access object
-    if (matchAclList(list, true))
-        markFinished(ACCESS_ALLOWED, "all ACLs matched");
-    else if (!finished())
-        markFinished(ACCESS_DENIED, "ACL mismatched");
+    // matchAndFinish() takes care of the ALLOW case
+    cbdataReference(accessList); // required for cbdataReferenceValid()
+    if (accessList && cbdataReferenceValid(accessList))
+        matchAndFinish(); // calls markFinished() on success
+    if (!finished())
+        markFinished(ACCESS_DENIED, "ACLs failed to match");
+
+    cbdataReferenceDone(accessList);
     PROF_stop(aclCheckFast);
     return currentAnswer();
 }
@@ -409,14 +325,12 @@
     PROF_start(aclCheckFast);
 
     preCheck("fast rules");
+    asyncCaller_ = false;
 
-    allow_t lastSeenKeyword = ACCESS_DUNNO;
     debugs(28, 5, "aclCheckFast: list: " << accessList);
-    const acl_access *acl = cbdataReference(accessList);
-    while (acl != NULL && cbdataReferenceValid(acl)) {
-        // on a match, finish
-        if (matchAclList(acl->aclList, true))
-            markFinished(acl->allow, "first matching rule won");
+    const Acl::Tree *acl = cbdataReference(accessList);
+    if (acl != NULL && cbdataReferenceValid(acl)) {
+        matchAndFinish(); // calls markFinished() on success
 
         // if finished (on a match or in exceptional cases), stop
         if (finished()) {
@@ -425,15 +339,12 @@
             return currentAnswer();
         }
 
-        // on a mismatch, try the next access rule
-        lastSeenKeyword = acl->allow;
-        const acl_access *A = acl;
-        acl = cbdataReference(acl->next);
-        cbdataReferenceDone(A);
+        // fall through for mismatch handling
     }
 
     // There were no rules to match or no rules matched
-    calcImplicitAnswer(lastSeenKeyword);
+    calcImplicitAnswer();
+    cbdataReferenceDone(acl);
     PROF_stop(aclCheckFast);
 
     return currentAnswer();
@@ -443,8 +354,11 @@
 /// action (or ACCESS_DUNNO if the reversal is not possible). The caller
 /// should set lastSeenAction to ACCESS_DUNNO if there were no rules to see.
 void
-ACLChecklist::calcImplicitAnswer(const allow_t &lastSeenAction)
+ACLChecklist::calcImplicitAnswer()
 {
+    // XXX: rename lastSeenAction after review and before commit
+    const allow_t lastSeenAction = (accessList && cbdataReferenceValid(accessList)) ?
+        accessList->lastAction() : allow_t(ACCESS_DUNNO);
     allow_t implicitRuleAnswer = ACCESS_DUNNO;
     if (lastSeenAction == ACCESS_DENIED) // reverse last seen "deny"
         implicitRuleAnswer = ACCESS_ALLOWED;
@@ -458,18 +372,6 @@
 }
 
 bool
-ACLChecklist::checking() const
-{
-    return checking_;
-}
-
-void
-ACLChecklist::checking (bool const newValue)
-{
-    checking_ = newValue;
-}
-
-bool
 ACLChecklist::callerGone()
 {
     return !cbdataReferenceValid(callback_data);

=== modified file 'src/acl/Checklist.h'
--- src/acl/Checklist.h	2012-09-01 14:38:36 +0000
+++ src/acl/Checklist.h	2013-05-13 22:48:23 +0000
@@ -31,7 +31,8 @@
 #ifndef SQUID_ACLCHECKLIST_H
 #define SQUID_ACLCHECKLIST_H
 
-#include "acl/Acl.h"
+#include "acl/InnerNode.h"
+#include <stack>
 
 /// ACL checklist callback
 typedef void ACLCB(allow_t, void *);
@@ -67,9 +68,6 @@
     public:
         virtual void checkForAsync(ACLChecklist *) const = 0;
         virtual ~AsyncState() {}
-
-    protected:
-        void changeState (ACLChecklist *, AsyncState *) const;
     };
 
     class NullState : public AsyncState
@@ -153,25 +151,29 @@
      *
      * If there are no ACLs to check at all, the result becomes ACCESS_ALLOWED.
      */
-    allow_t const & fastCheck(const ACLList * list);
-
-    // whether the last checked ACL of the current rule needs
-    // an async operation to determine whether there was a match
-    bool asyncNeeded() const;
-    bool asyncInProgress() const;
-    void asyncInProgress(bool const);
+    allow_t const & fastCheck(const Acl::Tree *list);
+
+    /// If slow lookups are allowed, switches into "async in progress" state.
+    /// Otherwise, returns false; the caller is expected to handle the failure.
+    bool goAsync(AsyncState *);
+
+    /// Matches (or resumes matching of) a child node while maintaning 
+    /// resumption breadcrumbs if a [grand]child node goes async.
+    bool matchChild(const Acl::InnerNode *parent, Acl::Nodes::const_iterator pos, const ACL *child);
+
+    /// Whether we should continue to match tree nodes or stop/pause.
+    bool keepMatching() const { return !finished() && !asyncInProgress(); }
 
     /// whether markFinished() was called
-    bool finished() const;
+    bool finished() const { return finished_; }
+    /// async call has been started and has not finished (or failed) yet
+    bool asyncInProgress() const { return asyncStage_ != asyncNone; }
     /// called when no more ACLs should be checked; sets the final answer and
     /// prints a debugging message explaining the reason for that answer
     void markFinished(const allow_t &newAnswer, const char *reason);
 
     const allow_t &currentAnswer() const { return allow_; }
 
-    void changeState(AsyncState *);
-    AsyncState *asyncState() const;
-
     // XXX: ACLs that need request or reply have to use ACLFilledChecklist and
     // should do their own checks so that we do not have to povide these two
     // for ACL::checklistMatches to use
@@ -182,46 +184,58 @@
     /// Calls non-blocking check callback with the answer and destroys self.
     void checkCallback(allow_t answer);
 
-    void checkAccessList();
-    void checkForAsync();
+    void matchAndFinish();
+
+    void changeState(AsyncState *);
+    AsyncState *asyncState() const;
 
 public:
-    const acl_access *accessList;
+    const Acl::Tree *accessList;
 
     ACLCB *callback;
     void *callback_data;
 
-    /**
-     * Performs non-blocking check starting with the current rule.
-     * Used by nonBlockingCheck() to initiate the checks and by
-     * async operation callbacks to resume checks after the async
-     * operation updates the current Squid state. See nonBlockingCheck()
-     * for details on final result determination.
-     */
-    void matchNonBlocking();
+    /// Resumes non-blocking check started by nonBlockingCheck() and
+    /// suspended until some async operation updated Squid state.
+    void resumeNonBlockingCheck(AsyncState *state);
 
 private: /* internal methods */
+    /// Position of a child node within an ACL tree.
+    class Breadcrumb {
+    public:
+        Breadcrumb(): parent(NULL) {}
+        Breadcrumb(const Acl::InnerNode *aParent, Acl::Nodes::const_iterator aPos): parent(aParent), position(aPos) {}
+        bool operator ==(const Breadcrumb &b) const { return parent == b.parent && (!parent || position == b.position); }
+        bool operator !=(const Breadcrumb &b) const { return !this->operator ==(b); }
+        void clear() { parent = NULL; }
+        const Acl::InnerNode *parent; ///< intermediate node in the ACL tree
+        Acl::Nodes::const_iterator position; ///< child position inside parent
+    };
+
     /// possible outcomes when trying to match a single ACL node in a list
     typedef enum { nmrMatch, nmrMismatch, nmrFinished, nmrNeedsAsync }
     NodeMatchingResult;
 
     /// prepare for checking ACLs; called once per check
     void preCheck(const char *what);
-    bool matchAclList(const ACLList * list, bool const fast);
-    bool matchNodes(const ACLList * head, bool const fast);
-    NodeMatchingResult matchNode(const ACLList &node, bool const fast);
-    void calcImplicitAnswer(const allow_t &lastSeenAction);
+    bool prepNonBlocking();
+    void completeNonBlocking();
+    void calcImplicitAnswer();
 
-    bool async_;
+    bool asyncCaller_; ///< whether the caller supports async/slow ACLs
     bool finished_;
     allow_t allow_;
+
+    enum AsyncStage { asyncNone, asyncStarting, asyncRunning, asyncFailed };
+    AsyncStage asyncStage_;
     AsyncState *state_;
-
-    bool checking_;
-    bool checking() const;
-    void checking (bool const);
+    Breadcrumb matchLoc_; ///< location of the node running matches() now
+    Breadcrumb asyncLoc_; ///< currentNode_ that called goAsync()
 
     bool callerGone();
+
+    /// suspended (due to an async lookup) matches() in the ACL tree
+    std::stack<Breadcrumb> matchPath;
 };
 
 #endif /* SQUID_ACLCHECKLIST_H */

=== modified file 'src/acl/DestinationDomain.cc'
--- src/acl/DestinationDomain.cc	2013-01-27 17:35:07 +0000
+++ src/acl/DestinationDomain.cc	2013-05-13 22:48:23 +0000
@@ -53,7 +53,6 @@
 DestinationDomainLookup::checkForAsync(ACLChecklist *cl) const
 {
     ACLFilledChecklist *checklist = Filled(cl);
-    checklist->asyncInProgress(true);
     fqdncache_nbgethostbyaddr(checklist->dst_addr, LookupDone, checklist);
 }
 
@@ -61,13 +60,9 @@
 DestinationDomainLookup::LookupDone(const char *fqdn, const DnsLookupDetails &details, void *data)
 {
     ACLFilledChecklist *checklist = Filled((ACLChecklist*)data);
-    assert (checklist->asyncState() == DestinationDomainLookup::Instance());
-
-    checklist->asyncInProgress(false);
-    checklist->changeState (ACLChecklist::NullState::Instance());
     checklist->markDestinationDomainChecked();
     checklist->request->recordLookup(details);
-    checklist->matchNonBlocking();
+    checklist->resumeNonBlockingCheck(DestinationDomainLookup::Instance());
 }
 
 int
@@ -112,8 +107,9 @@
     } else if (!checklist->destinationDomainChecked()) {
         /* FIXME: Using AclMatchedName here is not OO correct. Should find a way to the current acl */
         debugs(28, 3, "aclMatchAcl: Can't yet compare '" << AclMatchedName << "' ACL for '" << checklist->request->GetHost() << "'");
-        checklist->changeState(DestinationDomainLookup::Instance());
-        return 0;
+        if (checklist->goAsync(DestinationDomainLookup::Instance()))
+            return -1;
+        // else fall through to "none" match, hiding the lookup failure (XXX)
     }
 
     return data->match("none");

=== modified file 'src/acl/DestinationIp.cc'
--- src/acl/DestinationIp.cc	2013-05-13 03:57:03 +0000
+++ src/acl/DestinationIp.cc	2013-05-13 23:32:23 +0000
@@ -86,11 +86,12 @@
     } else if (!checklist->request->flags.destinationIpLookedUp) {
         /* No entry in cache, lookup not attempted */
         debugs(28, 3, "aclMatchAcl: Can't yet compare '" << name << "' ACL for '" << checklist->request->GetHost() << "'");
-        checklist->changeState (DestinationIPLookup::Instance());
-        return 0;
-    } else {
-        return 0;
+        if (checklist->goAsync(DestinationIPLookup::Instance()))
+            return -1;
+        // else fall through to mismatch, hiding the lookup failure (XXX)
     }
+
+    return 0;
 }
 
 DestinationIPLookup DestinationIPLookup::instance_;
@@ -105,7 +106,6 @@
 DestinationIPLookup::checkForAsync(ACLChecklist *cl)const
 {
     ACLFilledChecklist *checklist = Filled(cl);
-    checklist->asyncInProgress(true);
     ipcache_nbgethostbyname(checklist->request->GetHost(), LookupDone, checklist);
 }
 
@@ -113,12 +113,9 @@
 DestinationIPLookup::LookupDone(const ipcache_addrs *, const DnsLookupDetails &details, void *data)
 {
     ACLFilledChecklist *checklist = Filled((ACLChecklist*)data);
-    assert (checklist->asyncState() == DestinationIPLookup::Instance());
     checklist->request->flags.destinationIpLookedUp = true;
     checklist->request->recordLookup(details);
-    checklist->asyncInProgress(false);
-    checklist->changeState (ACLChecklist::NullState::Instance());
-    checklist->matchNonBlocking();
+    checklist->resumeNonBlockingCheck(DestinationIPLookup::Instance());
 }
 
 ACL *

=== modified file 'src/acl/FilledChecklist.h'
--- src/acl/FilledChecklist.h	2012-11-13 18:19:17 +0000
+++ src/acl/FilledChecklist.h	2013-05-13 22:48:23 +0000
@@ -2,6 +2,7 @@
 #define SQUID_ACLFILLED_CHECKLIST_H
 
 #include "acl/Checklist.h"
+#include "acl/forward.h"
 #include "ip/Address.h"
 #if USE_AUTH
 #include "auth/UserRequest.h"

=== modified file 'src/acl/Gadgets.cc'
--- src/acl/Gadgets.cc	2013-05-11 20:59:44 +0000
+++ src/acl/Gadgets.cc	2013-05-13 23:32:23 +0000
@@ -41,6 +41,7 @@
 #include "acl/AclNameList.h"
 #include "acl/AclDenyInfoList.h"
 #include "acl/Checklist.h"
+#include "acl/Tree.h"
 #include "acl/Strategised.h"
 #include "acl/Gadgets.h"
 #include "ConfigParser.h"
@@ -134,7 +135,7 @@
 
     while ((t = strtok(NULL, w_space))) {
         L = (AclNameList *)memAllocate(MEM_ACL_NAME_LIST);
-        xstrncpy(L->name, t, ACL_NAME_SZ);
+        xstrncpy(L->name, t, ACL_NAME_SZ-1);
         *Tail = L;
         Tail = &L->next;
     }
@@ -153,84 +154,87 @@
 }
 
 void
-aclParseAccessLine(ConfigParser &parser, acl_access ** head)
+aclParseAccessLine(const char *directive, ConfigParser &, acl_access **treep)
 {
-    char *t = NULL;
-    acl_access *A = NULL;
-    acl_access *B = NULL;
-    acl_access **T = NULL;
-
     /* first expect either 'allow' or 'deny' */
+    const char *t = ConfigParser::strtokFile();
 
-    if ((t = strtok(NULL, w_space)) == NULL) {
+    if (!t) {
         debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
         debugs(28, DBG_CRITICAL, "aclParseAccessLine: missing 'allow' or 'deny'.");
         return;
     }
 
-    A = new acl_access;
-
+    allow_t action = ACCESS_DUNNO;
     if (!strcmp(t, "allow"))
-        A->allow = ACCESS_ALLOWED;
+        action = ACCESS_ALLOWED;
     else if (!strcmp(t, "deny"))
-        A->allow = ACCESS_DENIED;
+        action = ACCESS_DENIED;
     else {
         debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
         debugs(28, DBG_CRITICAL, "aclParseAccessLine: expecting 'allow' or 'deny', got '" << t << "'.");
-        delete A;
         return;
     }
 
-    aclParseAclList(parser, &A->aclList);
+    const int ruleId = ((treep && *treep) ? (*treep)->childrenCount() : 0) + 1;
+    MemBuf ctxBuf;
+    ctxBuf.init();
+    ctxBuf.Printf("%s#%d", directive, ruleId);
+    ctxBuf.terminate();
 
-    if (A->aclList == NULL) {
-        debugs(28, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line);
+    Acl::AndNode *rule = new Acl::AndNode;
+    rule->context(ctxBuf.content(), config_input_line);
+    rule->lineParse();
+    if (rule->empty()) {
+        debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line);
         debugs(28, DBG_CRITICAL, "aclParseAccessLine: Access line contains no ACL's, skipping");
-        delete A;
+        delete rule;
         return;
     }
 
-    A->cfgline = xstrdup(config_input_line);
     /* Append to the end of this list */
 
-    for (B = *head, T = head; B; T = &B->next, B = B->next);
-    *T = A;
+    assert(treep);
+    if (!*treep) {
+        *treep = new Acl::Tree;
+        (*treep)->context(directive, config_input_line);
+    }
+
+    (*treep)->add(rule, action);
 
     /* We lock _acl_access structures in ACLChecklist::matchNonBlocking() */
 }
 
+// aclParseAclList does not expect or set actions (cf. aclParseAccessLine)
 void
-aclParseAclList(ConfigParser &parser, ACLList ** head)
+aclParseAclList(ConfigParser &, Acl::Tree **treep, const char *label)
 {
-    ACLList **Tail = head;	/* sane name in the use below */
-    ACL *a = NULL;
-    char *t;
-
-    /* next expect a list of ACL names, possibly preceeded
-     * by '!' for negation */
-
-    while ((t = parser.strtokFile())) {
-        ACLList *L = new ACLList;
-
-        if (*t == '!') {
-            L->negated (true);
-            ++t;
-        }
-
-        debugs(28, 3, "aclParseAclList: looking for ACL name '" << t << "'");
-        a = ACL::FindByName(t);
-
-        if (a == NULL) {
-            debugs(28, DBG_CRITICAL, "aclParseAclList: ACL name '" << t << "' not found.");
-            delete L;
-            parser.destruct();
-            continue;
-        }
-
-        L->_acl = a;
-        *Tail = L;
-        Tail = &L->next;
-    }
+    // accomodate callers unable to convert their ACL list context to string
+    if (!label)
+        label = "...";
+
+    MemBuf ctxLine;
+    ctxLine.init();
+    ctxLine.Printf("(%s %s line)", cfg_directive, label);
+    ctxLine.terminate();
+
+    Acl::AndNode *rule = new Acl::AndNode;
+    rule->context(ctxLine.content(), config_input_line);
+    rule->lineParse();
+
+    MemBuf ctxTree;
+    ctxTree.init();
+    ctxTree.Printf("%s %s", cfg_directive, label);
+    ctxTree.terminate();
+
+    // We want a cbdata-protected Tree (despite giving it only one child node).
+    Acl::Tree *tree = new Acl::Tree;
+    tree->add(rule);
+    tree->context(ctxTree.content(), config_input_line);
+
+    assert(treep);
+    assert(!*treep);
+    *treep = tree;
 }
 
 /*********************/
@@ -253,32 +257,20 @@
 }
 
 void
-aclDestroyAclList(ACLList ** head)
+aclDestroyAclList(ACLList **list)
 {
-    ACLList *l;
     debugs(28, 8, "aclDestroyAclList: invoked");
-
-    for (l = *head; l; l = *head) {
-        *head = l->next;
-        delete l;
-    }
+    assert(list);
+    cbdataFree(*list);
 }
 
 void
 aclDestroyAccessList(acl_access ** list)
 {
-    acl_access *l = NULL;
-    acl_access *next = NULL;
-
-    for (l = *list; l; l = next) {
-        debugs(28, 3, "aclDestroyAccessList: '" << l->cfgline << "'");
-        next = l->next;
-        aclDestroyAclList(&l->aclList);
-        safe_free(l->cfgline);
-        cbdataFree(l);
-    }
-
-    *list = NULL;
+    assert(list);
+    if (*list)
+        debugs(28, 3, "destroying: " << *list << ' ' << (*list)->name);
+    cbdataFree(*list);
 }
 
 /* maex@space.net (06.09.1996)

=== modified file 'src/acl/Gadgets.h'
--- src/acl/Gadgets.h	2013-01-10 01:00:54 +0000
+++ src/acl/Gadgets.h	2013-05-13 22:48:23 +0000
@@ -2,11 +2,8 @@
 #define SQUID_ACL_GADGETS_H
 
 #include "err_type.h"
+#include "acl/forward.h"
 
-class acl_access;
-class ACL;
-class AclDenyInfoList;
-class ACLList;
 class ConfigParser;
 class dlink_list;
 class StoreEntry;
@@ -18,10 +15,22 @@
 void aclDestroyAcls(ACL **);
 /// \ingroup ACLAPI
 void aclDestroyAclList(ACLList **);
-/// \ingroup ACLAPI
-void aclParseAccessLine(ConfigParser &parser, acl_access **);
-/// \ingroup ACLAPI
-void aclParseAclList(ConfigParser &parser, ACLList **);
+/// Parses a single line of a "action followed by acls" directive (e.g., http_access).
+/// \ingroup ACLAPI
+void aclParseAccessLine(const char *directive, ConfigParser &parser, Acl::Tree **);
+/// Parses a single line of a "some context followed by acls" directive (e.g., note n v).
+/// The label parameter identifies the context (for debugging).
+/// \ingroup ACLAPI
+void aclParseAclList(ConfigParser &parser, Acl::Tree **, const char *label);
+/// Template to convert various context lables to strings. \ingroup ACLAPI
+template <class Any>
+inline
+void aclParseAclList(ConfigParser &parser, Acl::Tree **tree, const Any any) {
+    std::ostringstream buf;
+    buf << any;
+    aclParseAclList(parser, tree, buf.str().c_str());
+}
+
 /// \ingroup ACLAPI
 int aclIsProxyAuth(const char *name);
 /// \ingroup ACLAPI

=== added file 'src/acl/InnerNode.cc'
--- src/acl/InnerNode.cc	1970-01-01 00:00:00 +0000
+++ src/acl/InnerNode.cc	2013-05-13 22:48:23 +0000
@@ -0,0 +1,110 @@
+#include "squid.h"
+#include "acl/Acl.h"
+#include "acl/BoolOps.h"
+#include "acl/Checklist.h"
+#include "acl/Gadgets.h"
+#include "acl/InnerNode.h"
+#include "cache_cf.h"
+#include "ConfigParser.h"
+#include "Debug.h"
+#include "globals.h"
+#include "wordlist.h"
+#include <algorithm>
+
+
+// "delete acl" class to use with std::for_each() in InnerNode::~InnerNode()
+class AclDeleter {
+public:
+    void operator()(ACL* acl) {
+        // Do not delete explicit ACLs; they are maintained by Config.aclList.
+        if (acl && !acl->registered)
+            delete acl;
+    }
+};
+
+
+Acl::InnerNode::~InnerNode()
+{
+    std::for_each(nodes.begin(), nodes.end(), AclDeleter());
+}
+
+void
+Acl::InnerNode::prepareForUse()
+{
+    std::for_each(nodes.begin(), nodes.end(), std::mem_fun(&ACL::prepareForUse));
+}
+
+bool
+Acl::InnerNode::empty() const
+{
+    return nodes.empty();
+}
+
+void
+Acl::InnerNode::add(ACL *node)
+{
+    assert(node != NULL);
+    nodes.push_back(node);
+}
+
+// one call parses one "acl name acltype name1 name2 ..." line
+// kids use this method to handle [multiple] parse() calls correctly
+void
+Acl::InnerNode::lineParse()
+{
+    // XXX: not precise, may change when looping or parsing multiple lines
+    if (!cfgline)
+        cfgline = xstrdup(config_input_line);
+
+    // expect a list of ACL names, each possibly preceeded by '!' for negation
+
+    while (const char *t = ConfigParser::strtokFile()) {
+        const bool negated = (*t == '!');
+        if (negated)
+            ++t;
+
+        debugs(28, 3, "looking for ACL " << t);
+        ACL *a = ACL::FindByName(t);
+
+        if (a == NULL) {
+            debugs(28, DBG_CRITICAL, "ACL not found: " << t);
+            self_destruct();
+            return;
+        }
+
+        // append(negated ? new NotNode(a) : a);
+        if (negated)
+            add(new NotNode(a));
+        else
+            add(a);
+    }
+
+    return;
+}
+
+wordlist*
+Acl::InnerNode::dump() const
+{
+    wordlist *values = NULL;
+    for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
+        wordlistAdd(&values, (*i)->name);
+    return values;
+}
+
+int
+Acl::InnerNode::match(ACLChecklist *checklist)
+{
+    return doMatch(checklist, nodes.begin());
+}
+
+bool
+Acl::InnerNode::resumeMatchingAt(ACLChecklist *checklist, Nodes::const_iterator pos) const
+{
+    debugs(28, 5, "checking " << name << " at " << (pos-nodes.begin()));
+    const int result = doMatch(checklist, pos);
+    const char *extra = checklist->asyncInProgress() ? " async" : "";
+    debugs(28, 3, "checked: " << name << " = " << result << extra);
+
+    // merges async and failures (-1) into "not matched"
+    return result == 1;
+}

=== added file 'src/acl/InnerNode.h'
--- src/acl/InnerNode.h	1970-01-01 00:00:00 +0000
+++ src/acl/InnerNode.h	2013-05-13 22:48:23 +0000
@@ -0,0 +1,47 @@
+#ifndef SQUID_ACL_INNER_NODE_H
+#define SQUID_ACL_INNER_NODE_H
+
+#include "acl/Acl.h"
+#include <vector>
+
+namespace Acl {
+
+typedef std::vector<ACL*> Nodes; ///< a collection of nodes
+
+/// An intermediate ACL tree node. Manages a collection of child tree nodes.
+class InnerNode: public ACL
+{
+public:
+    virtual ~InnerNode();
+
+    /// Resumes matching (suspended by an async call) at the given position.
+    bool resumeMatchingAt(ACLChecklist *checklist, Acl::Nodes::const_iterator pos) const;
+
+    /// the number of children nodes
+    Nodes::size_type childrenCount() const { return nodes.size(); }
+
+    /* ACL API */
+    virtual void prepareForUse();
+    virtual bool empty() const;
+    virtual wordlist *dump() const;
+
+    /// parses one "acl name type acl1 acl2..." line, appending to nodes
+    void lineParse();
+
+    /// appends the node to the collection and takes control over it
+    void add(ACL *node);
+
+protected:
+    /// checks whether the nodes match, starting with the given one
+    /// kids determine what a match means for their type of intermediate nodes
+    virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const = 0;
+
+    /* ACL API */
+    virtual int match(ACLChecklist *checklist);
+
+    std::vector<ACL*> nodes; ///< children nodes of this intermediate node
+};
+
+} // namespace Acl
+
+#endif /* SQUID_ACL_INNER_NODE_H */

=== modified file 'src/acl/Makefile.am'
--- src/acl/Makefile.am	2012-11-13 18:19:17 +0000
+++ src/acl/Makefile.am	2013-05-13 22:48:23 +0000
@@ -8,7 +8,14 @@
 	Acl.cc \
 	Acl.h \
 	Checklist.cc \
-	Checklist.h
+	Checklist.h \
+	forward.h \
+	InnerNode.cc \
+	InnerNode.h \
+	BoolOps.cc \
+	BoolOps.h \
+	Tree.cc \
+	Tree.h
 
 ## Data-dependent Squid/transaction state used by specific ACLs.
 ## Does not refer to specific ACLs to avoid circular dependencies.
@@ -36,6 +43,10 @@
 	TimeData.h \
 	Asn.cc \
 	Asn.h \
+	AllOf.cc \
+	AllOf.h \
+	AnyOf.cc \
+	AnyOf.h \
 	Browser.cc \
 	Browser.h \
 	DestinationAsn.h \

=== modified file 'src/acl/SourceDomain.cc'
--- src/acl/SourceDomain.cc	2013-01-27 17:35:07 +0000
+++ src/acl/SourceDomain.cc	2013-05-13 22:48:23 +0000
@@ -51,7 +51,6 @@
 void
 SourceDomainLookup::checkForAsync(ACLChecklist *checklist) const
 {
-    checklist->asyncInProgress(true);
     fqdncache_nbgethostbyaddr(Filled(checklist)->src_addr, LookupDone, checklist);
 }
 
@@ -59,13 +58,9 @@
 SourceDomainLookup::LookupDone(const char *fqdn, const DnsLookupDetails &details, void *data)
 {
     ACLFilledChecklist *checklist = Filled((ACLChecklist*)data);
-    assert (checklist->asyncState() == SourceDomainLookup::Instance());
-
-    checklist->asyncInProgress(false);
-    checklist->changeState (ACLChecklist::NullState::Instance());
     checklist->markSourceDomainChecked();
     checklist->request->recordLookup(details);
-    checklist->matchNonBlocking();
+    checklist->resumeNonBlockingCheck(SourceDomainLookup::Instance());
 }
 
 int
@@ -79,8 +74,9 @@
     } else if (!checklist->sourceDomainChecked()) {
         /* FIXME: Using AclMatchedName here is not OO correct. Should find a way to the current acl */
         debugs(28, 3, "aclMatchAcl: Can't yet compare '" << AclMatchedName << "' ACL for '" << checklist->src_addr << "'");
-        checklist->changeState(SourceDomainLookup::Instance());
-        return 0;
+        if (checklist->goAsync(SourceDomainLookup::Instance()))
+            return -1;
+        // else fall through to "none" match, hiding the lookup failure (XXX)
     }
 
     return data->match("none");

=== added file 'src/acl/Tree.cc'
--- src/acl/Tree.cc	1970-01-01 00:00:00 +0000
+++ src/acl/Tree.cc	2013-05-13 22:48:23 +0000
@@ -0,0 +1,76 @@
+#include "squid.h"
+#include "acl/Tree.h"
+#include "wordlist.h"
+
+CBDATA_NAMESPACED_CLASS_INIT(Acl, Tree);
+
+
+allow_t
+Acl::Tree::winningAction() const
+{
+    return actionAt(lastMatch_ - nodes.begin());
+}
+
+allow_t
+Acl::Tree::lastAction() const
+{
+    if (actions.empty())
+        return ACCESS_DUNNO;
+    return actions.back();
+}
+
+/// computes action that corresponds to the position of the matched rule
+allow_t
+Acl::Tree::actionAt(const Nodes::size_type pos) const
+{
+    assert(0 <= pos && pos < nodes.size());
+    if (actions.size()) {
+        assert(actions.size() == nodes.size());
+        return actions[pos];
+    }
+    // default for matched rules in trees without actions
+    return ACCESS_ALLOWED;
+}
+
+void
+Acl::Tree::add(ACL *rule, const allow_t &action)
+{
+    // either all rules have actions or none
+    assert(nodes.size() == actions.size());
+    InnerNode::add(rule);
+    actions.push_back(action);
+}
+
+void
+Acl::Tree::add(ACL *rule)
+{
+    // either all rules have actions or none
+    assert(actions.empty());
+    InnerNode::add(rule);
+}
+
+wordlist*
+Acl::Tree::treeDump(const char *prefix, const ActionToString &convert) const
+{
+    wordlist *text = NULL;
+    Actions::const_iterator action = actions.begin();
+    typedef Nodes::const_iterator NCI;
+    for (NCI node = nodes.begin(); node != nodes.end(); ++node) {
+
+        wordlistAdd(&text, prefix);
+
+        if (action != actions.end()) {
+            const char *act = convert ? convert[action->kind] :
+                (*action == ACCESS_ALLOWED ? "Allow" : "Deny");
+            wordlistAdd(&text, act ? act : "???");
+            ++action;
+        }
+
+        wordlist *rule = (*node)->dump();
+        wordlistAddWl(&text, rule);
+        wordlistDestroy(&rule);
+
+        wordlistAdd(&text, "\n");
+    }
+    return text;
+}

=== added file 'src/acl/Tree.h'
--- src/acl/Tree.h	1970-01-01 00:00:00 +0000
+++ src/acl/Tree.h	2013-05-13 22:48:23 +0000
@@ -0,0 +1,44 @@
+#ifndef SQUID_ACL_TREE_H
+#define SQUID_ACL_TREE_H
+
+#include "acl/BoolOps.h"
+
+namespace Acl {
+
+/// An ORed set of rules at the top of the ACL expression tree, providing two
+/// unique properties: cbdata protection and optional rule actions.
+class Tree: public OrNode
+{
+public:
+    /// dumps <name, action, rule, new line> tuples
+    /// action.kind is mapped to a string using the supplied conversion table
+    typedef const char **ActionToString;
+    wordlist* treeDump(const char *name, const ActionToString &convert) const;
+
+    /// Returns the corresponding action after a successful tree match.
+    allow_t winningAction() const;
+
+    /// what action to use if no nodes matched
+    allow_t lastAction() const;
+
+    /// appends and takes control over the rule with a given action
+    void add(ACL *rule, const allow_t &action);
+    void add(ACL *rule); ///< same as InnerNode::add()
+
+protected:
+    allow_t actionAt(const Nodes::size_type pos) const;
+
+    /// if not empty, contains actions corresponding to InnerNode::nodes
+    typedef std::vector<allow_t> Actions;
+    Actions actions;
+
+private:
+    // XXX: We should use refcounting instead, but it requires making ACLs
+    // refcounted as well. Otherwise, async lookups will reach deleted ACLs.
+    CBDATA_CLASS2(Tree);
+};
+
+
+} // namespace Acl
+
+#endif /* SQUID_ACL_TREE_H */

=== added file 'src/acl/forward.h'
--- src/acl/forward.h	1970-01-01 00:00:00 +0000
+++ src/acl/forward.h	2013-05-13 22:48:23 +0000
@@ -0,0 +1,30 @@
+#ifndef SQUID_ACL_FORWARD_H
+#define SQUID_ACL_FORWARD_H
+
+class ACL;
+class ACLChecklist;
+class ACLFilledChecklist;
+class ACLList;
+
+class AclAddress;
+class AclDenyInfoList;
+class AclSizeLimit;
+
+
+namespace Acl {
+
+class InnerNode;
+class NotNode;
+class AndNode;
+class OrNode;
+class Tree;
+
+} // namespace Acl
+
+#define ACL_NAME_SZ 64
+
+// XXX: remove after review and before commit, after renaming all users?
+#define acl_access Acl::Tree
+#define ACLList Acl::Tree
+
+#endif /* SQUID_ACL_FORWARD_H */

=== modified file 'src/adaptation/AccessRule.cc'
--- src/adaptation/AccessRule.cc	2012-09-23 09:04:21 +0000
+++ src/adaptation/AccessRule.cc	2013-05-13 22:48:23 +0000
@@ -20,7 +20,7 @@
 void
 Adaptation::AccessRule::parse(ConfigParser &parser)
 {
-    aclParseAccessLine(parser, &acl);
+    aclParseAccessLine("adaptation_access", parser, &acl);
 }
 
 void

=== modified file 'src/adaptation/AccessRule.h'
--- src/adaptation/AccessRule.h	2012-09-21 14:57:30 +0000
+++ src/adaptation/AccessRule.h	2013-05-13 22:48:23 +0000
@@ -1,10 +1,10 @@
 #ifndef SQUID_ADAPTATION__ACCESS_RULE_H
 #define SQUID_ADAPTATION__ACCESS_RULE_H
 
+#include "acl/forward.h"
+#include "adaptation/forward.h"
 #include "SquidString.h"
-#include "adaptation/forward.h"
 
-class acl_access;
 class ConfigParser;
 
 namespace Adaptation

=== modified file 'src/adaptation/Config.h'
--- src/adaptation/Config.h	2012-10-26 19:42:31 +0000
+++ src/adaptation/Config.h	2013-05-13 22:48:23 +0000
@@ -2,6 +2,7 @@
 #define SQUID_ADAPTATION__CONFIG_H
 
 #include "event.h"
+#include "acl/forward.h"
 #include "acl/Gadgets.h"
 #include "base/AsyncCall.h"
 #include "adaptation/forward.h"
@@ -9,7 +10,6 @@
 #include "Notes.h"
 #include "SquidString.h"
 
-class acl_access;
 class ConfigParser;
 class HttpRequest;
 class HttpReply;

=== modified file 'src/adaptation/icap/Config.h'
--- src/adaptation/icap/Config.h	2012-09-01 14:38:36 +0000
+++ src/adaptation/icap/Config.h	2013-05-13 22:48:23 +0000
@@ -36,11 +36,10 @@
 
 #include "event.h"
 #include "base/AsyncCall.h"
+#include "acl/forward.h"
 #include "adaptation/Config.h"
 #include "adaptation/icap/ServiceRep.h"
 
-class acl_access;
-
 namespace Adaptation
 {
 namespace Icap

=== modified file 'src/auth/Acl.cc'
--- src/auth/Acl.cc	2013-05-13 03:57:03 +0000
+++ src/auth/Acl.cc	2013-05-13 23:32:23 +0000
@@ -58,8 +58,10 @@
         break;
 
     case AUTH_ACL_HELPER:
-        debugs(28, 4, HERE << "returning " << ACCESS_DUNNO << " sending credentials to helper.");
-        checklist->changeState(ProxyAuthLookup::Instance());
+        if (checklist->goAsync(ProxyAuthLookup::Instance()))
+            debugs(28, 4, "returning " << ACCESS_DUNNO << " sending credentials to helper.");
+        else
+            debugs(28, 2, "cannot go async; returning " << ACCESS_DUNNO);
         return ACCESS_DUNNO; // XXX: break this down into DUNNO, EXPIRED_OK, EXPIRED_BAD states
 
     case AUTH_ACL_CHALLENGE:

=== modified file 'src/auth/AclMaxUserIp.cc'
--- src/auth/AclMaxUserIp.cc	2013-01-25 16:53:16 +0000
+++ src/auth/AclMaxUserIp.cc	2013-05-13 22:48:23 +0000
@@ -139,8 +139,8 @@
     case ACCESS_AUTH_REQUIRED:
     default:
         // If the answer is not allowed or denied (matches/not matches) and
-        // async authentication is not needed (asyncNeeded), then we are done.
-        if (!checklist->asyncNeeded())
+        // async authentication is not in progress, then we are done.
+        if (checklist->keepMatching())
             checklist->markFinished(answer, "AuthenticateAcl exception");
         return -1; // other
     }

=== modified file 'src/auth/AclProxyAuth.cc'
--- src/auth/AclProxyAuth.cc	2013-04-04 06:15:00 +0000
+++ src/auth/AclProxyAuth.cc	2013-05-13 22:48:23 +0000
@@ -92,8 +92,8 @@
     case ACCESS_AUTH_REQUIRED:
     default:
         // If the answer is not allowed or denied (matches/not matches) and
-        // async authentication is not needed (asyncNeeded), then we are done.
-        if (!checklist->asyncNeeded())
+        // async authentication is not in progress, then we are done.
+        if (checklist->keepMatching())
             checklist->markFinished(answer, "AuthenticateAcl exception");
         return -1; // other
     }
@@ -140,7 +140,6 @@
 {
     ACLFilledChecklist *checklist = Filled(cl);
 
-    checklist->asyncInProgress(true);
     debugs(28, 3, HERE << "checking password via authenticator");
 
     /* make sure someone created auth_user_request for us */
@@ -154,8 +153,6 @@
 {
     ACLFilledChecklist *checklist = Filled(static_cast<ACLChecklist*>(data));
 
-    assert (checklist->asyncState() == ProxyAuthLookup::Instance());
-
     if (checklist->auth_user_request == NULL || !checklist->auth_user_request->valid() || checklist->conn() == NULL) {
         /* credentials could not be checked either way
          * restart the whole process */
@@ -167,9 +164,7 @@
         }
     }
 
-    checklist->asyncInProgress(false);
-    checklist->changeState (ACLChecklist::NullState::Instance());
-    checklist->matchNonBlocking();
+    checklist->resumeNonBlockingCheck(ProxyAuthLookup::Instance());
 }
 
 ACL *

=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc	2013-05-13 03:57:03 +0000
+++ src/cache_cf.cc	2013-05-13 23:32:23 +0000
@@ -38,6 +38,7 @@
 #include "acl/AclSizeLimit.h"
 #include "acl/Gadgets.h"
 #include "acl/MethodData.h"
+#include "acl/Tree.h"
 #include "anyp/PortCfg.h"
 #include "AuthReg.h"
 #include "base/RunnersRegistry.h"
@@ -1282,11 +1283,15 @@
  *****************************************************************************/
 
 static void
+dump_wordlist(StoreEntry * entry, wordlist *words)
+{
+    for (wordlist *word = words; word; word = words->next)
+        storeAppendPrintf(entry, "%s ", word->key);
+}
+
+static void
 dump_acl(StoreEntry * entry, const char *name, ACL * ae)
 {
-    wordlist *w;
-    wordlist *v;
-
     while (ae != NULL) {
         debugs(3, 3, "dump_acl: " << name << " " << ae->name);
         storeAppendPrintf(entry, "%s %s %s %s ",
@@ -1294,13 +1299,8 @@
                           ae->name,
                           ae->typeString(),
                           ae->flags.flagsStr());
-        v = w = ae->dump();
-
-        while (v != NULL) {
-            debugs(3, 3, "dump_acl: " << name << " " << ae->name << " " << v->key);
-            storeAppendPrintf(entry, "%s ", v->key);
-            v = v->next;
-        }
+        wordlist *w = ae->dump();
+        dump_wordlist(entry, w);
 
         storeAppendPrintf(entry, "\n");
         wordlistDestroy(&w);
@@ -1323,33 +1323,29 @@
 void
 dump_acl_list(StoreEntry * entry, ACLList * head)
 {
-    ACLList *l;
-
-    for (l = head; l; l = l->next) {
-        storeAppendPrintf(entry, " %s%s",
-                          l->op ? null_string : "!",
-                          l->_acl->name);
-    }
+    wordlist *values = head->dump();
+    dump_wordlist(entry, values);
+    wordlistDestroy(&values);
 }
 
 void
 dump_acl_access(StoreEntry * entry, const char *name, acl_access * head)
 {
-    acl_access *l;
-
-    for (l = head; l; l = l->next) {
-        storeAppendPrintf(entry, "%s %s",
-                          name,
-                          l->allow ? "Allow" : "Deny");
-        dump_acl_list(entry, l->aclList);
-        storeAppendPrintf(entry, "\n");
-    }
+    wordlist *lines = head->treeDump(name, NULL);
+    dump_wordlist(entry, lines);
+    wordlistDestroy(&lines);
 }
 
 static void
 parse_acl_access(acl_access ** head)
 {
-    aclParseAccessLine(LegacyParser, head);
+    aclParseAccessLine(cfg_directive, LegacyParser, head);
+}
+
+static void
+parse_acl_access(const char *directive, acl_access ** head)
+{
+    aclParseAccessLine(directive, LegacyParser, head);
 }
 
 static void
@@ -1426,7 +1422,7 @@
     CBDATA_INIT_TYPE_FREECB(AclAddress, freed_acl_address);
     l = cbdataAlloc(AclAddress);
     parse_address(&l->addr);
-    aclParseAclList(LegacyParser, &l->aclList);
+    aclParseAclList(LegacyParser, &l->aclList, l->addr);
 
     while (*tail)
         tail = &(*tail)->next;
@@ -1494,7 +1490,7 @@
 
     l->tos = (tos_t)tos;
 
-    aclParseAclList(LegacyParser, &l->aclList);
+    aclParseAclList(LegacyParser, &l->aclList, token);
 
     while (*tail)
         tail = &(*tail)->next;
@@ -1565,7 +1561,7 @@
 
     l->nfmark = mark;
 
-    aclParseAclList(LegacyParser, &l->aclList);
+    aclParseAclList(LegacyParser, &l->aclList, token);
 
     while (*tail)
         tail = &(*tail)->next;
@@ -1623,7 +1619,7 @@
 
     parse_b_int64_t(&l->size);
 
-    aclParseAclList(LegacyParser, &l->aclList);
+    aclParseAclList(LegacyParser, &l->aclList, l->size);
 
     while (*tail)
         tail = &(*tail)->next;
@@ -1760,7 +1756,10 @@
     HeaderManglers *manglers = *pm;
     headerMangler *mangler = manglers->track(t);
     assert(mangler);
-    parse_acl_access(&mangler->access_list);
+
+    std::string directive = "http_header_access ";
+    directive += t;
+    parse_acl_access(directive.c_str(), &mangler->access_list);
 }
 
 static void
@@ -2523,7 +2522,9 @@
         return;
     }
 
-    aclParseAccessLine(LegacyParser, &p->access);
+    std::string directive = "peer_access ";
+    directive += host;
+    aclParseAccessLine(directive.c_str(), LegacyParser, &p->access);
 }
 
 static void
@@ -4056,7 +4057,7 @@
 
     if (strcmp(filename, "none") == 0) {
         cl->type = Log::Format::CLF_NONE;
-        aclParseAclList(LegacyParser, &cl->aclList);
+        aclParseAclList(LegacyParser, &cl->aclList, filename);
         while (*logs)
             logs = &(*logs)->next;
         *logs = cl;
@@ -4105,7 +4106,7 @@
     if (cl->type == Log::Format::CLF_UNKNOWN)
         setLogformat(cl, "squid", true);
 
-    aclParseAclList(LegacyParser, &cl->aclList);
+    aclParseAclList(LegacyParser, &cl->aclList, cl->filename);
 
     while (*logs)
         logs = &(*logs)->next;
@@ -4507,7 +4508,7 @@
         return;
     }
 
-    aclParseAclList(LegacyParser, &ca->aclList);
+    aclParseAclList(LegacyParser, &ca->aclList, al);
 
     while (*cert_adapt)
         cert_adapt = &(*cert_adapt)->next;
@@ -4561,7 +4562,7 @@
         return;
     }
 
-    aclParseAclList(LegacyParser, &cs->aclList);
+    aclParseAclList(LegacyParser, &cs->aclList, al);
 
     while (*cert_sign)
         cert_sign = &(*cert_sign)->next;
@@ -4647,31 +4648,30 @@
         sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd;
     }
 
-    acl_access *A = new acl_access;
-    A->allow = allow_t(ACCESS_ALLOWED);
+    allow_t action = allow_t(ACCESS_ALLOWED);
 
     if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0) {
-        A->allow.kind = Ssl::bumpClientFirst;
+        action.kind = Ssl::bumpClientFirst;
         bumpCfgStyleNow = bcsNew;
     } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) {
-        A->allow.kind = Ssl::bumpServerFirst;
+        action.kind = Ssl::bumpServerFirst;
         bumpCfgStyleNow = bcsNew;
     } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) {
-        A->allow.kind = Ssl::bumpNone;
+        action.kind = Ssl::bumpNone;
         bumpCfgStyleNow = bcsNew;
     } else if (strcmp(bm, "allow") == 0) {
         debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated "
                "\"ssl_bump allow <acl>\" to \"ssl_bump client-first <acl>\" which "
                "is usually inferior to the newer server-first "
                "bumping mode. Update your ssl_bump rules.");
-        A->allow.kind = Ssl::bumpClientFirst;
+        action.kind = Ssl::bumpClientFirst;
         bumpCfgStyleNow = bcsOld;
         sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpClientFirst;
     } else if (strcmp(bm, "deny") == 0) {
         debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated "
                "\"ssl_bump deny <acl>\" to \"ssl_bump none <acl>\". Update "
                "your ssl_bump rules.");
-        A->allow.kind = Ssl::bumpNone;
+        action.kind = Ssl::bumpNone;
         bumpCfgStyleNow = bcsOld;
         sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpNone;
     } else {
@@ -4689,22 +4689,26 @@
 
     bumpCfgStyleLast = bumpCfgStyleNow;
 
-    aclParseAclList(LegacyParser, &A->aclList);
-
-    acl_access *B, **T;
-    for (B = *ssl_bump, T = ssl_bump; B; T = &B->next, B = B->next);
-    *T = A;
+    ACL *rule = new Acl::AndNode;
+    rule->parse();
+    // empty rule OK
+    rule->context("(ssl_bump rule)", config_input_line);
+
+    assert(ssl_bump);
+    if (!*ssl_bump) {
+        *ssl_bump = new Acl::Tree;
+        (*ssl_bump)->context("(ssl_bump rules)", config_input_line);
+    }
+
+    (*ssl_bump)->add(rule, action);
 }
 
 static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
 {
-    acl_access *sb;
-    for (sb = ssl_bump; sb != NULL; sb = sb->next) {
-        storeAppendPrintf(entry, "%s ", name);
-        storeAppendPrintf(entry, "%s ", Ssl::bumpMode(sb->allow.kind));
-        if (sb->aclList)
-            dump_acl_list(entry, sb->aclList);
-        storeAppendPrintf(entry, "\n");
+    if (ssl_bump) {
+        wordlist *lines = ssl_bump->treeDump(name, Ssl::BumpModeStr);
+        dump_wordlist(entry, lines);
+        wordlistDestroy(&lines);
     }
 }
 
@@ -4758,7 +4762,8 @@
         }
         hwa.valueFormat = nlf;
     }
-    aclParseAclList(LegacyParser, &hwa.aclList);
+
+    aclParseAclList(LegacyParser, &hwa.aclList, (hwa.fieldName + ':' + hwa.fieldValue).c_str());
     (*headers)->push_back(hwa);
 }
 

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2013-05-13 03:57:03 +0000
+++ src/cf.data.pre	2013-05-13 23:32:23 +0000
@@ -1076,6 +1076,29 @@
 	  # The SHA1 digest algorithm is the default and is currently
 	  # the only algorithm supported (-sha1).
 ENDIF
+	acl aclname any-of acl1 acl2 ...
+	  # match any one of the acls [fast or slow]
+	  # The first matching ACL stops further ACL evaluation.
+	  #
+	  # ACLs from multiple any-of lines with the same name are ORed.
+	  # For example, A = (a1 or a2) or (a3 or a4) can be written as
+	  #   acl A any-of a1 a2
+	  #   acl A any-of a3 a4
+	  #
+	  # This group ACL is fast if all evaluated ACLs in the group are fast
+	  # and slow otherwise.
+
+	acl aclname all-of acl1 acl2 ... 
+	  # match all of the acls [fast or slow]
+	  # The first mismatching ACL stops further ACL evaluation.
+	  #
+	  # ACLs from multiple all-of lines with the same name are ORed.
+	  # For example, B = (b1 and b2) or (b3 and b4) can be written as
+	  #   acl B all-of b1 b2
+	  #   acl B all-of b3 b4
+	  #
+	  # This group ACL is fast if all evaluated ACLs in the group are fast
+	  # and slow otherwise.
 
 	Examples:
 		acl macaddress arp 09:00:2b:23:45:67

=== modified file 'src/cf_gen.cc'
--- src/cf_gen.cc	2013-04-23 12:35:44 +0000
+++ src/cf_gen.cc	2013-05-13 22:48:23 +0000
@@ -631,6 +631,7 @@
     fout << "    if (!strcmp(token, \"" << aName << "\")) {" << std::endl;
     if (ifdef.size())
         fout << "#if " << ifdef << std::endl;
+    fout << "        cfg_directive = \"" << aName << "\";" << std::endl;
     fout << "        ";
     if (type.compare("obsolete") == 0) {
         fout << "debugs(0, DBG_CRITICAL, \"ERROR: Directive '" << aName << "' is obsolete.\");\n";
@@ -645,6 +646,7 @@
         fout << "parse_" << type << "(&" << loc << (array_flag ? "[0]" : "") << ");";
     }
     fout << std::endl;
+    fout << "        cfg_directive = NULL;" << std::endl;
     if (ifdef.size()) {
         fout <<
         "#else" << std::endl <<

=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc	2013-05-13 03:57:03 +0000
+++ src/client_side_request.cc	2013-05-13 23:32:23 +0000
@@ -780,8 +780,7 @@
     debugs(85, 2, "The request " <<
            RequestMethodStr(http->request->method) << " " <<
            http->uri << " is " << answer <<
-           ", because it matched '" <<
-           (AclMatchedName ? AclMatchedName : "NO ACL's") << "'" );
+           "; last ACL checked: " << (AclMatchedName ? AclMatchedName : "[none]"));
 
 #if USE_AUTH
     char const *proxy_auth_msg = "<null>";

=== modified file 'src/client_side_request.h'
--- src/client_side_request.h	2013-03-16 04:57:43 +0000
+++ src/client_side_request.h	2013-05-13 22:48:23 +0000
@@ -30,6 +30,7 @@
 #ifndef SQUID_CLIENTSIDEREQUEST_H
 #define SQUID_CLIENTSIDEREQUEST_H
 
+#include "acl/forward.h"
 #include "HttpHeader.h"
 #include "clientStream.h"
 #include "client_side.h"
@@ -45,8 +46,6 @@
 class HttpMsg;
 #endif
 
-class acl_access;
-class ACLFilledChecklist;
 class ClientRequestContext;
 class ConnStateData;
 class MemObject;

=== modified file 'src/defines.h'
--- src/defines.h	2013-01-13 01:13:10 +0000
+++ src/defines.h	2013-05-13 22:48:23 +0000
@@ -41,7 +41,6 @@
 #define BUFSIZ  4096            /* make unreasonable guess */
 #endif
 
-#define ACL_NAME_SZ 32
 #define BROWSERNAMELEN 128
 
 #define ACL_SUNDAY	0x01

=== modified file 'src/external_acl.cc'
--- src/external_acl.cc	2013-04-29 13:31:05 +0000
+++ src/external_acl.cc	2013-05-13 22:48:23 +0000
@@ -777,14 +777,17 @@
 static allow_t
 aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch)
 {
-    const char *key = "";
     debugs(82, 9, HERE << "acl=\"" << acl->def->name << "\"");
     external_acl_entry *entry = ch->extacl_entry;
 
+    external_acl_message = "MISSING REQUIRED INFORMATION";
+
     if (entry) {
         if (cbdataReferenceValid(entry) && entry->def == acl->def) {
             /* Ours, use it.. if the key matches */
-            key = makeExternalAclKey(ch, acl);
+            const char *key = makeExternalAclKey(ch, acl);
+            if (!key)
+                return ACCESS_DUNNO; // insufficent data to continue
             if (strcmp(key, (char*)entry->key) != 0) {
                 debugs(82, 9, HERE << "entry key='" << (char *)entry->key << "', our key='" << key << "' dont match. Discarded.");
                 // too bad. need a new lookup.
@@ -796,7 +799,7 @@
             debugs(82, 9, HERE << "entry " << entry << " not valid or not ours. Discarded.");
             if (entry) {
                 debugs(82, 9, HERE << "entry def=" << entry->def << ", our def=" << acl->def);
-                key = makeExternalAclKey(ch, acl);
+                const char *key = makeExternalAclKey(ch, acl); // may be nil
                 debugs(82, 9, HERE << "entry key='" << (char *)entry->key << "', our key='" << key << "'");
             }
             cbdataReferenceDone(ch->extacl_entry);
@@ -804,8 +807,6 @@
         }
     }
 
-    external_acl_message = "MISSING REQUIRED INFORMATION";
-
     if (!entry) {
         debugs(82, 9, HERE << "No helper entry available");
 #if USE_AUTH
@@ -820,7 +821,7 @@
             debugs(82, 3, HERE << acl->def->name << " user is authenticated.");
         }
 #endif
-        key = makeExternalAclKey(ch, acl);
+        const char *key = makeExternalAclKey(ch, acl);
 
         if (!key) {
             /* Not sufficient data to process */
@@ -847,7 +848,8 @@
 
             if (acl->def->theHelper->stats.queue_size < (int)acl->def->theHelper->childs.n_active) {
                 debugs(82, 2, HERE << "\"" << key << "\": queueing a call.");
-                ch->changeState(ExternalACLLookup::Instance());
+                if (!ch->goAsync(ExternalACLLookup::Instance()))
+                    debugs(82, 2, "\"" << key << "\": no async support!");
                 debugs(82, 2, HERE << "\"" << key << "\": return -1.");
                 return ACCESS_DUNNO; // expired cached or simply absent entry
             } else {
@@ -900,8 +902,8 @@
     case ACCESS_AUTH_REQUIRED:
     default:
         // If the answer is not allowed or denied (matches/not matches) and
-        // async authentication is not needed (asyncNeeded), then we are done.
-        if (!checklist->asyncNeeded())
+        // async authentication is not in progress, then we are done.
+        if (checklist->keepMatching())
             checklist->markFinished(answer, "aclMatchExternal exception");
         return -1; // other
     }
@@ -974,7 +976,9 @@
             str = ch->rfc931;
 
             if (!str || !*str) {
-                ch->changeState(IdentLookup::Instance());
+                // if we fail to go async, we still return NULL and the caller
+                // will detect the failure in ACLExternal::match().
+                (void)ch->goAsync(IdentLookup::Instance());
                 return NULL;
             }
 
@@ -1392,7 +1396,7 @@
 
     ACLFilledChecklist *ch = Filled(checklist);
     const char *key = makeExternalAclKey(ch, acl);
-    assert(key);
+    assert(key); // XXX: will fail if EXT_ACL_IDENT case needs an async lookup
 
     debugs(82, 2, HERE << (inBackground ? "bg" : "fg") << " lookup in '" <<
            def->name << "' for '" << key << "'");
@@ -1542,7 +1546,6 @@
     assert(acl);
     ACLExternal *me = dynamic_cast<ACLExternal *> (acl);
     assert (me);
-    checklist->asyncInProgress(true);
     ACLExternal::ExternalAclLookup(checklist, me);
 }
 
@@ -1552,9 +1555,7 @@
 {
     ACLFilledChecklist *checklist = Filled(static_cast<ACLChecklist*>(data));
     checklist->extacl_entry = cbdataReference((external_acl_entry *)result);
-    checklist->asyncInProgress(false);
-    checklist->changeState (ACLChecklist::NullState::Instance());
-    checklist->matchNonBlocking();
+    checklist->resumeNonBlockingCheck(ExternalACLLookup::Instance());
 }
 
 /* This registers "external" in the registry. To do dynamic definitions

=== modified file 'src/globals.h'
--- src/globals.h	2013-02-17 01:55:00 +0000
+++ src/globals.h	2013-05-13 22:48:23 +0000
@@ -47,6 +47,8 @@
 extern char ThisCache[RFC2181_MAXHOSTNAMELEN << 1];
 extern char ThisCache2[RFC2181_MAXHOSTNAMELEN << 1];
 extern char config_input_line[BUFSIZ];
+/// During parsing, the name of the current squid.conf directive being parsed.
+extern const char *cfg_directive; /* NULL */
 extern const char *DefaultConfigFile;	/* DEFAULT_CONFIG_FILE */
 extern const char *cfg_filename;	/* NULL */
 extern const char *dash_str;	/* "-" */

=== modified file 'src/ident/AclIdent.cc'
--- src/ident/AclIdent.cc	2012-09-01 14:38:36 +0000
+++ src/ident/AclIdent.cc	2013-05-13 22:48:23 +0000
@@ -89,14 +89,18 @@
     } else if (checklist->conn() != NULL && checklist->conn()->clientConnection != NULL && checklist->conn()->clientConnection->rfc931[0]) {
         return data->match(checklist->conn()->clientConnection->rfc931);
     } else if (checklist->conn() != NULL && Comm::IsConnOpen(checklist->conn()->clientConnection)) {
-        debugs(28, 3, HERE << "switching to ident lookup state");
-        checklist->changeState(IdentLookup::Instance());
-        return 0;
+        if (checklist->goAsync(IdentLookup::Instance())) {
+            debugs(28, 3, "switching to ident lookup state");
+            return -1;
+        }
+        // else fall through to ACCESS_DUNNO failure below
     } else {
         debugs(28, DBG_IMPORTANT, HERE << "Can't start ident lookup. No client connection" );
-        checklist->markFinished(ACCESS_DUNNO, "cannot start ident lookup");
-        return -1;
+        // fall through to ACCESS_DUNNO failure below
     }
+
+    checklist->markFinished(ACCESS_DUNNO, "cannot start ident lookup");
+    return -1;
 }
 
 wordlist *
@@ -133,7 +137,6 @@
     // check that ACLIdent::match() tested this lookup precondition
     assert(conn && Comm::IsConnOpen(conn->clientConnection));
     debugs(28, 3, HERE << "Doing ident lookup" );
-    checklist->asyncInProgress(true);
     Ident::Start(checklist->conn()->clientConnection, LookupDone, checklist);
 }
 
@@ -141,7 +144,6 @@
 IdentLookup::LookupDone(const char *ident, void *data)
 {
     ACLFilledChecklist *checklist = Filled(static_cast<ACLChecklist*>(data));
-    assert(checklist->asyncState() == IdentLookup::Instance());
 
     if (ident) {
         xstrncpy(checklist->rfc931, ident, USER_IDENT_SZ);
@@ -156,9 +158,7 @@
     if (checklist->conn() != NULL && checklist->conn()->clientConnection != NULL && !checklist->conn()->clientConnection->rfc931[0])
         xstrncpy(checklist->conn()->clientConnection->rfc931, checklist->rfc931, USER_IDENT_SZ);
 
-    checklist->asyncInProgress(false);
-    checklist->changeState(ACLChecklist::NullState::Instance());
-    checklist->matchNonBlocking();
+    checklist->resumeNonBlockingCheck(IdentLookup::Instance());
 }
 
 #endif /* USE_IDENT */

=== modified file 'src/ip/QosConfig.h'
--- src/ip/QosConfig.h	2012-10-04 00:23:44 +0000
+++ src/ip/QosConfig.h	2013-05-13 22:48:23 +0000
@@ -1,6 +1,7 @@
 #ifndef SQUID_QOSCONFIG_H
 #define SQUID_QOSCONFIG_H
 
+#include "acl/forward.h"
 #include "hier_code.h"
 #include "ip/forward.h"
 
@@ -16,7 +17,6 @@
 #include <limits>
 #endif
 
-class ACLList;
 class fde;
 
 // TODO: move to new ACL framework

=== modified file 'src/log/CustomLog.h'
--- src/log/CustomLog.h	2013-05-11 20:59:44 +0000
+++ src/log/CustomLog.h	2013-05-13 23:32:23 +0000
@@ -29,9 +29,9 @@
  *
  */
 //#include "format/Format.h"
+#include "acl/forward.h"
 #include "log/Formats.h"
 
-class ACLList;
 class Logfile;
 namespace Format
 {

=== modified file 'src/ssl/ProxyCerts.h'
--- src/ssl/ProxyCerts.h	2012-10-04 00:23:44 +0000
+++ src/ssl/ProxyCerts.h	2013-05-13 22:48:23 +0000
@@ -30,7 +30,7 @@
  */
 
 #if USE_SSL
-class ACLList;
+#include "acl/forward.h"
 
 class sslproxy_cert_sign
 {

# Begin bundle
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

