diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/ssi/SsiProcessor.java b/platform/built-in-server/src/org/jetbrains/builtInWebServer/ssi/SsiProcessor.java index 38d73c9ffc6b..439e0b094247 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/ssi/SsiProcessor.java +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/ssi/SsiProcessor.java @@ -13,429 +13,419 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.builtInWebServer.ssi; +package org.jetbrains.builtInWebServer.ssi -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.util.SmartList; -import gnu.trove.THashMap; -import io.netty.buffer.ByteBufUtf8Writer; -import org.jetbrains.annotations.NotNull; +import com.intellij.openapi.diagnostic.Logger +import com.intellij.util.SmartList +import gnu.trove.THashMap +import io.netty.buffer.ByteBufUtf8Writer +import java.io.FileInputStream +import java.io.IOException +import java.util.* -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; +internal val LOG = Logger.getInstance(SsiProcessor::class.java) -public class SsiProcessor { - static final Logger LOG = Logger.getInstance(SsiProcessor.class); +internal val COMMAND_START = "" - protected static final String COMMAND_START = ""; +interface SsiCommand { + fun process(state: SsiProcessingState, commandName: String, paramNames: List, paramValues: Array, writer: ByteBufUtf8Writer): Long +} - protected final Map commands = new THashMap(); +class SsiProcessor(allowExec: Boolean) { + private val commands: MutableMap = THashMap() - @SuppressWarnings("SpellCheckingInspection") - public SsiProcessor(boolean allowExec) { - commands.put("config", new SsiCommand() { - @Override - public long process(@NotNull SsiProcessingState state, @NotNull String commandName, @NotNull List paramNames, @NotNull String[] paramValues, @NotNull ByteBufUtf8Writer writer) { - for (int i = 0; i < paramNames.size(); i++) { - String paramName = paramNames.get(i); - String paramValue = paramValues[i]; - String substitutedValue = state.substituteVariables(paramValue); - if (paramName.equalsIgnoreCase("errmsg")) { - state.configErrorMessage = substitutedValue; + init { + commands.put("config", object : SsiCommand { + override fun process(state: SsiProcessingState, commandName: String, paramNames: List, paramValues: Array, writer: ByteBufUtf8Writer): Long { + for (i in paramNames.indices) { + val paramName = paramNames[i] + val paramValue = paramValues[i] + val substitutedValue = state.substituteVariables(paramValue) + if (paramName.equals("errmsg", ignoreCase = true)) { + state.configErrorMessage = substitutedValue } - else if (paramName.equalsIgnoreCase("sizefmt")) { - state.configSizeFmt = substitutedValue; + else if (paramName.equals("sizefmt", ignoreCase = true)) { + state.configSizeFmt = substitutedValue } - else if (paramName.equalsIgnoreCase("timefmt")) { - state.setConfigTimeFormat(substitutedValue, false); + else if (paramName.equals("timefmt", ignoreCase = true)) { + state.setConfigTimeFormat(substitutedValue, false) } else { - LOG.info("#config--Invalid attribute: " + paramName); + LOG.info("#config--Invalid attribute: " + paramName) // We need to fetch this value each time, since it may change during the loop - writer.write(state.configErrorMessage); + writer.write(state.configErrorMessage) } } - return 0; + return 0 } - }); - commands.put("echo", new SsiCommand() { - @Override - public long process(@NotNull SsiProcessingState state, @NotNull String commandName, @NotNull List paramNames, @NotNull String[] paramValues, @NotNull ByteBufUtf8Writer writer) { - String encoding = "entity"; - String originalValue = null; - String errorMessage = state.configErrorMessage; - for (int i = 0; i < paramNames.size(); i++) { - String paramName = paramNames.get(i); - String paramValue = paramValues[i]; - if (paramName.equalsIgnoreCase("var")) { - originalValue = paramValue; + }) + commands.put("echo", object : SsiCommand { + override fun process(state: SsiProcessingState, commandName: String, paramNames: List, paramValues: Array, writer: ByteBufUtf8Writer): Long { + var encoding = "entity" + var originalValue: String? = null + val errorMessage = state.configErrorMessage + for (i in paramNames.indices) { + val paramName = paramNames[i] + val paramValue = paramValues[i] + if (paramName.equals("var", ignoreCase = true)) { + originalValue = paramValue } - else if (paramName.equalsIgnoreCase("encoding")) { - if (paramValue.equalsIgnoreCase("url") || paramValue.equalsIgnoreCase("entity") || paramValue.equalsIgnoreCase("none")) { - encoding = paramValue; + else if (paramName.equals("encoding", ignoreCase = true)) { + if (paramValue.equals("url", ignoreCase = true) || paramValue.equals("entity", ignoreCase = true) || paramValue.equals("none", ignoreCase = true)) { + encoding = paramValue } else { - LOG.info("#echo--Invalid encoding: " + paramValue); - writer.write(errorMessage); + LOG.info("#echo--Invalid encoding: " + paramValue) + writer.write(errorMessage) } } else { - LOG.info("#echo--Invalid attribute: " + paramName); - writer.write(errorMessage); + LOG.info("#echo--Invalid attribute: " + paramName) + writer.write(errorMessage) } } - assert originalValue != null; - String variableValue = state.getVariableValue(originalValue, encoding); - writer.write(variableValue == null ? "(none)" : variableValue); - return System.currentTimeMillis(); + val variableValue = state.getVariableValue(originalValue!!, encoding) + writer.write(variableValue ?: "(none)") + return System.currentTimeMillis() } - }); + }) //noinspection StatementWithEmptyBody if (allowExec) { // commands.put("exec", new SsiExec()); } - commands.put("include", new SsiCommand() { - @Override - public long process(@NotNull SsiProcessingState state, @NotNull String commandName, @NotNull List paramNames, @NotNull String[] paramValues, @NotNull ByteBufUtf8Writer writer) { - long lastModified = 0; - String configErrorMessage = state.configErrorMessage; - for (int i = 0; i < paramNames.size(); i++) { - String paramName = paramNames.get(i); - if (paramName.equalsIgnoreCase("file") || paramName.equalsIgnoreCase("virtual")) { - String substitutedValue = state.substituteVariables(paramValues[i]); + commands.put("include", object : SsiCommand { + override fun process(state: SsiProcessingState, commandName: String, paramNames: List, paramValues: Array, writer: ByteBufUtf8Writer): Long { + var lastModified: Long = 0 + val configErrorMessage = state.configErrorMessage + for (i in paramNames.indices) { + val paramName = paramNames[i] + if (paramName.equals("file", ignoreCase = true) || paramName.equals("virtual", ignoreCase = true)) { + val substitutedValue = state.substituteVariables(paramValues[i]) try { - boolean virtual = paramName.equalsIgnoreCase("virtual"); - lastModified = state.ssiExternalResolver.getFileLastModified(substitutedValue, virtual); - File file = state.ssiExternalResolver.findFile(substitutedValue, virtual); + val virtual = paramName.equals("virtual", ignoreCase = true) + lastModified = state.ssiExternalResolver.getFileLastModified(substitutedValue, virtual) + val file = state.ssiExternalResolver.findFile(substitutedValue, virtual) if (file == null) { - LOG.warn("#include-- Couldn't find file: " + substitutedValue); - return 0; + LOG.warn("#include-- Couldn't find file: " + substitutedValue) + return 0 } - InputStream in = new FileInputStream(file); + val `in` = FileInputStream(file) try { - writer.write(in, (int)file.length()); + writer.write(`in`, file.length().toInt()) } finally { - in.close(); + `in`.close() } } - catch (IOException e) { - LOG.warn("#include--Couldn't include file: " + substitutedValue, e); - writer.write(configErrorMessage); + catch (e: IOException) { + LOG.warn("#include--Couldn't include file: " + substitutedValue, e) + writer.write(configErrorMessage) } + } else { - LOG.info("#include--Invalid attribute: " + paramName); - writer.write(configErrorMessage); + LOG.info("#include--Invalid attribute: " + paramName) + writer.write(configErrorMessage) } } - return lastModified; + return lastModified } - }); - commands.put("flastmod", new SsiCommand() { - @Override - public long process(@NotNull SsiProcessingState state, @NotNull String commandName, @NotNull List paramNames, @NotNull String[] paramValues, @NotNull ByteBufUtf8Writer writer) { - long lastModified = 0; - String configErrMsg = state.configErrorMessage; - for (int i = 0; i < paramNames.size(); i++) { - String paramName = paramNames.get(i); - String paramValue = paramValues[i]; - String substitutedValue = state.substituteVariables(paramValue); - if (paramName.equalsIgnoreCase("file") || paramName.equalsIgnoreCase("virtual")) { - boolean virtual = paramName.equalsIgnoreCase("virtual"); - lastModified = state.ssiExternalResolver.getFileLastModified(substitutedValue, virtual); - Strftime strftime = new Strftime(state.configTimeFmt, Locale.US); - writer.write(strftime.format(new Date(lastModified))); + }) + commands.put("flastmod", object : SsiCommand { + override fun process(state: SsiProcessingState, commandName: String, paramNames: List, paramValues: Array, writer: ByteBufUtf8Writer): Long { + var lastModified: Long = 0 + val configErrMsg = state.configErrorMessage + for (i in paramNames.indices) { + val paramName = paramNames[i] + val paramValue = paramValues[i] + val substitutedValue = state.substituteVariables(paramValue) + if (paramName.equals("file", ignoreCase = true) || paramName.equals("virtual", ignoreCase = true)) { + val virtual = paramName.equals("virtual", ignoreCase = true) + lastModified = state.ssiExternalResolver.getFileLastModified(substitutedValue, virtual) + val strftime = Strftime(state.configTimeFmt, Locale.US) + writer.write(strftime.format(Date(lastModified))) } else { - LOG.info("#flastmod--Invalid attribute: " + paramName); - writer.write(configErrMsg); + LOG.info("#flastmod--Invalid attribute: " + paramName) + writer.write(configErrMsg) } } - return lastModified; + return lastModified } - }); - commands.put("fsize", new SsiFsize()); - commands.put("printenv", new SsiCommand() { - @Override - public long process(@NotNull SsiProcessingState state, @NotNull String commandName, @NotNull List paramNames, @NotNull String[] paramValues, @NotNull ByteBufUtf8Writer writer) { - long lastModified = 0; + }) + commands.put("fsize", SsiFsize()) + commands.put("printenv", object : SsiCommand { + override fun process(state: SsiProcessingState, commandName: String, paramNames: List, paramValues: Array, writer: ByteBufUtf8Writer): Long { + var lastModified: Long = 0 // any arguments should produce an error if (paramNames.isEmpty()) { - Set variableNames = new LinkedHashSet(); + val variableNames = LinkedHashSet() //These built-in variables are supplied by the mediator ( if not over-written by the user ) and always exist - variableNames.add("DATE_GMT"); - variableNames.add("DATE_LOCAL"); - variableNames.add("LAST_MODIFIED"); - state.ssiExternalResolver.addVariableNames(variableNames); - for (String variableName : variableNames) { - String variableValue = state.getVariableValue(variableName); + variableNames.add("DATE_GMT") + variableNames.add("DATE_LOCAL") + variableNames.add("LAST_MODIFIED") + state.ssiExternalResolver.addVariableNames(variableNames) + for (variableName in variableNames) { + var variableValue: String? = state.getVariableValue(variableName) // This shouldn't happen, since all the variable names must have values if (variableValue == null) { - variableValue = "(none)"; + variableValue = "(none)" } - writer.write(variableName); - writer.write('='); - writer.write(variableValue); - writer.write('\n'); - lastModified = System.currentTimeMillis(); + writer.append(variableName).append('=').append(variableValue).append('\n') + lastModified = System.currentTimeMillis() } } else { - writer.write(state.configErrorMessage); + writer.write(state.configErrorMessage) } - return lastModified; + return lastModified } - }); - commands.put("set", new SsiCommand() { - @Override - public long process(@NotNull SsiProcessingState state, @NotNull String commandName, @NotNull List paramNames, @NotNull String[] paramValues, @NotNull ByteBufUtf8Writer writer) { - long lastModified = 0; - String errorMessage = state.configErrorMessage; - String variableName = null; - for (int i = 0; i < paramNames.size(); i++) { - String paramName = paramNames.get(i); - String paramValue = paramValues[i]; - if (paramName.equalsIgnoreCase("var")) { - variableName = paramValue; + }) + commands.put("set", object : SsiCommand { + override fun process(state: SsiProcessingState, commandName: String, paramNames: List, paramValues: Array, writer: ByteBufUtf8Writer): Long { + var lastModified: Long = 0 + val errorMessage = state.configErrorMessage + var variableName: String? = null + for (i in paramNames.indices) { + val paramName = paramNames[i] + val paramValue = paramValues[i] + if (paramName.equals("var", ignoreCase = true)) { + variableName = paramValue } - else if (paramName.equalsIgnoreCase("value")) { + else if (paramName.equals("value", ignoreCase = true)) { if (variableName != null) { - String substitutedValue = state.substituteVariables(paramValue); - state.ssiExternalResolver.setVariableValue(variableName, substitutedValue); - lastModified = System.currentTimeMillis(); + val substitutedValue = state.substituteVariables(paramValue) + state.ssiExternalResolver.setVariableValue(variableName, substitutedValue) + lastModified = System.currentTimeMillis() } else { - LOG.info("#set--no variable specified"); - writer.write(errorMessage); - throw new SsiStopProcessingException(); + LOG.info("#set--no variable specified") + writer.write(errorMessage) + throw SsiStopProcessingException() } } else { - LOG.info("#set--Invalid attribute: " + paramName); - writer.write(errorMessage); - throw new SsiStopProcessingException(); + LOG.info("#set--Invalid attribute: " + paramName) + writer.write(errorMessage) + throw SsiStopProcessingException() } } - return lastModified; + return lastModified } - }); + }) - SsiConditional ssiConditional = new SsiConditional(); - commands.put("if", ssiConditional); - commands.put("elif", ssiConditional); - commands.put("endif", ssiConditional); - commands.put("else", ssiConditional); + val ssiConditional = SsiConditional() + commands.put("if", ssiConditional) + commands.put("elif", ssiConditional) + commands.put("endif", ssiConditional) + commands.put("else", ssiConditional) } /** * @return the most current modified date resulting from any SSI commands */ - public long process(@NotNull SsiExternalResolver ssiExternalResolver, @NotNull String fileContents, long lastModifiedDate, @NotNull ByteBufUtf8Writer writer) { - SsiProcessingState ssiProcessingState = new SsiProcessingState(ssiExternalResolver, lastModifiedDate); - int index = 0; - boolean inside = false; - StringBuilder command = new StringBuilder(); + fun process(ssiExternalResolver: SsiExternalResolver, fileContents: String, lastModifiedDate: Long, writer: ByteBufUtf8Writer): Long { + var resultLastModifiedDate = lastModifiedDate + val ssiProcessingState = SsiProcessingState(ssiExternalResolver, resultLastModifiedDate) + var index = 0 + var inside = false + val command = StringBuilder() try { - while (index < fileContents.length()) { - char c = fileContents.charAt(index); + while (index < fileContents.length) { + val c = fileContents[index] if (inside) { - if (c == COMMAND_END.charAt(0) && charCmp(fileContents, index, COMMAND_END)) { - inside = false; - index += COMMAND_END.length(); - String commandName = parseCommand(command); - if (LOG.isDebugEnabled()) { - LOG.debug("SSIProcessor.process -- processing command: " + commandName); + if (c == COMMAND_END[0] && charCmp(fileContents, index, COMMAND_END)) { + inside = false + index += COMMAND_END.length + val commandName = parseCommand(command) + if (LOG.isDebugEnabled) { + LOG.debug("SSIProcessor.process -- processing command: " + commandName) } - List paramNames = parseParamNames(command, commandName.length()); - String[] paramValues = parseParamValues(command, commandName.length(), paramNames.size()); + val paramNames = parseParamNames(command, commandName.length) + val paramValues = parseParamValues(command, commandName.length, paramNames.size) // We need to fetch this value each time, since it may change during the loop - String configErrMsg = ssiProcessingState.configErrorMessage; - SsiCommand ssiCommand = commands.get(commandName.toLowerCase(Locale.ENGLISH)); - String errorMessage = null; + val configErrMsg = ssiProcessingState.configErrorMessage + val ssiCommand = commands[commandName.toLowerCase(Locale.ENGLISH)] + var errorMessage: String? = null if (ssiCommand == null) { - errorMessage = "Unknown command: " + commandName; + errorMessage = "Unknown command: " + commandName } else if (paramValues == null) { - errorMessage = "Error parsing directive parameters."; + errorMessage = "Error parsing directive parameters." } - else if (paramNames.size() != paramValues.length) { - errorMessage = "Parameter names count does not match parameter values count on command: " + commandName; + else if (paramNames.size != paramValues.size) { + errorMessage = "Parameter names count does not match parameter values count on command: " + commandName } else { // don't process the command if we are processing conditional commands only and the command is not conditional - if (!ssiProcessingState.conditionalState.processConditionalCommandsOnly || ssiCommand instanceof SsiConditional) { - long newLastModified = ssiCommand.process(ssiProcessingState, commandName, paramNames, paramValues, writer); - if (newLastModified > lastModifiedDate) { - lastModifiedDate = newLastModified; + if (!ssiProcessingState.conditionalState.processConditionalCommandsOnly || ssiCommand is SsiConditional) { + val newLastModified = ssiCommand.process(ssiProcessingState, commandName, paramNames, paramValues, writer) + if (newLastModified > resultLastModifiedDate) { + resultLastModifiedDate = newLastModified } } } if (errorMessage != null) { - LOG.warn(errorMessage); - writer.write(configErrMsg); + LOG.warn(errorMessage) + writer.write(configErrMsg) } } else { - command.append(c); - index++; + command.append(c) + index++ } } - else if (c == COMMAND_START.charAt(0) && charCmp(fileContents, index, COMMAND_START)) { - inside = true; - index += COMMAND_START.length(); - command.setLength(0); + else if (c == COMMAND_START[0] && charCmp(fileContents, index, COMMAND_START)) { + inside = true + index += COMMAND_START.length + command.setLength(0) } else { if (!ssiProcessingState.conditionalState.processConditionalCommandsOnly) { - writer.write(c); + writer.write(c.toInt()) } - index++; + index++ } } } - catch (SsiStopProcessingException e) { + catch (e: SsiStopProcessingException) { //If we are here, then we have already stopped processing, so all is good } - return lastModifiedDate; + + return resultLastModifiedDate } - @NotNull - protected List parseParamNames(@NotNull StringBuilder command, int start) { - int bIdx = start; - List values = new SmartList(); - boolean inside = false; - StringBuilder builder = new StringBuilder(); - while (bIdx < command.length()) { + protected fun parseParamNames(command: StringBuilder, start: Int): List { + var bIdx = start + val values = SmartList() + var inside = false + val builder = StringBuilder() + while (bIdx < command.length) { if (inside) { - while (bIdx < command.length() && command.charAt(bIdx) != '=') { - builder.append(command.charAt(bIdx)); - bIdx++; + while (bIdx < command.length && command[bIdx] != '=') { + builder.append(command[bIdx]) + bIdx++ } - values.add(builder.toString()); - builder.setLength(0); - inside = false; - int quotes = 0; - boolean escaped = false; - for (; bIdx < command.length() && quotes != 2; bIdx++) { - char c = command.charAt(bIdx); + values.add(builder.toString()) + builder.setLength(0) + inside = false + var quotes = 0 + var escaped = false + while (bIdx < command.length && quotes != 2) { + val c = command[bIdx] // Need to skip escaped characters if (c == '\\' && !escaped) { - escaped = true; - continue; + escaped = true + bIdx++ + continue } if (c == '"' && !escaped) { - quotes++; + quotes++ } - escaped = false; + escaped = false + bIdx++ } } else { - while (bIdx < command.length() && isSpace(command.charAt(bIdx))) { - bIdx++; + while (bIdx < command.length && isSpace(command[bIdx])) { + bIdx++ } - if (bIdx >= command.length()) { - break; + if (bIdx >= command.length) { + break } - inside = true; + inside = true } } - return values; + return values } @SuppressWarnings("AssignmentToForLoopParameter") - protected String[] parseParamValues(@NotNull StringBuilder command, int start, int count) { - int valueIndex = 0; - boolean inside = false; - String[] values = new String[count]; - StringBuilder builder = new StringBuilder(); - char endQuote = 0; - for (int bIdx = start; bIdx < command.length(); bIdx++) { + protected fun parseParamValues(command: StringBuilder, start: Int, count: Int): Array? { + var valueIndex = 0 + var inside = false + val values = arrayOfNulls(count) + val builder = StringBuilder() + var endQuote: Char = 0.toChar() + var bIdx = start + while (bIdx < command.length) { if (!inside) { - while (bIdx < command.length() && !isQuote(command.charAt(bIdx))) { - bIdx++; + while (bIdx < command.length && !isQuote(command[bIdx])) { + bIdx++ } - if (bIdx >= command.length()) { - break; + if (bIdx >= command.length) { + break } - inside = true; - endQuote = command.charAt(bIdx); + inside = true + endQuote = command[bIdx] } else { - boolean escaped = false; - for (; bIdx < command.length(); bIdx++) { - char c = command.charAt(bIdx); + var escaped = false + while (bIdx < command.length) { + val c = command[bIdx] // Check for escapes if (c == '\\' && !escaped) { - escaped = true; - continue; + escaped = true + bIdx++ + continue } // If we reach the other " then stop if (c == endQuote && !escaped) { - break; + break } // Since parsing of attributes and var // substitution is done in separate places, // we need to leave escape in the string if (c == '$' && escaped) { - builder.append('\\'); + builder.append('\\') } - escaped = false; - builder.append(c); + escaped = false + builder.append(c) + bIdx++ } // If we hit the end without seeing a quote // the signal an error - if (bIdx == command.length()) { - return null; + if (bIdx == command.length) { + return null } - values[valueIndex++] = builder.toString(); - builder.setLength(0); - inside = false; + values[valueIndex++] = builder.toString() + builder.setLength(0) + inside = false } + bIdx++ } - return values; + @Suppress("CAST_NEVER_SUCCEEDS") + return values as Array } - @NotNull - private String parseCommand(@NotNull StringBuilder instruction) { - int firstLetter = -1; - int lastLetter = -1; - for (int i = 0; i < instruction.length(); i++) { - char c = instruction.charAt(i); + private fun parseCommand(instruction: StringBuilder): String { + var firstLetter = -1 + var lastLetter = -1 + for (i in 0..instruction.length - 1) { + val c = instruction[i] if (Character.isLetter(c)) { if (firstLetter == -1) { - firstLetter = i; + firstLetter = i } - lastLetter = i; + lastLetter = i } else if (isSpace(c)) { if (lastLetter > -1) { - break; + break } } else { - break; + break } } - return firstLetter == -1 ? "" : instruction.substring(firstLetter, lastLetter + 1); + return if (firstLetter == -1) "" else instruction.substring(firstLetter, lastLetter + 1) } - protected boolean charCmp(String buf, int index, String command) { - return buf.regionMatches(index, command, 0, command.length()); - } + protected fun charCmp(buf: String, index: Int, command: String) = buf.regionMatches(index, command, 0, command.length) + protected fun isSpace(c: Char) = c == ' ' || c == '\n' || c == '\t' || c == '\r' - protected boolean isSpace(char c) { - return c == ' ' || c == '\n' || c == '\t' || c == '\r'; - } - - protected boolean isQuote(char c) { - return c == '\'' || c == '\"' || c == '`'; - } + protected fun isQuote(c: Char) = c == '\'' || c == '\"' || c == '`' } \ No newline at end of file