Write Permission Issue on Contribute File Deployer? - coldfusion-8

I've been using Contribute's File Deployer tool for some time, and it's worked wonderfully until we replaced the servers that it pushes files to. Pushing files by itself works fine, permissions and all. But part of the main feature is it scans the directory that your file is being pushed to, and if it doesn't exist then it creates said directory.
As of now, it's always failing on this part of the tool. The format of the full path of the file being pushed is \\\x.x.x.x\sync$\path/to/folder (the mixed slashes always work.)
This is on Windows XP sp3 with ColdFusion 8.
<cftry>
<cfinvoke method="MakeSurePathExists" path="#serverPathToPush##siteRelative#">
<cfcatch type="any">
<cfthrow errorcode="NoLiveServerAccess" message="Can not access or do not have sufficient permissions to write to: #serverPathToPush##siteRelative#">
<cflog application="yes" text="Can not access or do not have sufficient permissions to write to: #serverPathToPush##siteRelative#" file="Filedeployer" />
</cfcatch>
</cftry>
<cffile action="copy" source="#settings.stagingFileSystemPath & siteRelative#" destination="#serverPathToPush##siteRelative#">
<!--- touch the file so it gets the current date, so the browser will pull down the new one when previewed --->
<cffile action="append" file="#serverPathToPush##siteRelative#" output="">
<!--- This function checks if the directory exist of the given file.
If it doesn't, it tries to build path. If it fails, the function throws --->
<cffunction name="MakeSurePathExists">
<cfargument name="path" type="string" required="true">
<cfset createList = ArrayNew(1)>
<cfinvoke method="RemoveLastFileFromPath" path="#path#" returnvariable="parentdir">
<cfloop condition="not DirectoryExists( parentDir ) and Len( parentDir ) gt 0 ">
<cfset temp = ArrayAppend( createList, parentDir ) >
<cfinvoke method="RemoveLastFileFromPath" path="parentdir" returnvariable="parentdir">
</cfloop>
<cfloop from="#ArrayLen( createList )#" to="1" step="-1" index="index">
<cfdirectory action="create" directory="#createList[index]#">
</cfloop>
</cffunction>
<cfscript>
function RemoveLastFileFromPath( path )
{
rpath = Reverse( path ) ;
idx2 = Find( "\", rpath ) ;
idx = Find( "/", rpath ) ;
if( idx2 is not "0" and idx2 lt idx )
idx = idx2 ;
if( idx is not "0" ) {
rpath = Right( rpath, Len(rpath) - idx ) ;
return Reverse( rpath ) ;
}
return "" ;
}
</cfscript>
The friendly error I get is:
Can not access or do not have sufficient permissions to write to: \x.x.x.x.\sync$\ path/to/folder/the-file.cfm
CFDUMP's Error:
The most likely cause of this error is that \x.x.x.x.\sync$\ path/to/folder/already exists on your file system. The exception occurred during a cfdirectory action="create".
I'm aware of the space between the end of the share URL and the relative path. Again, this has not been an issue before, and I'm not sure that it is now.

It turns out, in this case, the space in the URL is affecting the push. I had to add a trim() around the server address, and that resolved the issue. However, why it is only an issue now and not before I will never know.

Related

Error with undefined variables while looping over a query

I have an error while looping over a query using cfloop.
When I use a cfdump on the query (inside the loop, mind you), I can see all the data just fine. But when I try to get the value of each variable as you normally do in cfloop, I get a message that says they are undefined. I then changed each variable to reference the query specifically, and now the problem is that the variable is undefined in the query. Here's the code:
<cffunction name="writeCourses">
<cfargument name="recordset" required="yes" type="query">
<cfif recordset.RecordCount NEQ 0>
<cfset temp = "">
<cfoutput>
<cfloop query="recordset">
<!--- <cfdump var="#recordset#"> <cfabort/> --->
<cfset temp = temp & "<strong>#recordset.courseType# #recordset.courseNum# ">
<cfif isDefined("recordset.courseTHM") AND recordset.courseTHM EQ 1>
<cfset temp = temp & "(#left(recordset.courseNum,3)#4) ">
</cfif>
<cfif isDefined("recordset.courseName")>
<cfset temp = temp & "#recordset.courseName# </strong><br>">
</cfif>
<cfset temp = temp & "#recordset.courseDESC#<br>">
<cfset temp = temp & "#recordset.courseHours#<br><br>">
</cfloop>
</cfoutput>
<cfelse>
<cfset temp = "">
</cfif>
<cfreturn temp>
</cffunction>
So as you can see, each variable is enclosed in ## tags. Originally none of them were proceeded by recordset. but they were still undefined. And when I uncomment the cfdump and cfabort tags, those work fine and I can see the recordset query with all the data as it should be.
Every other time I have used cfloop with a query it works as expected. Also, I did not write this code, I am having to modify it (the original author no longer works here).
Here's an example of the recordset dump:
The error message:
Detail: [empty string]
ErrNumber: 0
Message: Element COURSETYPE is undefined in RECORDSET.
Resolvedname: RECORDSET
The error line is:
<cfset temp = temp & "<strong>#recordset.courseType# #recordset.courseNum# ">
<cfif isDefined("recordset.courseTHM") AND recordset.courseTHM EQ 1>
<cfset temp = temp & "(#left(recordset.courseNum,3)#4) ">
</cfif>
<cfif isDefined("recordset.courseName")>
<cfset temp = temp & "#recordset.courseName# </strong><br>">
</cfif>
That's all one line :/
The stored procedure/function calling the above:
<cffunction name="getCoursesByDept">
<cfargument name="deptCode" required="yes" type="string">
<CFSTOREDPROC procedure="dbo.GetCourses" datasource="WebCatalog">
<CFPROCPARAM type="IN" dbvarname="#deptCode" value="#deptCode#" cfsqltype="CF_SQL_CHAR">
<CFPROCRESULT name="result">
</CFSTOREDPROC>
<cfinvoke method="writeCourses" recordset="#result#" returnvariable="output">
<cfreturn output>
</cffunction>
Your problem appears to be failure to scope. Here are your first 4 lines:
<cffunction name="writeCourses">
<cfargument name="recordset" required="yes" type="query">
<cfif recordset.RecordCount NEQ 0>
<cfset temp = "">
Try it like this:
<cffunction name="writeCourses">
<cfargument name="recordset" required="yes" type="query">
<cfset var temp = "">
<cfif arguments.recordset.RecordCount NEQ 0>
The differences are the use of the var keyword for your local variable temp, and adding the arguments scope to the recordset variable.
(In addition to Dan's comments ...)
If [the procedure] can't find anything it returns a query containing
an error message (i.e. no course found)
Then that means the COURSETYPE column does not always exist in the resultset, which is exactly what the error message is reporting. If the procedure returns any result, regardless of the contents, the code inside the cfif block will execute. Since the first line of code uses that column, without verifying it exists, it would cause the exact error you are seeing.
Also, as I mentioned in the comments, you really need to localize the function variables result, output, temp, etectera. Lack of var scoping can create problems, even within same page, if you reuse variable names. As #Dan suggested you should fully scope all variables - in particular, the function arguments.
(As an aside, I understand you are modifying existing code, but the error message should really be handled in CF, not inside the procedure. The procedure's job is just to return data. The CF code should check the recordCount and take the appropriate action if no records are found.)

ColdFusion searching robots.txt for specific page exception

We're adding some functionality to our CMS whereby when a user creates a page, they can select an option to allow/disallow search engine indexing of that page.
If they select yes, then something like the following would apply:
<cfif request.variables.indexable eq 0>
<cffile
action = "append"
file = "C:\websites\robots.txt"
output = "Disallow: /blocked-page.cfm"
addNewLine = "yes">
<cfelse>
<!-- check if page already disallowed in robots.txt and remove line if it does --->
</cfif>
It's the <cfelse> clause I need help with.
What would be the best way to parse robots.txt to see if this page had already been disallowed? Would it be a cffile action="read", then do a find() on the read variable?
Actually, the check on whether the page has already been disallowed would probably go further up, to avoid double-adding.
You keep the list of pages in database and each page record has a indexable bit, right? If yes, simpler and more reliable approach would be to generate new robots.txt each time some page is added/deleted/changes indexable bit.
<!--- TODO: query for indexable pages ---->
<!--- lock the code to prevent concurrent changes --->
<cflock name="robots.txt" type="exclusive" timeout="30">
<!--- flush the file, or simply start with writing something --->
<cffile
action = "write"
file = "C:\websites\robots.txt"
output = "Sitemap: http://www.mywebsite.tld/sitemap.xml"
addNewLine = "yes">
<!--- append indexable entry to the file --->
<cfloop query="getPages">
<!--- we assume that page names are not entered by user (= safe names) --->
<cffile
action = "append"
file = "C:\websites\robots.txt"
output = "Disallow: /#getPages.name#.cfm"
addNewLine = "yes">
</cfloop>
</cflock>
Sample code is not tested, be aware of typos/bugs.
Using the Robots.txt files for this purpose is a bad idea. Robots.txt is not a security measure and you're handing "evildoers" a list of pages that you don't want indexed.
You're much better off using the robots meta tag, which will not provide anyone with a list of pages that you don't want indexed, and gives you greater control of the individual actions a robot can perform.
Using the meta tags, you would simply output the tags when generating the page as usual.
<!--- dummy page to block --->
<cfset request.pageToBlock = "/blocked-page.cfm" />
<!--- read in current robots.txt --->
<cffile action="read" file="#expandPath('robots.txt')#" variable="data" />
<!--- build a struct of all blocked pages --->
<cfset pages = {} />
<cfloop list="#data#" delimiters="#chr(10)#" index="i">
<cfset pages[listLast(i,' ')] = '' />
</cfloop>
<cfif request.variables.indexable eq 0>
<!--- If the page is not yet blocked add it --->
<cfif not structKeyExists(pages,pageToBlock)>
<cffile action="append" file="C:\websites\robots.txt"
output="Disallow: #request.pageToBLock#" addNewLine="yes" />
<!--- not sure if this is in a loop but if it is add it to the struct for nex iteration --->
<cfset pages[request.pageToBlock] = '' />
</cfif>
</cfif>
This should do it. Read in the file, loop over it and build a struct of the bloocked pages. Only add a new page if it's not already blocked.

How to get the temporary path of a file pulled with CFHTTP in Coldfusion?

I'm using Coldfusion8 and need to fetch images from a remote server, which I'm doing like this:
<cfhttp timeout="45" throwonerror="no" url="#variables.testFilePath#" method="get" useragent="Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.8.1.12) Gecko/20080201 Firefox/2.0.0.12" getasbinary="yes" result="variables.objGet">
<cfset variables.objImage = ImageNew(variables.objGet.FileContent)>
I now need to save the image to Amazon S3, but the function I want to use:
<cfset result = s3.putObject(url.b,file.serverFile,file.contentType,'300',form.cacheControl,'30',form.acl,form.storageClass,form.keyName,GetTempDirectory())>
Requires the directory where my generated image can be found in.
Question:
Is there a way to get the directory of an image file pulled with cfhttp and converted to an image using imageNew? Or do I need to save the file to disk first? I also need to resize before storing, so I might not be able to get by without saving to disk first.
Thanks for pointers!
EDIT:
I got it working like this:
<!--- getAsBinary --->
<cfhttp timeout="45"
throwonerror="no"
url="#variables.testFilePath#"
method="get"
useragent="..."
getasbinary="yes"
result="objGet">
<!--- validate --->
<cfif len(variables.testFilePath) EQ 0>
<cfset variables.errorCount = variables.errorCount+1>
<cfset variables.failedLoads = "FILE NOT FOUND" >
<cfelse>
<cfif len(objGet.Filecontent) EQ 0>
<cfset variables.errorCount = variables.errorCount+1>
<cfset variables.failedLoads = "COULD NOT LOAD IMAGE">
<cfelseif NOT listfindnocase(variables.allow, variables.fileExt) >
<cfset variables.errorCount = variables.errorCount+1>
<cfset variables.failedLoads = "WRONG FILE TYPE">
<cfelse>
<cftry>
<cfscript>
objImage = ImageNew(objGet.FileContent);
ImageSetAntialiasing(objImage,"on");
<!--- resize/crop --->
variables.keyName = Session.loginid & "_S_";
</cfscript>
<!--- convert modified image back to binary --->
<cfset variables.filekey = toBase64( objImage )>
<!--- pass to s3.cfc --->
<cfset result = s3.putObject(variables.bucketName, variables.filekey, variables.contentType, variables.httptimeout, variables.cacheControl, variables.cacheDays, variables.acl, variables.storageClass, variables.keyName, variables.imageSrc, "true" )>
<cfcatch>
<cfset variables.errorCount = variables.errorCount+1>
<cfset variables.failedLoads = "NO IMAGE">
</cfcatch>
</cftry>
</cfif>
I need to re-convert the cropped image to binary, because the s3.putobject will otherwise do another cffile action="readBinary" and breaks on trying to construct the image file path (the image is still in temp) right here:
<cffile action="readBinary" file="#arguments.uploadDir##arguments.fileKey#" variable="binaryFileData">
While I can get the temporary file path using this trick and set uploadDir it doesn't help, because CF docs say the path must be either an absolute path starting with drive letter or slash, otherwise the www-root temp directory will be taken.
In my case the temp www-root directory was on C:/ while the temp file CFFile-Servlet was on E:/ and a relative path did not work either (file not found). So as I found no way to re-read the image from the s3.cfc, I'm now converting back to binary before calling S3.cfc. I pass another parameter (1/0) telling s3.cfc, that I'm already sending the binary image and there is no need to re-read it.
Like so:
<!--- if encoded is true, filekey already is the encoded image --->
<cfif arguments.encoded EQ "true">
<!--- already sending binary image --->
<cfset binaryFileData = arguments.fileKey>
<cfelse>
<!--- Default --->
<cffile action="readBinary" file="#arguments.uploadDir##arguments.fileKey#" variable="binaryFileData">
</cfif>
I'm not sure if this is the smartest way performance wise, but it seems to work pretty smooth. Comments are welcome!
I guess you could use path and file attributes instead of result. Generate some temporary path using GetTempDirectory() + CreateUUID(), fetch and then drop it. Plus it may be a bit more memory-efficient thatn fetching content to the variable, then writing to the intermediate file.
Cfhttp result stores the data in a memory variable.
ImageNew creates a 'ColdFusion' image meaning it's resident in memory only also. You'd have to save it to make it a physical file to send either in cfhttp or imagewrite, etc.
Without saving it to a physical file you must use cffile action = "writetobrowser" to send it to a browser but that ends up saving it in a temp location for the browser to access but wouldn't do you much good here I don't think.
http://livedocs.adobe.com/coldfusion/8/htmldocs/functions_h-im_34.html
http://livedocs.adobe.com/coldfusion/8/htmldocs/Images_19.html
AmazonS3Client has a putObject method that takes an InputStream, so you can wrap the binary data in a ByteArrayInputStream and pass it in without worrying about the backing file.
Something like this:
<cfhttp method="get" url="#local.url#" result="local.httpResult" getAsBinary="true"/>
<cfif local.httpResult.statusCode eq "200 OK">
<cfset var fileInputStream = createObject("java", "java.io.ByteArrayInputStream" ).init(local.httpResult.fileContent) />
<cfset var objectMetadata = createObject("java", "com.amazonaws.services.s3.model.ObjectMetadata" ).init() />
<cfset application.S3UTIL.s3Client.putObject("some.bucket", "folder/file.ext", local.fileInputStream, local.objectMetadata)/>
</cfif>

Find MAC address in ColdFusion

We are looking for a way to find the MAC address of a client in Coldfusion.
Is there a way to do this? We are using CF 9 on JDK 1.6 which, I believe, allows us to use the Java network layer, but I would prefer to get be closer to the CFML layer.
You can't get the client's MAC address through java, as it's not passed in anywhere with the request. If you did want it, you would need some code that ran on the client's side. See here for more information: how to get a client's MAC address from HttpServlet?
You are able to get the server's MAC address using the below code as referenced in the answer above.
<cfset LocalHost = CreateObject( "java", "java.net.InetAddress" ).getLocalHost() />
<cfset Mac = CreateObject( "java", "java.net.NetworkInterface" ).getByInetAddress( LocalHost ).getHardWareAddress() />
<cfset MacAddress = '' />
<cfloop from="1" to="#ArrayLen( Mac )#" index="Pair">
<!--- Convert it to Hex, and only use the right two AFTER the conversion--->
<cfset NewPair = Right( FormatBaseN( Mac[ Pair ], 16 ), 2 ) />
<!--- If it's only one letter/string, pad it --->
<cfset NewPair = Len( NewPair ) EQ 1 ? '0' & NewPair : NewPair />
<!--- Append NewPair --->
<cfset MacAddress &= UCase( NewPair ) />
<!--- Add the dash --->
<cfif ArrayLen( Mac ) NEQ Pair>
<cfset MacAddress &= '-' />
</cfif>
</cfloop>
<cfdump var="#MacAddress#" />
I have not done this personally, but here's a link to a forum discussion were they explain how you can do this http://www.raymondcamden.com/forums/messages.cfm?threadid=39CC3269-19B9-E658-9DD1131DAB233CA8 otherwise this should work also http://tutorial17.learncf.com/

Use coldfusion to get x number of words total including keyword

Okay this is part of my search results project, in it, I have description being returned from multiple tables. All of that part works 100%.
I currently use a trim_text function, which I pass a string, and how many words I want to keep.
However, now I need to modify it to make sure the keyword/search term is in the returning description to help show the validity of that in the search results.
Here below is the existing trim_text function, that I need your help to modify.
<cffunction name="trim_text" output="false" access="remote" returntype="string">
<cfargument name="string" type="string" required="true">
<cfargument name="word_limit" type="integer" required="false">
<cfparam name="word_limit" default=20>
<cfparam name="snippet" default="">
<cfparam name="return_string" default="">
<cfset return_string = "">
<cfset return_string = reReplace( string, "</?\w+(\s*[\w:]+\s*=\s*(""[^""]*""|'[^']*'))*\s*/?>", " ", "all" ) />
<cfset return_string = reReplace( trim( return_string ), "\s+", " ", "all" ) />
<cfset snippet = reMatch( "([^\s]+\s?){1,#word_limit#}", return_string ) />
<cfif !arrayLen( snippet )>
<cfreturn "" />
</cfif>
<cfset charCount = listlen(snippet[1]) />
<cfset wordCount = ( (word_limit * (arrayLen( snippet ) - 1)) + listLen( snippet[ arrayLen( snippet ) ], " " ) ) />
<cfif charCount gt 190>
<cfreturn left(snippet[1],190) & "..." />
</cfif>
<cfset return_string = snippet[1] & "..." />
<cfreturn return_string />
</cffunction>
So my end goal is a description that contains the keyword.
So for example.
Let us say I am searching for the keyword 'business'
And I get the correct search result, however the description doesn't have that word in the description shown, since we are limiting the description to 25 words, via the trim_text function. It makes all the descriptions look similar in size. But doesn't help prove the validity of results where the keyword is further down in the description.
Any questions? I hope I made this very clear.
I am using Coldfusion 8 Standard. I am testing this on my development server.
Thank You...
Sounds like you need to find the position of the keyword in the string, and then take the characters either side.
Treat your string as a list, and use whitespace characters and punction as the delimters.
Something like this:
<cfset wordFoundPos = listFindNoCase(string, searchTerm, " ,.-:;") />
Say that returns 42 - that is, the searchTerm is the 42nd word.
Convert that to a character position like so:
<cfset charPos = findnocase(1, string, searchTerm) />
Then grab the characters either side of that character:
<cfset context = mid(190, charPos-90, string) />
You'll need to detect when the searchterm is found too close to the start or end of the string to avoide errors, and to work out when to append and/or prepend ellipses to the context.