Building a CGI Web Server Application

Building a CGI Web Server Application

Top  Previous  Next

 

There are advanced third party web based packages available for QM such as Pavuk or DesignBais but for many applications use of built-in QM features may be sufficient. See Simple Web Services for more information.

 

Alternatively, a simple CGI program gives an easy way to achieve web connectivity with no additional software as described below. This uses the QMClient API which can also be called from PHP, ASP, etc for alternative development styles.

 

See "Using the C API" in the QMClient API section for details of libraries that must be included when this application is compiled and linked. The executable program file should be placed in the cgi-bin subdirectory of the relevant web account.

 

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <\qmsys\syscom\qmclilib.h>       [ Windows ]

#include </usr/qmsys/SYSCOM/qmclilib.h>   [ Unix/Linux based systems ]

 

// Set the following six lines as appropriate for your system

#define SERVER_ADDRESS "localhost" /* Server network address or name... */

#define SERVER_PORT 4243           /* ...and port on which to connect */

#define SERVER_USER "xxx"          /* Server user name... */

#define SERVER_PASSWORD "xxxxx"    /* ...and password */

#define SERVER_ACCOUNT "xxxxx"     /* Web server QM account name */

#define SERVER_PROGRAM "xxxxx"     /* Catalogued program to run on server */

 

char NullString[] = "";

 

char InputData[32767] = "";   /* Incoming data stream */

char Response[99999] = "";    /* Response to send */

char * ClientIP;              /* Client IP address */

char * ClientUser;            /* User name if web authentication used */

 

/* ====================================================================== */

 

int main()

{

char * RequestMethod;

char * p;

 

RequestMethod = getenv("REQUEST_METHOD");

if (RequestMethod == NULL)

 {

  printf("Program must be executed by a Web browser\n");

  return 1;

 }

 

ClientIP = ((p = getenv("REMOTE_ADDR")) != NULL)?p:NullString;

ClientUser = ((p = getenv("REMOTE_USER")) != NULL)?p:NullString;

 

if (!strcmp(RequestMethod,"GET"))

 {

  if ((p = getenv("QUERY_STRING")) != NULL) strcpy(InputData, p);

 }

else if (!strcmp(RequestMethod,"POST"))

 {

  if ((p = getenv("CONTENT_LENGTH")) != NULL)

   {

    fread(InputData, atoi(p), 1, stdin);

   }

 }

 

/* Check for locally processed screens */

 

ParseInputData();

 

if (!QMConnect(SERVER_ADDRESS, SERVER_PORT, SERVER_USER, SERVER_PASSWORD, SERVER_ACCOUNT))

 {

  strcpy(Response, "Failed to connect. The server may be offline.");

 }

else

 {

  QMCall(SERVER_PROGRAM, 4, InputData, ClientIP, ClientUser, Response);

  QMDisconnect();

 }

 

printf("Content-type: text/html\n\n");

printf("<meta http-equiv=\"Pragma\" content=\"no-cache\">\n");

printf("%s\n", Response);

 

return 0;

}

 

Web requests received by this program will be passed to the catalogued QMBasic subroutine identified by SERVER_PROGRAM. The declaration of this is

SUBROUTINE SERVER(INPUT.DATA, CLIENT.IP, CLIENT.USER, RESPONSE)

 

When used with HTML forms with the method attribute set to "post" or "get", any data sent with the form will be passed to the QM server program via the first argument (INPUT.DATA). Typically, the form would include an item in this data that can be used to determine the screen being processed.

 

The CLIENT.IP is the network address of the client user and can be used for simple security checking.

 

If the user has been authenticated using the conventional web user authentication process, the user name appears in CLIENT.USER. If authentication has not been performed, this will be a null string.

 

The server subroutine must return valid HTML data to be returned to the web client via the RESPONSE argument. For applications that return very large HTML strings it may be better for the QMBasic component to write the data to a temporary file and pass this name back to the C program. This avoids the need for the Response variable in the above example to be sized to fit the largest string that could ever be returned.