package io.undertow.servlet.compat.rewrite;
import io.undertow.servlet.UndertowServletLogger;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletResponse;
public class RewriteConfigFactory {
public static RewriteConfig build(InputStream inputStream) {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
try {
return parse(reader);
} finally {
try {
reader.close();
} catch (IOException e) {
}
}
}
private static RewriteConfig parse(BufferedReader reader) {
ArrayList<RewriteRule> rules = new ArrayList<RewriteRule>();
ArrayList<RewriteCond> conditions = new ArrayList<RewriteCond>();
Map<String, RewriteMap> maps = new HashMap<>();
while (true) {
try {
String line = reader.readLine();
if (line == null) {
break;
}
Object result = parse(line);
if (result instanceof RewriteRule) {
RewriteRule rule = (RewriteRule) result;
if (UndertowServletLogger.ROOT_LOGGER.isDebugEnabled()) {
UndertowServletLogger.ROOT_LOGGER.debug("Add rule with pattern " + rule.getPatternString()
+ " and substitution " + rule.getSubstitutionString());
}
for (int i = (conditions.size() - 1); i > 0; i--) {
if (conditions.get(i - 1).isOrnext()) {
conditions.get(i).setOrnext(true);
}
}
for (int i = 0; i < conditions.size(); i++) {
if (UndertowServletLogger.ROOT_LOGGER.isDebugEnabled()) {
RewriteCond cond = conditions.get(i);
UndertowServletLogger.ROOT_LOGGER.debug("Add condition " + cond.getCondPattern()
+ " test " + cond.getTestString() + " to rule with pattern "
+ rule.getPatternString() + " and substitution "
+ rule.getSubstitutionString() + (cond.isOrnext() ? " [OR]" : "")
+ (cond.isNocase() ? " [NC]" : ""));
}
rule.addCondition(conditions.get(i));
}
conditions.clear();
rules.add(rule);
} else if (result instanceof RewriteCond) {
conditions.add((RewriteCond) result);
} else if (result instanceof Object[]) {
String mapName = (String) ((Object[]) result)[0];
RewriteMap map = (RewriteMap) ((Object[]) result)[1];
maps.put(mapName, map);
}
} catch (IOException e) {
UndertowServletLogger.ROOT_LOGGER.errorReadingRewriteConfiguration(e);
}
}
RewriteRule[] rulesArray = rules.toArray(new RewriteRule[0]);
for (int i = 0; i < rulesArray.length; i++) {
rulesArray[i].parse(maps);
}
return new RewriteConfig(rulesArray, maps);
}
private static Object parse(String line) {
StringTokenizer tokenizer = new StringTokenizer(line);
if (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
if (token.equals("RewriteCond")) {
RewriteCond condition = new RewriteCond();
if (tokenizer.countTokens() < 2) {
throw UndertowServletLogger.ROOT_LOGGER.invalidRewriteConfiguration(line);
}
condition.setTestString(tokenizer.nextToken());
condition.setCondPattern(tokenizer.nextToken());
if (tokenizer.hasMoreTokens()) {
String flags = tokenizer.nextToken();
if (flags.startsWith("[") && flags.endsWith("]")) {
flags = flags.substring(1, flags.length() - 1);
}
StringTokenizer flagsTokenizer = new StringTokenizer(flags, ",");
while (flagsTokenizer.hasMoreElements()) {
parseCondFlag(line, condition, flagsTokenizer.nextToken());
}
}
return condition;
} else if (token.equals("RewriteRule")) {
RewriteRule rule = new RewriteRule();
if (tokenizer.countTokens() < 2) {
throw UndertowServletLogger.ROOT_LOGGER.invalidRewriteConfiguration(line);
}
rule.setPatternString(tokenizer.nextToken());
rule.setSubstitutionString(tokenizer.nextToken());
if (tokenizer.hasMoreTokens()) {
String flags = tokenizer.nextToken();
if (flags.startsWith("[") && flags.endsWith("]")) {
flags = flags.substring(1, flags.length() - 1);
}
StringTokenizer flagsTokenizer = new StringTokenizer(flags, ",");
while (flagsTokenizer.hasMoreElements()) {
parseRuleFlag(line, rule, flagsTokenizer.nextToken());
}
}
return rule;
} else if (token.equals("RewriteMap")) {
if (tokenizer.countTokens() < 2) {
throw UndertowServletLogger.ROOT_LOGGER.invalidRewriteConfiguration(line);
}
String name = tokenizer.nextToken();
String rewriteMapClassName = tokenizer.nextToken();
RewriteMap map = null;
try {
map = (RewriteMap) (Class.forName(rewriteMapClassName).newInstance());
} catch (Exception e) {
throw UndertowServletLogger.ROOT_LOGGER.invalidRewriteMap(rewriteMapClassName);
}
if (tokenizer.hasMoreTokens()) {
map.setParameters(tokenizer.nextToken());
}
Object[] result = new Object[2];
result[0] = name;
result[1] = map;
return result;
} else if (token.startsWith("#")) {
} else {
throw UndertowServletLogger.ROOT_LOGGER.invalidRewriteConfiguration(line);
}
}
return null;
}
protected static void parseCondFlag(String line, RewriteCond condition, String flag) {
if (flag.equals("NC") || flag.equals("nocase")) {
condition.setNocase(true);
} else if (flag.equals("OR") || flag.equals("ornext")) {
condition.setOrnext(true);
} else {
throw UndertowServletLogger.ROOT_LOGGER.invalidRewriteFlags(line, flag);
}
}
protected static void parseRuleFlag(String line, RewriteRule rule, String flag) {
if (flag.equals("chain") || flag.equals("C")) {
rule.setChain(true);
} else if (flag.startsWith("cookie=") || flag.startsWith("CO=")) {
rule.setCookie(true);
if (flag.startsWith("cookie")) {
flag = flag.substring("cookie=".length());
} else if (flag.startsWith("CO=")) {
flag = flag.substring("CO=".length());
}
StringTokenizer tokenizer = new StringTokenizer(flag, ":");
if (tokenizer.countTokens() < 2) {
throw UndertowServletLogger.ROOT_LOGGER.invalidRewriteFlags(line);
}
rule.setCookieName(tokenizer.nextToken());
rule.setCookieValue(tokenizer.nextToken());
if (tokenizer.hasMoreTokens()) {
rule.setCookieDomain(tokenizer.nextToken());
}
if (tokenizer.hasMoreTokens()) {
try {
rule.setCookieLifetime(Integer.parseInt(tokenizer.nextToken()));
} catch (NumberFormatException e) {
throw UndertowServletLogger.ROOT_LOGGER.invalidRewriteFlags(line);
}
}
if (tokenizer.hasMoreTokens()) {
rule.setCookiePath(tokenizer.nextToken());
}
if (tokenizer.hasMoreTokens()) {
rule.setCookieSecure(Boolean.parseBoolean(tokenizer.nextToken()));
}
if (tokenizer.hasMoreTokens()) {
rule.setCookieHttpOnly(Boolean.parseBoolean(tokenizer.nextToken()));
}
} else if (flag.startsWith("env=") || flag.startsWith("E=")) {
rule.setEnv(true);
if (flag.startsWith("env=")) {
flag = flag.substring("env=".length());
} else if (flag.startsWith("E=")) {
flag = flag.substring("E=".length());
}
int pos = flag.indexOf(':');
if (pos == -1 || (pos + 1) == flag.length()) {
throw UndertowServletLogger.ROOT_LOGGER.invalidRewriteFlags(line);
}
rule.addEnvName(flag.substring(0, pos));
rule.addEnvValue(flag.substring(pos + 1));
} else if (flag.startsWith("forbidden") || flag.startsWith("F")) {
rule.setForbidden(true);
} else if (flag.startsWith("gone") || flag.startsWith("G")) {
rule.setGone(true);
} else if (flag.startsWith("host") || flag.startsWith("H")) {
rule.setHost(true);
} else if (flag.startsWith("last") || flag.startsWith("L")) {
rule.setLast(true);
} else if (flag.startsWith("next") || flag.startsWith("N")) {
rule.setNext(true);
} else if (flag.startsWith("nocase") || flag.startsWith("NC")) {
rule.setNocase(true);
} else if (flag.startsWith("noescape") || flag.startsWith("NE")) {
rule.setNoescape(true);
} else if (flag.startsWith("qsappend") || flag.startsWith("QSA")) {
rule.setQsappend(true);
} else if (flag.startsWith("redirect") || flag.startsWith("R")) {
if (flag.startsWith("redirect=")) {
flag = flag.substring("redirect=".length());
rule.setRedirect(true);
rule.setRedirectCode(Integer.parseInt(flag));
} else if (flag.startsWith("R=")) {
flag = flag.substring("R=".length());
rule.setRedirect(true);
rule.setRedirectCode(Integer.parseInt(flag));
} else {
rule.setRedirect(true);
rule.setRedirectCode(HttpServletResponse.SC_FOUND);
}
} else if (flag.startsWith("skip") || flag.startsWith("S")) {
if (flag.startsWith("skip=")) {
flag = flag.substring("skip=".length());
} else if (flag.startsWith("S=")) {
flag = flag.substring("S=".length());
}
rule.setSkip(Integer.parseInt(flag));
} else if (flag.startsWith("type") || flag.startsWith("T")) {
if (flag.startsWith("type=")) {
flag = flag.substring("type=".length());
} else if (flag.startsWith("T=")) {
flag = flag.substring("T=".length());
}
rule.setType(true);
rule.setTypeValue(flag);
} else {
throw UndertowServletLogger.ROOT_LOGGER.invalidRewriteFlags(line, flag);
}
}
}