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