/* * Copyright (C) 2000 - 2011 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: cfSession.java 2486 2015-01-22 03:22:37Z alan $ */ package com.naryx.tagfusion.cfm.engine; /** * This class represents the session between the client and the server for one * request. This is not the session as in the CFML session scope, nor is it a * session in the Servlet context. */ import java.io.File; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.Stack; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.aw20.collections.FastStack; import com.nary.io.FileUtils; import com.nary.util.FastMap; import com.nary.util.string; import com.naryx.tagfusion.cfm.application.ScriptProtect; import com.naryx.tagfusion.cfm.application.cfAPPLICATION; import com.naryx.tagfusion.cfm.application.cfApplicationData; import com.naryx.tagfusion.cfm.file.cfFile; import com.naryx.tagfusion.cfm.file.cfmlFileCache; import com.naryx.tagfusion.cfm.file.cfmlURI; import com.naryx.tagfusion.cfm.parser.CFCall; import com.naryx.tagfusion.cfm.parser.CFCallStack; import com.naryx.tagfusion.cfm.parser.CFContext; import com.naryx.tagfusion.cfm.parser.CFGlobalScope; import com.naryx.tagfusion.cfm.parser.CFUndefinedValue; import com.naryx.tagfusion.cfm.parser.cfLData; import com.naryx.tagfusion.cfm.parser.runTime; import com.naryx.tagfusion.cfm.parser.script.CFParsedStatement; import com.naryx.tagfusion.cfm.parser.script.userDefinedFunction; import com.naryx.tagfusion.cfm.sql.cfSQLQueryData; import com.naryx.tagfusion.cfm.sql.cfTRANSACTION; import com.naryx.tagfusion.cfm.sql.cfTransactionCache; import com.naryx.tagfusion.cfm.sql.preparedData; import com.naryx.tagfusion.cfm.sql.resultSetHolder; import com.naryx.tagfusion.cfm.tag.cfERROR; import com.naryx.tagfusion.cfm.tag.cfFLUSH; import com.naryx.tagfusion.cfm.tag.cfFUNCTION; import com.naryx.tagfusion.cfm.tag.cfMODULE; import com.naryx.tagfusion.cfm.tag.cfTag; import com.naryx.tagfusion.expression.function.GetHttpStatusLabel; import com.naryx.tagfusion.util.debugRecorder; import com.naryx.tagfusion.util.debuggerListener; import com.naryx.tagfusion.util.fullRecorder; import com.naryx.tagfusion.util.nullDebugger; import com.naryx.tagfusion.util.nullRecorder; public class cfSession { // key for storing a cfSession instance in the request scope for CFINCLUDE public static final String ATTR_NAME = "com.naryx.tagfusion.cfm.engine.cfSession"; /* * ATTENTION! Don't initialize any instance attributes that are initialized in * the "copy" constructor cfSession( cfSession _oldSession, boolean * _escapeSingleQuotes ). */ public int sessionID; public HttpServletRequest REQ; public cfHttpServletResponse RES; public ServletContext CTX; private boolean bForwardRedirect; // forwarded or redirected private boolean bPostRequest; private variableStore dataStore; private CFContext cfContext; private cfSession topLevelSession; private FastStack<cfFile> fileStack; private FastStack<cfOutputFilter> filterStack; private FastStack<cfTag> tagStack; private FastStack<cfComponentData> _componentDataStack; private FastStack<cfFUNCTION> _componentTagStack; private FastStack<userDefinedFunction> userDefinedFunctionStack; private FastStack<cfStructData> superStack; private FastStack<cfStructData> callerStack; private FastStack<cfStructData> variableStack; private FastStack<Stack<cfQueryResultData>> queryStack; private FastStack<customTagVariableWrapper> baseTagDataStack; private Map<String, cfFile> fileCache; private cfOutputFilter outputFilter; private int activeExpression; private Locale locale; private Map<String, Object> dataBin; private boolean bProcessingCFOUTPUT; // These two variables support the private int cfSettingCounter; // the CFSETTING feature private boolean escapeSingleQuotes; private boolean hasBufferReset; public debuggerListener debugger; // A debugger instance private debugRecorder recorder; // A debugRecorder instance private Map<String, Random> randoms = null; private long totalPageOut; // These members are used to store information about the last called cfflush tag. private boolean cfFlushCalled = false; private String cfFlushFilePath; private int cfFlushLine; private int cfFlushColumn; // per-request mappings from CFMAPPING private Map<String, String> mappings; // for aborting threads externally (from another thread) private boolean threadStopped; // per-request database connections private Map<String, Connection> connections; // metric collection private int metricTotalQuery=0; private long metricTotalQueryTime=0; public boolean isStopped() { return topLevelSession.threadStopped; } public void stopThread() { threadStopped = true; } public int getSessionID(){ return sessionID; } /** * Create a session from a parent session. This constructor is used by * cfTag.renderToString() for rendering tag bodies as strings (such as for * CFQUERY, for example). Make sure to buffer the entire output--don't send * any to the client! Also, it's expected that no changes will be made to * cookies, response headers, response content type, etc. */ public cfSession(cfSession _oldSession, boolean _escapeSingleQuotes) { escapeSingleQuotes = _escapeSingleQuotes; REQ = _oldSession.REQ; RES = _oldSession.RES.createChild(this); CTX = _oldSession.CTX; dataStore = _oldSession.dataStore; cfContext = _oldSession.cfContext; topLevelSession = _oldSession.topLevelSession; fileStack = _oldSession.fileStack; filterStack = _oldSession.filterStack; tagStack = _oldSession.tagStack; _componentDataStack = _oldSession._componentDataStack; _componentTagStack = _oldSession._componentTagStack; userDefinedFunctionStack = _oldSession.userDefinedFunctionStack; superStack = _oldSession.superStack; callerStack = _oldSession.callerStack; variableStack = _oldSession.variableStack; queryStack = _oldSession.queryStack; fileCache = _oldSession.fileCache; baseTagDataStack = _oldSession.baseTagDataStack; outputFilter = _oldSession.outputFilter; locale = _oldSession.locale; dataBin = _oldSession.dataBin; bPostRequest = _oldSession.bPostRequest; bProcessingCFOUTPUT = _oldSession.bProcessingCFOUTPUT; cfSettingCounter = _oldSession.cfSettingCounter; debugger = _oldSession.debugger; recorder = _oldSession.recorder; sessionID = _oldSession.sessionID; if ( _oldSession.randoms != null ){ randoms = new FastMap<String, Random>(); randoms.putAll( _oldSession.randoms ); } totalPageOut = _oldSession.totalPageOut; mappings = _oldSession.mappings; connections = _oldSession.connections; } /** * Creates a cfSession instance for the specified request * * @param req - * the client's request * @param res - * the server's response to this request */ public cfSession(HttpServletRequest req, HttpServletResponse res, ServletContext ctx) { REQ = req; RES = new cfHttpServletResponse(this, res); CTX = ctx; dataStore = new variableStore(this); cfContext = new CFContext(new CFGlobalScope(this), this); bPostRequest = REQ.getMethod().equalsIgnoreCase( "POST" ); fileStack = new FastStack<cfFile>(); filterStack = new FastStack<cfOutputFilter>(); tagStack = new FastStack<cfTag>(40); _componentDataStack = new FastStack<cfComponentData>(); _componentTagStack = new FastStack<cfFUNCTION>(); userDefinedFunctionStack = new FastStack<userDefinedFunction>(); superStack = new FastStack<cfStructData>(); callerStack = new FastStack<cfStructData>(); variableStack = new FastStack<cfStructData>(10); queryStack = new FastStack<Stack<cfQueryResultData>>(2); fileCache = new FastMap<String, cfFile>(); topLevelSession = this; locale = Locale.getDefault(); dataBin = new FastMap<String, Object>(2); debugger = nullDebugger.staticInstance; sessionID = cfEngine.sessionCounter.getAndIncrement(); boolean debug = cfEngine.isDebuggerOutputEnabled() && checkDebugIP(req.getRemoteAddr()); // if debug enabled and request is from valid IP if ( debug ) { this.registerDebugRecorder( new fullRecorder() ); recorder.startRequest(); } else { this.registerDebugRecorder( nullRecorder.staticInstance ); } mappings = new FastMap<String, String>(); connections = new FastMap<String, Connection>(); } public boolean setSuppressWhiteSpace( boolean suppress ) { boolean temp = RES.isSuppressWhiteSpace(); RES.setSuppressWhiteSpace( suppress ); return temp; } public boolean isEscapeSingleQuotes() { return escapeSingleQuotes; } public String getRemoteIP(){ return REQ.getRemoteAddr(); } public void setEscapeSingleQuotes( boolean escape ) { escapeSingleQuotes = escape; } public String getEncoding() { return RES.getCharacterEncoding(); } /** --------------------------------------------------------------- * We keep track of the active UserDefiniedFunction. This will us * intelligently unwrap to the right point in the event of an exception ocurring. * * This is used in the userDefinedFunction.execute() and the exceptionCatcher.prepareSession() methods * * @param udf */ public void pushUserDefinedFunction(userDefinedFunction udf){ userDefinedFunctionStack.push(udf); } public userDefinedFunction popUserDefinedFunction(){ return userDefinedFunctionStack.pop(); } public boolean isUserDefinedFunctionEmpty(){ return userDefinedFunctionStack.empty(); } public userDefinedFunction peekUserDefinedFunction(){ if ( !userDefinedFunctionStack.empty() ) return userDefinedFunctionStack.peek(); else return null; } // ---------------------------------------------------------------- public Object getDataBin( String key ) { return dataBin.get( key ); } public void setDataBin( String key, Object value ) { dataBin.put( key, value ); } public void deleteDataBin( String key ) { dataBin.remove( key ); } public CFContext getCFContext() { return cfContext; } // used by CFTRY for snapshot public CFCallStack getCFCallStack() { CFCallStack copy = new CFCallStack(); copy.addAll( cfContext.getCallStack() ); return copy; } public void setCFCallStack( CFCallStack callStack ) { CFCallStack _callStack = cfContext.getCallStack(); _callStack.removeAllElements(); _callStack.addAll( callStack ); } public Stack<CFCallStack> getCallStackStack() { Stack<CFCallStack> copy = new Stack<CFCallStack>(); copy.addAll( cfContext.getCallStackStack() ); return copy; } public void setCallStackStack( Stack<CFCallStack> _callStackStack ) { Stack<CFCallStack> callStackStack = cfContext.getCallStackStack(); callStackStack.removeAllElements(); callStackStack.addAll( _callStackStack ); } // ---------------------------------------------------------------- public void setActiveExpression( int _e ){ activeExpression = _e; } public int getActiveExpression(){ return activeExpression; } public FastStack<customTagVariableWrapper> getBaseTagData() { return baseTagDataStack; } // used by CFTRY for snapshot @SuppressWarnings("unchecked") public FastStack<cfComponentData> getComponentDataStack() { return (FastStack<cfComponentData>)_componentDataStack.clone(); } public void setComponentDataStack( FastStack<cfComponentData> data ) { _componentDataStack.replaceWith( data ); } @SuppressWarnings("unchecked") public FastStack<cfFUNCTION> getComponentTagStack() { return (FastStack<cfFUNCTION>)_componentTagStack.clone(); } public void setComponentTagStack( FastStack<cfFUNCTION> data ) { _componentTagStack.replaceWith( data ); } // returns null if no active component public cfComponentData getActiveComponentData() { cfComponentData componentData = null; if ( !_componentDataStack.empty() ) { componentData = _componentDataStack.peek(); } return componentData; } /** * If there's an active component, return the fully qualified path to the * component in dotted notation, minus the component name. Returns null if no * active component. */ public String getActiveComponentPath() { cfComponentData componentData = getActiveComponentData(); if ( componentData != null ) { return componentData.getComponentPackagePath(); } return null; } // returns null if no active component public cfFUNCTION getActiveComponentTag() { cfFUNCTION componentTag = null; if ( !_componentTagStack.empty() ) { componentTag = _componentTagStack.peek(); } return componentTag; } public void pushComponentData( cfComponentData data, cfFUNCTION tag ) { _componentDataStack.push(data); _componentTagStack.push(tag); } public cfComponentData popComponentData() { cfComponentData data = _componentDataStack.pop(); _componentTagStack.pop(); return data; } // replace the cfFUNCTION tag at the top of the _componentTagStack with the // one specified public void replaceComponentTag( cfFUNCTION oldTag, cfFUNCTION newTag ) { if ( !_componentTagStack.empty() && ( _componentTagStack.peek() == oldTag ) ) { _componentTagStack.pop(); _componentTagStack.push( newTag ); } } /** * Set-up for tag-based UDF function call. */ public CFCall enterUDF( cfStructData newArguments, cfComponentData newSuper, boolean _includeQueryStack ) { // set component instances if present cfStructData newVariables = null; cfComponentData componentData = this.getActiveComponentData(); if ( componentData != null ) { newVariables = componentData.getVariablesScope(); if ( newSuper == null ) { newSuper = componentData.getSuperComponent(); } } return this.enterUDF( newArguments, newSuper, newVariables, _includeQueryStack ); } public CFCall enterUDF( cfStructData newArguments, cfStructData newSuper,cfStructData newVariables, boolean _includeQueryStack ) { // function locals CFCall call = new CFCall( newArguments ); cfContext.pushCall( call ); cfStructData oldVariables = pushVariables( _includeQueryStack ); // super if ( newSuper != null ) { dataStore.setQualifiedData( variableStore.SUPER_SCOPE, newSuper ); } // variables (use old variables if new variables not provided) dataStore.setQualifiedData( variableStore.VARIABLES_SCOPE, newVariables != null ? newVariables : oldVariables ); return call; } public boolean executingUDF() { return !cfContext.isCallEmpty(); } public void leaveUDF() { popVariables(); cfContext.popCall(); } public CFCall enterCFThread( cfStructData _attribs, cfStructData threadData ) throws cfmRunTimeException{ CFCall call = new CFCall(); call.put( "attributes", _attribs, cfContext ); call.put( "thread", threadData, cfContext ); cfContext.pushCall( call ); return call; } // for use by CFDUMP public cfStructData getLocalScope() { Map<String, cfData> localScope = cfContext.getLocalScope(); return ( localScope == null ? null : new cfStructData( localScope ) ); } public void enterCustomTag( cfStructData thisTag, cfStructData attributes, String tagName ) { enterCustomTag(thisTag, attributes, tagName, new cfStructData(), true ); } public void enterCustomTag( cfStructData thisTag, cfStructData attributes, String tagName, cfStructData newVariables ) { enterCustomTag(thisTag, attributes, tagName, newVariables, true ); } public void enterCustomTag( cfStructData thisTag, cfStructData attributes, String _tagName, cfStructData newVariables, boolean _addBaseTagData ) { cfCallerData variables = new cfCallerData(this, pushVariables( false )); newVariables.setData(cfMODULE.THISTAG_SCOPE, thisTag); newVariables.setData(variableStore.ATTRIBUTES_SCOPE_NAME, attributes); newVariables.setData(variableStore.CALLER_SCOPE_NAME, variables); if ( _addBaseTagData ) { if ( baseTagDataStack == null ) baseTagDataStack = new FastStack<customTagVariableWrapper>(); baseTagDataStack.push(new customTagVariableWrapper(_tagName, newVariables)); } dataStore.setQualifiedData( variableStore.VARIABLES_SCOPE, newVariables ); dataStore.setQualifiedData( variableStore.CALLER_SCOPE, variables ); cfContext.enterCustomTag(); // if rendering custom tag within component function, "forget" about // the fact that we're within the component (see bug #2170) pushComponentData( null, null ); } // restores the variables, caller, and arguments scopes to the state they // were in prior to entering the custom tag public cfStructData leaveCustomTag() { popComponentData(); cfContext.leaveCustomTag(); return popVariables(); } // called at the end of custom tag processing public void clearCustomTag() { leaveCustomTag(); baseTagDataStack.pop(); } /** * pushVariables * * Remove the following scopes in preparation to entering a UDF or custom tag: * function locals, arguments, caller, variables. Save these on a stack to be * restored later. Returns the saved variables scope. */ private cfStructData pushVariables( boolean _pushQueryStack ) { callerStack.push(dataStore.deleteQualifiedData(variableStore.CALLER_SCOPE)); superStack.push(dataStore.deleteQualifiedData(variableStore.SUPER_SCOPE)); variableStack.push(dataStore.deleteQualifiedData(variableStore.VARIABLES_SCOPE)); queryStack.push( _pushQueryStack ? dataStore.getQueryStack() : dataStore.removeQueryStack() ); return (cfStructData)variableStack.peek(); } /** * popVariables * * Restores the following scopes after returning from a UDF or custom tag: * arguments, caller, variables. Returns the old variables scope. */ public cfStructData popVariables() { // caller dataStore.deleteQualifiedData(variableStore.CALLER_SCOPE); cfStructData poppedCaller = callerStack.pop(); if ( poppedCaller != null ) { dataStore.setQualifiedData(variableStore.CALLER_SCOPE, poppedCaller); } // super dataStore.deleteQualifiedData(variableStore.SUPER_SCOPE); cfStructData poppedSuper = superStack.pop(); if ( poppedSuper != null ) { dataStore.setQualifiedData(variableStore.SUPER_SCOPE, poppedSuper); } // variables cfStructData oldVariables = (cfStructData) dataStore.deleteQualifiedData(variableStore.VARIABLES_SCOPE); cfStructData poppedVariables = (cfStructData)variableStack.pop(); if ( poppedVariables != null ) { dataStore.setQualifiedData(variableStore.VARIABLES_SCOPE, poppedVariables); } else { // this should never happen! dataStore.setQualifiedData(variableStore.VARIABLES_SCOPE,new cfStructData()); } // query stack dataStore.setQueryStack( queryStack.pop() ); return oldVariables; } // these methods are to save and restore the variable stacks for CFTRY/CFCATCH public FastStack<cfStructData> getSuperStack() { return (FastStack<cfStructData>)superStack.clone(); } public FastStack<cfStructData> getCallerStack() { return (FastStack<cfStructData>)callerStack.clone(); } public FastStack<cfStructData> getVariableStack() { return (FastStack<cfStructData>)variableStack.clone(); } public FastStack<Stack<cfQueryResultData>> getQueryStack() { return (FastStack<Stack<cfQueryResultData>>)queryStack.clone(); } public void setSuperStack( FastStack<cfStructData> supStack ) { superStack.replaceWith(supStack); } public void setCallerStack( FastStack<cfStructData> callStack ) { callerStack.replaceWith(callStack); } public void setVariableStack( FastStack<cfStructData> varStack ) { variableStack.replaceWith(varStack); } public void setQueryStack( FastStack<Stack<cfQueryResultData>> qryStack ) { queryStack.replaceWith(qryStack); } // ---------------------------------------------------------------- // --[ Methods required for debugging // ---------------------------------------------------------------- public void setShowDebugOutput( boolean _show ) { recorder.setShow(_show); } public boolean getShowDebugOutput() { return recorder.getShow(); } public boolean getShowDBActivity() { return recorder.getShowDBActivity(); } public boolean isDebugEnabled() { return !(recorder == nullRecorder.staticInstance); } public void recordException( cfmRunTimeException e, cfFile f, cfTag t ) { recorder.exceptionThrown(e, f, t); } public void recordQuery( cfFile f, String _name, cfSQLQueryData _q, List<preparedData> _qParams ) { // If the filename is null then use the URI. String template = f.getName(); if ( template == null ) template = f.getCfmlURI().getURI(); recorder.queryRan(template, _name, _q, _qParams); } public void recordUpdate( cfFile f, String _datasrc, String _q ) { // If the filename is null then use the URI. String template = f.getName(); if ( template == null ) template = f.getCfmlURI().getURI(); recorder.updateRan(template, _datasrc, _q); } public void recordInsert( cfFile f, String _datasrc, String _q ) { // If the filename is null then use the URI. String template = f.getName(); if ( template == null ) template = f.getCfmlURI().getURI(); recorder.insertRan(template, _datasrc, _q); } public void storedProcRan( cfFile f, String _datasrc, String _procName, long _execTime, List<preparedData> _params, List<resultSetHolder> _results ) { // If the filename is null then use the URI. String template = f.getName(); if ( template == null ) template = f.getCfmlURI().getURI(); recorder.storedProcRan(template, _datasrc, _procName, _execTime, _params, _results); } public void recordTracepoint( String tracePoint ) { recorder.recordTracepoint(tracePoint); } public void recordTimer( String timing ) { recorder.recordTimer(timing); } // ---------------------------------------------------------------- // --[ Methods for handling the present cfFile // ---------------------------------------------------------------- public void pushActiveFile( cfFile svrFile ) { fileStack.push(svrFile); debugger.startFile(svrFile); recorder.startFile(svrFile); } public cfFile popActiveFile() { cfFile f = fileStack.pop(); debugger.endFile(f); recorder.endFile(f); return f; } public cfFile activeFile() { if ( fileStack.size() == 0 || fileStack.empty() ) return null; else return fileStack.peek(); } public Enumeration<cfFile> fileStackEnumeration() { return fileStack.elements(); } // used by cftry to snapshot @SuppressWarnings("unchecked") public FastStack<cfFile> getFileStack() { return (FastStack<cfFile>)fileStack.clone(); } // used by cftry to restore snapshot public void setFileStack( FastStack<cfFile> _ht ) { fileStack.replaceWith(_ht); } public String getBaseTemplatePath() { cfFile f = (cfFile) fileStack.firstElement(); return f.getName(); } public File getCurrentFile(){ cfFile file = (com.naryx.tagfusion.cfm.file.cfFile) fileStack.peek(); return file.getCfmlURI().getFile(); } // ---------------------------------------------------------------- // --[ Tag Stack access functions // ---------------------------------------------------------------- public void pushTag( cfTag thisTag ) { tagStack.push(thisTag); debugger.startTag(thisTag); } public void popTag() { cfTag tag = tagStack.pop(); debugger.endTag(tag); } public void replaceTag( cfTag newTag ) { tagStack.pop(); tagStack.push(newTag); } public void clearTagStack() { tagStack.removeAllElements(); } public cfTag activeTag() { return (tagStack.empty() ? null : tagStack.peek()); } // used by cftry to snapshot public FastStack<cfTag> getTagStack() { return (FastStack<cfTag>)tagStack.clone(); } // used by cftry to snapshot public void setTagStack(FastStack<cfTag> _tg ) { tagStack.replaceWith(_tg); } public Enumeration<cfTag> getTagElements() { return tagStack.elements(); } public int getTagStackSize() { return tagStack.size(); } public cfTag getTagElement( int x ) { return tagStack.elementAt(x); } public void startScriptStatement( CFParsedStatement _statement ){ debugger.startScriptStatement( _statement ); } // ---------------------------------------------------------------- public Random getRandom( String _alg ) throws NoSuchAlgorithmException { Random random = null; String algorithm = _alg.toUpperCase(); if ( randoms == null ){ randoms = new FastMap<String, Random>(); }else{ random = randoms.get( algorithm ); } if ( random == null ) { if ( algorithm.equals( "CFMX_COMPAT" ) ){ random = new Random(); }else{ random = java.security.SecureRandom.getInstance( algorithm ); } randoms.put( algorithm, random ); } return random; } public void setRandomSeed( cfData seed, String _alg ) throws dataNotSupportedException, NoSuchAlgorithmException { Random rand = getRandom( _alg ); rand.setSeed(seed.getLong()); } // ---------------------------------------------------------------- public Locale getLocale() { return locale; } public String getLocaleDisplayName() { return locale.getDisplayName( Locale.US ); } public void setLocale( Locale _newLocale ) { locale = _newLocale; } /** * This is called from the cfEngine serviceCfcMethod/service methods to optionally decode the incoming * POST request, which includes the upload file forms. * * @param bProcessLegacyFormValidation * @throws cfmRunTimeException */ public void checkAndDecodePost( boolean bProcessLegacyFormValidation ) throws cfmRunTimeException { if ( !bPostRequest ) return; cfDecodedInput.checkAndDecodePost(this); if ( bProcessLegacyFormValidation ) ((cfFormData) dataStore.getData("form")).validateFormFields(this); } /** * If manage client data is required, this method be called to save the * session data */ public void closeClient() throws cfmRunTimeException { com.naryx.tagfusion.cfm.application.cfAPPLICATION.closeClient(this); } public Map<String, cfStructData> getDataStore() { return dataStore.getDataStore();// Used only for CFDUMPSESSION } public void setDataInSecurityStore( String key, Map<String, String> val, long rolesLifespan ) { variableStore.getSecurityStore().add(key, val, rolesLifespan); } public Map<String, String> getDataFromSecurityStore( String key ) { Map<String, String> data = null; try { data = variableStore.getSecurityStore().get(key); if ( data != null && data.isEmpty() ) data = null; // don't hand out an empty Map } catch (Exception e) { } return data; } public void removeDataFromSecurityStore( String key ) { variableStore.getSecurityStore().remove(key); } /** * ATTENTION! Calls to setQualifiedData() and getQualifiedData() must always * be made using the constants defined in the variableStore class as keys. */ public cfStructData getQualifiedData( int _key ) { return dataStore.getQualifiedData(_key); } public void setQualifiedData( int _key, cfStructData _cfData ) { dataStore.setQualifiedData(_key, _cfData); } public void setData( String _key, cfData _cfData ) throws cfmRunTimeException { cfData var = runTime.runExpression( this, _key, false ); // always set variables via cfLData.Set() to insure loop index invalidation ((cfLData)var).Set( _cfData, cfContext ); } public cfData getData( String _key ) { return dataStore.getData( _key, true, true ); } public cfData getData( String _key, boolean _doQuerySearch ) { return dataStore.getData( _key, _doQuerySearch, true ); } public cfData getData( String _key, boolean _doQuerySearch, boolean _doVarSearch ) { return dataStore.getData( _key, _doQuerySearch, _doVarSearch ); } public cfApplicationData getApplicationData() { cfData appData = getQualifiedData(variableStore.APPLICATION_SCOPE); if ( (appData != null) && (appData instanceof cfApplicationData) ) { return (cfApplicationData) appData; } return null; } public void deleteData( String _key ) throws cfmRunTimeException { dataStore.deleteData( _key ); } public void pushQuery( cfQueryResultData _query ) { dataStore.pushQuery(_query); } public cfQueryResultData popQuery() { return dataStore.popQuery(); } public cfQueryResultData peekQuery() { return dataStore.peekQuery(); } // ---------------------------------------------------------------- // --[ Output methods public void close() { // This is the last method that is called Look for Java objects if ( dataBin != null ){ Iterator<Map.Entry<String, Object>> iter = dataBin.entrySet().iterator(); while (iter.hasNext()) { Object obj = iter.next(); if ( obj instanceof cfJavaObjectData ) ((cfJavaObjectData) obj).removeCfmlPageContext(); } dataBin.clear(); } fileCache.clear(); } public boolean hasBufferReset(){ return hasBufferReset; } public void setBufferReset( boolean _reset ){ hasBufferReset = _reset; } /** * Reset the entire response, including headers and output buffer. */ public void reset() { try { RES.reset(); hasBufferReset = true; } catch (IllegalStateException ignore) { // IllegalStateException is thrown if the response is already committed; // reset() is invoked primarily for error handling, so just ignore it } } /** * Reset the output buffer, but not the response headers. */ public void resetBuffer() throws cfmRunTimeException { try { RES.resetBuffer(); hasBufferReset = true; } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder("Unable to reset response buffer because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot reset response buffer"); throw new cfmRunTimeException(catchData); } } public void setStatus( int statusCode ) throws cfmRunTimeException { try { RES.setStatus(statusCode); debugger.setHTTPStatus(statusCode, ""); } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder("Unable to set HTTP response status code because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot set HTTP response status code"); throw new cfmRunTimeException(catchData); } } public void setStatus( int statusCode, String value ) throws cfmRunTimeException { try { RES.setStatus(statusCode, value); debugger.setHTTPStatus(statusCode, value); } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder("Unable to set HTTP response status code because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot set HTTP response status code"); throw new cfmRunTimeException(catchData); } } public void setHeader( String _name, String _value ) throws cfmRunTimeException { try { RES.addHeader(_name, _value); debugger.setHTTPHeader(_name, _value); } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder("Unable to set HTTP response header because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot set HTTP response header"); throw new cfmRunTimeException(catchData); } } public void setContentType( String _contentType ) throws cfmRunTimeException { try { RES.setContentType(_contentType); } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder("Unable to set HTTP response content type because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot set HTTP response content type"); throw new cfmRunTimeException(catchData); } } public void setBufferSize( int size ) throws cfmRunTimeException { try { RES.setBufferSize(size); } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder("Unable to set page buffer size because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot set page buffer size"); throw new cfmRunTimeException(catchData); } } private void appendCfFlushInfo( StringBuilder detail ) { if ( cfFlushCalled ) { detail.append(" by a CFFLUSH tag at line "); detail.append(cfFlushLine); detail.append(" column "); detail.append(cfFlushColumn); detail.append(" in file "); detail.append(cfFlushFilePath); } } // --------------------------------------------------------- // used by tags to flag that output should be sent to page // regardless of whether <CFPROCESSINGDIRECTIVE ENABLECFOUPUTONLY=true> // // anyone who invokes this method should save the old value (returned by // this method) and restore it when finished public boolean setProcessingCfOutput( boolean _yes ) { boolean temp = bProcessingCFOUTPUT; bProcessingCFOUTPUT = _yes; return temp; } public boolean getProcessingCfOutput() { return bProcessingCFOUTPUT; } public void clearCfSettings() { cfSettingCounter = 0; } public void enableCfSettings( boolean _yes ) { if ( _yes ) cfSettingCounter += 1; else cfSettingCounter -= 1; if ( cfSettingCounter < 0 ) cfSettingCounter = 0; } public void suspendFilter() { filterStack.push(outputFilter); outputFilter = null; } public void unsuspendFilter() { outputFilter = filterStack.pop(); } public boolean isFiltered() { if ( outputFilter != null ) return true; else return false; } public boolean setOutputFilter( cfOutputFilter _newFilter ) { if ( outputFilter != null ) { if ( _newFilter != null ) { if ( outputFilter.shouldWeIgnore() ) return true; else return false; } else { outputFilter = null; return true; } } outputFilter = _newFilter; return true; } public void setHeadElement( String str, boolean append ) throws cfmRunTimeException { try { RES.setHeadElement( str, append); } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder("Unable to add text to HTML <head> tag because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot add text to HTML <head> tag"); throw new cfmRunTimeException(catchData); } } public void setBodyElement( String str, boolean append ) throws cfmRunTimeException { try { RES.setBodyElement( str, append); } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder("Unable to add text to HTML <body> tag because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot add text to HTML <body> tag"); throw new cfmRunTimeException(catchData); } } public void write( String line ) { if ( !bProcessingCFOUTPUT && cfSettingCounter > 0 ) return; RES.write( line ); totalPageOut += line.length(); } public void forceWrite( String line ) { RES.write( line ); totalPageOut += line.length(); } public void write( char[] buffer ) { if ( !bProcessingCFOUTPUT && cfSettingCounter > 0 ) return; RES.write(buffer, 0, buffer.length); totalPageOut += buffer.length; } public void write( byte[] buf ) throws cfmRunTimeException { try { RES.write(this,buf); } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder("Unable to write response data because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot write response data"); throw new cfmRunTimeException(catchData); } catch (IOException ignore) { // this should only happen when the user hits the browser "Stop" button // and breaks the connection } } public void write( byte[] buf, int off, int len ) throws cfmRunTimeException { try { RES.write(this,buf, off, len); } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder( "Unable to write response data because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot write response data"); throw new cfmRunTimeException(catchData); } catch (IOException ignore) { // this should only happen when the user hits the browser "Stop" button // and breaks the connection } } public String getOutputAsString() { return RES.getOutputAsString(); } public void abortPageProcessing() throws cfmAbortException { throw new cfmAbortException( false ); } public void abortPageProcessing( boolean flushOutput ) throws cfmAbortException { throw new cfmAbortException( flushOutput ); } public void sendRedirect( String absURL ) throws cfmRunTimeException { sendRedirect( absURL, true, 302 ); } public void sendRedirect( String absURL, boolean abort ) throws cfmRunTimeException { sendRedirect( absURL, abort, 302 ); } public void sendRedirect( String absURL, boolean abort, int statusCode ) throws cfmRunTimeException { try { // note this is to workaround a bug in WebSphere (see bug #2862) if ( absURL.toLowerCase().startsWith("mailto:") ){ RES.setStatus( statusCode ); RES.setHeader("Location", absURL ); }else{ if ( statusCode == 302 ) RES.sendRedirect(absURL); else{ RES.setStatus( statusCode, GetHttpStatusLabel.getStatusLabel(statusCode) ); RES.setHeader("Location", absURL ); } } } catch (IllegalStateException e) { StringBuilder detail = new StringBuilder("Unable to perform redirect because the page has been flushed"); appendCfFlushInfo(detail); cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail(detail.toString()); catchData.setMessage("Cannot perform redirect"); throw new cfmRunTimeException(catchData); } catch (IOException e) { cfCatchData catchData = new cfCatchData(); catchData.setType(cfCatchData.TYPE_TEMPLATE); catchData.setDetail("Redirect failed due to IO Exception: " + e); catchData.setMessage("Redirect failed due to IO Exception"); throw new cfmRunTimeException(catchData); } bForwardRedirect = true; if ( abort ) { throw new cfmAbortException(); } } public String getRequestURIPath() { String tmp = getRequestURI(); if ( tmp.charAt(tmp.length() - 1) == '/' ) return tmp; int c1 = tmp.lastIndexOf("/"); if ( c1 == -1 ) return "/"; else return tmp.substring(0, c1 + 1); } public String getRequestURI() { String uriName = (String) REQ.getAttribute("javax.servlet.include.servlet_path"); if ( uriName == null ) uriName = REQ.getServletPath(); if ( uriName.indexOf("?") != -1 ) uriName = uriName.substring(0, uriName.indexOf("?")); if ( uriName.charAt(uriName.length() - 1) == '/' ) uriName = REQ.getPathInfo(); return uriName; } public cfFile getRequestFile() throws cfmBadFileException { String uriName = getRequestURI(); String uriNameUpper = uriName.toUpperCase(); // Check the application.cfm and onrequestend.cfm page if ( uriNameUpper.endsWith("/APPLICATION.CFM") || uriNameUpper.endsWith("/APPLICATION.CFC") || uriNameUpper.endsWith("/ONREQUESTEND.CFM") || uriNameUpper.endsWith("/SERVER.CFC") ) { throw new cfmBadFileException(catchDataFactory.applicationRequestFileException(uriName.substring(uriName.lastIndexOf("/") + 1))); } else return getUriFile(uriName); } public cfmlURI getFilePath( String _uri ) { return new cfmlURI(REQ, cleanURI(_uri)); } private String cleanURI( String _uri ) { String URI = _uri.replace('\\', '/'); // normalize to remove Windows file separator chars if ( URI.charAt(0) != '/' ) URI = getPresentURI() + URI; return com.nary.io.FileUtils.cleanPath(URI); } /** * If you already have a cfmlURI instance, it's better to invoke getFile( cfmlURI ) * directly instead of getUriFile( cfmlURL.getURI() ) */ public cfFile getUriFile( String URI ) throws cfmBadFileException { cfFile cffile = getFile( new cfmlURI( REQ, cleanURI( URI ) ) ); if ( cffile.getURI() == null ) { cffile.setURI( URI ); } return cffile; } /** * If you already have a cfmlURI instance, it's better to invoke getFile( cfmlURI ) * directly instead of getRealFile( cfmlURL.getRealPath() ) */ public cfFile getRealFile( String realPath ) throws cfmBadFileException { return getFile( new cfmlURI( realPath, true ) ); } public cfFile getFile( cfmlURI uFile ) throws cfmBadFileException { String key = uFile.getCacheKey(); if ( fileCache.containsKey( key ) ) return fileCache.get( key ); cfFile thisFile = cfmlFileCache.getCfmlFile( CTX, uFile, REQ ); fileCache.put( key, thisFile ); return thisFile; } public void removeFromFileCache( String _file ) { Iterator<String> it = fileCache.keySet().iterator(); while (it.hasNext()) { String nextKey = it.next(); if ( nextKey.endsWith(_file) ) { it.remove(); } } } public String getPresentURI() { String URI = ""; cfFile thisFile; for (int x = fileStack.size() - 1; x >= 0; --x) { thisFile = fileStack.elementAt(x); if ( thisFile.getURI() != null ) { URI = thisFile.getURI(); break; } } // Special case, for the application.cfm / onrequestend.cfm files if ( fileStack.size() == 0 ) { URI = REQ.getServletPath(); if ( URI.charAt(URI.length() - 1) == '/' ) URI = REQ.getPathInfo(); } // --[ Clean up the URI int c1 = URI.lastIndexOf("/"); if ( c1 != -1 ) { if ( URI.charAt(0) == '/' ) URI = URI.substring(0, c1 + 1); else URI = "/" + URI.substring(0, c1 + 1); } else URI = "/"; return URI; } public String getPresentURIPath() { String tmp = getPresentURI(); if ( tmp.charAt(tmp.length() - 1) == '/' ) return tmp; int c1 = tmp.lastIndexOf("/"); if ( c1 == -1 ) return "/"; else return tmp.substring(0, c1 + 1); } /** * Returns a file path suitable for use in constructing a cfmlURI; that is, UNIX/Linux * physical paths are preceded with "$"; therefore, do not use the return value from * this method for constructing a java.io.File instance--use getPresentPhysicalPath() * instead */ public String getPresentFilePath() { String presentPath = getPresentDirectory(); if ( ( presentPath != null ) && ( presentPath.charAt(0) == '/' ) ) { // make sure the path is interpreted as a real path on UNIX/Linux, not a URL path presentPath = "$" + presentPath; } return presentPath; } /** * Returns a physical path suitable for constructing a java.io.File instance */ public String getPresentDirectory() { String presentPath = null; if ( fileStack.size() > 0 ) { presentPath = fileStack.elementAt(fileStack.size() - 1).getPath(); if ( presentPath != null ) { int c1 = presentPath.lastIndexOf('/'); if ( c1 != -1 ) presentPath = presentPath.substring(0, c1 + 1); } } return presentPath; } /* * Returns the physical path of the calling PAGE template; ie the first element in the stack */ public String getPhyscialPathOfCallingTemplate(){ String presentPath = null; if ( fileStack.size() > 0 ) { presentPath = fileStack.firstElement().getPath(); if ( presentPath != null ) { int c1 = presentPath.lastIndexOf('/'); if ( c1 != -1 ) presentPath = presentPath.substring(0, c1 + 1); } } return presentPath; } // ---------------------------------------------------------------- // ---] Page Processing // ---------------------------------------------------------------- // returns true if the repsonse has been flushed public boolean isFlushed() { return RES.isCommitted(); } public void pageFlush() { RES.flush(); } public void pageFlush( cfFLUSH tag ) { RES.flush(); cfFlushCalled = true; cfFlushFilePath = tag.getFile().getName(); cfFlushLine = tag.posLine; cfFlushColumn = tag.posColumn; } public void pageEnd() { if ( !bForwardRedirect ) recorder.dump(this); // in case there is any cfTransaction blocks outstanding // do this before closing the session to avoid problems writing client // variables to database cfTransactionCache tCache = (cfTransactionCache) getDataBin(cfTRANSACTION.DATA_BIN_KEY); if ( tCache != null ) tCache.close(); // Close the session try { closeClient(); } catch (cfmRunTimeException ignore) {} // This call must be made after closeClient() since it will use a database // connection if client data is being stored in a database. closeAllConnections(); if ( !bForwardRedirect ) pageFlush(); // Clear up any temporary files cfDecodedInput DI = (cfDecodedInput) getDataBin(cfDecodedInput.DATA_BIN_KEY); if ( DI != null ) DI.deleteFiles(); // Tell the debugger this is it debugger.endSession(); recorder.endRequest(); } /* * sessionEnd * * This method is called by code that creates a session that is not used * during normal page processing. In this case pageEnd() is not called so * sessionEnd() must be called to clean up the session. * NOTE: refer to bug NA#3174. */ public void sessionEnd() { closeAllConnections(); } /* ---------------------------------------------------------------- * Debugger */ public void registerDebugger( debuggerListener newDebugger ) { debugger = newDebugger; debugger.registerSession(this); } public void deRegisterDebugger() { debugger = nullDebugger.staticInstance; } public boolean isDebuggerRegistered(){ return !(debugger == nullDebugger.staticInstance); } /* ---------------------------------------------------------------- * DebugRecorder */ public void registerDebugRecorder( debugRecorder newRecorder ) { recorder = newRecorder; } public void deRegisterDebugRecorder() { recorder = nullRecorder.staticInstance; } public debugRecorder getDebugRecorder() { return recorder; } // ---------------------------------------------------------------- public void writeAndClose( byte[] dataToSend ) { /* * This method is only called from the graphingEngine class. It is for any * class that requires to dump raw data to the client stream, such as an * image. * * The caller of this method MUST set the content type via * cfSession.RES.setContentType() before invoking this method! */ try { RES.getOutputStream().write(dataToSend); RES.flushBuffer(); } catch (Exception e) { cfEngine.log("Error writing byte[] response: " + e); } } public String encodeURL(String urlToEncode) throws cfmRunTimeException { // See if there is an application first of all to encode, if not return cfApplicationData appData = getApplicationData(); if (appData == null) return urlToEncode; if (!appData.isCookiesWorking() && (appData.isClientEnabled() || (appData.isSessionEnabled() && !appData.isJ2EESessionEnabled()))) { // Need to add the CFID/CFTOKEN pairs onto this URL before encoding cfData urltoken = getQualifiedData(appData.isClientEnabled() ? variableStore.CLIENT_SCOPE : variableStore.SESSION_SCOPE).getData("urltoken"); if (urltoken != null) { if (urlToEncode.indexOf("?") == -1) urlToEncode += "?" + urltoken.getString(); else urlToEncode += "&" + urltoken.getString(); } } if (appData.isJ2EESessionEnabled()) return RES.encodeURL(urlToEncode); return urlToEncode; } /** * @_ipAddr An IP address to check against the list of IPs that are configured * to view Debug output. * * @return whether or not debug output should be enabled based on the ip * address of the request (true if the passed in client IP should be * allowed to see debug output, else false is returned. */ public static boolean checkDebugIP( String _ipAddr ) { // if no IP specified then int[] debugIPS = cfEngine.getDebugIPs(); if ( debugIPS.length == 0 ) return false; else return containsIpMatch( DecodeIPs( _ipAddr ), debugIPS ); } public static int[] DecodeIPs( String ipString ) { if ( ipString == null ) return null; List<String> tokens = string.split( ipString, ", " ); int[] result = new int[ tokens.size() * 4 ]; int index = 0; for ( int i = 0; i < tokens.size(); i++ ) { String ip = tokens.get( i ).toString(); List<String> nums = string.split( ip, "." ); for ( int j = 0; j < 4; j++ ) { if ( j < nums.size() ) { String num = nums.get( j ).toString(); if ( num.equals( "*" ) ) { result[ index + j ] = -1; } else { try { result[ index + j ] = Integer.parseInt( num ); } catch ( Exception e ) { result[ index + j ] = -1; } } } else { result[ index + j ] = -1; } } index += 4; } return result; } /** * -1 is used to represent the wildcard ('*') value * * @param ip * 4 digits representing a single IP address * @param ips * multiple IP addresses * @return true if ip is found to match any ip in ips, else false is * returned */ private static boolean containsIpMatch( int[] ip, int[] ips ) { boolean result = false; if ( ip != null && ip.length == 4 && ips != null && ips.length >= 4 ) { for ( int i = 0; i + 3 < ips.length; i += 4 ) { if ( ips[ i ] != -1 && ip[ 0 ] != ips[ i ] ) continue; if ( ips[ i + 1 ] != -1 && ip[ 1 ] != ips[ i + 1 ] ) continue; if ( ips[ i + 2 ] != -1 && ip[ 2 ] != ips[ i + 2 ] ) continue; if ( ips[ i + 3 ] != -1 && ip[ 3 ] != ips[ i + 3 ] ) continue; result = true; break; } } return result; } public void abortAfterForward() throws cfmAbortException { bForwardRedirect = true; throw new cfmAbortException(); } public boolean isWindowsOrMacUser() { String userAgent = REQ.getHeader("User-Agent"); if ( userAgent != null ) { return ((userAgent.indexOf("Windows") >= 0) || (userAgent.indexOf("Macintosh") >= 0)); } return true; // more likely to be Windows than not } public int getBytesSent() { return (int) totalPageOut; } /** * TODO: I *think* there is a bug here, that if there a mapping is defined in the Application.cfc then * we are missing the 'mappings' page that was custom. i think it should include both thisMappings+mappings * @return */ public Map<String, String> getCFMappings() { if ( dataBin.size() == 0 ) return mappings; cfStructData thisMappings = (cfStructData) dataBin.get( cfAPPLICATION.MAPPINGS ); if ( thisMappings != null && thisMappings.size() > 0 ){ Map<String,String> allMappings = new FastMap<String,String>( thisMappings ); Iterator it = thisMappings.keySet().iterator(); while ( it.hasNext() ){ String next = it.next().toString(); if ( next.startsWith( "/" ) ){ // ignore mappings that don't start with / try { allMappings.put( next, ( (cfData) thisMappings.getData( next ) ).getString() ); } catch (dataNotSupportedException ignored) {} // we don't need to error in this case, just ignore it } } return allMappings; }else{ return mappings; } } public void setCFMapping( String logicalPath, String directoryPath ) { mappings.put(logicalPath, directoryPath); } public Connection getConnection( String key ) { return connections.get( key ); } public void putConnection( String key, Connection con ) { connections.put( key, con ); } public void removeConnection( String key ){ connections.remove( key ); } private void closeAllConnections() { Iterator<Connection> iter = connections.values().iterator(); while ( iter.hasNext() ) { try { iter.next().close(); } catch ( SQLException e ) { cfEngine.log( "Error closing connection: " + e ); } iter.remove(); } } /** * Used by CFTHREAD to copy error handling data from a "parent" cfSession * to the virtual cfSession used by the spawned thread. */ public void setErrorHandling( cfSession parent ) { // for Application.cfc onError() event handler this.applicationCfc = parent.applicationCfc; // for CFERROR processing Object cfErrorData = parent.getDataBin( cfERROR.DATA_BIN_KEY ); if ( cfErrorData != null ) { this.setDataBin( cfERROR.DATA_BIN_KEY, cfErrorData ); } } /*********************************************************************** * * * Application.cfm, OnRequestEnd.cfm, and Application.cfc processing * * * ************************************************************************/ public static final String APPLICATION_CFC = "Application.cfc"; public static final String APPLICATION_CFM = "Application.cfm"; public static final String ON_REQUEST_END_CFM = "OnRequestEnd.cfm"; private static final String ON_REQUEST_START = "onRequestStart"; public static final String ON_REQUEST = "onRequest"; public static final String ON_CFCREQUEST = "onCFCRequest"; private static final String ON_REQUEST_END = "onRequestEnd"; private static final String ON_MISSING_TEMPLATE = "onMissingTemplate"; // these only matter to the top-level session, so don't copy in "copy" constructor private cfFile onRequestEndFile; private cfComponentData applicationCfc; /** * Render either the Application.cfc or Application.cfm for this request. * This method never throws a file-not-found exception. */ public void onRequestStart( cfFile requestFile ) throws cfmRunTimeException { pushActiveFile( requestFile ); cfFile applicationFile = findApplicationFile( requestFile ); if ( applicationFile != null ) { if ( applicationFile.getName().endsWith( "m" ) ) { renderApplicationCfm( applicationFile ); } else { applicationFile.setComponentName( ComponentFactory.normalizeComponentName( getPresentURIPath() + "Application" ) ); applicationCfc = new cfComponentData( this, applicationFile, false ); // false = don't allow abstract // initialize application, session, and client scopes (equivalent to CFAPPLICATION tag) // invoke the onApplicationStart() and onSessionStart() methods if appropriate cfAPPLICATION.getAppManager().loadApplication( applicationCfc, this ); onRequestStart( requestFile.getURI() ); // invoke the onRequestStart method } }else if ( cfEngine.isScriptProtect() ){ ScriptProtect.applyScriptProtection(this, variableStore.CGI_SCOPE); ScriptProtect.applyScriptProtection(this, variableStore.FORM_SCOPE); ScriptProtect.applyScriptProtection(this, variableStore.URL_SCOPE); ScriptProtect.applyScriptProtection(this, variableStore.COOKIE_SCOPE); } } /** * Render the Application.cfm file. */ private void renderApplicationCfm( cfFile appFile ) throws cfmRunTimeException { pushActiveFile( appFile ); try { appFile.render( this ); } catch ( cfmExitException EF ) { // processing continues if CFEXIT appears within Application.cfm clearTagStack(); } popActiveFile(); try { // return OnRequestEnd.cfm file, or null if it doesn't exist if ( appFile.getCfmlURI().isRealFile() ) { // do physical path lookup String onRequestEndFileName = FileUtils.getOnRequestEndCfm( appFile.getPath() ); if ( onRequestEndFileName != null ) { onRequestEndFile = getRealFile( onRequestEndFileName ); } } else { // URI lookup for packed WARs and BDAs onRequestEndFile = getFile( new cfmlURI( appFile.getCfmlURI().getParentURI(), ON_REQUEST_END_CFM ) ); } } catch ( cfmBadFileException BF ) { if ( !BF.fileNotFound() ) throw BF; } } /** * Render the Application.cfc and invoke the onRequestStart() method */ private void onRequestStart( String requestUri ) throws cfmRunTimeException { try { List<cfStringData> args = new ArrayList<cfStringData>(); args.add( new cfStringData( requestUri ) ); // targetPage cfcMethodData methodData = new cfcMethodData( this, ON_REQUEST_START, args ); cfData returnCode = applicationCfc.invokeApplicationFunction( this, methodData ); if ( ( returnCode != null ) && ( returnCode != CFUndefinedValue.UNDEFINED ) && ( returnCode.getDataType() != cfData.CFNULLDATA ) && !returnCode.getBoolean() ) {// returned false abortPageProcessing(); } } catch ( cfmRunTimeException e ) { invokeOnError( applicationCfc, e, ON_REQUEST_START ); abortPageProcessing(); } } /** * Find Application.cfc or Application.cfm; return null if not found. */ private cfFile findApplicationFile( cfFile requestFile ) throws cfmBadFileException { String realPath = requestFile.getPath(); // null if BDA or packed WAR if ( realPath != null ) { // physical file path search String appFilePath = FileUtils.findApplicationFile( realPath ); return ( appFilePath == null ? null : getRealFile( appFilePath ) ); } // URI path search for packed WARs and BDAs return findApplicationFile( requestFile.getURI() ); } private cfFile findApplicationFile( String uriPath ) throws cfmBadFileException { cfFile appFile = null; do { int c1 = uriPath.lastIndexOf( "/" ); if ( c1 == -1 ) { return null; } uriPath = uriPath.substring( 0, c1 ); appFile = getApplicationFile( uriPath ); } while ( appFile == null ); return appFile; } private cfFile getApplicationFile( String uriPath ) throws cfmBadFileException { try { return getUriFile( uriPath + "/" + APPLICATION_CFC ); } catch ( cfmBadFileException BFE ) { if ( !BFE.fileNotFound() ) throw BFE; } try { return getUriFile( uriPath + "/" + APPLICATION_CFM ); } catch ( cfmBadFileException BFE ) { if ( !BFE.fileNotFound() ) throw BFE; } return null; } public void onRequest( cfFile requestFile ) throws cfmRunTimeException { try { // Does the ApplicationCFC exist if ( applicationCfc != null ) { List<cfStringData> args = new ArrayList<cfStringData>(); args.add( new cfStringData( requestFile.getURI() ) ); cfcMethodData methodData = new cfcMethodData( this, ON_REQUEST, args ); cfData returnCode = applicationCfc.invokeApplicationFunction( this, methodData ); if ( returnCode != null ) return; } requestFile.render( this ); } catch ( cfmRunTimeException e ) { invokeOnError( applicationCfc, e, applicationCfc == null ? e.getMessage() : ON_REQUEST ); abortPageProcessing(); } } public void onCFCRequest( String uri, String compName, String methodName, cfArgStructData arguments, String formatMethod, boolean serializeQueryByColumns, String jsonpCallback, String jsonCase ) throws cfmRunTimeException { try { cfData rsp; cfComponentData comp; cfcMethodData invocationData; // Does the ApplicationCFC exist if ( applicationCfc != null && applicationCfc.isMethodAvailable(ON_CFCREQUEST) ){ // Little clean up of the uri which would come in as /a/b/c/d.cfc int pos = uri.lastIndexOf("."); if ( pos != -1 ) uri = uri.substring(0,pos); uri = uri.replace('/', '.'); if ( uri.charAt(0) == '.' ) uri = uri.substring(1); // Remove the method from the original one arguments.removeData("method"); // Package the arguments cfArgStructData appcfcArguments = new cfArgStructData(); appcfcArguments.setData("cfcname", new cfStringData(uri) ); appcfcArguments.setData("cfcmethod", new cfStringData(methodName) ); appcfcArguments.setData("args", arguments ); invocationData = new cfcMethodData(this, ON_CFCREQUEST, appcfcArguments); comp = applicationCfc; }else{ // Invoke the original method comp = new cfComponentData(this, compName ); invocationData = new cfcMethodData(this, methodName, arguments); if (!comp.isRemoteable(invocationData)){ cfCatchData catchData = new cfCatchData(); catchData.setMessage("an invalid request was attempted"); throw new cfmRunTimeException(catchData); } } // Invoke the component rsp = comp.invokeComponentFunction( this, invocationData); if (rsp.getDataType() != cfData.CFNULLDATA) { boolean processingCfOutput = setProcessingCfOutput(true); resetBuffer(); write( invocationData.formatReturnData( this, comp, rsp, formatMethod, serializeQueryByColumns, jsonpCallback, jsonCase ) ); setProcessingCfOutput(processingCfOutput); } } catch ( cfmRunTimeException e ) { invokeOnError( applicationCfc, e, applicationCfc == null ? e.getMessage() : ON_CFCREQUEST ); abortPageProcessing(); } } public void onError( cfmRunTimeException e, String eventHandler ) throws cfmRunTimeException { invokeOnError( applicationCfc, e, eventHandler ); } public void invokeOnError( cfComponentData _applicationCfc, cfmRunTimeException e, String eventHandler ) throws cfmRunTimeException { if ( _applicationCfc == null ) { throw e; } write( e.getOutput() ); //this is part of the fix for NA bug #3282 List<cfJavaObjectData> args = new ArrayList<cfJavaObjectData>(); args.add( catchDataFactory.eventHandlerException( eventHandler, e.getCatchData() ) ); // Exception args.add( new cfStringData( eventHandler ) ); // EventName cfcMethodData methodData = new cfcMethodData( this, "onError", args ); cfData returnCode = _applicationCfc.invokeApplicationFunction( this, methodData ); if ( returnCode == null ) { // onError not implemented throw e; } } public void onRequestEnd( String requestUri ) throws cfmRunTimeException { if ( onRequestEndFile != null ) { renderOnRequestEnd(); } else if ( applicationCfc != null ) { invokeOnRequestEnd( requestUri ); } popActiveFile(); // pop the request file } private void renderOnRequestEnd() throws cfmRunTimeException { pushActiveFile( onRequestEndFile ); onRequestEndFile.render( this ); popActiveFile(); } private void invokeOnRequestEnd( String requestUri ) throws cfmRunTimeException { try { List<cfStringData> args = new ArrayList<cfStringData>(); args.add( new cfStringData( requestUri ) ); // targetPage cfcMethodData methodData = new cfcMethodData( this, ON_REQUEST_END, args ); applicationCfc.invokeApplicationFunction( this, methodData ); } catch ( cfmRunTimeException e ) { invokeOnError( applicationCfc, e, ON_REQUEST_END ); } } public boolean onMissingTemplate() throws cfmRunTimeException { String requestUri = getRequestURI(); cfFile applicationFile = findApplicationFile( requestUri ); if ( applicationFile != null ) { if ( applicationFile.getName().endsWith( "m" ) ) { renderApplicationCfm( applicationFile ); return false; // Application.cfm must CFABORT, or file-not-found exception will be rethrown } else { applicationFile.setComponentName( ComponentFactory.normalizeComponentName( getPresentURIPath() + "Application" ) ); applicationCfc = new cfComponentData( this, applicationFile, false ); // false = don't allow abstract cfAPPLICATION.getAppManager().loadApplication( applicationCfc, this ); List<cfStringData> args = new ArrayList<cfStringData>(); args.add( new cfStringData( requestUri ) ); // targetPage cfcMethodData methodData = new cfcMethodData( this, ON_MISSING_TEMPLATE, args ); cfData returnCode = applicationCfc.invokeApplicationFunction( this, methodData ); if ( returnCode == null ) { // onMissingTemplate not implemented return false; } else if ( returnCode.getDataType() == cfData.CFNULLDATA ) { // returned null return true; } else { return returnCode.getBoolean(); // returned boolean } } } return false; } /** * Used to track how long it took to render each query * @param ms */ public void metricQueryTimeAdd(long ms){ topLevelSession.metricTotalQuery++; topLevelSession.metricTotalQueryTime += ms; } /** * Returns the total queries ran * @return */ public int getMetricQuery(){ return topLevelSession.metricTotalQuery; } /** * Returns the total time in ms that querys took * @return */ public long getMetricQueryTotalTime(){ return topLevelSession.metricTotalQueryTime; } }