/**
*
* Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
* Copyright (c) 2016, Lucee Association Switzerland. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
**/
package lucee.transformer.cfml.evaluator.impl;
import java.util.Iterator;
import lucee.runtime.functions.owasp.ESAPIEncode;
import lucee.runtime.functions.owasp.EncodeForCSS;
import lucee.transformer.bytecode.Body;
import lucee.transformer.bytecode.Statement;
import lucee.transformer.bytecode.cast.CastOther;
import lucee.transformer.bytecode.cast.CastString;
import lucee.transformer.bytecode.expression.var.BIF;
import lucee.transformer.bytecode.statement.PrintOut;
import lucee.transformer.bytecode.statement.tag.Attribute;
import lucee.transformer.bytecode.statement.tag.Tag;
import lucee.transformer.bytecode.statement.tag.TagOutput;
import lucee.transformer.cfml.evaluator.EvaluatorException;
import lucee.transformer.cfml.evaluator.EvaluatorSupport;
import lucee.transformer.expression.Expression;
import lucee.transformer.expression.literal.Literal;
import lucee.transformer.expression.var.Member;
import lucee.transformer.expression.var.Variable;
import lucee.transformer.library.function.FunctionLib;
import lucee.transformer.library.function.FunctionLibFunction;
import lucee.transformer.library.tag.TagLibTag;
/**
* Prueft den Kontext des Tag output.
* Das Tag output darf nicht innerhalb eines output Tag verschachtelt sein,
* ausser das aeussere Tag besitzt ein group Attribute. Das innere Tag darf jedoch kein group Attribute besitzen.
*/
public final class Output extends EvaluatorSupport {
private static String ENCODE_FOR = EncodeForCSS.class.getName();
static {
// remove CSS
ENCODE_FOR=ENCODE_FOR.substring(0, ENCODE_FOR.length()-3);
}
@Override
public void evaluate(Tag tag, TagLibTag libTag, FunctionLib[] flibs) throws EvaluatorException {
TagOutput output=(TagOutput) tag;
// check if inside a query tag
TagOutput parent = output;
// encodeFor
Attribute encodeFor = tag.getAttribute("encodefor");
if(encodeFor!=null) {
Expression encodeForValue = CastString.toExprString(encodeFor.getValue());
if(encodeForValue instanceof Literal) {
Literal l=(Literal)encodeForValue;
short df=(short)-1;
short encType = ESAPIEncode.toEncodeType( l.getString(),df);
if(encType!=df)encodeForValue=encodeForValue.getFactory().createLitInteger(encType);
}
addEncodeToChildren(
tag.getBody().getStatements().iterator(),
encodeForValue,
getEncodeForFunction(flibs)
);
}
// query
boolean hasParentWithGroup=false;
boolean hasParentWithQuery=false;
boolean hasQuery=tag.containsAttribute("query");
while((parent=getParentTagOutput(parent))!=null) {
if(!hasParentWithQuery)hasParentWithQuery=parent.hasQuery();
if(!hasParentWithGroup)hasParentWithGroup=parent.hasGroup();
if(hasParentWithQuery && hasParentWithGroup)break;
}
if(hasQuery && hasParentWithQuery)
throw new EvaluatorException("Nesting of tags cfoutput with attribute query is not allowed");
if(hasQuery)
output.setType(TagOutput.TYPE_QUERY);
else if(tag.containsAttribute("group") && hasParentWithQuery)
output.setType(TagOutput.TYPE_GROUP);
else if(hasParentWithQuery) {
if(hasParentWithGroup) output.setType(TagOutput.TYPE_INNER_GROUP);
else output.setType(TagOutput.TYPE_INNER_QUERY);
}
else
output.setType(TagOutput.TYPE_NORMAL);
// attribute maxrows and endrow not allowd at the same time
if(tag.containsAttribute("maxrows") && tag.containsAttribute("endrow"))
throw new EvaluatorException("Wrong Context, you cannot use attribute maxrows and endrow at the same time.");
}
private FunctionLibFunction getEncodeForFunction(FunctionLib[] flibs) throws EvaluatorException {
FunctionLibFunction f;
if(flibs!=null)for(int i=0;i<flibs.length;i++) {
f = flibs[i].getFunction("ESAPIEncode");
if(f!=null) return f;
}
// should never happen
throw new EvaluatorException("could not find function ESAPIEncode ("+(flibs==null?"null":""+flibs.length)+")");
}
public static TagOutput getParentTagOutput(TagOutput stat) {
Statement parent = stat;
while(true) {
parent=parent.getParent();
if(parent==null)return null;
if(parent instanceof TagOutput) return (TagOutput) parent;
}
}
private void addEncodeToChildren(Iterator it, Expression encodeForValue, FunctionLibFunction encodeForBIF) {
Statement stat;
while(it.hasNext()) {
stat=(Statement) it.next();
if(stat instanceof PrintOut) {
PrintOut printOut = ((PrintOut)stat);
Expression e = removeCastString(printOut.getExpr());
if(!(e instanceof Literal)) {
if(e instanceof Variable) {
Member member = ((Variable)e).getFirstMember();
if(member instanceof BIF) {
BIF bif=(BIF) member;
String cn = bif.getClassDefinition().getClassName();
if(cn.startsWith(ENCODE_FOR) || cn.equals(ESAPIEncode.class.getName())) {
continue;
}
}
}
printOut.setEncodeFor(encodeForValue);
}
}
else if(stat instanceof Tag){
Body b=((Tag)stat).getBody();
if(b!=null)
addEncodeToChildren(b.getStatements().iterator(),encodeForValue,encodeForBIF);
}
else if(stat instanceof Body){
addEncodeToChildren(((Body)stat).getStatements().iterator(),encodeForValue,encodeForBIF);
}
}
}
private Expression removeCastString(Expression expr) {
while(true) {
if(expr instanceof CastString){
expr=((CastString)expr).getExpr();
}
else if(
expr instanceof CastOther &&
(
((CastOther) expr).getType().equalsIgnoreCase("String") ||
((CastOther) expr).getType().equalsIgnoreCase("java.lang.String")
)
){
expr=((CastOther) expr).getExpr();
}
else break;
}
return expr;
}
}