Lessons by Jon

Form Lesson 1 - Identifying Data

In this lesson we will learn how to actually identify and access the information that is passed from each form field to MacHTTP in the post_args. This is a fairly big step and adds the problem that you now have to work with the original form page to make sure it matches your code. As you should have learned already, form pages provide names for every button, field, list, etc. so they can be differentiated by a CGI application when extracting their information. These same names have to be used in the AppleScript that you use to create the CGI application.

Required OSAX

Tokenize
ScriptTools
DecodeURL
DePlus

If you don't already have these installed, look at instructions for obtaining and installing the needed software.


FormScript1.txt - Processing the Data in post_args

Here is the entire script for this lesson. The comments have been removed so you see only the lines that actually get compiled. The full script, including comments and special characters, is in the archive with the name "Script6.txt".
property crlf : (ASCII character 13) & (ASCII character 10)
property http_10_header : "HTTP/1.0 200 OK" & crlf & "Server: MacHTTP" & crlf & Ā
	"MIME-Version: 1.0" & crlf & "Content-type: text/html" & crlf & crlf
property idletime : 300
property datestamp : 0

set datestamp to current date

on «event WWW½sdoc» path_args ¬
   given «class kfor»:http_search_args, ¬
      «class post»:post_args, «class meth»:method, ¬
      «class addr»:client_address, «class user»:username, ¬
      «class pass»:password, «class frmu»:from_user, ¬
      «class svnm»:server_name, «class svpt»:server_port, ¬
      «class scnm»:script_name, «class ctyp»:content_type
	
 try
	
   set datestamp to current date

   set return_page to http_10_header ¬
      & "<HTML><HEAD><TITLE>Post_Args Results</TITLE></HEAD>" ¬
      & "<BODY><H1>Post_Args Results</H1>" & return ¬
      & "<H4>post_args</H4>" & return

   set postarglist to tokenize post_args with delimiters {"&"}
 
   set oldDelim to AppleScript's text item delimiters
   set AppleScript's text item delimiters to {"="}
   repeat with currpostarg in postarglist
      set currname to first text item of currpostarg
      if currname = "name" then
         set username to (Decode URL (dePlus (last text item of currpostarg)))
         set return_page to return_page & "<B>User: </B>" & username & return
      else if currname = "address" then
         set useraddress to (Decode URL (dePlus (last text item of currpostarg)))
         set return_page to return_page & "<BR><B>E-mail: </B>" & useraddress & return
      else if currname = "sub" then
         set sub_text to (Decode URL (dePlus (last text item of currpostarg)))
         set return_page to return_page & "<BR><B>Subject:</B><BR>" & sub_text & return
      else if currname = "message" then
         set message_text to (Decode URL (dePlus (last text item of currpostarg)))
         set return_page to return_page & "<BR><B>Message:</B><BR>" & message_text & return
      else if currname = "S" then
         -- ignore it.  That's the Submit button.
      else
         error ("Unknown variable in post_args: " & currname) number 100
      end if
   end repeat
   set AppleScript's text item delimiters to oldDelim
 
   set return_page to return_page & "<H4>client_address</H4>" & return ¬
      & client_address & return & "<HR><I>Results generated at: " & (current date) ¬
      & "</I>" & "</BODY></HTML>"
   return return_page

 on error errMsg number errNum
   set return_page to http_10_header ¬
      & "<HTML><HEAD><TITLE>Error Page</TITLE></HEAD>" ¬
      & "<BODY><H1>Error Encountered!</H1>" & return ¬
      & "An error was encountered while trying to run this script." & return
   set return_page to return_page ¬
      & "<H3>Error Message</H3>" & return & errMsg & return ¬
      & "<H3>Error Number</H3>" & return & errNum & return ¬
      & "<H3>Date</H3>" & return & (current date) & return
   set return_page to return_page ¬
      & "<HR>Please notify Jon Wiederspan at " ¬
      & "<A HREF=\"mailto:jonwd@tjp.washington.edu\">jonwd@tjp.washington.edu</A>" ¬
      & " of this error." & "</BODY></HTML>"
   return return_page
 end try
end «event WWW½sdoc»

on idle
   if (current date) > (datestamp + idletime) then
      quit
   end if
   return 5
end idle

on quit
   continue quit
end quit

Step By Step

Up until now, we haven't really cared what the form looked like that was used to access the script, or what kind of data was in the form (fields and buttons and such). That's an easy way to write your scripts, but not terribly useful. To do anything useful we're going to have to buckle down (hey, its the law!) and do some serious text processing.

The general plan of attack looks like this:

  1. take an item from the postargslist
  2. separate it into its name and data components (remember, the "=" character delimits those two)
  3. check the name to see what field/button/list it is from
  4. assign the data to the proper variable for later use
In this case "later use" means adding it to the return_page. In later lessons it might mean passing it to an email program or adding it to a database or replacing it with some random text to really freak out the user.

If you've been thinking about how your form is laid out you might be thinking that you can save some serious processing time by just remembering what order your fields are in and assigning the data to variables based on that. Thus, the data in item 1 is from field 1 so it goes in variable 1, and so on.

NOT!

WWW clients are not required to return the form data in the order that the fields occur in the form. If they want to do so, they can reverse the fields or even randomly mix them up before returning them. In case you're now feeling like this HTTP protocol is about as well designed as a blob of jello, let me tell you that this "looseness" is a Pretty Good Thing as far as I'm concerned. Rigid protocols leave little room for later improvements. HTTP is so loose that there's tons of room for improvement. In fact, I may even propose a new HTML-4 that will be so loose that the client could send a duck back to the server and you'd still have to deal with it properly (lots of water, occasional feedings, watch the beak). The following text contains most of the changed code in this lesson:

   set oldDelim to AppleScript's text item delimiters
   set AppleScript's text item delimiters to {"="}
   repeat with currpostarg in postarglist
      set currname to first text item of currpostarg
      if currname = "name" then
         set username to (Decode URL (dePlus (last text item of currpostarg)))
         set return_page to return_page & "<B>User: </B>" & username & return
      else if currname = "address" then
         set useraddress to (Decode URL (dePlus (last text item of currpostarg)))
         set return_page to return_page & "<BR><B>E-mail: </B>" & useraddress & return
      else if currname = "sub" then
         set sub_text to (Decode URL (dePlus (last text item of currpostarg)))
         set return_page to return_page & "<BR><B>Subject:</B><BR>" & sub_text & return
      else if currname = "message" then
         set message_text to (Decode URL (dePlus (last text item of currpostarg)))
         set return_page to return_page & "<BR><B>Message:</B><BR>" & message_text & return
      else if currname = "S" then
         -- ignore it.  That's the Submit button.
      else
         error ("Unknown variable in post_args: " & currname) number 100
      end if
   end repeat
   set AppleScript's text item delimiters to oldDelim
The first two lines are used to set the delimiter that will be used to separate the "field name" from the "field data". You could use Tokenize to do this as well if you wanted. I'm not sure how much it would improve the speed, though, and I'm not very interested right now in finding out. This is plenty fast for the time being (until my next revision is done).

The "if..else if" control structure is where all of the new work takes place. The "repeat" loop takes each list item from the list created by Tokenize, and assigns it to currpostarg. The first text item (everything before the "=") is taken to be the name of the field/button/list that contained this data. That name is passed down through the "if..else if" statements until a match is found, then the data is assigned to the proper variable. Finally, the data is decoded using DePlus and Decode URL. This whole process is pretty straightforward, but there are five things to notice:

  1. The names you are looking for (in the "if..else if" statements) must match the names you used on your form. From this point on you'll have a pretty much one-to-one relationship between your forms and your CGI's because of that.
  2. There is the possibility that you may receive a field name that isn't recognized by any of the "if..else if" statements. In the code above, any field that doesn't match falls through to the "else" statement which generates an informative error for the user.
  3. I'm ignoring a big factor - whether the data is actually correct. If you want to check that certain fields contain only numbers or a date or something, this is the place to do it. That should be easy to add, so I'm not going to worry about it.
  4. It is important that DePlus be run before Decode URL. DePlus will change all plus characters to spaces. Because Mosaic and Netscape use the plus character as a special delimiter, they first encode all real plus characters. If you reversed the order, the real plus characters would be decoded and DePlus would convert them to spaces as well.
  5. I can't think of a fifth thing, but I'm sure one will come to me during Star Trek DS9.

Everything else about this script is the same as in the CGI lessons. See how well I did this?!


Test the Script

This is a simple, do-nothing form that asks the user for information needed to send an e-mail message and then returns the information nicely formatted. No e-mail is sent - that's for the next lesson. Use the "View Source" option offered by most WWW clients to look at the HTML for the form. This will show you where the names come from that are sent to the CGI application.

Wrap It Up

This is as far as you can go with generic scripts. You now know everything you need to know to get information from forms and make it available for a wide variety of uses. Of course we haven't really done anything useful yet. From here we diverge into the teeming multitudes of "real-life applications". Godspeed.
[Back to Form Page]

Jon Wiederspan
Last Edited: December 11, 1994