Using Web Forms with RapidQ

Overview

There are two ways to work with Web forms in RapidQ. One is to have RapidQ create a form and send it to a web browser like Internet Explorer or Mozilla. There are several examples of using internet explorer using OLE (see example at bottom, or look in the Examples folder under OLE, or network, or Sockets). Another way is to do a RUN command with web browser and a command line option.

Another way to read and write web forms is to write a RapidQ program that is run on an internet Server (a server is computer that gets input over the net and delivers data to the 'client' that requested the data.). The RQ program will use CGI, which stands for "Common Gateway Interface" and in this case, the RapidQ program runs behind the scenes when a web form is filled out and submitted on line. To see how this works, let's go step by step.

1) A web page is written in HTML code, which could actually be written by a RapidQ program to a text file using HTML codes (for great tutorials on html and related coding see http://www.w3schools.com/ ). The user pushes a button on the web page, or a data entry screen, or enters an address in the browser.

2) Web server (Microsoft Server for Windows code!) received request and determines that it is a CGI request to run your RapidQ program written in CGI compatible code. CGI code is basically like printing to the CONSOLE or reading from the DOS ENVIRONMENT. There are no actual GUI forms created.

3) The Web server forks the CGI program, redirects your program's output to itself, and waits for the program to end.

4) The Web server returns the output of your program (ie., html code) back to the user that requested it. NOTE, if your program loops infinitely, the program will never terminate and the server will have nothing to send.

5) Web programming is  a stateless protocol. That is, each time your program is run it has to figure out where the user is in a given process)
and re-entrant (i.e., it should be able to start running again before a previous invocation has finished). Your program has no way of knowing if another request came in at the same time. You also have to figure out whether the user wants to keep a session going (ie., they have logged in and want to continually request information from your program. This is discussed below.

The Web Page

Creating a Web form allows an internet user to enter data into various fields. Applications for this are commonly seen as "guest books", reply forms, "contact us" forms, web e-mail processes, and many other database uses. A form is written in HTML code and when submitted to the web server (typically with a "submit" button), has a certain "action" associated with it. In the simplest example, the action could be the command "mailto:john.doe@testmail.com" and this action would send a long string of text representing all of the data entered in the form. It isn't pretty, but it works.

Here is a sample web page form:

<html>
<form method="POST" action="mailto:john.doe@testmail.com">
Enter your Name:<input type="text" name="Name" length=20><br>
Enter your e-mail:<input type="text" name="email" length=20><br>
<input type="submit" name="submit" label="submit">
</form>
</html>

This is not an HTML class, but let's spend a moment reviewing how the data starts its way "in" to the server. Notice the input fields that describe type=, name=, and length=. The name of each field is what we will use later when we process this information. In the "mailto" example shown above, the data might look something like this:

Name=Jane+Smith&email=jane.smith@whatevermail.com

This is what would come in to your mail if the form used the "mailto" action as shown. If there were numerous fields in the form, you can see that it would not be a friendly visual experience. Getting something like this from a long string of text in e-mail into a meaningful data file would also require further action, so just take the e-mail example as a type of action for illustration purposes only.

Back to the string of data, notice the format shows fieldname=value. Each field is separated by a & and each word in a field is separated by a + instead of a space. These delimiters will be used by the CGI program to parse (split up) the string into meaningful data. That is the main purpose of the CGI program - to parse the data into fields and to allow us to do something with those fields.

Typical CGI programs on Unix systems are written in the Perl or 'C' languages, which are not very easy to read or maintain by the average computer person. Windows CGI programs could be Perl, 'C', ASP, CFM or other languages, but now enter RapidQ !! (Sound the bugles... King William, take a bow! Ok, back to work).

The CGI Program

RapidQ gives us a better way of processing web data in a format that is easy to read and maintain. That's the advantage over other languages. Similar to a regular RapidQ program, there is an include statement for CGI programs. The QCGI.INC file should be used as the first statement in the program, and it uses certain parts of the environment to interface with the web form.

Here is a sample CGI program written in RapidQ:
'-------------------------------------
$TYPECHECK ON
$INCLUDE <rapidq.inc>
$INCLUDE <qcgi.inc>
$APPTYPE CGI
$ESCAPECHARS ON

DECLARE SUB Main
DECLARE SUB PrintMenu(errorType AS INTEGER, name$ AS STRING)
DECLARE SUB PrintFile(name$ AS STRING)

CREATE CGI AS QCGI
  AutoConvert = 1 'true
END CREATE

SUB Main
  DEFSTR name$
  'This tries to prevents a malicious user from getting to files outside the directory the program is in...
  'Get value
  name$ = CGI.Get("name", name$)
  'Remove other slashes
  name$ = REPLACESUBSTR$(name$, "%5C", "")
  'Don't forget lowercase...
  name$ = REPLACESUBSTR$(name$, "%5c", "")
  'Remove real slashes
  name$ = REPLACESUBSTR$(name$, "\\", "")
  'Remove ..
  name$ = REPLACESUBSTR$(name$, "..", "")
  'Remove forward slashes (in hex)
  name$ = REPLACESUBSTR$(name$, "%2F", "")
  'Don't forget lowercase...
  name$ = REPLACESUBSTR$(name$, "%2f", "")
  'Remove forward slashes
  name$ = REPLACESUBSTR$(name$, "/", "")

IF (LEN(name$) = 0) THEN
  PrintMenu(0, name$)
ELSE 
  PrintFile(name$)
END IF
END SUB

Main()

SUB PrintMenu(errorType AS INTEGER, name$ AS STRING)
  PRINT "Content-type: text/html\n\n"
  PRINT "<html>"
  PRINT "<head>"
  PRINT "<title>CGI Test - Enter a filename</title>"
  PRINT "</head>"
  PRINT "<body>"

IF (errorType = 1 OR errorType = 2) THEN
  DEFSTR text$ = name$ + " could not be "

SELECT CASE errorType
CASE 1:
  text$ = text$ + "found"
CASE 2:
  text$ = text$ + "opened"
DEFAULT:
  text$ = text$ + "selected"
END SELECT
text$ = text$ + "!"

PRINT "<p style=\"color: red\">" + text$ + "</p>"
END IF

PRINT "<p>Please enter a filename:"
PRINT "<form action=\"" + Application.ExeName + "\" method=\"post\">"
PRINT "<input name=\"name\" type=\"text\" width=\"30\" maxlength=\"256\">"
PRINT "</form>"
PRINT "</p>"
PRINT "</body>"
PRINT "</html>"
END
END SUB

SUB PrintFile(name$ AS STRING)
DIM File AS QFILESTREAM
IF name$ = Application.ExeName THEN
  PrintMenu(1, name$) 'File not found error
END IF

'another security procedure is to start eveything from a working directory
DEFSTR filename$ = CURDIR$ + "\\" + name$

IF FILEEXISTS(filename$) THEN
  IF File.Open(filename$, fmOpenRead) THEN
  PRINT "Content-type: text/plain\n\n"
  WHILE NOT(File.EOF)
    PRINT File.ReadLine
  WEND
  ELSE
  PrintMenu(2, name$)
  END IF
ELSE
  PrintMenu(1, name$)
END IF
END SUB
'-------------------------------------

Compile this program and place it in the "cgi-bin" directory of your home page. Whatever name is given to this program, such as "cgitest.exe" should be placed with the action command in the HTML form. For example, the line would be:

<form method="POST" action="/cgi-bin/cgitest.exe">

instead of the "mailto" syntax in our example above.  Sometimes users don't want to be running an .exe file. It is customary to just rename your .exe file with a .cgi extension instead (ie., "cgitest.exe" becomes "cgitest.cgi").  And a user can type in the web browser http:/TheWebsite.com/cgi-bin/cgitest.cgi?name=user

The program runs as expected, at least on Microsoft IIS 2003 server... This is just a small example of how CGI programs can be created. Take the time to read through the CGI.INC file and you will see a list of environment variables that you can use. Display them in a test CGI program and discover useful ways to work with them. For example, it is possible to identify the address of the person completing the web form, along with other meaningful information.

The Test Environment

First you can set up your computer on a network to be a web server see (http://www.w3schools.com/html/html_webserver.asp). A decent way to setup a local development environment is to use the Xitami webserver (or the MS personal web server) running on your local Win95/98/NT work station.  Put the .EXE file in the CGI-BIN directory and the .HTM file in the "webpages" directory.  Point your browser at http://11.22.33.44/formtest.htm  (where 11.22.33.44 is your local IP address) and "formtest.htm" is the web page form.

In this fashion, your single PC is the client and the server and you can edit, compile, and run the program quickly without having to FTP programs and web pages to your "live" web server.  You can also setup a one line batch file in your path as follows:

rcc.bat
rc.exe -Ic:\progra~1\rapidq -Lc:\progra~1\rapidq  %1

This way you can be in the cgi-bin directory editing your program and compiling it with the statement "rcc cgidemo.bas".  The "bat" file will look to your rapidq directory for the include and library files. Note that rc.exe also needs to be in your path. Remember to "refresh" your browser screen if you make any changes to the web page.

here is another way to test your RapidQ program as a server (Basically your code has a QSocket.Open(80) command)
1)  run your code on the computer 
2) start your browser (MS internet explorer,,)
3) type http://localhost in the address (this is the 'loopback' address)
4) watch the output on the RQ server program
5) hit the cancel on the browser to disconnect from the server

Testing your own code

You can test all that on your own computer using the 'Localhost' (known as the loopback address). When you want to connect one of your home brewed servers, run it and connect it with your client by simply connecting 'Localhost', both client and server running on the same computer: yours. What works that way will work in the 'real world'.

How do you debug your CGI code ? There is no simple way, but you can create a QStringList at the beginnning of your code and store the results.
Dim MyDebug as QstringList     'at begining on your CGI
MyDebug.Additems ("reached line #34")     'or whatever you need to know
...
Before closing CGI application do 
MyDebug.SaveToFile ("Output.txt")       'enter the file name you need here


Maintaining a session with a user

Remember your CGI program runs once then terminates. How do you establish a continual session with a specific user, say someone who is logged in?
The answer is to either use a cookie with some identifier/session data in it or to add a special parameter to the query string. However, you have to have some way of persisting the session data on the server between requests. Often, a special table in a database is used. There also has to be some way of handling expired/abandoned sessions. For instance, if you stored data in a QFileStream, then since each request has its own invocation of your application and since different invocations have different memory spaces, a QFILESTREAM in one invocation is completely separate from one in another invocation. Think of it this way: the web server will create an array of program instances to handle the various requests.

Using one file for keeping track of users can be a bottleneck. Consider 5 clients accessing the CGI program relatively simultaneously (within a
few milliseconds of each other). The first request would be handled just fine, but subsequent requests would be handled either serially or
not at all (depending upon how your program handles a locked file). You could implement a system similar to what PHP does (under the
default configuration).-- Each user is assigned a unique session token which is stored on the client's computer as a temporary cookie or is passed as an extra parameter in URLs. On the server, there is a directory for storing session data. In the directory, each session id has its own file containing the session data. That way, when multiple requesting come in simultaneously, your program can handle them because it is opening different file each time.

Basically, your program needs to be both stateless This last requirement is oddly
similar to writing certain kinds of multi-threaded applications.


Helpful links
Intro to CGI
http://www.jayeckles.com/cgi/
http://www.cgi101.com/book/
http://www.eps.surrey.ac.uk/FAQ/cgitutor/

What the heck are IPs?
http://www.bleepingcomputer.com/tutorials/

Socket tutorial
http://www.exegesis.uklinux.net/gandalf/winsock/



How to generate a web form on a single Windows computer (not via the internet)

$ESCAPECHARS ON
$TYPECHECK ON
$INCLUDE "RAPIDQ.INC"
Declare Sub OnClose_frmRapidView
'
Sub DummyProc
End Sub

CREATE frmRapidView AS QFORM
    Autoscroll = false
    WindowState = wsMaximized
    Caption = "Demo RQ & HTML"
    Visible = True 
    WndProc = DummyProc  'this just minimizes to taskbar
CREATE msHTML AS QOLECONTAINER
    Align = alClient
    createObject("Shell.Explorer.1")
END CREATE
    Visible = False
    Onclose = OnClose_frmRapidView
END CREATE

DIM c as string             'this is our html code
c = "about:<head><basefont face=\"courrier\"></head><body scroll=\"no\">"
c = c + "<center><br><h1>Text Out, Anything you want<BR> One more line<br>WILL BE TOO SLOW</center></h1>"
c = c + "<H2><HR><U>GOOD LUCK</U></H2></basefont></head>"
msHTML.Navigate(c)

Sub OnClose_frmRapidView
   application.Terminate
   End
End Sub
'
' ***RUN IT *********************************
frmRapidView.ShowModal


created by "Scott" 4/21/00 SandDune@maine.rr.com
Chris W. and Jacques P. and John K