/* * Copyright (C) 2000 - 2012 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD 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 for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://openbd.org/ * $Id: cfFile.java 2374 2013-06-10 22:14:24Z alan $ */ package com.naryx.tagfusion.cfm.file; import java.io.CharArrayWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import com.naryx.tagfusion.cfm.engine.catchDataFactory; import com.naryx.tagfusion.cfm.engine.cfCatchData; import com.naryx.tagfusion.cfm.engine.cfComponentData; import com.naryx.tagfusion.cfm.engine.cfData; import com.naryx.tagfusion.cfm.engine.cfSession; import com.naryx.tagfusion.cfm.engine.cfmBadFileException; import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; import com.naryx.tagfusion.cfm.engine.variableStore; import com.naryx.tagfusion.cfm.parser.script.userDefinedFunction; import com.naryx.tagfusion.cfm.tag.cfParseTag; import com.naryx.tagfusion.cfm.tag.cfTag; import com.naryx.tagfusion.cfm.tag.cfTagReturnType; import com.naryx.tagfusion.cfm.tag.tagReader; import com.naryx.tagfusion.expression.compile.expressionEngine; public class cfFile implements java.io.Serializable { static final long serialVersionUID = 1; protected String pathToCFMFile; private String rawComponentName = null; protected String URI = null; protected cfmlURI address = null; private String encoding; // WARNING! always set using the setEncoding() method private boolean processPageEncoding = false; protected cfTag fileBody; private transient List<userDefinedFunction> udfList; private transient Map<String,cfmlURI> componentCache; private List<String> importPaths; public cfFile() { fileBody = null; setEncoding("UTF-8"); // default } public cfFile(cfmlURI uri, File _CFMFile) throws cfmBadFileException { init(uri, _CFMFile); Reader inFile = null; try { cfFileEncoding fileEncoding = new cfFileEncoding(_CFMFile); setEncoding(fileEncoding.getEncoding()); inFile = fileEncoding.getReader(_CFMFile); readFile(inFile); inFile.close(); } catch (org.alanwilliamson.lang.java.cfScriptCompilationException CFE) { throw CFE; } catch (cfmBadFileException BF) { throw new cfmBadFileException(pathToCFMFile, BF); } catch (cfmRunTimeException rte) { throw new cfmBadFileException(rte.getCatchData()); } catch (FileNotFoundException E) { throw new cfmBadFileException(pathToCFMFile); } catch (IOException e) { cfCatchData catchData = new cfCatchData(); catchData.setMessage(e.toString()); catchData.setDetail(pathToCFMFile); throw new cfmBadFileException(catchData); } finally { try { if (inFile != null) inFile.close(); } catch (IOException ignore) { } } } // note: the Reader instance should be i18n friendly public cfFile(cfmlURI uri, Reader _inFromWAR, String _encoding) throws cfmBadFileException { address = uri; if (uri.isRealFile()) pathToCFMFile = uri.getURI(); Reader inFile = null; setEncoding(_encoding); try { inFile = _inFromWAR; readFile(inFile); inFile.close(); } catch (cfmBadFileException BF) { throw new cfmBadFileException(pathToCFMFile, BF); } catch (cfmRunTimeException rte) { throw new cfmBadFileException("[WAR] " + pathToCFMFile); } catch (IOException E) { throw new cfmBadFileException("[WAR] " + pathToCFMFile); } finally { try { if (inFile != null) inFile.close(); } catch (IOException ignore) { } } } // this constructor is used to look for the CFPROCESSINGDIRECTIVE tag public cfFile(String fileBody) throws cfmBadFileException { try { encoding = null; processPageEncoding = true; Reader inFile = new StringReader(fileBody); readFile(inFile); } catch (cfmBadFileException bfe) { // we only care about exceptions throw by the CFPROCESSINGDIRECTIVE tag, // all other exceptions are ignored; the fact that we're only processing the first // 4K of the page will cause a variety of other exceptions to be thrown if (bfe.isPageEncodingException()) throw bfe; } catch (cfmRunTimeException ignore) { } catch (IOException ignore) {} } public void init(cfmlURI uri, File _CFMFile) { pathToCFMFile = _CFMFile.getAbsolutePath().replace('\\', '/'); address = uri; } public void addUDF(cfTag cftag, userDefinedFunction udf) throws cfmBadFileException { if (udfList == null) { udfList = new ArrayList<userDefinedFunction>(); } String name = udf.getName(); // check for built-in function name for UDF; allow override if CFC function if (!cftag.isSubordinate("CFCOMPONENT") && expressionEngine.isFunction(name)) { throw cftag.newBadFileException("Illegal Function Name", "The name \"" + name + "\" is the name of a built-in CFML function"); } // check for duplicate names Iterator<userDefinedFunction> iter = udfList.iterator(); while (iter.hasNext()) { if (name.equalsIgnoreCase(iter.next().getName())) { throw cftag.newBadFileException("Illegal Function Name", "The function name \"" + name + "\" is declared more than once"); } } udfList.add(udf); } public void addComponentPath( String _name, cfmlURI _path ){ if ( componentCache == null ) componentCache = new HashMap<String,cfmlURI>(); componentCache.put( _name, _path ); } public cfmlURI getComponentPath( String _name ){ if ( componentCache == null ){ return null; } return componentCache.get( _name ); } public cfTag getFileBody() { return fileBody; } public void setFileBody( cfTag tag ){ fileBody = tag; } // two cfFile objects are equal if they have the same physical path public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof cfFile) { // if either physical path is null, we cannot determine that the files are // equal. the physical path will be null when the file is in a BDA. if (pathToCFMFile == null || ((cfFile) obj).pathToCFMFile == null) return false; else return pathToCFMFile.equals(((cfFile) obj).pathToCFMFile); } return false; } // getName() may be overridden by subclasses to return something other than physical path public String getName() { return pathToCFMFile; } public String getPath() { return pathToCFMFile; } public String getEncoding() { return encoding; } public void setEncoding(String _encoding) { encoding = com.nary.util.Localization.convertCharSetToCharEncoding(_encoding); } public boolean processPageEncoding() { return processPageEncoding; } public String getURI() { return URI; } public cfFile setURI(String _URI) { URI = _URI; return this; } public cfmlURI getCfmlURI() { return address; } public void setCfmlURI(cfmlURI _address) { address = _address; } public String getComponentName() { return address.getComponentName(); } public void setComponentName(String _name) { address.setComponentName(_name); } /** * Returns the list of import paths * Returns null if there are none. */ public List<String> getImportedPaths() { return importPaths; } public synchronized void addImportPath( String _path ){ // we only create the importPaths list if it's going to be needed // and we can only determine that by these calls if ( importPaths == null ) importPaths = new ArrayList<String>(2); importPaths.add( _path ); } public synchronized void addImportPaths( List<String> _paths ){ // we only create the importPaths list if it's going to be needed // and we can only determine that by these calls if ( importPaths == null ) importPaths = new ArrayList<String>(_paths.size()); for ( int i = 0; i < _paths.size(); i++ ) importPaths.add( _paths.get(i) ); } protected void readFile(Reader _inFile) throws IOException, cfmRunTimeException { if (isCFENCODED(_inFile)) { cfCatchData _cfCatchData = new cfCatchData(); _cfCatchData.setMessage("CFML templates encoded with the ColdFusion CFENCODE utility are not supported"); throw new cfmBadFileException(_cfCatchData); } fileBody = new cfTag(this); cfParseTag parseTag = new cfParseTag(fileBody); tagReader tagR = new tagReader(_inFile); try { parseTag.readTag(tagR); } catch (cfmBadFileException bfe) { // This catch will catch exceptions thrown by cfOutputFilter.write() // which doesn't set the fileURI, line or column. In this case we // need to set them here. cfCatchData catchData = bfe.getCatchData(); cfmlURI fileURI = catchData.getFileURI(); if (fileURI == null) { catchData.setFileURI(address); catchData.setLine(tagR.getLine()); catchData.setColumn(tagR.getColumn()); } throw bfe; } catch (cfmRunTimeException e) { cfCatchData catchData = e.getCatchData(); catchData.setFileURI(address); catchData.setLine(tagR.getLine()); catchData.setColumn(tagR.getColumn()); throw new cfmBadFileException(catchData); } tagR.close(address); fileBody.normalise(processPageEncoding); } private static final String CFENCODE_HEADER = "Allaire Cold Fusion Template"; private static final int CFENCODE_HEADER_LEN = CFENCODE_HEADER.length(); private static boolean isCFENCODED(Reader _inFile) throws IOException { CharArrayWriter buffer = new CharArrayWriter(CFENCODE_HEADER_LEN); _inFile.mark(CFENCODE_HEADER_LEN); for (int i = 0; i < CFENCODE_HEADER_LEN; i++) { buffer.write(_inFile.read()); } if (buffer.toString().equals(CFENCODE_HEADER)) { return true; } _inFile.reset(); return false; } public boolean isFlushable() { return fileBody.isFlushable(); } public cfTagReturnType render(cfSession _Session) throws cfmRunTimeException { putUDFs(_Session); return fileBody.render(_Session); } public cfTagReturnType renderToString(cfSession _Session) throws cfmRunTimeException { putUDFs(_Session); return fileBody.renderToString(_Session, cfTag.DEFAULT_OPTIONS); } /** * This method is only invoked by cfMODULE.renderCustomTagEnd(). Do not invoke * putUDFs (see bug #2609). */ public cfTagReturnType renderToString(cfSession _Session, int options) throws cfmRunTimeException { return fileBody.renderToString(_Session, options); } public void putUDFs(cfSession _Session) throws cfmRunTimeException { if (udfList == null) { return; } // put UDFs defined in this template into the variables scope and the active // component's "this" scope cfData variablesScope = _Session.getQualifiedData(variableStore.VARIABLES_SCOPE); cfComponentData activeComponent = _Session.getActiveComponentData(); Iterator<userDefinedFunction> iter = udfList.iterator(); while (iter.hasNext()) { userDefinedFunction udf = iter.next(); String name = udf.getName(); // check for duplicate function declaration, such as within CFINCLUDE cfData cfdata = variablesScope.getData(name); if ((cfdata != null) && (cfdata.getDataType() == cfData.CFUDFDATA) && name.equalsIgnoreCase(((userDefinedFunction) cfdata).getName())) { throw new cfmRunTimeException(catchDataFactory.generalException("errorCode.runtimeError", "cffile.duplicatefunctioname", new String[] { name })); } variablesScope.setData(name, udf); // put UDF into variables scope if (activeComponent != null) { activeComponent.setData(name, udf); // put UDF into active component's scope } } } /* * This is for BDA originated files to be re-iniateted after loading */ public void reInitialiseTags() throws cfmBadFileException { fileBody.reInitialiseTags(); } /** * Added to support deserialization of cfc's on openbd-google * * @param rawComponentName */ public void setRawComponentName(String rawComponentName) { this.rawComponentName = rawComponentName; } /** * Added to support deserialization of cfc's on openbd-google * * @param rawComponentName */ public String getRawComponentName() { return rawComponentName; } }