Sorcerer's IsleCode QueryParam Scanner / files

     1<!---
     2Copyright 2006-2007 TeraTech, Inc. http://teratech.com/
     3
     4Licensed under the Apache License, Version 2.0 (the "License");
     5you may not use this file except in compliance with the License.
     6You may obtain a copy of the License at
     7
     8http://www.apache.org/licenses/LICENSE-2.0
     9
    10Unless required by applicable law or agreed to in writing, software
    11distributed under the License is distributed on an "AS IS" BASIS,
    12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13See the License for the specific language governing permissions and
    14limitations under the License.
    15--->
    16<cfcomponent hint="I provide the per-request myFusebox data structure and some convenience methods.">
    17	<cfscript>
    18	this.version.runtime     = "5.5.1";
    19	//this.version.runtime     = "5.5.0.#REReplace('$LastChangedRevision:683 $','[^0-9]','','all')#";
    20	  
    21	this.version.loader      = "unknown";
    22	this.version.transformer = "unknown";
    23	this.version.parser      = "unknown";
    24	  
    25	this.thisCircuit = "";
    26	this.thisFuseaction =  "";
    27	this.thisPlugin = "";
    28	this.thisPhase = "";
    29	this.plugins = structNew();
    30	this.parameters = structNew();
    31	
    32	// the basic default is development-full-load mode:
    33	this.parameters.load = true;
    34	this.parameters.parse = true;
    35	this.parameters.execute = true;
    36	// FB5: new execution parameters:
    37	this.parameters.clean = false;	 	// don't delete parsed files by default
    38	this.parameters.parseall = false;	// don't compile all fuseactions by default
    39	  
    40	this.parameters.userProvidedLoadParameter = false;
    41	this.parameters.userProvidedCleanParameter = false;
    42	this.parameters.userProvidedParseParameter = false;
    43	this.parameters.userProvidedParseAllParameter = false;
    44	this.parameters.userProvidedExecuteParameter = false;
    45	
    46	// stack frame for do/include parameters:
    47	this.stack = structNew();
    48	
    49	// FB55: ability to turn debug output off per-request:
    50	this.showDebug = true;
    51	</cfscript>
    52	
    53	<cffunction name="init" returntype="myFusebox" access="public" output="false" 
    54				hint="I am the constructor.">
    55		<cfargument name="appKey" type="string" required="true" 
    56					hint="I am FUSEBOX_APPLICATION_KEY." />
    57		<cfargument name="attributes" type="struct" required="true" 
    58					hint="I am the attributes (URL and form variables) structure." />
    59		<cfargument name="topLevelVariablesScope" type="any" required="true" 
    60					hint="I am the top-level variables scope." />
    61		
    62		<cfset var theFusebox = structNew() />
    63		<cfset var urlParam = "" />
    64		<cfset var urlLastArg = "" />
    65		<cfset var urlIsArg = true />
    66		<cfset var pathInfo = CGI.PATH_INFO />
    67		
    68		<cfset variables.variablesScope = arguments.topLevelVariablesScope />
    69		
    70		<cfset variables.created = getTickCount() />
    71		<cfset variables.log = arrayNew(1) />
    72		<cfset variables.occurrence = structNew() />
    73
    74		<cfset variables.appKey = arguments.appKey />
    75		<cfset variables.attributes = arguments.attributes />
    76		
    77		<!--- FB5: indicates whether application was started on this request --->
    78		<cfset this.applicationStart = false />
    79
    80		<!--- we can't guarantee the fusebox exists in application scope yet... --->
    81		<cfif structKeyExists(application,variables.appKey)>
    82			<cfset theFusebox = application[variables.appKey] />
    83		</cfif>
    84		
    85		<!--- default myFusebox.parameters depending on "mode" of the application set in fusebox.xml --->
    86		<cfif structKeyExists(theFusebox,"mode")>
    87			<cfswitch expression="#theFusebox.mode#">
    88			<!--- FB41 backward compatibility - now deprecated --->
    89			<cfcase value="development">
    90				<cfif structKeyExists(theFusebox,"strictMode") and theFusebox.strictMode>
    91					<!--- since we don't load fusebox.xml if we throw an exception, we must fixup the value for the next run --->
    92					<cfset theFusebox.mode = "development-full-load" />
    93					<cfthrow type="fusebox.badGrammar.deprecated" 
    94							message="Deprecated feature"
    95							detail="'development' is a deprecated execution mode - use 'development-full-load' instead." />
    96				</cfif>
    97				<cfset this.parameters.load = true />
    98				<cfset this.parameters.parse = true />
    99				<cfset this.parameters.execute = true />
   100			</cfcase>
   101			<!--- FB5: replacement for old development mode --->
   102			<cfcase value="development-full-load">
   103				<cfset this.parameters.load = true />
   104				<cfset this.parameters.parse = true />
   105				<cfset this.parameters.execute = true />
   106			</cfcase>
   107			<!--- FB5: new option - does not load fusebox.xml and therefore does not (re-)load fuseboxApplication object --->
   108			<cfcase value="development-circuit-load">
   109				<cfset this.parameters.load = false />
   110				<cfset this.parameters.parse = true />
   111				<cfset this.parameters.execute = true />
   112			</cfcase>
   113			<cfcase value="production">
   114				<cfset this.parameters.load = false />
   115				<cfset this.parameters.parse = false />
   116				<cfset this.parameters.execute = true />
   117			</cfcase>
   118			<cfdefaultcase>
   119				<!--- since we don't load fusebox.xml if we throw an exception, we must fixup the value for the next run --->
   120				<cfset theFusebox.mode = "development-full-load" />
   121				<cfthrow type="fusebox.badGrammar.invalidParameterValue" 
   122						message="Parameter has invalid value" 
   123						detail="The parameter 'mode' must be one of 'development-full-load', 'development-circuit-load' or 'production' in the fusebox.xml file." />
   124			</cfdefaultcase>
   125			</cfswitch>
   126		</cfif>
   127		
   128		<!--- handle SES URLs if appropriate --->
   129		<cfif structKeyExists(theFusebox,"queryStringStart") and theFusebox.queryStringStart is not "?">
   130			<!--- looks like SES URL generation is enabled, process CGI.PATH_INFO (we add &= to catch improperly formed URLs) --->
   131			<!--- ticket 313 - canonicalize pathInfo for IIS 5 --->
   132			<cfif len(pathInfo) gt len(CGI.SCRIPT_NAME) and left(pathInfo,len(CGI.SCRIPT_NAME)) is CGI.SCRIPT_NAME>
   133				<cfset pathInfo = right(pathInfo,len(pathInfo)-len(CGI.SCRIPT_NAME)) />
   134			</cfif>
   135			<cfloop list="#pathInfo#" index="urlParam" 
   136					delimiters="#theFusebox.queryStringStart##theFusebox.queryStringSeparator##theFusebox.queryStringEqual#&=">
   137			   <cfif urlIsArg>
   138			      <cfset urlLastArg = urlParam />
   139			   <cfelse>
   140			      <cfset variables.attributes[urlLastArg] = urlParam />
   141			   </cfif>
   142			   <cfset urlIsArg = not urlIsArg />
   143			</cfloop>
   144		</cfif>
   145		
   146		<!--- did the user pass in any special "fuseboxDOT" parameters for this request? --->
   147		<!--- If so, process them --->
   148		<!--- note: only if attributes.fusebox.password matches the application password --->
   149		<cfif not structKeyExists(variables.attributes,"fusebox.password")>
   150			<cfset variables.attributes["fusebox.password"] = "" />
   151		</cfif>
   152		<cfif structKeyExists(theFusebox,"password") and
   153				theFusebox.password is variables.attributes['fusebox.password']>
   154			<!--- FB5: does a load and wipes the parsed files out --->
   155			<cfif structKeyExists(variables.attributes,'fusebox.loadclean') and isBoolean(variables.attributes['fusebox.loadclean'])>
   156				<cfset this.parameters.load = variables.attributes['fusebox.loadclean'] />
   157				<cfset this.parameters.clean = variables.attributes['fusebox.loadclean'] />
   158				<cfset this.parameters.userProvidedLoadParameter = true />
   159				<cfset this.parameters.userProvidedCleanParameter = true />
   160			</cfif>
   161			<cfif structKeyExists(variables.attributes,'fusebox.load') and isBoolean(variables.attributes['fusebox.load'])>
   162				<cfset this.parameters.load = variables.attributes['fusebox.load'] />
   163				<cfset this.parameters.userProvidedLoadParameter = true />
   164			</cfif>
   165			<cfif structKeyExists(variables.attributes,'fusebox.parseall') and isBoolean(variables.attributes['fusebox.parseall'])>
   166				<cfset this.parameters.parse = variables.attributes['fusebox.parseall'] />
   167				<cfset this.parameters.parseall = variables.attributes['fusebox.parseall'] />
   168				<cfif this.parameters.parseall>
   169					<cfset this.parameters.load = true />
   170				</cfif>
   171				<cfset this.parameters.userProvidedParseParameter = true />
   172				<cfset this.parameters.userProvidedParseAllParameter = true />
   173			</cfif>
   174			<cfif structKeyExists(variables.attributes,'fusebox.parse') and isBoolean(variables.attributes['fusebox.parse'])>
   175				<cfset this.parameters.parse = variables.attributes['fusebox.parse'] />
   176				<cfset this.parameters.userProvidedParseParameter = true />
   177			</cfif>
   178			<cfif structKeyExists(variables.attributes,'fusebox.execute') and isBoolean(variables.attributes['fusebox.execute'])>
   179				<cfset this.parameters.execute = variables.attributes['fusebox.execute'] />
   180				<cfset this.parameters.userProvidedExecuteParameter = true />
   181			</cfif>
   182		</cfif>
   183		
   184		<!---
   185			force a load if the runtime and core versions differ: this allows a new
   186			version to be dropped in and the framework will automatically reload!
   187			note: that we must *force* a load, by pretending this is user-provided!
   188		--->
   189		<cfif structKeyExists(theFusebox,"getVersion") and
   190				isCustomFunction(theFusebox.getVersion)>
   191			<cfif this.version.runtime is not theFusebox.getVersion()>
   192				<cfset this.parameters.userProvidedLoadParameter = true />
   193				<cfset this.parameters.load = true />
   194			</cfif>
   195		<cfelse>
   196			<!--- hmm, doesn't look like the core is present (or it's not FB5 Alpha 2 or higher) --->
   197			<cfset this.parameters.userProvidedLoadParameter = true />
   198			<cfset this.parameters.load = true />
   199		</cfif>
   200
   201		<!--- if the fusebox doesn't already exist we definitely want to reload --->
   202		<cfif structKeyExists(theFusebox,"isFullyLoaded") and
   203				theFusebox.isFullyLoaded>
   204			<!--- if fully loaded, leave the load parameter alone --->
   205		<cfelse>
   206			<cfset this.parameters.load = true />
   207		</cfif>
   208		
   209		<cfreturn this />
   210	</cffunction>
   211	
   212	<cffunction name="getApplication" returntype="any" access="public" output="false" 
   213				hint="I am a convenience method to return the fuseboxApplication object without needing to know reference application scope or the FUSEBOX_APPLICATION_KEY variable.">
   214	
   215		<!---
   216			this is a bit of a hack since we're accessing application scope directly 
   217			but it's probably cleaner than exposing a method to allow fuseboxApplication
   218			to inject itself back into myFusebox during compileRequest()...
   219		--->
   220		<cfreturn application[variables.appKey] />
   221	
   222	</cffunction>
   223	
   224	<cffunction name="getApplicationData" returntype="struct" access="public" output="false"
   225				hint="I am a convenience method to return a reference to the application data cache.">
   226	
   227		<cfreturn getApplication().getApplicationData() />
   228	
   229	</cffunction>
   230	
   231	<cffunction name="getCurrentCircuit" returntype="any" access="public" output="false" 
   232				hint="I am a convenience method to return the current Fusebox circuit object.">
   233	
   234		<cfreturn getApplication().circuits[this.thisCircuit] />
   235	
   236	</cffunction>
   237	
   238	<cffunction name="getCurrentFuseaction" returntype="any" access="public" output="false" 
   239				hint="I am a convenience method to return the current fuseboxAction (fuseaction) object.">
   240	
   241		<cfreturn getCurrentCircuit().fuseactions[this.thisFuseaction] />
   242	
   243	</cffunction>
   244	
   245	<cffunction name="getOriginalCircuit" returntype="any" access="public" output="false" 
   246				hint="I am a convenience method to return the original Fusebox circuit object.">
   247	
   248		<cfreturn getApplication().circuits[this.originalCircuit] />
   249	
   250	</cffunction>
   251	
   252	<cffunction name="getOriginalFuseaction" returntype="any" access="public" output="false" 
   253				hint="I am a convenience method to return the original fuseboxAction (fuseaction) object.">
   254	
   255		<cfreturn getCurrentCircuit().fuseactions[this.originalFuseaction] />
   256	
   257	</cffunction>
   258	
   259	<cffunction name="getSelf" returntype="string" access="public" output="false"
   260				hint="I return the 'self' string, e.g., index.cfm.">
   261
   262		<cfif not structKeyExists(variables,"self")>
   263			<cfset variables.self = getApplication().self />
   264		</cfif>
   265		
   266		<cfreturn variables.self />
   267
   268	</cffunction>	
   269	
   270	<cffunction name="setSelf" returntype="void" access="public" output="false" 
   271				hint="I override the default value of 'self' and I also reset the value of 'myself'.">
   272		<cfargument name="self" type="string" required="true" 
   273					hint="I am the new value of 'self', e.g., /myapp/entry.cfm" />
   274		
   275		<cfset variables.self = arguments.self />
   276		<!--- reset myself for consistency with self --->
   277		<cfset variables.myself = getApplication().getDefaultMyself(variables.self) />
   278		
   279	</cffunction>
   280
   281	<cffunction name="getMyself" returntype="string" access="public" output="false" 
   282				hint="I return the 'myself' string, e.g., index.cfm?fuseaction=.">
   283
   284		<cfif not structKeyExists(variables,"myself")>
   285			<cfset variables.myself = getApplication().myself />
   286		</cfif>
   287		
   288		<cfreturn variables.myself />
   289
   290	</cffunction>	
   291	
   292	<cffunction name="setMyself" returntype="void" access="public" output="false" 
   293				hint="I override the default value of 'myself'.">
   294		<cfargument name="myself" type="string" required="true" 
   295					hint="I am the new value of 'myself'." />
   296		
   297		<cfset variables.myself = arguments.myself />
   298		
   299	</cffunction>
   300
   301	<cffunction name="do" returntype="string" access="public" output="true" 
   302				hint="I compile and execute a specific fuseaction.">
   303		<cfargument name="action" type="string" required="true" 
   304					hint="I am the full name of the requested fuseaction (circuit.fuseaction)." />
   305		<cfargument name="contentVariable" type="string" default="" 
   306					hint="I indicate an attributes / event scope variable in which to store the output." />
   307		<cfargument name="returnOutput" type="boolean" default="false" 
   308					hint="I indicate whether to display output (false - default) or return the output (true)." />
   309		<cfargument name="append" type="boolean" default="false" 
   310					hint="I indicate whether to append output (false - default) to the content variable." />
   311
   312		<cfset var c = this.thisCircuit />
   313		<cfset var f = this.thisFuseaction />
   314		<cfset var output = 
   315				getApplication().do(
   316					arguments.action,
   317					this,
   318					arguments.returnOutput or arguments.contentVariable is not "") />
   319	
   320		<cfset this.thisFuseaction = f />
   321		<cfset this.thisCircuit = c />
   322			
   323		<cfif arguments.contentVariable is not "">
   324			<!--- ticket #290 - allow append on content variables --->
   325			<cfif structKeyExists(variables.variablesScope,arguments.contentVariable) and arguments.append>
   326				<cfset variables.variablesScope[arguments.contentVariable] = variables.variablesScope[arguments.contentVariable] & output />
   327			<cfelse>
   328				<cfset variables.variablesScope[arguments.contentVariable] = output />
   329			</cfif>
   330		</cfif>
   331		
   332		<cfreturn output />
   333		
   334	</cffunction>
   335	
   336	<cffunction name="relocate" returntype="void" access="public" output="true" 
   337				hint="I provide the same functionality as the relocate verb.">
   338		<cfargument name="url" type="string" required="false" />
   339		<cfargument name="xfa" type="string" required="false" />
   340		<cfargument name="addtoken" type="boolean" default="false" />
   341		<cfargument name="type" type="string" default="client" />
   342
   343		<cfset var theUrl = "" />
   344		
   345		<!--- url/xfa - exactly one is required --->
   346		<cfif structKeyExists(arguments,"url")>
   347			<cfif structKeyExists(arguments,"xfa")>
   348				<cfthrow type="fusebox.badGrammar.requiredAttributeMissing" 
   349						message="Required attribute is missing" 
   350						detail="Either the attribute 'url' or 'xfa' is required, for a 'relocate' verb in fuseaction #this.thiscircuit#.#this.thisFuseaction#." />
   351			<cfelse>
   352				<cfset theUrl = arguments.url />
   353			</cfif>
   354		<cfelseif structKeyExists(arguments,"xfa")>
   355			<cfset theUrl = getMyself() & variables.variablesScope.xfa[arguments.xfa] />
   356		<cfelse>
   357			<cfthrow type="fusebox.badGrammar.requiredAttributeMissing" 
   358					message="Required attribute is missing" 
   359					detail="Either the attribute 'url' or 'xfa' is required, for a 'relocate' verb in fuseaction #this.thiscircuit#.#this.thisFuseaction#." />
   360		</cfif>
   361		
   362		<!--- type - server|client|moved - we do not support javascript here --->
   363		<cfif arguments.type is "server">
   364
   365			<cfset getPageContext().forward(theUrl) />
   366
   367		<cfelseif arguments.type is "client">
   368
   369			<cflocation url="#theUrl#" addtoken="#arguments.addtoken#" />
   370
   371		<cfelseif arguments.type is "moved">
   372
   373			<cfheader statuscode="301" statustext="Moved Permanently" />
   374			<cfheader name="Location" value="#theUrl#" />
   375			
   376		<cfelse>
   377			<cfthrow type="fusebox.badGrammar.invalidAttributeValue" 
   378					message="Attribute has invalid value" 
   379					detail="The attribute 'type' must either be ""server"", ""client"" or ""moved"", for a 'relocate' verb in fuseaction #this.thisCircuit#.#this.thisFuseaction#." />
   380		</cfif>
   381		
   382		<cfabort />
   383
   384	</cffunction>
   385	
   386	<cffunction name="variables" returntype="any" access="public" output="false" hint="I return the top-level variables scope.">
   387	
   388		<cfreturn variables.variablesScope />
   389	
   390	</cffunction>
   391	
   392	<cffunction name="enterStackFrame" returntype="void" access="public" output="false" 
   393				hint="I create a new stack frame (for scoped parameters to do/include).">
   394		
   395		<cfset var frame = structNew() />
   396		
   397		<cfset frame.__fuseboxStack = this.stack />
   398		<cfset this.stack = frame />
   399		
   400	</cffunction>
   401	
   402	<cffunction name="leaveStackFrame" returntype="void" access="public" output="false" 
   403				hint="I pop the last stack frame (for scoped parameters to do/include).">
   404		
   405		<cfset this.stack = this.stack.__fuseboxStack />
   406		
   407	</cffunction>
   408	
   409	<cffunction name="trace" returntype="void" access="public" output="false" 
   410				hint="I add a line to the execution trace log.">
   411		<cfargument name="type" hint="I am the type of trace (Fusebox, Compiler, Runtime are used by the framework)." />
   412		<cfargument name="message" hint="I am the message to put in the execution trace." />
   413		
   414		<cfset addTrace(getTickCount() - variables.created,arguments.type,arguments.message) />
   415		
   416	</cffunction>
   417
   418	<cffunction name="addTrace" returntype="void" access="private" output="false" 
   419				hint="I add a detailed line to the execution trace log.">
   420		<cfargument name="time" hint="I am the time taken to get to this point in the request." />
   421		<cfargument name="type" hint="I am the type of trace." />
   422		<cfargument name="message" hint="I am the trace message." />
   423		<cfargument name="occurrence" default="0" hint="I am a placeholder for part of the struct that is added to the log." />
   424		
   425		<cfif structKeyExists(variables.occurrence,arguments.message)>
   426			<cfset variables.occurrence[arguments.message] = 1 + variables.occurrence[arguments.message] />
   427		<cfelse>
   428			<cfset variables.occurrence[arguments.message] = 1 />
   429		</cfif>
   430		<cfset arguments.occurrence = variables.occurrence[arguments.message] />
   431		<cfset arrayAppend(variables.log,arguments) />
   432		
   433	</cffunction>
   434	
   435	<cffunction name="renderTrace" returntype="string" access="public" output="false" hint="I render the trace log as HTML.">
   436		
   437		<cfset var result = "" />
   438		<cfset var i = 0 />
   439		
   440		<cfif this.showDebug>
   441			<cfsavecontent variable="result">
   442				<style type="text/css">
   443					.fuseboxdebug {clear:both;padding-top:10px;}
   444					.fuseboxdebug * {font-family:verdana,sans-serif;}
   445					.fuseboxdebug h3 {margin:16px 0 16px 0;padding:0;border-bottom:1px solid #CCC;font-size:16px;}
   446					.fuseboxdebug table th {font-size:11pt;text-align:left;}
   447					.fuseboxdebug table tr.odd {background:#F9F9F9;}
   448					.fuseboxdebug table tr.even {background:#FFF;}
   449					.fuseboxdebug table td {border-bottom:1px solid #CCC;font-size:10pt;text-align:left;vertical-align:top;}
   450					.fuseboxdebug table td.count {text-align:center;}
   451				</style>
   452				<div class="fuseboxdebug">
   453					<h3>Fusebox debugging:</h3>
   454					<table cellpadding="2" cellspacing="0" width="100%">
   455						<tr>
   456							<th>Time</td>
   457							<th>Category</td>
   458							<th>Message</td>
   459							<th>Count</td>
   460						</tr>
   461						<cfloop index="i" from="1" to="#arrayLen(variables.log)#">
   462							<cfoutput>
   463								<cfif i mod 2>
   464									<tr class="odd">
   465								<cfelse>
   466									<tr class="even">
   467								</cfif>
   468								<td>#variables.log[i].time#ms</td>
   469								<td>#variables.log[i].type#</td>
   470								<td>#variables.log[i].message#</td>
   471								<td class="count">#variables.log[i].occurrence#</td>
   472							</tr></cfoutput>
   473						</cfloop>
   474					</table>
   475				</div>
   476			</cfsavecontent>
   477		</cfif>
   478		
   479		<cfreturn result />
   480		
   481	</cffunction>
   482</cfcomponent>