VariablesResolver.java

/* 
 * 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.
 *
 */

package org.esigate.vars;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.cookie.Cookie;
import org.esigate.ConfigurationException;
import org.esigate.Driver;
import org.esigate.DriverFactory;
import org.esigate.http.IncomingRequest;
import org.esigate.impl.DriverRequest;
import org.esigate.util.HttpRequestHelper;
import org.esigate.util.UriUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Manage variables replacement.
 * 
 * @author Alexis Thaveau
 * @author Nicolas Richeton
 */
public final class VariablesResolver {

    private static final Logger LOG = LoggerFactory.getLogger(VariablesResolver.class);
    private static Pattern userAgentVersion = Pattern.compile("^[A-Za-z]+/([0-9]+\\.[0-9]+)");

    static {
        // Load default settings
        configure();
    }
    /**
     * Properties file
     */
    private static Properties properties;

    private VariablesResolver() {
    }

    /**
     * Loads variables from properties.
     * 
     * @param props
     *            properties
     */
    public static void configure(Properties props) {
        properties = props;
    }

    /**
     * Loads variables according to default configuration file org/esigate/vars.properties.
     */
    public static void configure() {
        InputStream inputStream = null;
        try {
            LOG.debug("Loading esigate-vars.properties file");
            inputStream = Driver.class.getResourceAsStream("/esigate-vars.properties");
            if (inputStream == null) {
                inputStream = Driver.class.getResourceAsStream("vars.properties");
            }
            if (inputStream != null) {
                properties = new Properties();
                properties.load(inputStream);
            }
        } catch (IOException e) {
            throw new ConfigurationException(e);
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // nothing to do
                }
            }
        }
    }

    /**
     * @return The URL of the variables file.
     */
    public static URL getVariablessUrl() {
        URL varsUrl = Driver.class.getResource("/esigate-vars.properties");
        if (varsUrl == null) {
            varsUrl = Driver.class.getResource("vars.properties");
        }
        return varsUrl;
    }

    /**
     * Regexp to find variables
     */
    private static final Pattern VAR_PATTERN = Pattern.compile("\\$\\((.*?)\\)");

    /**
     * 
     * @param strVars
     *            a String that may contain variables.
     * @return true if contains variables
     */
    static boolean containsVariable(String strVars) {
        return strVars.contains("$(") && strVars.contains(")");
    }

    /**
     * Replace all ESI variables found in strVars by their matching value in vars.properties.
     * 
     * @param strVars
     *            a String containing variables.
     * @return The resulting String
     */
    public static String replaceAllVariables(String strVars) {
        return replaceAllVariables(strVars, null);
    }

    /**
     * Replace all ESI variables found in strVars by their matching value in vars.properties.
     * 
     * @param strVars
     *            a String containing variables.
     * @param request
     * @return The resulting String
     */
    public static String replaceAllVariables(String strVars, DriverRequest request) {
        String result = strVars;
        if (VariablesResolver.containsVariable(strVars)) {
            Matcher matcher = VAR_PATTERN.matcher(strVars);

            while (matcher.find()) {
                String group = matcher.group();
                String var = group.substring(2, group.length() - 1);
                String arg = null;

                // try to find argument
                int argIndex = var.indexOf('{');
                if (argIndex != -1) {
                    arg = VarUtils.removeSimpleQuotes(var.substring(argIndex + 1, var.indexOf('}')));
                }

                // try to find default value
                // ESI 1.0 spec :
                // 4.2 Variable Default Values
                // Variables whose values are empty, nonexistent variables and
                // undefined substructures of variables will evaluate to an
                // empty string when they are accessed.
                String defaultValue = StringUtils.EMPTY;
                int defaultValueIndex = var.indexOf('|');
                if (defaultValueIndex != -1) {
                    defaultValue = VarUtils.removeSimpleQuotes(var.substring(defaultValueIndex + 1));
                }

                String value = getProperty(var, arg, request);

                if (value == null) {
                    value = defaultValue;
                }

                result = result.replace(group, value);

            }

        }
        return result;
    }

    private static String getProperty(String var, String arg, DriverRequest request) {
        String result = processVar(var, arg, request);
        if (properties != null) {
            result = properties.getProperty(var, result);
        }
        LOG.debug("Resolve property $({})={}", var, result);
        return result;
    }

    private static String processVar(String var, String arg, DriverRequest request) {
        IncomingRequest incomingRequest = null;
        if (request != null) {
            incomingRequest = request.getOriginalRequest();
        }
        String res = null;
        if (var.contains("QUERY_STRING")) {
            if (arg == null) {
                res = UriUtils.getRawQuery(incomingRequest.getRequestLine().getUri());
            } else {
                res = HttpRequestHelper.getParameter(request, arg);
            }
        } else if (var.contains("HTTP_ACCEPT_LANGUAGE")) {
            String langs = HttpRequestHelper.getFirstHeader("Accept-Language", incomingRequest);
            if (arg == null) {
                res = langs;
            } else {
                res = String.valueOf(!(langs == null || !langs.contains(arg)));
            }
        } else if (var.contains("HTTP_HEADER")) {
            res = HttpRequestHelper.getFirstHeader(arg, incomingRequest);
        } else if (var.contains("HTTP_HOST")) {
            res = HttpRequestHelper.getFirstHeader("Host", incomingRequest);
        } else if (var.contains("HTTP_REFERER")) {
            res = HttpRequestHelper.getFirstHeader("Referer", incomingRequest);
        } else if (var.contains("HTTP_COOKIE")) {
            if (arg == null) {
                // Add cookies
                // In request header
                String cookieHeaderValue = StringUtils.EMPTY;
                for (Cookie c : request.getOriginalRequest().getCookies()) {
                    if (StringUtils.isNotBlank(cookieHeaderValue)) {
                        cookieHeaderValue = cookieHeaderValue + "; ";
                    }
                    cookieHeaderValue = cookieHeaderValue + c.getName() + "=" + c.getValue();
                }
                if (StringUtils.isNotBlank(cookieHeaderValue)) {
                    res = cookieHeaderValue;
                }
            } else {
                Cookie[] cookies = request.getOriginalRequest().getCookies();
                for (Cookie c : cookies) {
                    if (c.getName().equals(arg)) {
                        res = c.getValue();
                        break;
                    }
                }
            }
        } else if (var.contains("HTTP_USER_AGENT")) {
            if (arg == null) {
                res = HttpRequestHelper.getFirstHeader("User-agent", incomingRequest);
            } else {
                String userAgent =
                        StringUtils.defaultString(HttpRequestHelper.getFirstHeader("User-Agent", incomingRequest))
                                .toLowerCase();
                if (arg.equals("os")) {
                    if (userAgent.contains("unix")) {
                        res = "UNIX";
                    } else if (userAgent.contains("mac")) {
                        res = "MAC";
                    } else if (userAgent.contains("windows")) {
                        res = "WIN";
                    } else {
                        res = "OTHER";
                    }
                } else if (arg.equals("browser")) {
                    if (userAgent.contains("msie")) {
                        res = "MSIE";
                    } else {
                        res = "MOZILLA";
                    }
                } else if (arg.equals("version")) {
                    Matcher m = userAgentVersion.matcher(userAgent);

                    if (m.find()) {
                        res = m.group(1);
                    }
                }
            }
        } else if (var.contains("PROVIDER")) {
            String providerUrl = StringUtils.EMPTY;
            try {
                Driver driver = DriverFactory.getInstance(arg);
                providerUrl =
                        driver.getConfiguration().getBaseUrlRetrieveStrategy().getBaseURL(request.getOriginalRequest());
            } catch (Exception e) {
                // No driver available for this id.
            }

            return providerUrl;

        }
        return res;
    }
}