/* * RHQ Management Platform * Copyright (C) 2005-2009 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * 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 and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.plugins.apache.mapping; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.rhq.augeas.node.AugeasNode; /** * The httpd lens doesn't give us very detailed information about the directive * parameters. * It just supplies a list of "param" nodes each containing a value for one * parameter of a directive (how we wish it could be otherwise and the lens be more sophisticated). * <p> * In order to extract the useful information from this that we can use for mapping these * params to RHQ configuration properties, we define a series of regular expressions * that are designed to work with a space concatenated list of params supplied by Augeas. * <p> * In these regular expressions, each capturing group corresponds (in the exact order) to * the properties defined in the resource configurations. * * @author Filip Drabek * @author Lukas Krejci */ public class ApacheDirectiveRegExpression { private static final Map<String, DirectiveMapping> MAPPING_TYPE; static { MAPPING_TYPE = new HashMap<String, DirectiveMapping>(); MAPPING_TYPE.put("Alias", DirectiveMapping.DIRECTIVE_PER_MAP); MAPPING_TYPE.put("Listen", DirectiveMapping.DIRECTIVE_PER_MAP); MAPPING_TYPE.put("ErrorDocument", DirectiveMapping.DIRECTIVE_PER_MAP); MAPPING_TYPE.put("Options", DirectiveMapping.PARAM_PER_MAP); MAPPING_TYPE.put("ServerAlias", DirectiveMapping.PARAM_PER_MAP); MAPPING_TYPE.put("Allow", DirectiveMapping.PARAM_PER_MAP); MAPPING_TYPE.put("Deny", DirectiveMapping.PARAM_PER_MAP); MAPPING_TYPE.put("CustomLog", DirectiveMapping.DIRECTIVE_PER_MAP_INDEX); MAPPING_TYPE.put("AllowOverride", DirectiveMapping.PARAM_PER_MAP); MAPPING_TYPE.put("DirectoryIndex", DirectiveMapping.PARAM_PER_MAP); MAPPING_TYPE.put("NameVirtualHost", DirectiveMapping.DIRECTIVE_PER_MAP_INDEX); MAPPING_TYPE.put("IfModules", DirectiveMapping.POSITION_PROPERTY); } public static final String WORD = "\"(?:[^\"\n]|\\\")*\"|'(?:[^'\n]|\\\')*'|[^'\" \t\n]+"; public static final String WS = "[ \t]*"; public static final String WS_MAN = "[ \t]+"; public static final String NUM = "[0-9]+"; /** * This is the map of regular expressions for each individual directive * that needs "special treatment". * A directive can need multiple regexes to produce mappable values. */ private static final Map<String, Pattern[]> DIRECTIVE_REGEX; static { DIRECTIVE_REGEX = new HashMap<String, Pattern[]>(); DIRECTIVE_REGEX.put("Alias", new Pattern[] { Pattern.compile(WS + "(" + WORD + ")" + WS + "(" + WORD + ")" + WS) }); DIRECTIVE_REGEX.put("CustomLog", new Pattern[] { Pattern.compile(WS + "(" + WORD + ")" + "(?:" + WS_MAN + "(" + WORD + ")" + ")?" + WS) }); DIRECTIVE_REGEX.put("ErrorDocument", new Pattern[] { Pattern.compile(WS + "(" + NUM + ")" + WS_MAN + "(" + WORD + ")" + WS) }); DIRECTIVE_REGEX.put("Options", new Pattern[] { Pattern.compile(WS + "(Add|Remove|Set)" + WS + "([a-zA-Z]+)") }); DIRECTIVE_REGEX.put("Allow", new Pattern[] { Pattern.compile("from"), Pattern.compile("(?:" + WS_MAN + "(" + WORD + "))") }); DIRECTIVE_REGEX.put("Deny", new Pattern[] { Pattern.compile("from"), Pattern.compile("(?:" + WS_MAN + "(" + WORD + "))") }); DIRECTIVE_REGEX.put( "Listen", new Pattern[] { Pattern.compile("(?:((?:\\[[a-zA-Z0-9:]+\\])|(?:[0-9\\.]+)):)?([0-9]+)(?:" + WS_MAN + "(" + WORD + "))?") }); DIRECTIVE_REGEX.put("ServerAlias", new Pattern[] { Pattern.compile(WS + "(" + WORD + ")") }); DIRECTIVE_REGEX.put("AllowOverride", new Pattern[] { Pattern.compile(WS + "(All|None|AuthConfig|FileInfo|Indexes|Limit|Options)") }); DIRECTIVE_REGEX.put("DirectoryIndex", new Pattern[] { Pattern.compile(WS + "(" + WORD + ")" + WS) }); DIRECTIVE_REGEX.put("NameVirtualHost", new Pattern[] { Pattern.compile(WS + "(" + WORD + ")" + WS) }); } private static final Map<String, Pattern[]> DIRECTIVEREGEX_TO_AUGEAS; static { DIRECTIVEREGEX_TO_AUGEAS = new HashMap<String, Pattern[]>(); DIRECTIVEREGEX_TO_AUGEAS.put("Options", new Pattern[] { Pattern.compile(WS + "([+-]?" + WS + "[a-zA-Z]+)" + WS) }); DIRECTIVEREGEX_TO_AUGEAS.put("Allow", new Pattern[] { Pattern.compile("(?:" + WS_MAN + "(" + WORD + "))") }); DIRECTIVEREGEX_TO_AUGEAS.put("Deny", new Pattern[] { Pattern.compile("(?:" + WS_MAN + "(" + WORD + "))") }); DIRECTIVEREGEX_TO_AUGEAS.put("ServerAlias", new Pattern[] { Pattern.compile(WS_MAN + "(" + WORD + ")") }); DIRECTIVEREGEX_TO_AUGEAS.put("DirectoryIndex", new Pattern[] { Pattern.compile(WS_MAN + "(" + WORD + ")") }); DIRECTIVEREGEX_TO_AUGEAS.put("Alias", new Pattern[] { Pattern.compile(WS_MAN + "(" + WORD + ")" + WS_MAN + "(" + WORD + ")" + WS) }); DIRECTIVEREGEX_TO_AUGEAS.put("CustomLog", new Pattern[] { Pattern.compile(WS_MAN + "(" + WORD + ")" + "(?:" + WS_MAN + "(" + WORD + ")" + ")?") }); DIRECTIVEREGEX_TO_AUGEAS.put("AllowOverride", new Pattern[] { Pattern.compile(WS + "(All|None|AuthConfig|FileInfo|Indexes|Limit|Options)") }); DIRECTIVEREGEX_TO_AUGEAS.put("Listen", new Pattern[] { Pattern.compile(WS + "(" + WORD + ")") }); DIRECTIVEREGEX_TO_AUGEAS.put("NameVirtualHost", new Pattern[] { Pattern.compile(WS + "(" + WORD + ")" + WS) }); DIRECTIVEREGEX_TO_AUGEAS.put("ErrorDocument", new Pattern[] { Pattern.compile(WS_MAN + "(" + NUM + ")" + WS_MAN + "(" + WORD + ")") }); } /** * Parses the parameters of the supplied node and returns a list * of strings, each containing a value for a simple property * corresponding to that parameter. * <p> * A null value means that the parameters for given property * was not defined in the configuration file. * * @param parentNode the node containing the "param" sub nodes to parse * @return the list of mappable parameters */ public static List<String> getParams(AugeasNode parentNode) { StringBuilder value = new StringBuilder(); String nodeName = parentNode.getLabel(); List<String> result = new ArrayList<String>(); List<AugeasNode> nodes = parentNode.getChildNodes(); for (AugeasNode node : nodes) { value.append(node.getValue()); value.append(" "); } if (value.length() > 0) { value.deleteCharAt(value.length() - 1); } if (!DIRECTIVE_REGEX.containsKey(nodeName)) { result.add(value.toString()); return result; } value = SpecificParams.prepareForConfiguration(nodeName, value); //each regex is applied as long as it matches something Pattern[] patterns = DIRECTIVE_REGEX.get(nodeName); int startIndex = 0; boolean updated = true; while (updated & startIndex < value.length()) updated = false; for (Pattern pattern : patterns) { Matcher m = pattern.matcher(value); while (m.find(startIndex)) { for (int i = 1; i <= m.groupCount(); i++) { String val = m.group(i); result.add(val); } updated = true; startIndex = m.end(); } } return result; } public static List<String> createParams(String params, String name) { List<String> nodeParams = new ArrayList<String>(); if (!DIRECTIVEREGEX_TO_AUGEAS.containsKey(name)) { nodeParams.add(params); return nodeParams; } if (name.equals("Allow") || name.equals("Deny")) { nodeParams.add("from"); } //each regex is applied as long as it matches something Pattern[] patterns = DIRECTIVEREGEX_TO_AUGEAS.get(name); int startIndex = 0; boolean updated = true; params = SpecificParams.prepareForAugeas(name, params); while (updated & startIndex < params.length()) updated = false; for (Pattern pattern : patterns) { Matcher m = pattern.matcher(params); while (m.find(startIndex)) { for (int i = 1; i <= m.groupCount(); i++) { String val = m.group(i); if (val != null) nodeParams.add(val); } updated = true; startIndex = m.end(); } } if (name.equals("Options")) { int i = 0; for (String param : nodeParams) { param = param.replaceAll(" ", ""); nodeParams.set(i, param); i++; } } return nodeParams; } /** * The properties in the resource configuration share a couple of common * patterns how they map to the augeas tree. This method returns the corresponding * mapping type for a directive. * * @param directiveName the directive to find out the mapping type of. * @return the mapping type that can be used to perform the mapping to the property. */ public static DirectiveMapping getMappingType(String directiveName) { DirectiveMapping map = MAPPING_TYPE.get(directiveName); if (map == null) map = DirectiveMapping.SIMPLE_PROP; return map; } }