text: /*
   *
   * Copyright (c) 2006 The Apache Software Foundation
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *    http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   */
  
  #include "qpid/acl/AclData.h"
  #include "qpid/acl/AclValidator.h"
  #include "qpid/log/Statement.h"
  #include "qpid/sys/IntegerTypes.h"
  #include <boost/lexical_cast.hpp>
  #include <boost/shared_ptr.hpp>
  #include <sstream>
  #include <iomanip>
  
  namespace qpid {
  namespace acl {
  
  //
  // Instantiate the keyword strings
  //
  const std::string AclData::ACL_KEYWORD_USER_SUBST        = "${user}";
  const std::string AclData::ACL_KEYWORD_DOMAIN_SUBST      = "${domain}";
  const std::string AclData::ACL_KEYWORD_USERDOMAIN_SUBST  = "${userdomain}";
  const std::string AclData::ACL_KEYWORD_ALL               = "all";
  const std::string AclData::ACL_KEYWORD_ACL               = "acl";
  const std::string AclData::ACL_KEYWORD_GROUP             = "group";
  const std::string AclData::ACL_KEYWORD_QUOTA             = "quota";
  const std::string AclData::ACL_KEYWORD_QUOTA_CONNECTIONS = "connections";
  const std::string AclData::ACL_KEYWORD_QUOTA_QUEUES      = "queues";
  const char        AclData::ACL_SYMBOL_WILDCARD           = '*';
  const std::string AclData::ACL_KEYWORD_WILDCARD          = "*";
  const char        AclData::ACL_SYMBOL_LINE_CONTINUATION  = '\\';
  const std::string AclData::ACL_KEYWORD_DEFAULT_EXCHANGE  = "amq.default";
  
  //
  // constructor
  //
  AclData::AclData():
      decisionMode(qpid::acl::DENY),
      transferAcl(false),
      aclSource("UNKNOWN"),
      connectionDecisionMode(qpid::acl::ALLOW),
      connQuotaRuleSettings(new quotaRuleSet),
      queueQuotaRuleSettings(new quotaRuleSet),
      connBWHostsGlobalRules(new bwHostRuleSet),
      connBWHostsUserRules(new bwHostUserRuleMap)
  {
      for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++) {
          actionList[cnt]=0;
      }
  }
  
  
  //
  // clear
  //
  void AclData::clear ()
  {
      for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++) {
          if (actionList[cnt]) {
              for (unsigned int cnt1=0; cnt1< qpid::acl::OBJECTSIZE; cnt1++) {
                  delete actionList[cnt][cnt1];
              }
          }
          delete[] actionList[cnt];
      }
      transferAcl = false;
      connectionDecisionMode = qpid::acl::ALLOW;
      connQuotaRuleSettings->clear();
      queueQuotaRuleSettings->clear();
      connBWHostsGlobalRules->clear();
      connBWHostsUserRules->clear();
  }
  
  void AclData::printDecisionRules(int userFieldWidth) {
      AclValidator validator;
      QPID_LOG(trace, "ACL: Decision rule cross reference");
      for (int act=0; act<acl::ACTIONSIZE; act++) {
          acl::Action action = acl::Action(act);
          for (int obj=0; obj<acl::OBJECTSIZE; obj++) {
              acl::ObjectType object = acl::ObjectType(obj);
              if (actionList[act] != NULL && actionList[act][obj] != NULL) {
                  for (actObjItr aoitr = actionList[act][obj]->begin();
                      aoitr != actionList[act][obj]->end();
                      aoitr++) {
                      std::string user = (*aoitr).first;
                      ruleSetItr rsitr = (*aoitr).second.end();
                      for (size_t rCnt=0; rCnt < (*aoitr).second.size(); rCnt++) {
                          rsitr--;
                          std::vector<int> candidates;
                          validator.findPossibleLookupMatch(
                              action, object, rsitr->props, candidates);
                          std::stringstream ss;
                          std::string sep("");
                          for (std::vector<int>::const_iterator
                              itr = candidates.begin(); itr != candidates.end(); itr++) {
                              ss << sep << *itr;
                              sep = ",";
                          }
                          QPID_LOG(trace, "ACL: User: "
                              << std::setfill(' ') << std::setw(userFieldWidth +1) << std::left
                              << user << " "
                              << std::setfill(' ') << std::setw(acl::ACTION_STR_WIDTH +1) << std::left
                              << AclHelper::getActionStr(action)
                              << std::setfill(' ') << std::setw(acl::OBJECTTYPE_STR_WIDTH) << std::left
                              << AclHelper::getObjectTypeStr(object)
                              << " Rule: "
                              << rsitr->toString() << " may match Lookups : ("
                              << ss.str() << ")");
                      }
                  }
              } else {
                  // no rules for action/object
              }
          }
      }
  }
  
  //
  // matchProp
  //
  // Compare a rule's property name with a lookup name,
  // The rule's name may contain a trailing '*' to specify a wildcard match.
  //
  bool AclData::matchProp(const std::string& ruleStr,
                          const std::string& lookupStr)
  {
      // allow wildcard on the end of rule strings...
      if (ruleStr.data()[ruleStr.size()-1]==ACL_SYMBOL_WILDCARD) {
          return ruleStr.compare(0,
                                 ruleStr.size()-1,
                                 lookupStr,
                                 0,
                                 ruleStr.size()-1  ) == 0;
      } else {
          return ruleStr.compare(lookupStr) == 0;
      }
  }
  
  
  //
  // lookupMatchRule
  //
  // Check a single rule and if it's a match return the decision
  //
  bool AclData::lookupMatchRule(
      const ruleSetItr&                rsItr,
      const std::string&               id,
      const std::string&               name,
      const std::map<Property, std::string>* params,
      AclResult&                       aclresult)
  {
      QPID_LOG(debug, "ACL: checking rule " <<  rsItr->toString());
  
      bool match = true;
      bool limitChecked = true;
  
      // Iterate this rule's properties. A 'match' is true when
      // all of the rule's properties are found to be satisfied
      // in the lookup param list. The lookup may specify things
      // (they usually do) that are not in the rule properties but
      // these things don't interfere with the rule match.
  
      for (specPropertyMapItr rulePropMapItr  = rsItr->props.begin();
                             (rulePropMapItr != rsItr->props.end()) && match;
                              rulePropMapItr++) {
          // The rule property map's NAME property is given in
          // the calling args and not in the param map.
          if (rulePropMapItr->first == acl::SPECPROP_NAME)
          {
              // substitute user name into object name
              bool result;
              if (rsItr->ruleHasUserSub[PROP_NAME]) {
                  std::string sName(rulePropMapItr->second);
                  substituteUserId(sName, id);
                  result = matchProp(sName, name);
              } else {
                  result = matchProp(rulePropMapItr->second, name);
              }
  
              if (result) {
                  QPID_LOG(debug, "ACL: lookup name '" << name
                              << "' matched with rule name '"
                              << rulePropMapItr->second << "'");
              } else {
                  match = false;
                  QPID_LOG(debug, "ACL: lookup name '" << name
                              << "' didn't match with rule name '"
                              << rulePropMapItr->second << "'");
              }
          } else {
              if (params) {
                  // The rule's property map non-NAME properties
                  //  found in the lookup's params list.
                  // In some cases the param's index is not the same
                  //  as rule's index.
                  propertyMapItr lookupParamItr;
                  switch (rulePropMapItr->first) {
                  case acl::SPECPROP_MAXPAGESLOWERLIMIT:
                  case acl::SPECPROP_MAXPAGESUPPERLIMIT:
                      lookupParamItr = params->find(PROP_MAXPAGES);
                      break;
  
                  case acl::SPECPROP_MAXPAGEFACTORLOWERLIMIT:
                  case acl::SPECPROP_MAXPAGEFACTORUPPERLIMIT:
                      lookupParamItr = params->find(PROP_MAXPAGEFACTOR);
                      break;
  
                  case acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT:
                  case acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT:
                      lookupParamItr = params->find(PROP_MAXQUEUECOUNT);
                      break;
  
                  case acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT:
                  case acl::SPECPROP_MAXQUEUESIZELOWERLIMIT:
                      lookupParamItr = params->find(PROP_MAXQUEUESIZE);
                      break;
  
                  case acl::SPECPROP_MAXFILECOUNTUPPERLIMIT:
                  case acl::SPECPROP_MAXFILECOUNTLOWERLIMIT:
                      lookupParamItr = params->find(PROP_MAXFILECOUNT);
                      break;
  
                  case acl::SPECPROP_MAXFILESIZEUPPERLIMIT:
                  case acl::SPECPROP_MAXFILESIZELOWERLIMIT:
                      lookupParamItr = params->find(PROP_MAXFILESIZE);
                      break;
  
                  default:
                      lookupParamItr = params->find((Property)rulePropMapItr->first);
                      break;
                  };
  
                  if (lookupParamItr == params->end()) {
                      // Now the rule has a specified property
                      // that does not exist in the caller's
                      // lookup params list.
                      // This rule does not match.
                      match = false;
                      QPID_LOG(debug, "ACL: lookup parameter map doesn't contain the rule property '"
                                  << AclHelper::getPropertyStr(rulePropMapItr->first) << "'");
                  } else {
                      // Now account for the business of rules
                      // whose property indexes are mismatched.
                      switch (rulePropMapItr->first) {
                      case acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT:
                      case acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT:
                      case acl::SPECPROP_MAXFILECOUNTUPPERLIMIT:
                      case acl::SPECPROP_MAXFILESIZEUPPERLIMIT:
                      case acl::SPECPROP_MAXPAGESUPPERLIMIT:
                      case acl::SPECPROP_MAXPAGEFACTORUPPERLIMIT:
                          limitChecked &=
                              compareInt(
                                  rulePropMapItr->first,
                                  boost::lexical_cast<std::string>(rulePropMapItr->second),
                                  boost::lexical_cast<std::string>(lookupParamItr->second),
                                  true);
                          break;
  
                      case acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT:
                      case acl::SPECPROP_MAXQUEUESIZELOWERLIMIT:
                      case acl::SPECPROP_MAXFILECOUNTLOWERLIMIT:
                      case acl::SPECPROP_MAXFILESIZELOWERLIMIT:
                      case acl::SPECPROP_MAXPAGESLOWERLIMIT:
                      case acl::SPECPROP_MAXPAGEFACTORLOWERLIMIT:
                          limitChecked &=
                              compareInt(
                                  rulePropMapItr->first,
                                  boost::lexical_cast<std::string>(rulePropMapItr->second),
                                  boost::lexical_cast<std::string>(lookupParamItr->second),
                                  false);
                          break;
  
                      default:
                          bool result;
                          if ((SPECPROP_ALTERNATE  == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_ALTERNATE])  ||
                              (SPECPROP_QUEUENAME  == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_QUEUENAME])) {
                              // These properties are allowed to have username substitution
                              std::string sName(rulePropMapItr->second);
                              substituteUserId(sName, id);
                              result = matchProp(sName, lookupParamItr->second);
                          } else if (SPECPROP_ROUTINGKEY == rulePropMapItr->first) {
                              // Routing key is allowed to have username substitution
                              // and it gets topic exchange matching
                              if (rsItr->ruleHasUserSub[PROP_ROUTINGKEY]) {
                                  std::string sKey(lookupParamItr->second);
                                  substituteKeywords(sKey, id);
                                  result = rsItr->matchRoutingKey(sKey);
                              } else {
                                  result = rsItr->matchRoutingKey(lookupParamItr->second);
                              }
                          } else {
                              // Rules without substitution
                              result = matchProp(rulePropMapItr->second, lookupParamItr->second);
                          }
  
                          if (result) {
                              QPID_LOG(debug, "ACL: the pair("
                                       << AclHelper::getPropertyStr(lookupParamItr->first)
                                       << "," << lookupParamItr->second
                                       << ") given in lookup matched the pair("
                                       << AclHelper::getPropertyStr(rulePropMapItr->first) << ","
                                       << rulePropMapItr->second
                                       << ") given in the rule");
                          } else {
                              match = false;
                              QPID_LOG(debug, "ACL: the pair("
                                       << AclHelper::getPropertyStr(lookupParamItr->first)
                                       << "," << lookupParamItr->second
                                       << ") given in lookup doesn't match the pair("
                                       << AclHelper::getPropertyStr(rulePropMapItr->first)
                                       << "," << rulePropMapItr->second
                                       << ") given in the rule");
                          }
                          break;
                      };
                  }
              } else {
                  // params don't exist.
              }
          }
      }
      if (match) {
          aclresult = rsItr->ruleMode;
          if (!limitChecked) {
              // Now a lookup matched all rule properties but one
              //  of the numeric limit checks has failed.
              // Demote allow rules to corresponding deny rules.
              switch (aclresult) {
              case acl::ALLOW:
                  aclresult = acl::DENY;
                  break;
              case acl::ALLOWLOG:
                  aclresult = acl::DENYLOG;
                  break;
              default:
                  break;
              };
          }
          QPID_LOG(debug,"ACL: Successful match, the decision is:"
                   << AclHelper::getAclResultStr(aclresult));
      } else {
          // This rule did not match the requested lookup and
          // does not contribute to an ACL decision.
      }
      return match;
  }
  
  //
  // lookup - general ACL lookup
  //
  // The ACL main business logic function of matching rules and declaring
  // an allow or deny result.
  //
  AclResult AclData::lookup(
      const std::string&               id,
      const Action&                    action,
      const ObjectType&                objType,
      const std::string&               name,
      std::map<Property, std::string>* params)
  {
      QPID_LOG(debug, "ACL: Lookup for id:" << id
               << " action:" << AclHelper::getActionStr((Action) action)
               << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType)
               << " name:" << name
               << " with params " << AclHelper::propertyMapToString(params));
  
      // A typical log looks like:
      // ACL: Lookup for id:bob@QPID action:create objectType:queue name:q2
      //  with params { durable=false passive=false autodelete=false
      //  exclusive=false alternate= policytype= maxqueuesize=0
      //  maxqueuecount=0 }
  
      // Default result is blanket decision mode for the entire ACL list.
      AclResult aclresult = decisionMode;
  
      // Test for lists of rules at the intersection of the Action & Object
      if (actionList[action] && actionList[action][objType]) {
          // Find the list of rules for this actorId
          AclData::actObjItr itrRule = actionList[action][objType]->find(id);
  
          // If individual actorId not found then find a rule set for '*'.
          if (itrRule == actionList[action][objType]->end()) {
              itrRule = actionList[action][objType]->find(ACL_KEYWORD_WILDCARD);
          }
          if (itrRule != actionList[action][objType]->end()) {
              // A list of rules exists for this actor/action/object tuple.
              // Iterate the rule set to search for a matching rule.
              ruleSetItr rsItr = itrRule->second.end();
              for (int cnt = itrRule->second.size(); cnt != 0; cnt--) {
                  rsItr--;
                  if (lookupMatchRule(rsItr, id, name, params, aclresult)) {
                      return aclresult;
                  }
              }
          } else {
              // The Action-Object list has entries but not for this actorId
              // nor for *.
          }
      } else {
          // The Action-Object list has no entries.
      }
  
      QPID_LOG(debug,"ACL: No successful match, defaulting to the decision mode "
               << AclHelper::getAclResultStr(aclresult));
      return aclresult;
  }
  
  
  //
  // lookupMatchPublishExchangeRule
  //
  // check a single publish exchange rule
  //
  bool AclData::lookupMatchPublishExchangeRule(
      const ruleSetItr&                rsItr,
      const std::string&               id,
      const std::string&               name,
      const std::string&               routingKey,
      AclResult&                       aclresult)
  {
      QPID_LOG(debug, "ACL: checking rule " <<  rsItr->toString());
  
      // Search on exchange name and routing key only if specfied in rule.
      bool match =true;
      if (rsItr->pubExchNameInRule) {
          // substitute user name into object name
          bool result;
  
          if (rsItr->ruleHasUserSub[PROP_NAME]) {
              std::string sName(rsItr->pubExchName);
              substituteUserId(sName, id);
              result = matchProp(sName, name);
          }
          else if (rsItr->pubExchNameMatchesBlank) {
              result = name.empty();
          } else {
              result = matchProp(rsItr->pubExchName, name);
          }
  
          if (result) {
              QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup exchange name '"
                      << name << "' matched with rule name '"
                      << rsItr->pubExchName << "'");
          } else {
              match= false;
              QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup exchange name '"
                      << name << "' did not match with rule name '"
                      << rsItr->pubExchName << "'");
          }
      }
  
      if (match && rsItr->pubRoutingKeyInRule) {
          if ((routingKey.find(ACL_KEYWORD_USER_SUBST, 0)       != std::string::npos) ||
              (routingKey.find(ACL_KEYWORD_DOMAIN_SUBST, 0)     != std::string::npos) ||
              (routingKey.find(ACL_KEYWORD_USERDOMAIN_SUBST, 0) != std::string::npos)) {
              // The user is not allowed to present a routing key with the substitution key in it
              QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum <<
                      " User-specified routing key has substitution wildcard:" << routingKey
                      << ". Rule match prohibited.");
              match = false;
          } else {
              bool result;
              if (rsItr->ruleHasUserSub[PROP_ROUTINGKEY]) {
                  std::string sKey(routingKey);
                  substituteKeywords(sKey, id);
                  result = rsItr->matchRoutingKey(sKey);
              } else {
                  result = rsItr->matchRoutingKey(routingKey);
              }
  
              if (result) {
                  QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '"
                          << routingKey << "' matched with rule routing key '"
                          << rsItr->pubRoutingKey << "'");
              } else {
                  QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '"
                          << routingKey << "' did not match with rule routing key '"
                          << rsItr->pubRoutingKey << "'");
                  match = false;
              }
          }
      }
  
      if (match) {
          aclresult = rsItr->ruleMode;
          QPID_LOG(debug,"ACL: Rule: " << rsItr->rawRuleNum << " Successful match, the decision is:"
                  << AclHelper::getAclResultStr(aclresult));
      }
      return match;
  }
  
  //
  // lookup - special PUBLISH EXCHANGE lookup
  //
  // The ACL main business logic function of matching rules and declaring
  // an allow or deny result. This lookup is the fastpath per-message
  // lookup to verify if a user is allowed to publish to an exchange with
  // a given key.
  //
  AclResult AclData::lookup(
      const std::string&              id,
      const Action&                   action,
      const ObjectType&               objType,
      const std::string& /*Exchange*/ name,
      const std::string&              routingKey)
  {
  
      QPID_LOG(debug, "ACL: Lookup for id:" << id
               << " action:" << AclHelper::getActionStr((Action) action)
               << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType)
               << " exchange name:" << name
               << " with routing key " << routingKey);
  
      AclResult aclresult = decisionMode;
  
      if (actionList[action] && actionList[action][objType]){
          AclData::actObjItr itrRule = actionList[action][objType]->find(id);
  
          if (itrRule == actionList[action][objType]->end()) {
              itrRule = actionList[action][objType]->find(ACL_KEYWORD_WILDCARD);
          }
          if (itrRule != actionList[action][objType]->end() ) {
              // Found a rule list for this user-action-object set.
              // Search the rule list for a matching rule.
              ruleSetItr rsItr = itrRule->second.end();
              for (int cnt = itrRule->second.size(); cnt != 0; cnt--) {
                  rsItr--;
  
                  if (lookupMatchPublishExchangeRule(rsItr, id, name, routingKey, aclresult)) {
                      return aclresult;
                  }
              }
          }
      }
      QPID_LOG(debug,"ACL: No successful match, defaulting to the decision mode "
               << AclHelper::getAclResultStr(aclresult));
      return aclresult;
  
  }
  
  
  
  //
  //
  //
  void AclData::setConnQuotaRuleSettings (
      boost::shared_ptr<quotaRuleSet> quotaPtr)
  {
      connQuotaRuleSettings = quotaPtr;
  }
  
  
  //
  // getConnQuotaForUser
  //
  // Return the true or false value of connQuotaRulesExist,
  //  indicating whether any kind of lookup was done or not.
  //
  // When lookups are performed return the result value of
  //  1. The user's setting else
  //  2. The 'all' user setting else
  //  3. Zero
  // When lookups are not performed then return a result value of Zero.
  //
  bool AclData::getConnQuotaForUser(const std::string& theUserName,
                                    uint16_t* theResult) const {
      if (this->enforcingConnectionQuotas()) {
          // look for this user explicitly
          quotaRuleSetItr nameItr = (*connQuotaRuleSettings).find(theUserName);
          if (nameItr != (*connQuotaRuleSettings).end()) {
              QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
                       << " explicitly set to : " << (*nameItr).second);
              *theResult = (*nameItr).second;
          } else {
              // Look for the 'all' user
              nameItr = (*connQuotaRuleSettings).find(ACL_KEYWORD_ALL);
              if (nameItr != (*connQuotaRuleSettings).end()) {
                  QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
                           << " chosen through value for 'all' : " << (*nameItr).second);
                  *theResult = (*nameItr).second;
              } else {
                  // Neither userName nor "all" found.
                  QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
                           << " absent in quota settings. Return value : 0");
                  *theResult = 0;
              }
          }
      } else {
          // Rules do not exist
          QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
                   << " unavailable; quota settings are not specified. Return value : 0");
          *theResult = 0;
      }
      return this->enforcingConnectionQuotas();
  }
  
  //
  //
  //
  void AclData::setQueueQuotaRuleSettings (
      boost::shared_ptr<quotaRuleSet> quotaPtr)
  {
      queueQuotaRuleSettings = quotaPtr;
  }
  
  
  //
  // getQueueQuotaForUser
  //
  // Return the true or false value of queueQuotaRulesExist,
  //  indicating whether any kind of lookup was done or not.
  //
  // When lookups are performed return the result value of
  //  1. The user's setting else
  //  2. The 'all' user setting else
  //  3. Zero
  // When lookups are not performed then return a result value of Zero.
  //
  bool AclData::getQueueQuotaForUser(const std::string& theUserName,
                                     uint16_t* theResult) const {
      if (this->enforcingQueueQuotas()) {
          // look for this user explicitly
          quotaRuleSetItr nameItr = (*queueQuotaRuleSettings).find(theUserName);
          if (nameItr != (*queueQuotaRuleSettings).end()) {
              QPID_LOG(trace, "ACL: Queue quota for user " << theUserName
                       << " explicitly set to : " << (*nameItr).second);
              *theResult = (*nameItr).second;
          } else {
              // Look for the 'all' user
              nameItr = (*queueQuotaRuleSettings).find(ACL_KEYWORD_ALL);
              if (nameItr != (*queueQuotaRuleSettings).end()) {
                  QPID_LOG(trace, "ACL: Queue quota for user " << theUserName
                           << " chosen through value for 'all' : " << (*nameItr).second);
                  *theResult = (*nameItr).second;
              } else {
                  // Neither userName nor "all" found.
                  QPID_LOG(trace, "ACL: Queue quota for user " << theUserName
                           << " absent in quota settings. Return value : 0");
                  *theResult = 0;
              }
          }
      } else {
          // Rules do not exist
          QPID_LOG(trace, "ACL: Queue quota for user " << theUserName
                   << " unavailable; quota settings are not specified. Return value : 0");
          *theResult = 0;
      }
      return this->enforcingQueueQuotas();
  }
  
  void AclData::setConnGlobalRules (boost::shared_ptr<bwHostRuleSet> cgr) {
      connBWHostsGlobalRules = cgr;
  }
  
  void AclData::setConnUserRules (boost::shared_ptr<bwHostUserRuleMap> hurm) {
      connBWHostsUserRules = hurm;
  }
  
  AclResult AclData::isAllowedConnection(const std::string& userName,
                                         const std::string& hostName,
                                         std::string& logText) {
      bool decisionMade(false);
      AclResult result(ALLOW);
      for (bwHostRuleSetItr it=connBWHostsGlobalRules->begin();
                            it!=connBWHostsGlobalRules->end(); it++) {
          if (it->getAclHost().match(hostName)) {
              // This host matches a global spec and controls the
              // allow/deny decision for this connection.
              result = it->getAclResult();
              logText = QPID_MSG("global rule " << it->toString()
                      << (AclHelper::resultAllows(result) ? " allows" : " denies")
                      << " connection for host " << hostName << ", user "
                      << userName);
              decisionMade = true;
              break;
          } else {
              // This rule in the global spec doesn't match and
              // does not control the allow/deny decision.
          }
      }
  
      // Run user deny/allow list check
      if (!decisionMade) {
          bwHostUserRuleMapItr itrRule = connBWHostsUserRules->find(userName);
          if (itrRule != connBWHostsUserRules->end()) {
              for (bwHostRuleSetItr it=(*itrRule).second.begin();
                   it!=(*itrRule).second.end(); it++) {
                  if (it->getAclHost().match(hostName)) {
                      // This host matches a user spec and controls the
                      // allow/deny decision for this connection.
                      result = it->getAclResult();
                      logText = QPID_MSG("global rule " << it->toString()
                              << (AclHelper::resultAllows(result) ? " allows" : " denies")
                              << " connection for host " << hostName << ", user "
                              << userName);
                      decisionMade = true;
                      break;
                  } else {
                      // This rule in the user's spec doesn't match and
                      // does not control the allow/deny decision.
                  }
              }
          }
      }
  
      // Apply global connection mode
      if (!decisionMade) {
          result = connectionDecisionMode;
          logText = QPID_MSG("default connection policy "
                  << (AclHelper::resultAllows(result) ? "allows" : "denies")
                  << " connection for host " << hostName << ", user "
                  << userName);
      }
      return result;
  }
  
  //
  //
  //
  AclData::~AclData()
  {
      clear();
  }
  
  
  //
  // Limit check an int limit
  //
  bool AclData::compareInt(const qpid::acl::SpecProperty theProperty,
                           const std::string             theAclValue,
                           const std::string             theLookupValue,
                           bool                          theMaxFlag)
  {
      uint64_t aclRuleValue (0);
      uint64_t lookupValue  (0);
  
      QPID_LOG(debug, "ACL: "
               << (theMaxFlag ? "Upper" : "Lower") << "-limit comparison for property "
               << AclHelper::getPropertyStr(theProperty)
               << ". Success if lookup(" << theLookupValue
               << ") "
               << (theMaxFlag ? "<=" : ">=") << " rule(" << theAclValue << ")");
  
      try {
          aclRuleValue = boost::lexical_cast<uint64_t>(theAclValue);
      }
      catch(const boost::bad_lexical_cast&) {
          assert (false);
          return false;
      }
  
      if (aclRuleValue == 0) {
          QPID_LOG(debug, "ACL: Comparison is always true when ACL rule value is zero");
          return true;
      }
  
      try {
          lookupValue = boost::lexical_cast<uint64_t>(theLookupValue);
      }
      catch(const boost::bad_lexical_cast&) {
          QPID_LOG(error,"ACL: Illegal value given in lookup for property '"
                   << AclHelper::getPropertyStr(theProperty)
                   << "' : " << theLookupValue);
          return false;
      }
  
      bool result =
          (theMaxFlag ? lookupValue > aclRuleValue : lookupValue < aclRuleValue);
      if ( result ) {
          QPID_LOG(debug, "ACL: Limit exceeded for property '"
                   << AclHelper::getPropertyStr(theProperty) << "'");
          return false;
      }
  
      return true;
  }
  
  const std::string DOMAIN_SEPARATOR("@");
  const std::string PERIOD(".");
  const std::string UNDERSCORE("_");
  //
  // substituteString
  //   Given a name string from an Acl rule, substitute the replacement into it
  //   wherever the placeholder directs.
  //
  void AclData::substituteString(std::string& targetString,
                                 const std::string& placeholder,
                                 const std::string& replacement)
  {
      assert (!placeholder.empty());
      if (placeholder.empty()) {
          return;
      }
      size_t start_pos(0);
      while((start_pos = targetString.find(placeholder, start_pos)) != std::string::npos) {
          targetString.replace(start_pos, placeholder.length(), replacement);
          start_pos += replacement.length();
      }
  }
  
  
  //
  // normalizeUserId
  //   Given a name string return it in a form usable as topic keys:
  //     change "@" and "." to "_".
  //
  std::string AclData::normalizeUserId(const std::string& userId)
  {
      std::string normalId(userId);
      substituteString(normalId, DOMAIN_SEPARATOR, UNDERSCORE);
      substituteString(normalId, PERIOD,           UNDERSCORE);
      return normalId;
  }
  
  
  //
  // substituteUserId
  //   Given an Acl rule and an authenticated userId
  //   do the keyword substitutions on the rule.
  //
  void AclData::substituteUserId(std::string& ruleString,
                                 const std::string& userId)
  {
      size_t locDomSeparator(0);
      std::string user("");
      std::string domain("");
      std::string userdomain = normalizeUserId(userId);
  
      locDomSeparator = userId.find(DOMAIN_SEPARATOR);
      if (std::string::npos == locDomSeparator) {
          // "@" not found. There's just a user name
          user   = normalizeUserId(userId);
      } else {
          // "@" found, split the names. Domain may be blank.
          user   = normalizeUserId(userId.substr(0,locDomSeparator));
          domain = normalizeUserId(userId.substr(locDomSeparator+1));
      }
  
      substituteString(ruleString, ACL_KEYWORD_USER_SUBST,       user);
      substituteString(ruleString, ACL_KEYWORD_DOMAIN_SUBST,     domain);
      substituteString(ruleString, ACL_KEYWORD_USERDOMAIN_SUBST, userdomain);
  }
  
  
  //
  // substituteKeywords
  //   Given an Acl rule and an authenticated userId
  //   do reverse keyword substitutions on the rule.
  //   That is, replace the normalized name in the rule string with
  //   the keyword that represents it. This stragegy is used for
  //   topic key lookups where the keyword string proper is in the
  //   topic key search tree.
  //
  void AclData::substituteKeywords(std::string& ruleString,
                                   const std::string& userId)
  {
      size_t locDomSeparator(0);
      std::string user("");
      std::string domain("");
      std::string userdomain = normalizeUserId(userId);
  
      locDomSeparator = userId.find(DOMAIN_SEPARATOR);
      if (std::string::npos == locDomSeparator) {
          // "@" not found. There's just a user name
          user   = normalizeUserId(userId);
      } else {
          // "@" found, split the names
          user   = normalizeUserId(userId.substr(0,locDomSeparator));
          domain = normalizeUserId(userId.substr(locDomSeparator+1));
      }
      std::string oRule(ruleString);
      substituteString(ruleString, userdomain, ACL_KEYWORD_USERDOMAIN_SUBST);
      substituteString(ruleString, user,       ACL_KEYWORD_USER_SUBST);
      substituteString(ruleString, domain,     ACL_KEYWORD_DOMAIN_SUBST);
  }
  }}
  