Improving Our Program
Although this program isn't bad, it might be useful if it were a little more flexible. One thing we might
want to do is allow it to take parameters, so that it can do not only a GET operation, but
also a POST. We could also decide to allow the program to output the header information that
it received from the web server, which can be very handy when trying to debug routines that retrieve data
from a web server. A method of handling and validating command line parameters might also be useful. Let's
add support for not only the output file, but also a flag to decide if the header is output, and also
support for passing variables through using the POST method.
As a first step, we can update our usage() function with the new information. The
new version looks like this:
usage() Function
function usage()
string s
s = "smprun[32.exe] urldump.smp <url> [--outfile=<filename>] \
[--showheader]{d}{a}"
s = s + " [--vars=<varlist>]{d}{a}{d}{a}"
s = s + " Where the vars need to already be URL-encoded and \
if they violate the{d}{a}"
s = s + " shell rules they will also need to be escaped to \
be hidden from the{d}{a}"
s = s + " shell. It is recommended to place quotes around \
the --vars= entry.{d}{a}"
s = s + " The equals sign and what follows CANNOT allow \
spaces! If necessary,{d}{a}"
s = s + " surround any entry with quotes.{d}{a}"
end function s
Now that we have decided what the parameters are going to be (and incidentally also the format), we can
add the code to handle the parameters. This is probably best done using a specifically designed data
type. This will allow us to offload most of the work to the type itself, without cluttering our existing
main() function with all the associated code. It will also make it easier to lift
it and use it again in another program, or even in the future to create a more versatile type that is
more universal. Let's see what that code looks like:
type parameters
embed
string outfilename
boolean showheader
string variables
string operation
reference
function getparam
end type
function parameters.new(parameters me)
me.outfilename = ""
me.showheader = .false
me.variables = ""
me.operation = "GET"
end function me
function parameters.getparam(parameters me, string parameter)
if parameter <= ""
// do nothing
else if .like1(parameter, "--outfile=*")
me.outfilename = .substr(parameter, .instr(parameter, "=") + \
1, .inf)
else if .like1(parameter, "--showheader")
me.showheader = .true
else if .like1(parameter, "--vars=*")
me.variables = .substr(parameter, .instr(parameter, "=") + \
1, .inf)
end if
end function
The parameters type has properties to store all the information that we will use for this
call to the program. It defaults to running in GET mode, and will not return the header from
the web server. The way the getparam() method has been coded requires that each
parameter that has a value component must be separated from the value by an equals (=)
sign and no white space to either side. Part of the reason for this is that allowing white space would
require a more complex algorithm, since each of the white space separated items would arrive as separate
parameter values from the shell to the main() function.
We now have a method of handling the various parameters to the program, and one of the nice features of
this approach is that the order of the parameters does not matter. The only parameter that has a fixed
position is the URL itself, since it must be first. Using this approach requires some changes to the
main() function as well. Let's have a look at those now.
main() Function
function main(string sUrl, string param1, string param2, \
string param3)
string errtext
integer e
fsfileoutputstream fpo
httpresponse response
parameters params
e = 0
if sUrl <= ""
errtext = usage()
else
params =@ parameters.new()
params.getparam(param1)
params.getparam(param2)
params.getparam(param3)
errtext = ""
if params.variables > ""
response =@ httppost(sUrl, params.variables)
else
response =@ httpget(sUrl)
end if
if response !@= .nul
if response.errorstatus > ""
errtext = response.errorstatus
else if params.outfilename > ""
fpo =@ fsfileoutputstream.new(params.outfilename, error=e)
if fpo =@= .nul or e != 0
errtext = sERRTXT_FILEOPENFAILED + sCRLF
else
if response.statuscode < 200 or \
response.statuscode >= 300
errtext = sERRTXT_PAGE + sUrl + sERRTXT_NOTFOUND + \
sCRLF
else
errtext = sERRTXT_PAGE + sUrl + sERRTXT_SUCCESS + sCRLF
end if
fpo.putstring(.if(params.showheader, \
makenotnull(response.fullheader) + \
sCRLF + sCRLF, "") + \
.if(response.entitybody != .nul, \
response.entitybody.getstring(1, \
.inf, 1),""), 1)
end if
else
if response.statuscode < 200 or response.statuscode >= 300
errtext = sERRTXT_PAGE + sUrl + sERRTXT_NOTFOUND + sCRLF
else
errtext = ""
end if
errtext = errtext + makenotnull(.if(params.showheader, \
response.fullheader + \
sCRLF + sCRLF, "")) + \
.if(response.entitybody != .nul, \
response.entitybody.getstring(1, \
.inf, 1),"")
end if
end if
end if
end function errtext
As we can see from the previous code, not a lot has changed from the original version. We now support
the POST operation if we were given variables (which must be URL-encoded when they are
passed in). We can also optionally return the entire header from the web server if requested to do
so. All of the actual handling of the parameters is done by the parameter type and its
getparam() method.


