ColdFusion in Context: Read Encrypted Files

Suppose you need to make a secure text archive available on a low storage budget. You'll need a way to expand the contents of secure files and present them to your users. This tip gives you a way to do that. Along the way, you'll manipulate programs, files, strings, and lists.

Set the Stage: Securely Shrink a File

You'll need something to pack for this example. Save this text as permit.csv. Note that when commas appear in the data, the data is surrounded by double quotes. This is a comma separated values file.

PERMIT,FACILITY,CITY
AR0035459,USA-COE ALPINE RIDGE REC AREA,ARKADELPHIA
AR0036749,ARKADELPHIA HUMAN DEVELOPMENT,ARKADELPHIA
AR0035751,"ARKANSAS CITY, CITY OF",ARKANSAS CITY
AR0046663,MG INDUSTRIES,ARMOREL
AR0045977,NUCOR STEEL - ARKANSAS,ARMOREL
AR0049166,"IPSCO TUBULARS, INC.",ARMOREL
AR0046523,MAVERICK TUBE CORPORATION,ARMOREL
AR0041742,"ASH FLAT, CITY OF",ASH FLAT
AR0049379,"HANSON AGGREGATES WEST, INC.",ASHDOWN
AR0048411,DOMTAR A.W. CORPORATION,ASHDOWN

You'll need something to shrink it with. The venerable pkzip will do. It would be a good idea to purchase pkzip version 2.50 if you want to continue this sort of thing; because, it supports working with long filenames at the command line. The more popular version, 2.04g, does not. You can find them both at the maker's web site. For purely non-commerical use, arj will do this too. There are other alternatives, but they usually require installation of dynamic linked libraries (dll's) and therefore are not an option if you don't host your own server. (Of course, you have a world of options if you're using a UNIX-flavored operating system.)

The format of the command to save text securely with pkzip requires the -s (secure) flag with the password immediately following it as shown. This line creates fred.zip if it doesn't already exist and secures permit.txt within it:

pkzip -sfrank fred.zip permit.txt

Extract it to the Screen

The pkunzip command has several parameters. The -c expands the text to the screen (where other code in this tip will divert it to a variable for processing). The -s immediately precedes the password (parameter %1 of the batch file), as in -sfrank. The next parameter (%2) is the file to be unpacked. Because you want all the text, the specific files to be extracted are not named.

When working at the command line level in a Windows substitute for DOS nowadays, one problem is that support for long directory names and long filenames is spotty. Even Windows applications sometimes only understand the short equivalents of paths and filenames. As you might imagine, many external non-Windows applications have the same problem, and unless you maintain the host machine yourself, you won't know the short equivalents (which vary with the directory structure anyway). Therefore, your best bet is to navigate to the drive and directory you'll put the files into and then have the external program work in what is now the current directory. Here's an example; call this code sread.bat.

d:
cd \yourdirectory\unpack\dirin
d:\yourdirectory\unpack\pkunzip.exe -c -s%1 %2

Prepare to Call the Batch File

You'll need to set up code to execute the batch file; call it sread.cfm. The password and filename would typically be supplied in variables. The cfexecute tag takes a single argument, but the space separating them causes the batch file it calls to see two separate parameters. Timeout is set for fifteen seconds. Wrapping the cfexecute tag in pre tags preserves the line breaks in the output; line breaks are a big help when parsing the output later.

<cfparam name="form.Filein" default="fred.zip">
<cfset Password="frank">
<pre>
<cfexecute
name="d:\yourdirectory\unpack\sread.bat"
arguments="#Password# #form.Filein#"
timeout="15">
</cfexecute>
</pre>

Capture the Screen Output

In order to manipulate the output of the cfexecute tag before it reaches the screen, you'll need to capture it somehow. Everything between a custom tag's beginning and ending tag is automatically copied to ThisTag.GeneratedContent. So, if your custom tag copies it to a variable that can be read by the calling page (any variable prefixed with caller), it can work with this output. Call this code channel.cfm.

<cfset caller.Answer=ThisTag.GeneratedContent> 

Let Users Pick a File

The user needs to be able to select a document from the archive directory; call this code pick.cfm. The list action of the cfdirectory tag pulls information about the directory into a query with the name you give it (Filelist in this example). Skipping entries that start with a period, loop through the query to add select options to the form.

<cfdirectory action="list"
directory="d:\yourdirectory\unpack\dirin" name="Filelist">
<!--- cfdirectory returns a query --->
<form name="pickfile" action="show.cfm" method="post">
<select name="Filein">
<option>~none~
<cfloop query="Filelist">
<cfif left(filelist.name,1) is not ".">
  <option><cfoutput>#Filelist.name#</cfoutput>
</cfif>
</cfloop>
</select>
<input type="submit" name="doit" value="read">
</form>

Display the Result

Provide a way for the user to read the file's contents; call it show.cfm. If no filename was specified, bounce the user. Wrap the included file that does the work in the custom tag defined earlier to capture its output. Wrap that cfsilent tags to keep the output from getting to the screen directly. Then execute this file and see what happens.

<cfparam name="form.Filein" default="~none~">
<cfif form.Filein is "~none~">
  No file was selected; press your back button to continue.
  <cfabort>
</cfif>
<cfsilent>
<cf_channel>
<cfinclude template="sread.cfm">
</cf_channel>
</cfsilent>
=====
<cfset dummy=replaceNoCase(Answer,"#chr(13)#","")>
<cfset Real=listLen(Answer,"#chr(10)#")>
<cfset Realline=0>
<cfset Show=0>
<cfset Showline=0>
<cfloop list="#Answer#" index="Line" delimiters="#chr(10)#">
<cfset Realline=Realline+1>
<cfif findNoCase("zip:",Line)>
  <cfset Show=1>
</cfif>
<cfif Show>
  <cfset Showline=Showline+1>
  <cfif (Showline gt 2) and (Real-Realline gt 2)>
    <cfoutput>#Line#<br></cfoutput>
  </cfif>
</cfif>
</cfloop>
=====
<form name="getlist" action="pick.cfm" method="post">
<input type="submit" name="doit" value="pick a file">
</form>

Now, Do More

Now extend the concept. Imagine automatically unencrypting files, and posting their contents to a database, creating an update report, and deleting successfully read files. Imagine reversing the process, creating files, sending them to a distant location, confirming their receipt and deleting the originals. You work's cut out for you. =Marty=