<cfcomponent output="false">

<!---TODO write setters --->
<cfset variables.tokenLifetimeHours=1>

<cfset variables.applicationStructName='sessionTokens'>
<cfif not structKeyExists(application,variables.applicationStructName)>
	<cfset application[variables.applicationStructName]=structNew()>
</cfif>

<cffunction name="get" output="false" access="public">
	<cfargument name="data" required="false" default="" type="any">
	<cfset expireOldTokens()>
	<cfreturn makeNewToken(arguments.data) >
</cffunction>

<cffunction name="check" output="false" access="public">
	<cfargument name="token" required="true">
	<cfargument name="checkRemoteIP" required="false" default="true" type="boolean">
	<cfset var dat=''>
	<cfset expireOldTokens()>
	<cfset dat= checkToken(arguments.token,arguments.checkRemoteIp)>
	<cfset updateTokenTime(arguments.token)>
	<cfreturn dat>
</cffunction>

<!--- although this is public, so you can could from a scheduled task
there's probably no need --->
<cffunction name="expireOldTokens" output="false" access="public">
	<cfset var i=''>
	<cfset var curTok=''>
	<cfset var item=''>
	<cfset var tokList=structKeyList(application[variables.applicationStructName])>
	<cfset var count=listLen(tokList)>
	<!---TODO lock --->
	<cfloop from="1" to="#count#" index="i">
		<cfset curTok=listGetAt(tokList,i)>
		<cfset item=application[variables.applicationStructName][curTok]>
		<!--- if now() is more than N hours from last access, kill token --->
		<cfif dateCompare(now(),
				dateAdd('h',variables.tokenLifetimeHours,item.lastAccessTime)
			) eq 1 >
			<cfset structDelete(application[variables.applicationStructName],curTok)>
		</cfif>
	</cfloop>
</cffunction>

<cffunction name="updateTokenTime" output="false" access="private">
	<cfargument name="token" required="true">
	<cfset application[variables.applicationStructName][arguments.token].lastAccessTime=now()>
</cffunction>

<cffunction name="checkToken" output="false" access="private">
	<cfargument name="token" required="true">
	<cfargument name="checkRemoteIP" required="true" type="boolean">
	<cfif not structKeyExists(application[variables.applicationStructName],arguments.token) or
		application[variables.applicationStructName][arguments.token].ip is not cgi.REMOTE_ADDR>
		<cfthrow type="com.falkensweb.service.SessionTokenException"
				message="Token invalid"
				detail="#arguments.token# is not a valid token in application.#variables.applicationStructName#">
	</cfif>

	<cfreturn application[variables.applicationStructName][arguments.token].data>
</cffunction>

<cffunction name="makeNewToken" output="false" access="private">
	<cfargument name="data" required="true" type="any">
	<cfset var tok=createUUID()>
	<cfset var struct=structNew()>
	<cfset struct.lastAccessTime=now()>
	<cfset struct.data=arguments.data>
	<cfset struct.ip=cgi.REMOTE_ADDR>
	<cfset structInsert(application[variables.applicationStructName],tok,struct)>
	<cfreturn tok>
</cffunction>
</cfcomponent>