SIMPOL Documentation

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:

Example 4.2. Updated 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:

Example 4.3. The parameters Type
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.

Example 4.4. The Final Version of the 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.