Using Application.cfc to route dns wildcards - coldfusion

I'm using a godaddy coldfusion shared hosting account and I want to provide my users friendlier/direct urls.
I've added a wildcard to my domain so all subdomains point to the same root folder.
How do I use just the Application.cfc to route dns wildcards to their appropriate destinations?
index.cfm in the root folder will just list cities
Going to city.domain.com should bring up domain.com/city/index.cfm if it exists and domain.com/index.cfm if it doesn't.
I tried cflocation but I think it went into an infinite loop.

You need to make sure that you're checking for the right thing in CGI.SERVER_NAME. Something like this should do the trick:
<cfif ListLen(CGI.SERVER_NAME, ".") NEQ 2 AND ListFirst(CGI.SERVER_NAME, ".") NEQ "www">
<cflocation url="http://domain.com/#ListFirst(CGI.SERVER_NAME, ".")#/index.cfm" />
</cfif>

You could always do a server side redirect, and hide it all away from the client.
So regular requests go to index.cfm (www.domain.com)
And then if there is a city (in onRequestStart()):
city = ListFirst(CGI.SERVER_NAME, ".");
if(!StructKeyExists(request, "forward") AND (city != "www" || city != "domain"))
{
request.forward = 1;
getPageContext().forward("/#city#/index.cfm");
abort;
}
I put the request scope variable 'forward' in there to stop potential infinite loops, as the Application.cfc is called again.
You can see more details here:
http://www.compoundtheory.com/?action=displayPost&ID=26
http://download.oracle.com/javaee/6/api/javax/servlet/jsp/PageContext.html#forward%28java.lang.String%29

It sounds like your existing code would work fine if placed at the top of root/index.cfm. You could supplement that with onMissingTemplate() in Application.cfc to handle 404 situations.

Related

Coldfusion cflocation strange behavior

I am storing a google review URL in my database as:
https://www.google.com/search?CFID=ac59cfdf-bbad-4017-9759-e88054f3f242&CFTOKEN=0&q=njcomputerrepair%2Bbrick%2Bnj&oq=njcomp&aqs=chrome.1.69i60j69i59j69i60j69i57j0l2.2762j0j9&sourceid=chrome&ie=UTF-8#lrd=0x89c18348735c2907:0x59aa614832a36b22,3,
And then in my application I set that URL to a variable and I redirect the user to that URL using cflocation.
<cfquery name="geturl" datasource="#datasource#">
select (residential_ReviewURL) as redirectURL
from subscribers
</cfquery>
<!--- Redirect to main html redirect page --->
<cfoutput>
<cflocation url="#getURL.redirectURL#">
</cfoutput>
However the URL gets changed at some point because I think that Coldfusion doesn't like the characters in the URL and it replaces them with % or removes them. Therefore when the user hits the google page, the page doesn't process as it should.
Here is how the URL looks after the redirect:
https://www.google.com/search?CFID=ac59cfdf-bbad-4017-9759-e88054f3f242&CFTOKEN=0&CFID=ac59cfdf-bbad-4017-9759-e88054f3f242&CFTOKEN=0&q=njcomputerrepair%2Bbrick%2Bnj&oq=njcomp&aqs=chrome.1.69i60j69i59j69i60j69i57j0l2.2762j0j9&sourceid=chrome&ie=UTF-8#lrd%3D0x89c18348735c2907%3A0x59aa614832a36b22%2C3%2C
How can I stop ColdFusion from changing the URL and keep id exactly as how it is stored in the database?
UPDATE
So I found that URLdecode will preserve the string. Here is what I have.
#urlDecode(getURL.redirectURL)#
The output is as follows
https://www.google.com/search?CFID=ac59cfdf-bbad-4017-9759-e88054f3f242&CFTOKEN=0&q=njcomputerrepair+brick+nj&oq=njcomp&aqs=chrome.1.69i60j69i59j69i60j69i57j0l2.2762j0j9&sourceid=chrome&ie=UTF-8#lrd=0x89c18348735c2907:0x59aa614832a36b22,3,
Why is it adding CFID and CFTOKEN to the URL though? I have it turned off in my Application.CFM:
<cfapplication name="yaya"
clientmanagement="no"
sessionmanagement="no"
setclientcookies="no"
setdomaincookies="no"
sessiontimeout="#CreateTimeSpan(0,2,0,0)#"
applicationtimeout="#CreateTimeSpan(1,0,0,0)#"
>
To help others coming here:
cflocation have a parameter addToken which needs to set to no if we do not want to add CFID and CFTOKEN to the generated URL.
Adobe CFML reference: https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-tags/tags-j-l/cflocation.html

Why does cflocation strip characters and not visit the URL?

I have a URL stored as:
<cfset VisitURL = 'http://www.toysexample.com/search.aspx?searchBox=christmasgifts≠Sort=high&search=new' />
(The above URL string actually comes from a database field that I have no control over)
When I ask cflocation to visit the above URL like this:
<cflocation url="#EncodeForURL(VisitURL)#" addtoken="no" />
It visits the URL as a subfolder within my site. So I end up going to the URL http://www.volumeoneexample.com/http:/www.toysexample.com/search.aspx?searchBox=christmasgifts≠Sort=high&search=new
As you can see it has dropped one of the forward-slashes from the http:// part and also appended the URL as a folder within my site. I don't get why its doing this?
As mentioned in the comments, you're "over encoding". The EncodeForURL() should only be on the URL parameters.
Since you're not able to retrieve the URL and parameters separately, you might be able to get away with treating them like a list.
<cflocation url="#ListFirst(VisitURL,"?")#?#EncodeForURL(ListRest(VisitURL,"?"))#" />
If things get a little wonky you'll need to do more manipulation than that.

Calling CFC's on another folder level

I have a page that I use cfc's on. Like this:
<cfset cfcDashboard = new dashboard()>
<cfset grab_image = cfcdashboard.getPicture()>
How do I call the cfc's if they are inside of a folder? As of right now they only work if they are on the same level or inside the same folder? How do you call a cfc that is on a different level?
Or am I not understanding the purpose of the cfc?
The new keyword is syntactic sugar for this call:
<cfset cfcDashboard = createObject("component", "Dashboard")>
The rules how ColdFusion resolves CFC names are in the docs.
If you use a cfinvoke or cfobject tag, or the CreateObject
function, to access the CFC from a CFML page, ColdFusion searches
directories in the following order:
Local directory of the calling CFML page
Web root
Directories specified on the Custom Tag Paths page of ColdFusion Administrator
You can use dot notation that corresponds to any of the defined search paths.
<cfset myDashboard = createObject("component", "my.custom.Dashboard")>
<cfset myDashboard = new my.custom.Dashboard()>
Will find (where . means the current template directory and / means the web root):
./my/custom/Dashboard.cfc
/my/custom/Dashboard.cfc
any/custom/tag/path/my/custom/Dashboard.cfc
Going "up" is not possible.
Granted there is no way to go "UP" but there is a way you can start at the top level of your website.
Place this line in your root Application.cfc file
<cfset this.directory = getDirectoryFromPath( getCurrentTemplatePath() ) />
<cfset this.mappings['/app'] = this.directory />
Then when you type your cfinvoke line type it like this
Of course, you want to replace [name of subfolder] with the folder your component is located and [name of component] when the name of your component. Everything else is the normal cfinvoke syntax.
So in a roundabout way, there is a way to start "up" from a sub-folder.

URL Rewrite with IIS using ColdFusion

I've done a bit of searching but just can't put it all together. Here's what I need:
I'd like someone to surf to:
www.mysite.com/thisPlace
and have it redirect them to
www.mysite.com/template.cfm?pm=ms&loc_id=4
To do so, I somehow need to capture that they didn't request an existing file in their http request and run a .cfm page that queried the database for a record where locationName = 'thisPlace' and then redirect them to a page like
template.cfm?pm=ms&loc_id=4, where 4 is the record id of the row that matched 'thisPlace'
If your default document in IIS is set to index.cfm you could create a folder (directory) called "thisPlace" and place an index.cfm file that contains nothing but a <cflocation> tag and the accompanying query/logic to figure the URL.
Website.com/thisPlace would then function as you describe.
Edit:
You could add a custom 404 page...
Make it a .cfm file instead of html. Scan the template path to see what the user is looking for. If you find it in your database, redirect them there, else redirect them to a general 404 page.
<!---Up to a certain point (the directory in which you store your code) this will always be the same so you can hard-code your number --->
<cfset QueryConstant = #LEFT(CGI.CF_Template_Path, 22)#>
<!---Find the overall length of the template path. --->
<cfset QueryVariable = #Len(CGI.CF_Template_Path)#>
<!---Take whatever is past your QueryConstant (AKA the string that produces a 404 error.) --->
<cfset theRightNumber = QueryVariable - 22>
<cfset QuerySearchString = #RIGHT(CGI.CF_Template_Path, theRightNumber)#>
<cfquery name="ListOfLocations" datasource="CRM">
SELECT TOP 1 LocationID
FROM LocationTable
WHERE LocationName LIKE '%#QuerySearchString#%'
</cfquery>
<cfif ListOfLocations.recordcount>
<cflocation url="/SomePage.cfm?LocationID=#ListOfLocations.LocationID#">
<cfelse>
<cflocation url="/Regular404page.html">
</cfif>
Thanks guys! Huge help! Using your inputs, here's what I did:
(had to use QUERY_STRING instead of CF_Template_Path, as CF_Template_Path did not pass along anything after the url of the custom error page.
I set up a custom 404 error Execute URL in IIS to a file named check404error.cfm.
When someone looks for www.example.com/thisPlace, IIS sends them to http://www.example.com/check404error.cfm. I use the CGI.QUERY_STRING (404;http://www.example.com:443/thisPlace) to ultimately get the "thisPlace" string to search with.
<!---Up to a certain point (the directory in which you store your code) this will always be the same so you can hard-code your number --->
<cfset QueryConstant = #LEFT(CGI.QUERY_STRING, 31)#>
<!---Find the overall length of the template path. --->
<!---31 is the length of '404;http://www.example.com:443/' --->
<cfset QueryVariable = #Len(CGI.QUERY_STRING)#>
<!---Take whatever is past your QueryConstant (AKA the string that produces a 404 error.) --->
<cfset theRightNumber = QueryVariable - 31>
<cfset QuerySearchString = #RIGHT(CGI.QUERY_STRING, theRightNumber)#>
<cfquery name="ListOfLocations" datasource="#request.dsn#">
SELECT location.id
FROM location WHERE url_name = <cfqueryparam value="#QuerySearchString#" cfsqltype="CF_SQL_VARCHAR" maxlength="255"> LIMIT 1
</cfquery>
<cfif ListOfLocations.recordcount>
<cflocation url="https://example.com/template.cfm?pm=ms&loc_id=#ListOfLocations.id#" addtoken="no" statusCode="301">
<cfelse>
<cflocation url="/404error.cfm" addtoken="no">
</cfif>
This is how most popular MVC Framework work today, by parsing out the URL segments.
Do you have access to any kind of URL rewrite software?
Since you are using IIS it has a built in rewrite engine where you could simply rewrite these kind of requests to a known file saving you the overhead of sending a 404 reply and parsing that out and having more request created as a result of that.
See http://wiki.coldbox.org/wiki/URLMappings.cfm for details. We use the Isapi rewrite version version to do just what you are asking for
Receive request for www.mysite.com/thisPlace
thisPlace isn't a directory
thisPlace isn't a file
Resend to index.cfm or a location of your chosing for additional parsing
Helicon Rewrite sends an HTTP header named HTTP_X_REWRITE_URL with the original requested URL so parsing it out is then very easy.
This all happens inline withing the one request so the client is never redirected.

Friendly Url in format 'mydomain.com/username' without Mod Rewrite?

I would like to know if there's an easier way other than Mod Rewrite (using the fusebox framework or directly in Coldfusion) to convert a url as follows:
from:
http://www.somedomain.com/salmahayek
or
http://localhost/someApp/salmahayek
to:
http://www.somedomain.com/index.cfm?action=profile.view&name=salmahayek
or
http://localhost/someApp/index.cfm?action=profile.view&name=salmahayek
My app is an existing Fusebox 5.5 application.
I just need to add that the url above is not static, i.e. "salmahayek" could be any name.
Any help would be greatly appreciated
Thanks
You could potentially use the "classic" way of doing it (not sure if Fusebox will interfere), using a 404 handler, something like this should do the trick:
Set up a 404 hander on your server, e.g. in .htaccess:
ErrorDocument 404 /404handler.cfm
set up 404handler.cfm to wrap around the framework, e.g.:
<cfset variables.checksok = false>
<!--- do some checks - example --->
<cfif cgi.REDIRECT_URL EQ 'salmahayek'>
<cfset variables.checksok = true>
</cfif>
<cfif variables.checksok EQ true>
<cfheader statuscode="200" statustext="OK">
<cfset url.action = "profile.view">
<cfset url.name = cgi.REDIRECT_URL>
<cfinclude template="index.cfm">
</cfif>
(not tested but should work)
I've doing some like this in one my apps currently, albeit in PHP:
http://localhost/index.cfm/profile.view/salmahayek/
<cfset urlArgs=listToArray(CGI.PATH_INFO, "/") />
<cfset action=urlArgs[1] />
<cfset name=urlArgs[2] />
This works perfectly, but you have to put up with the "index.cfm" if you don't want to rewrite.
I'm not sure about anyone else, but I don't understand why Mod Rewrite would be difficult, unless you are on IIS. A rewrite rule would simply have to be something like:
^(login|register)/([^/\.]+) index.cfm?action=profile.$1&step=$2 [L]
^([^/\.]+)/?$ index.cfm?action=profile.view&name=$1
I put in some extra examples to check if the user is actually trying to get to the registration or login page and what step they are on there.
i've actually done this in past using the Application.cfc's onMissingTemplate() method. you can either do some regexs against the arguments.targetpage that gets passed in or do a lookup in a database. either way you would do a cflocation to the correct page afterward. just remember to pass over any url parameters also.
one thing that i've never tried out and often wondered though is if this could be handled in the onRequestStart() method instead? the biggest problem i have with using onMissingTemplate() is that you're doing a cflocation which is a completely new request and you can't pass through form variables. yes i know you could probably use GetPageContext().Forward( strUrl ) instead, but you're still going threw the entire request lifecycle in for the original request. by doing this in onRequestStart() you would avoid this.
anyone want to test this out?