content: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You 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.
   */
  package org.apache.activemq.shiro.authz;
  
  import org.apache.shiro.authz.Permission;
  import org.apache.shiro.authz.permission.WildcardPermission;
  
  import java.lang.reflect.Method;
  import java.util.List;
  import java.util.Set;
  
  /**
   * @since 5.10.0
   */
  public class ActiveMQWildcardPermission extends WildcardPermission {
  
      private final boolean caseSensitive;
  
      public ActiveMQWildcardPermission(String wildcardString) {
          this(wildcardString, true);
      }
  
      public ActiveMQWildcardPermission(String wildcardString, boolean caseSensitive) {
          super(wildcardString, caseSensitive);
          this.caseSensitive = caseSensitive;
      }
  
      @Override
      public boolean implies(Permission p) {
          // By default only supports comparisons with other WildcardPermissions
          if (!(p instanceof WildcardPermission)) {
              return false;
          }
  
          WildcardPermission wp = (WildcardPermission) p;
  
          List<Set<String>> otherParts = getParts(wp);
  
          int i = 0;
          for (Set<String> otherPart : otherParts) {
              // If this permission has less parts than the other permission, everything after the number of parts contained
              // in this permission is automatically implied, so return true
              if (getParts().size() - 1 < i) {
                  return true;
              } else {
                  Set<String> thisPart = getParts().get(i);
  
                  // all tokens from otherPart must pass at least one token from thisPart
                  for (String otherToken : otherPart) {
                      if (!caseSensitive) {
                          otherToken = otherToken.toLowerCase();
                      }
                  	boolean otherIsMatched = false;
                  	for (String token : thisPart) {
                          if (token.equals(WILDCARD_TOKEN)) {
                          	otherIsMatched = true;
                          	break;
                          }
                          if (matches(token, otherToken)) {
                          	otherIsMatched = true;
                          	break;
                          }
                  	}
                  	if (!otherIsMatched) {
                  		return false;
                  	}
                  }
                  i++;
              }
          }
  
          // If this permission has more parts than the other parts, only imply it if all of the other parts are wildcards
          for (; i < getParts().size(); i++) {
              Set<String> part = getParts().get(i);
              if (!part.contains(WILDCARD_TOKEN)) {
                  return false;
              }
          }
  
          return true;
      }
  
      /**
       * Tests whether or not a string matches against a pattern.
       * The pattern may contain two special characters:<br>
       * '*' means zero or more characters<br>
       * '?' means one and only one character
       *
       * @param pattern pattern to match against.
       *                Must not be <code>null</code>.
       * @param value   string which must be matched against the pattern.
       *                Must not be <code>null</code>.
       * @return <code>true</code> if the string matches against the
       *         pattern, or <code>false</code> otherwise.
       */
      protected boolean matches(String pattern, String value) {
  
          char[] patArr = pattern.toCharArray();
          char[] valArr = value.toCharArray();
          int patIndex = 0;
          int patEndIndex = patArr.length - 1;
          int valIndex = 0;
          int valEndIndex = valArr.length - 1;
          char ch;
  
          boolean patternContainsStar = false;
          for (char patternChar : patArr) {
              if (patternChar == '*') {
                  patternContainsStar = true;
                  break;
              }
          }
  
          if (!patternContainsStar) {
              // No '*'s, so we make a shortcut
              if (patEndIndex != valEndIndex) {
                  return false; // Pattern and string do not have the same size
              }
              for (int i = 0; i <= patEndIndex; i++) {
                  ch = patArr[i];
                  if (ch != '?') {
                      if (ch != valArr[i]) {
                          return false;// Character mismatch
                      }
                  }
              }
              return true; // String matches against pattern
          }
  
  
          // Process characters before first star
          while ((ch = patArr[patIndex]) != '*' && valIndex <= valEndIndex) {
              if (ch != '?') {
                  if (ch != valArr[valIndex]) {
                      return false;// Character mismatch
                  }
              }
              patIndex++;
              valIndex++;
          }
          if (valIndex > valEndIndex) {
              // All characters in the value are used. Check if only '*'s remain
              // in the pattern. If so, we succeeded. Otherwise failure.
              for (int i = patIndex; i <= patEndIndex; i++) {
                  if (patArr[i] != '*') {
                      return false;
                  }
              }
              return true;
          }
  
          // Process characters after last star
          while ((ch = patArr[patEndIndex]) != '*' && valIndex <= valEndIndex) {
              if (ch != '?') {
                  if (ch != valArr[valEndIndex]) {
                      return false;// Character mismatch
                  }
              }
              patEndIndex--;
              valEndIndex--;
          }
          if (valIndex > valEndIndex) {
              // All characters in the value are used. Check if only '*'s remain
              // in the pattern. If so, we succeeded. Otherwise failure.
              for (int i = patIndex; i <= patEndIndex; i++) {
                  if (patArr[i] != '*') {
                      return false;
                  }
              }
              return true;
          }
  
          // process pattern between stars. patIndex and patEndIndex always point to a '*'.
          while (patIndex != patEndIndex && valIndex <= valEndIndex) {
              int innerPatternIndex = -1;
              for (int i = patIndex + 1; i <= patEndIndex; i++) {
                  if (patArr[i] == '*') {
                      innerPatternIndex = i;
                      break;
                  }
              }
              if (innerPatternIndex == patIndex + 1) {
                  // Two stars next to each other, skip the first one.
                  patIndex++;
                  continue;
              }
              // Find the pattern between patIndex & innerPatternIndex in the value between
              // valIndex and valEndIndex
              int innerPatternLength = (innerPatternIndex - patIndex - 1);
              int innerValueLength = (valEndIndex - valIndex + 1);
              int foundIndex = -1;
              innerValueLoop:
              for (int i = 0; i <= innerValueLength - innerPatternLength; i++) {
                  for (int j = 0; j < innerPatternLength; j++) {
                      ch = patArr[patIndex + j + 1];
                      if (ch != '?') {
                          if (ch != valArr[valIndex + i + j]) {
                              continue innerValueLoop;
                          }
                      }
                  }
  
                  foundIndex = valIndex + i;
                  break;
              }
  
              if (foundIndex == -1) {
                  return false;
              }
  
              patIndex = innerPatternIndex;
              valIndex = foundIndex + innerPatternLength;
          }
  
          // All characters in the string are used. Check if only '*'s are left
          // in the pattern. If so, we succeeded. Otherwise failure.
          for (int i = patIndex; i <= patEndIndex; i++) {
              if (patArr[i] != '*') {
                  return false;
              }
          }
  
          return true;
      }
  
      protected List<Set<String>> getParts(WildcardPermission wp) {
          if (wp instanceof ActiveMQWildcardPermission) {
              return ((ActiveMQWildcardPermission) wp).getParts();
          } else {
              return getPartsByReflection(wp);
          }
      }
  
      protected List<Set<String>> getPartsByReflection(WildcardPermission wp) {
          try {
              return doGetPartsByReflection(wp);
          } catch (Exception e) {
              String msg = "Unable to obtain WildcardPermission instance's 'parts' value.";
              throw new IllegalStateException(msg, e);
          }
      }
  
      @SuppressWarnings("unchecked")
      protected List<Set<String>> doGetPartsByReflection(WildcardPermission wp) throws Exception {
          Method getParts = WildcardPermission.class.getDeclaredMethod("getParts");
          getParts.setAccessible(true);
          return (List<Set<String>>) getParts.invoke(wp);
      }
  
      @Override
      public String toString() {
          StringBuilder buffer = new StringBuilder();
          for (Set<String> part : getParts()) {
              if (buffer.length() > 0) {
                  buffer.append(":");
              }
              boolean first = true;
              for (String token : part) {
                  if (!first) {
                      buffer.append(",");
                  }
                  buffer.append(token);
                  first = false;
              }
          }
          return buffer.toString();
      }
  }
  