/* BaseSBFunction
The idea here is to put the functions that are common to Action,
Sender and Check into one class.
*/
#include "BaseSBModule.h"
#include <ctype.h>
#include <vector>
#include <string>
#include "DBError.h"
#include "DBHandler.h"
#include "DBGateFactory.h"
#include "Log.h"
#ifdef USE_CORBA
#include "CorbaCltSwap.h"
#endif
extern Log * logObject;
using namespace std;
extern EnvObject * userdata;
extern DBGate * dbgate;
extern StringDBGatePtrVHMap dbgates;
extern HTTP * http;
extern Security * security;
extern Configuration * config;
extern RegExp * reVar;
extern string LoopKey;
BaseSBModule::BaseSBModule () {
#ifdef MEM_TEST
cout << "creating BaseSBModule " << this << "\n";
#endif
flag = 47;
UserInfo ui = security->getUserInfo();
if (ui.username == "") {
cout <<"No username was specified!"<<flush;
exit(1);
}
if (ui.institution == "") {
cout <<"No institution was specified!"<<flush;
exit(1);
}
//lets init the Swap!
#ifdef USE_CORBA
swap = new CorbaCltSwap(ui.username, ui.institution);
#else
swap = new Swap (ui.username, ui.institution);
#endif
// now that we have a swap, create a LoopInfo
loopinfo = new LoopInfo (swap, security->getLastPage());
parser = new SBParser();
}
BaseSBModule::~BaseSBModule () {
#ifdef MEM_TEST
cout << "deleting BaseSBModule " << this << "\n";
#endif
delete (swap);
delete (parser);
delete (loopinfo);
// MEMORY LEAK?
// delete (reVar);
}
void BaseSBModule::outputErrors(Error & err, string & title){
StringDLList::iterator i;
cout << "Content-type: text/html\n\n<CENTER><table border=1>\n";
if (err.numFatalErrors() > 0){
StringDLList *e = &(err.fatalErrors());
cout << "<tr bgcolor=red><td><font color=white><b>Fatal Errors - " <<
title << "</b></font></td></tr>\n";
for(i=e->begin();i!=e->end();i++){
cout << "<tr bgcolor=white><font color=black><td>\n"
<< (*i) << "</td></font></tr>\n";
}
}
if (err.numWarnings() > 0){
StringDLList *e = &(err.warnings());
cout << "<tr bgcolor=red text=white><td><font color=white><b>Warnings - " <<
title << "</b></font></td></tr>\n";
for(i=e->begin();i!=e->end();i++){
cout << "<tr bgcolor=white fgcolor=black><td><font color=black>\n"
<< (*i) << "</font></td></tr>\n";
}
}
cout << "</table></center>\n";
}
void BaseSBModule::callFunction (SGMLToken * t, Error & err) {
string command = t->command();
if (command == "ASSIGN" && (t->type()) == SGML_TAG_TYPE) {
processASSIGN ( (SGMLTag *) t, err);
} else if (command == "IF" && (t->type()) == SGML_IF_TYPE) {
processIF ( (SGMLIf *) t, err);
} else if (command == "SQL" && (t->type()) == SGML_TAG_TYPE) {
processSQL ( (SGMLTag *) t, err);
} else if (command == "LOAD" && (t->type()) == SGML_TAG_TYPE) {
processLOAD ( (SGMLTag *) t, err);
} else if (command == "SAVE" && (t->type()) == SGML_TAG_TYPE) {
processSAVE ( (SGMLTag *) t, err);
} else if (command == "CLEAR" && (t->type()) == SGML_TAG_TYPE) {
processCLEAR ( (SGMLTag *) t, err);
} else if (command == "COPY" && (t->type()) == SGML_TAG_TYPE) {
processCOPY ( (SGMLTag *) t, err);
} else if (command == "ACCUMULATE" && (t->type()) == SGML_TAG_TYPE) {
processACCUMULATE ( (SGMLTag *) t, err);
} else if (command == "DEFFUN" && (t->type()) == SGML_CONTAINER_TYPE) {
processDEFINE ( (SGMLContainer *) t, err);
} else if (command == "INCLUDE" && (t->type()) == SGML_TAG_TYPE) {
processINCLUDE ( (SGMLTag *) t, err);
} else if (command == "DECLARE" && (t->type()) == SGML_TAG_TYPE) {
processDECLARE ( (SGMLTag *) t, err);
} else if (command == "EVAL" && (t->type()) == SGML_CONTAINER_TYPE) {
processEVAL ( (SGMLContainer *) t, err);
} else if (command == "LOOP" && (t->type()) == SGML_CONTAINER_TYPE) {
processLOOP ( (SGMLContainer *) t, err);
} else if (command == "EMAIL" && (t->type()) == SGML_TAG_TYPE) {
processEMAIL ( (SGMLTag *) t, err);
} else if (command == "DBCONNECT" && (t->type()) == SGML_TAG_TYPE) {
processDBCONNECT ( (SGMLTag *) t, err);
} else if (command == "ABORT" && (t->type()) == SGML_TAG_TYPE) {
processABORT ( (SGMLTag *) t, err);
} else if (command == "LOGOUT" && (t->type()) == SGML_TAG_TYPE) {
processLOGOUT ( (SGMLTag *) t, err);
} else if (command == "LOG" && (t->type()) == SGML_TAG_TYPE) {
processLOG ( (SGMLTag *) t, err);
} else {
processGeneric (t, err);
}
}
/* This function looks at a SGMLTag and tries to determine what keys
getList would use in the AssocArrays that it returns.
It will try to preserve the proper order whenever possible, so for
SQL data sources, it will put the first column of the query
in the first slot in the resArray */
/* NOTE: this will bomb on a SELECT * FROM ... */
void BaseSBModule::orderDataSource (SGMLTag * t, string * resArray) {
AssocArray * flags = & (t->flags());
int i;
if ((*flags).contains("VALUE") ) {
//only one col allowed with values, and they will be called 'value'
resArray[0] = "value";
// blank out the rest
for (i =1; i <MAX_ORDER_DATA_SOURCE; i++) {
resArray[i] = "";
}
/* } else if ( (*flags).contains("SQL") ) {
* string sql;
* // Regex comma ("[ \n\t]*,[ \n\t]", 1);
* RegExp comma ("[ \n\t]*,[ \n\t]", 1);
* sql = (*flags)["SQL"];
*
* sql = sql.after (Regex ("[ \n\t][Ss][Ee][Ll][Ee][Cc][Tt][ \n\t]*"));
* sql = sql.before (Regex ("[ \n\t]*[Ff][Rr][Oo][Mm][ \n\t]"));
*
* // now we should have what is after the select and before the from
* // so we will split apart the col names
*
* for (i =0;i < MAX_ORDER_DATA_SOURCE; i++) {
* if (sql.contains(comma)) {
* // resArray[i] = sql.before (comma);
*
* // sql = sql.after (comma);
*
* } else {
* resArray[i] = sql;
* sql = "";
* }
* }
*/
} else {
// I don't know this data source
for (i=0; i < MAX_ORDER_DATA_SOURCE ; i++) {
resArray[i]="";
}
}
}
/* takes as input a base array name....this function will check
with the env object to see if there is a named array with the
loop key appended to the base_name, and if there is, the
key appended string will be returned. Otherwise the
inputed string is returned
*/
string findFullArrayName (string & base_name) {
string candidate;
candidate = base_name + LoopKey;
if (userdata->arrayGetLength(candidate) > 0) {
return candidate;
}
return base_name;
}
/* A token can specify data in four ways (these are the 'datasources'):
1) VALUE = "string" [SPLIT="splitchar"]
2) SQL="select ..."
3) ARRAY=arrayname [SPLIT="splitchar"]
4) FOR="num,num" [STEP=num]
This function will look at the token and spit out the proper data
list as an AssocArrayVec.
If specified, the Result object is placed in result (in which
case the user is responsible to delete it). It can then be used
to get the column names. If an SQL query is not done, and result
is specified, it is set to NULL.
The first column of all datasources is copied to the "value" index
of the returned associative arrays.
If an SQL statement is given, any variables in the SQL statement
originating from the "this" or "last" namespace is automatically
quoted.
*/
AssocArrayPtrDLList * BaseSBModule::getList (SGMLTag * t,
Result ** result) {
AssocArrayPtrDLList * aal = new AssocArrayPtrDLList();
AssocArray * flags = & (t->flags());
// If specified, first set result to NULL (and set it to something
// else only if used
if (result != NULL){
(* result) = NULL;
}
// I would use a switch, but (at least in C) you can't switch on
// complex types.
if (flags->contains("VALUE")) {
AssocArray * v;
string s;
interpretString ((*flags)["VALUE"], s);
// should we split the string?
if ( flags->contains("SPLIT") ) {
string split = (*flags)["SPLIT"];
while (s.find (split) != string::npos) {
// string item = s.before (split);
string item = s.substr (0, s.find (split.c_str()));
v = new AssocArray();
(*v)["value"] = item;
(*v)["0"] = item;
(*v)["LENGTH"] = string("1");
aal->push_back(v);
// s = s.after (split);
s = s.substr ( s.find(split.c_str()) + split.length() );
}
}
v = new AssocArray();
(*v)["value"] = s;
(*v)["0"] = s;
(*v)["LENGTH"] = string("1");
aal->push_back ( v );
} else if ( flags->contains("FOR") ) {
AssocArray * v;
string s;
string start, end;
int first, last, step, i;
char temp [255];
interpretString ((*flags)["FOR"], s);
// split the FOR on a comma
// start = s.before (",");
start = s.substr (0, s.find(","));
// end = s.after (",");
end = s.substr (s.find (",") +1);
first = atoi (start.c_str());
last = atoi (end.c_str());
step = atoi ( (interpretString ((*flags)["STEP"])).c_str() );
if (step == 0)
step = 1;
if (step > 0) {
// count up
for (i = first; i <= last; i += step) {
sprintf (temp, "%d", i);
v = new AssocArray();
(*v)["value"] = temp;
(*v)["0"] = temp;
(*v)["LENGTH"] = string("1");
aal->push_back ( v );
}
} else {
//count down
for (i = first; i >= last; i += step) {
sprintf (temp, "%d", i);
v = new AssocArray();
(*v)["value"] = temp;
(*v)["0"] = temp;
(*v)["LENGTH"] = string("1");
aal->push_back ( v );
}
}
} else if ( flags->contains("SQL") ) {
Result * r = NULL;
AssocArray * row;
AssocArray * row_clone;
string iSQL;
string a;
string b;
string key;
//next three used for bind vars
char * bound_strings[10];
string variable_names[10];
int bind_count = 0;
string db = interpretString ((*flags)["DB"]);
DBGate * mydbgate = NULL;
/* Determine the DB connection name */
if (db == ""){
db = "default";
}
mydbgate = dbgates[db];
if (mydbgate == NULL){
throw ("Unknown DB connection name");
}
a = (*flags)["SQL"];
b = iSQL;
interpretString (a, iSQL, TYPE_SQL, mydbgate);
// do the SQL
logObject->add ("sqllog", iSQL);
try {
mydbgate->setSQL ( iSQL.c_str() );
//are there any args to bind?
if (flags->contains("ARGS")) {
string split (",");
string args = (*flags)["ARGS"];
while (args.length() > 0) {
string s;
if (args.find(",") != string::npos) {
variable_names[bind_count] = args.substr (0, args.find(","));
bound_strings[bind_count] = strdup ( (interpretString (variable_names[bind_count])).c_str() );
} else {
variable_names[bind_count] = args;
bound_strings[bind_count] = strdup ( (interpretString (args)).c_str() );
}
if (args.find(",") != string::npos) {
args = args.substr (args.find(",") + 1);
} else {
args = "";
}
bind_count ++;
}
mydbgate->bind(bound_strings, bind_count);
}
mydbgate->execute();
if (! flags->contains("NOFETCH") ) {
r = mydbgate->getResult();
char num_cols[16];
sprintf(num_cols,"%d",r->numColumns());
while ( (row = r->nextRow()) != NULL) {
row_clone = new AssocArray ();
(*row_clone) = (*row);
// Also copy the first row to "value"
(*row_clone)["value"] = (*row)[r->columnName(0)];
(*row_clone)["LENGTH"] = num_cols;
aal->push_back (row_clone);
delete(row);
}
if (result != NULL){ /* If the user wants it, let them delete it */
(* result) = r;
} else if (r != NULL) {
delete(r);
}
}
}
catch (DBError dbe) {
//clean up after ourselves
delete (aal);
if (r != NULL) {
delete (r);
}
throw (dbe);
}
} else if ( flags->contains("ARRAY") ) {
int i;
AssocArray * v;
if (flags->contains("SPLIT")){
// Multiple columns so let's pull them out
StringDLList arrays;
StringDLList::iterator i;
int length;
string s;
s = (*flags)["ARRAY"];
string split = (*flags)["SPLIT"];
while (s.find (split) != string::npos) {
string item = s.substr (0, s.find (split.c_str()));
// check the loop key
item = findFullArrayName (item);
arrays.push_back(item);
s = s.substr ( s.find(split.c_str()) + split.length() );
}
s = findFullArrayName(s);
arrays.push_back(s);
// Now get the max length
int num_arrays = 0;
length=0;
for (i=arrays.begin(); i != arrays.end(); i++){
num_arrays++;
int nl = userdata->arrayGetLength(*i);
if (nl > length) length = nl;
}
int n;
char lenstr[7];
sprintf(lenstr,"%d",num_arrays);
for(n=0;n<length;n++){
char colstr[7];
int c = 0;
v = new AssocArray();
for (i=arrays.begin(); i != arrays.end(); i++){
sprintf(colstr,"%d",c);
(*v)[colstr] = userdata->arrayGetVal((*i),n);
if (c == 0){
(*v)["value"] = (*v)[colstr];
}
c ++;
}
(*v)["LENGTH"] = lenstr;
aal->push_back(v);
}
} else {
string name = findFullArrayName ((*flags)["ARRAY"]);
int length = userdata->arrayGetLength(name);
for(i=0;i<length;i++){
v = new AssocArray();
(*v)["value"] = userdata->arrayGetVal(name,i);
(*v)["0"] = (*v)["value"];
(*v)["LENGTH"] = string("1");
aal->push_back(v);
}
}
}
return (aal);
}
string BaseSBModule::interpretString (string & s) {
string r;
EvalInterpretString(s,r);
return(r);
}
void BaseSBModule::interpretString (string & s, string & result) {
// call basic interpreter
result = interpretString (s);
}
void BaseSBModule::interpretString (string & s, string & result, int type,
DBGate * mydbgate) {
if (type == TYPE_SQL) {
if (mydbgate != NULL){
EvalInterpretString(s,result,mydbgate);
} else {
EvalInterpretString(s,result,dbgate);
}
} else {
// I don't know this type
interpretString (s, result);
}
}
/*
@DATASOURCE
@USEHTML
@usage <ACCUMULATE NAME="array name"
@usage (ARRAY="array name" |
@usage FOR="number,number" [STEP="number"] |
@usage SQL="SQL statement" [DB="connection_name"] |
@usage VALUE="string or list" [SPLIT="delimiter"])>
@FLAG NAME Name of array to which values are added
@FLAG ARRAY Name of array from which values are added
@IFLAG FOR Starting and ending numbers through which to iterate
@IFLAG STEP Size to skip between the FOR range (default 1)
@IFLAG SQL Select statement whose first column is inserted into the array
@IFLAG DB DB connection name
@IFLAG VALUE A single value or a list inserted into the array
@FLAG SPLIT String used to separate elements in the value list
@sections ALL
@index Adds values to an array from the first column of a datasource.
Adds values to an array from the first column of a datasource. New
entries from the datasource are appended to the end of the array,
leaving the existing entries alone.
@ex <!-- The following reads all the usernames from
@ex the table "AuthUsers" into the array "users"
@ex and then prints them out in a loop -->
@ex
@ex <ACCUMULATE NAME=users
@ex SQL="select username from AuthUsers">
@ex The number of users are:
@ex <replace value="$users{LENGTH}">.
@ex They are:
@ex <loop name=userloop array=users>
@ex <replace value="$userloop{value}, ">
@ex </loop> */
void BaseSBModule::processACCUMULATE ( SGMLTag * t, Error & err) {
AssocArray * flags = & (t->flags());
string name = (*flags)["NAME"];
AssocArrayPtrDLList * results;
AssocArray * temp;
int index;
if (name.length() == 0) {
err.addWarning ("There is an ACCUMULATE tag without a name flag.");
}
try {
results = getList (t);
}
catch (DBError dbe) {
if (!handle (dbe, err)) {
throw ("Unhandled exception thrown by ACCUMULATE tag");
} else {
results = new AssocArrayPtrDLList();
}
}
index = userdata->arrayGetLength(name);
if (results != NULL) {
while (! results->empty()) {
temp = results->front();
results->pop_front();
if (temp != NULL) {
userdata->arraySetVal(name,index,(*temp)["value"]);
index ++;
delete(temp);
}
}
delete (results);
}
}
/*
@CONTAINER
@TAGNAME DEFFUN
@USEHTML
@seealso _EVAL_
@usage <DEFFUN FUNC="$var=funcname($argv1,$argv2,...)">
@FLAG FUNC Prototype the function in the form: $var = funcname ($arg1, $arg2, ...)"
@sections ALL
@index Creates a new function which can be called from Eval blocks.
Creates a new function which can be called from Eval blocks. In the
FUNC flag, the variable name to the left of the equal sign is the
return value of the function. Whatever value is in that variable when
the control flow reaches the end of the DEFFUN container is the return
result of the function. The function name is case insensitive, and the
argument names are meant to be descriptive and do not need to
correspond to any actual variables.
@ex <!-- Define a function to concatenate two stsrings -->
@ex <DEFFUN FUNC="$result = concatenate ($first, $second)">
@ex <assign name=$result value="$first$second">
@ex </DEFFUN>
@ex
@ex <eval>
@ex $a = "hi ";
@ex $c = concatenate ($a, 'there');
@ex print ($c); // will print the string "hi there"
@ex </eval> */
void BaseSBModule::processDEFINE (SGMLContainer * t, Error & err) {
if ( !(t->flags().contains("FUNC")) ) {
err.addWarning ("DEFINE tag without FUNC flag");
} else {
userdata->setFun (t);
}
}
/*
@USEHTML
@seealso _SAVE_ _CLEAR_
@usage <LOAD NAME=namespace
@usage (NAMESPACE=namespace |
@usage ARRAY=array )>
@FLAG NAME The namespace or array into which data is placed
@FLAG NAMESPACE The name of the namespace to be restored
@FLAG ARRAY The name of the array to be restored
@sections ALL
@INDEX Retrieves previously saved data from the Swap into a namespace or an array.
Retrieves previously saved data from the Swap into a namespace or
an array. The Swap is a server-side repository of data associated
with a particular user. Data is saved to the Swap with the
_SAVE_ command, or deleted with the
_CLEAR_ command. Any data already in the given
namespace or array is cleared.
@ex <assign name="$preferences{greeting}" value="Hi!">
@ex
@ex <!-- Store preferences to the Swap -->
@ex <SAVE NAMESPACE=preferences>
@ex
@ex <!-- Meanwhile, in a different file ... -->
@ex <LOAD NAMESPACE=preferences>
@ex <!-- The following prints "Hi!" even in a separate file -->
@ex <replace value="$preferences{greeting}"> */
void BaseSBModule::processLOAD ( SGMLTag * t, Error & err) {
AssocArray flags = t->flags();
string nspace;
nspace = (t->flags())["NAMESPACE"];
if (nspace.length() == 0){
nspace = (t->flags())["ARRAY"];
}
if (nspace.length() > 0){
userdata->setNameSpace (nspace, swap->load(nspace));
} else {
err.addWarning ("Missing namespace or array flag in LOAD tag");
}
}
/*
@USEHTML
@SEEALSO _LOAD_ _CLEAR_
@usage <SAVE (NAMESPACE=namespace |
@usage ARRAY=array )>
@FLAG NAMESPACE The name of the namespace to be stored
@FLAG ARRAY The name of the array to be stored
@SECTIONS ALL
@INDEX Stores a namespace or an array into the Swap.
Stores a namespace or an array into the Swap. The Swap is a
server-side repository of data associated to a particular user
(therefore data is not shared between users). Data can later be
retrieved from the Swap with the _LOAD_ command or deleted with the
_CLEAR_ command.
@ex <ACCUMULATE NAME=users
@ex SQL="select username from AuthUsers">
@ex <!-- Store an array into the Swap -->
@ex <SAVE ARRAY=users>
@ex
@ex <!-- Meanwhile, in a different file ... -->
@ex <LOAD ARRAY=users>
@ex <LOOP name=userloop ARRAY=users>
@ex <replace value="$userloop{value}, ">
@ex </LOOP> */
void BaseSBModule::processSAVE ( SGMLTag * t, Error & err) {
string nspace;
nspace = (t->flags())["NAMESPACE"];
if (nspace.length() == 0){
nspace = (t->flags())["ARRAY"];
}
if (nspace.length() > 0){
AssocArray * ns = (*userdata).getNameSpace(nspace);
if (ns != NULL) {
swap->save (nspace, *ns );
}
} else {
err.addWarning ("Missing namespace or array flag in SAVE tag");
}
}
/*
@USEHTML
@SEEALSO _LOAD_ _SAVE_
@usage <CLEAR (NAMESPACE=namespace |
@usage ARRAY=array )>
@FLAG NAMESPACE The name of the namespace to be deleted
@FLAG ARRAY The name of the array to be deleted
@SECTIONS ALL
@INDEX Clears out an array or namespace from memory and the Swap.
Clears out an array or namespace from memory and the Swap. The Swap
is a server-side repository of data associated to a particular user
(therefore data is not shared between users). Data can be stored
with _SAVE_ and retrieved with _LOAD_.
@ex <ACCUMULATE NAME=users
@ex SQL="select username from AuthUsers">
@ex <!-- Store an array into the Swap -->
@ex <SAVE ARRAY=users>
@ex
@ex <!-- Meanwhile, in a different file ... -->
@ex <CLEAR ARRAY=users>
@ex <!-- Now users is empty and LOAD with users will
@ex return nothing -->
*/
void BaseSBModule::processCLEAR ( SGMLTag * t, Error & err) {
string nspace;
nspace = (t->flags())["NAMESPACE"];
if (nspace.length() == 0){
nspace = (t->flags())["ARRAY"];
}
if (nspace.length() > 0){
AssocArray * empty = new AssocArray();
userdata->setNameSpace(nspace,empty);
swap->clear (nspace);
} else {
err.addWarning ("Missing namespace or array flag in CLEAR tag");
}
}
/*
@USEHTML
@SEEALSO _ACCUMULATE_
@usage <COPY SOURCE=name DESTINATION=name>
@FLAG SOURCE Namespace or array to be copied
@FLAG DESTINATION Namespace or array into which the copy is placed
@SECTIONS ALL
@INDEX Copies an array or namespace.
Copies an array or namespace from the SOURCE to the DESTINATION.
If the source is an array, the data overwrites the destination
array. If the source is a namespace, matching keys shared between
the source and destination will be overwritten, but other keys will
be maintained.
@ex <assign name="$a{a}" value="Hi">
@ex <assign name="$a{b}" value="Rob">
@ex <assign name="$b{b}" value="Kim">
@ex <copy source=b destination=a>
@ex <replace value="$a{a} $a{b}"> <!-- Hi Kim -->
*/
void BaseSBModule::processCOPY ( SGMLTag * t, Error & err) {
string source, dest;
source = (t->flags())["SOURCE"];
dest = (t->flags())["DESTINATION"];
if ((source.length() > 0) && (dest.length() > 0)){
AssocArray * aapSource = userdata->getNameSpace(source);
if (aapSource != NULL)
userdata->appendToNameSpace(dest, aapSource);
} else {
err.addWarning ("Missing source or destination flag in COPY tag");
}
}
/*
@USEHTML
@SEEALSO _REPLACE_ _ASSIGN_
@usage <DECLARE NAME="$var"
@usage (VALUE="value")>
@FLAG NAME Variable declared in the local scope.
@IFLAG VALUE Value to set the variable. (Defaults to "".)
@SECTIONS ALL
@INDEX Declares a variable in the current scope.
Declares a variable in the current scope. New scopes occur within
_LOOP_ and _DEFFUN_ containers.
@ex <assign name=$var value="global">
@ex <replace value="$var "> <!-- global -->
@ex <loop value="1" name="x">
@ex <replace value="$var "> <!-- global -->
@ex <DECLARE name=$var value="local">
@ex <replace value="$var "> <!-- local -->
@ex </loop>
@ex <replace value="$var "> <!-- global -->
*/
void BaseSBModule::processDECLARE ( SGMLTag * t, Error & err) {
string name;
string val;
if ((t->flags()).contains ("NAME") ) {
name = (t->flags())["NAME"];
if ( (t->flags()).contains ("VALUE")) {
interpretString ((t->flags())["VALUE"], val);
} else {
val = "";
}
userdata->declareVar (name, val);
} else {
err.addWarning ("Missing name flag in DECLARE tag");
}
}
/*
@USEHTML
@SEEALSO _REPLACE_ _DECLARE_
@usage <ASSIGN NAME="$variable"
@usage VALUE="interpreted string or Eval block"
@usage [EVAL]>
@FLAG NAME The variable assigned
@IFLAG VALUE Interpreted string or Eval block whose value is stored in the variable
@FLAG EVAL If given, the VALUE is passed through the Eval language interpreter
@SECTIONS ALL
@INDEX Assigns data to a variable.
Assigns data to a variable. By default, the VALUE passes through simple
string interpretation. If the EVAL flag is given, the VALUE flag is instead
passed through the Eval interpreter.
@ex <assign name=$a value="Hello World">
@ex <replace value="$a "> <!-- "Hello World " -->
@ex <assign name=$a value="substr($a,3,2)" EVAL>
@ex <replace value="$a "> <!-- "lo" -->
*/
void BaseSBModule::processASSIGN ( SGMLTag * t, Error & err) {
string name;
string val;
if ((t->flags()).contains ("NAME") && (t->flags()).contains ("VALUE") ) {
name = (t->flags())["NAME"];
val = (t->flags())["VALUE"];
if ((t->flags()).contains("EVAL")){
evalString(val,val,err,t->linebegin());
} else {
interpretString (val,val);
}
//Get the lValue of the variable
EvalLValue(name,name,err,t->linebegin());
if (name != ""){
userdata->setVar (name, val);
}
} else {
if (!(t->flags()).contains ("NAME") )
err.addWarning ("Missing name flag in ASSIGN tag");
if (!(t->flags()).contains ("VALUE") )
err.addWarning ("Missing value flag in ASSIGN tag");
}
}
/*
@usehtml
@usage <LOG TEXT="logtext">
@IFLAG TEXT Text that will be appended to the file given in the default-log configuration variable
@SECTIONS ALL
@INDEX Logs a string into a text file on the server.
Logs a string into a text file on the server. The data to be logged
comes from the TEXT flag, and it is appended to the file named in the
DefaultLog configuration variable.
@ex <LOG TEXT="I see $USER{username}">
*/
void BaseSBModule::processLOG ( SGMLTag * t, Error & err) {
AssocArray flags = t->flags();
string text = flags["TEXT"];
text = interpretString (text);
FILE *log;
log = fopen ( ( (*config)["defaultlog"]).c_str(), "a");
if (log != NULL) {
fprintf (log, "%s\n", text.c_str());
fclose(log);
}
}
/*
@usehtml
@DATASOURCE
@SEEALSO _LOOP_
@usage <SQL NAME="namespace" [DB="connection_name"]
@usage SQL="SQL statement" [NOFETCH] [DEBUG]
@usage [ARGS="$arg1,$arg2,..."]>
@FLAG NAME Namespace to which the first row (if any) is stored
@IFLAG SQL SQL to be executed
@IFLAG DB DB connection name
@FLAG NOFETCH Indicates no rows will be returned (not needed for insert or update)
@FLAG DEBUG Causes an HTML comment to be printed with the SQL (only in an HTML section)
@IFLAG ARGS Variable names to be bound in the SQL statement separated by commas
@SECTIONS ALL
@INDEX Executes an SQL statement and stores the first row (if any) into the given namespace.
Executes an SQL statement and stores the first row (if any) into the
given namespace. The data is stored into the namespace under the
column names assigned by the database (therefore they may not match
the column names given in the SQL statement). For cross database
compatibility, the data is also stored under the column numbers
(starting with 0) and the number of columns is stored in the key
"LENGTH". Finally, the first column is also stored under the key
"value".
<P>
If the SQL is not an insert or update and will return no rows of data,
the NOFETCH flag should be used. Some, but not all, databases will
generate errors if this is not done. For example, executing stored
procedures with ORACLE will cause an error if the NOFETCH flag is not
used.
<P>
Variables placed in the SQL statement which come from the $this
or $last namespace are considered "tainted" and quotes occuring
in them are automatically escaped depending upon the database used. All
other namespaces are passed to the SQL statement.
<P>
ARGS can be used to specify a list of bind variables to the SQL
statement. The syntax for the location in the SQL statement of the
bound variable depends upon the database.
@ex <sql sql="select USERNAME, PASSWORD from AuthUsers"
@ex name="user">
@ex <replace value="$user{USERNAME} $user{PASSWORD} "><br>
@ex <replace value="$user{0} $user{1} "><br>
@ex <replace value="$user{value}" <!-- same as $user{0} -->
*/
void BaseSBModule::processSQL ( SGMLTag * t, Error & err) {
AssocArray * flags = &(t->flags());
string name;
// String * sql = &(*flags)["SQL"];
AssocArrayPtrDLList * results;
AssocArray * temp = NULL;
if ((*flags).contains("SQL") && (*flags).contains("NAME")) {
name = (*flags)["NAME"];
// cout << "\n***"<<name<<"***\n"; cout.flush();
try {
results = getList (t);
}
catch (DBError dbe) {
if (! handle (dbe, err)) {
throw ("Unhandled exception thrown by sql tag");
} else {
//it was 'handled', so make a bogus empty list and finish out the call
results = new AssocArrayPtrDLList();
}
}
if (! results->empty() ) {
temp = results->front();
results->pop_front();
if (temp) {
userdata->appendToNameSpace (name, temp );
delete (temp);
temp = NULL;
}
}
//there may be lots of data left in this critter, we should free it
#ifdef NO_LEAK
while (! results->empty()) {
temp = results->front();
results->pop_front();
delete(temp);
}
delete (results);
#endif
if ((*flags).contains("DEBUG")) {
cout <<"\n<!--" << interpretString ((*flags)["SQL"]) << " -->";
}
} else {
if (!(t->flags()).contains ("NAME") )
err.addWarning ("Missing name flag in SQL tag");
if (!(t->flags()).contains ("SQL") )
err.addWarning ("Missing sql flag in SQL tag");
}
}
/* @nodoc
inStr should be somehting that looks like "$namespace{key}"
this function will set nspace = "namespace" and key = "key"
*/
void BaseSBModule::splitVar (string & inStr, string & nspace , string & key) {
char c [MAX_VAR_LENGTH];
char ns [MAX_VAR_LENGTH];
char k [MAX_VAR_LENGTH];
int i = 0;
int j = 0;
strcpy (c, inStr.c_str());
for (i =0; i < MAX_VAR_LENGTH ; i++) {
if (c[i] == '$') {
for (i++ , j=0; i <MAX_VAR_LENGTH && c[i] != '{'; i++, j++) {
ns[j] = c[i];
}
ns[j] = '\0';
break;
}
}
for (i++, j=0; i < MAX_VAR_LENGTH && c[i] != '}' ; i++, j++) {
k[j] = c[i];
}
k[j] ='\0';
nspace = ns;
key =k;
}
/*
@usehtml
@usage <IF BOOL="eval phrase">
@usage Then clause
@usage <ELSEIF BOOL="eval phrase">
@usage Elseif clause
@usage <!-- Additional ELSEIF clauses ... -->
@usage <ELSE>
@usage Else clause
@usage </IF>
@EFLAG BOOL Eval boolean expression (false if either "" or "0")
@SECTIONS ALL
@INDEX Allows conditional execution of SteelBlue code.
Allows conditional execution of SteelBlue code. The section between
the initial IF and the following ELSEIF, ELSE, or /IF tag is executed
if the expression in the BOOL tag evaluates to a string other than "" or "0".
Otherwise code between the ELSE (or optional ELSEIF) and /IF is executed.
<P>
_INPUT_ and _TEXTAREA_ elements in both the IF and ELSE sections of an
IF statement are automatically checked for datatypes on the server
when the data is submitted. Therefore _INPUT_ and _TEXTAREA_ elements
in IF statements should not have the REQUIRED flag.
@ex <!-- Determines if a user is in the admin group -->
@ex <if bool="group('admin')">
@ex You are in the admin group!
@ex </if>
@ex
@ex <!-- Does different actions based upon $choice -->
@ex <if bool="$choice eq ''">
@ex You didn't choose anything
@ex <elsif bool="$choice eq 'dog'">
@ex You chose dog
@ex <else>
@ex You chose something else
@ex </if> */
void BaseSBModule::processIF ( SGMLIf * t, Error & err) {
AssocArray & flags = t->flags();
if ( flags.contains( "BOOL")){
if (evalSimpleBool ( (SGMLTag *) t, err )) {
recurseOnTokens (t->thenTokens(), err);
} else {
recurseOnTokens (t->elseTokens(), err);
}
} else {
if (! flags.contains ("GROUP"))
err.addWarning ("No BOOL flag in IF tag.");
}
}
/*
@usehtml
@container
@DATASOURCE
@SEEALSO _SQL_
@usage <LOOP NAME="namespace"
@usage (ARRAY="array list" [SPLIT="delimiter"] |
@usage FOR="number,number" [STEP="number"] |
@usage SQL="SQL statement" [DB="connection_name"] |
@usage (BOOL|WHILE)="Boolean statement"
@usage VALUE="string or list" [SPLIT="delimiter"])
@usage [SAVELOOPINFO]>
@usage ...
@usage </LOOP>
@FLAG NAME Name of array to which data rows are copied
@FLAG ARRAY Array name or list from which rows are copied
@IFLAG FOR Starting and ending numbers through which to iterate
@IFLAG STEP Size to skip between the FOR range (default 1)
@IFLAG SQL Select statement from which rows are copied
@EFLAG BOOL Loop is repeated while the statement is true
@EFLAG WHILE Loop is repeated while the statement is true
@IFLAG DB DB connection name
@IFLAG VALUE A single value or a list from which values are copied
@FLAG SPLIT String used to separate elements in the value or array list
@SECTIONS ALL
@INDEX Iterates over multiple rows of data.
Iterates over multiple rows of data. For each row of
data, the namespace given in NAME is populated with the data and the
code in the LOOP container is executed. For SQL statements, the data
is stored into the namespace under the column names assigned by the
database (therefore they may not match the column names given in the
SQL statement). For all datasources, the data is stored under the
column numbers (starting with 0) and the number of columns is stored
in the key "LENGTH". Finally, the first column is also stored under
the key "value".
<P>
Variables placed in the SQL statement which come from the $this
or $last namespace are considered "tainted" and quotes occuring
in them are automatically escaped depending upon the database used. All
other namespaces are passed to the SQL statement.
<P>
Form elements placed inside a LOOP automatically have a loop name and
number appended to their name (this can be overridden for _INPUT_ boxes
with the STATIC flag). This allows input boxes to be easily manipulated
within LOOPS.
<P>
LOOPS placed in the HTML section can be replayed in the HTML, CHECK, and
ACTION sections. This is done by using a LOOP tag with the same namespace
as the original LOOP, but with no datasource. A replayed loop does not
populate the namespace with data, but is used to access user data created
by form elements in loops. If loops are nested, the flag SAVELOOPINFO is
required in the inner loops so that replay data is stored.
@ex <HTML>
@ex <BODY>
@ex <SHOWERRORS>
@ex <SBFORM name="myform" SCREEN="welcome.sb">
@ex <!-- Create an edit box for every user -->
@ex <LOOP SQL="select USERNAME
@ex from AuthUsers" NAME="users">
@ex <input name="username" value="$users{USERNAME}">
@ex <br>
@ex </LOOP>
@ex <input type="submit">
@ex </SBFORM>
@ex </BODY>
@ex </HTML>
@ex
@ex <CHECK>
@ex <SBFORM name="myform">
@ex <!-- Replay the loop and print out the user input -->
@ex <LOOP NAME="users">
@ex <error text="You entered $this{username}">
@ex </LOOP>
@ex </SBFORM>
@ex </CHECK>
*/
void BaseSBModule::processLOOP (SGMLContainer * t, Error & err) {
AssocArray * flags = &(t->flags());
AssocArrayPtrDLList * data = NULL; // the stuff we are looping over
string OrigLoopKey = LoopKey;
string name;
int i;
char temp [10]; //hack!
int length;
EnvObject * old_userdata = userdata;
interpretString ((*flags)["NAME"], name); //get the name of the loop
name = OrigLoopKey + "_" + name;
if (flags->contains("WHILE")){ // Support both BOOL and WHILE
(*flags)["BOOL"] = (*flags)["WHILE"];
}
if ( ! hasDataSource( (SGMLTag *) t) ) {
// get the number of times this loop has played before
// and init data to the right number of empty assoc arrays
length = loopinfo->getLoopInfo (name);
data = new AssocArrayPtrDLList();
for (int i=0;i<length;i++) {
AssocArray * a = new AssocArray();
data->push_back(a);
}
} else if ( ! flags->contains("BOOL") ) {
//they specified a non-dynamic data source
try {
data = getList (t); // get the data they want to loop over
}
catch (DBError dbe) {
if (!handle (dbe, err)) {
throw ("Unhandled exception thrown by LOOP tag");
} else {
data = new AssocArrayPtrDLList();
}
}
}
if (data != NULL) {
length = data->size();
// open up a new environemnt
userdata = new EnvObject (userdata);
for (i = 0; i < length; i++) {
AssocArray * aaptr;
sprintf (temp, "%d", i);
LoopKey = name + "_" + temp;
// grab the first 'row' of info that we are looping over
aaptr = data->front();
data->pop_front();
// store this in the namespace = flags[NAME]
userdata->setNameSpace ((*flags)["NAME"], aaptr);
recurseOnTokens ( t->contents() , err);
}
// clean up and reset environment
delete (userdata);
userdata = old_userdata;
delete (data);
} else if ( flags->contains("BOOL") ) {
// open up a new environemnt
userdata = new EnvObject (userdata);
length = 0;
while ( evalSimpleBool((SGMLTag *) t, err) ) {
sprintf (temp, "%d", length++);
LoopKey = name + "_" + temp;
// store this in the namespace = flags[NAME]
userdata->setNameSpace ((*flags)["NAME"], new AssocArray());
recurseOnTokens ( t->contents() , err );
}
// clean up and reset environment
delete (userdata);
userdata = old_userdata;
}
// save the loopinfo so we can access it by name later
// if this is a nested loop, only save it if they INSIST on
// it since the performance penelty is huge
if (OrigLoopKey == "" || flags->contains("SAVELOOPINFO") ) {
loopinfo->setLoopInfo (name, length);
}
LoopKey = OrigLoopKey;
}
/*
@usehtml
@DATASOURCE
@usage <EMAIL MESSAGE="message"
@usage [FROM="from"]
@usage [SUBJECT="subject"]
@usage (ARRAY="array" |
@usage SQL="SQL statement" [DB="connection_name"] |
@usage VALUE="string or list" [SPLIT="delimiter"])>
@IFLAG MESSAGE Message to be sent
@IFLAG FROM E-mail from whom the mail is sent
@IFLAG SUBJECT Subject of the e-mail
@FLAG ARRAY Array name of e-mail recipients
@IFLAG SQL Select statement for e-mail recipients
@IFLAG DB DB connection name
@IFLAG VALUE Single value or list of e-mail recipients
@FLAG SPLIT String used to separate elements in the value list
@SECTIONS ALL
@INDEX Sends e-mail using the sendmail program.
Sends e-mail using the sendmail program. The first column of the
datasource is used as a list to whom e-mail is sent. The path to the
sendmail executable should be set in the configuration element
"MailProg".
@ex <EMAIL VALUE="user@localhost.com"
@ex SUBJECT="Welcome to SteelBlue"
@ex MESSAGE="Hi!">
*/
void BaseSBModule::processEMAIL (SGMLTag * t, Error & err) {
#ifndef WIN32
AssocArrayPtrDLList * data; // the stuff we are looping over
AssocArray * aaptr;
AssocArray * flags = &(t->flags());
string from, to, subject, message;
FILE* outfile;
int i, length;
from = interpretString ((*flags)["FROM"]);
subject = interpretString ((*flags)["SUBJECT"]);
message = interpretString ((*flags)["MESSAGE"]);
if (! hasDataSource ( (SGMLTag *) t)) {
err.addWarning ("EMAIL tag has no data source!");
}
if ((*config)["mailprog"] == ""){
err.addWarning ("mail-prog path not defined");
return;
}
try {
data = getList (t);
}
catch (DBError dbe) {
if (!handle (dbe, err)) {
throw ("Unhandled exception thrown by Email tag");
} else {
data = new AssocArrayPtrDLList();
}
}
length = data->size();
if (data != NULL) {
// do the first entry special
aaptr = data->front();
data->pop_front();
to = (*aaptr)["value"];
delete (aaptr);
//loop over the rest of the items
for (i = 1; i < length; i++) {
// grab the next 'row' of info
aaptr = data->front();
data->pop_front();
to = to + ", " + (*aaptr)["value"];
delete (aaptr);
}
// now dump it all to sendmail
string command = (*config)["mailprog"] + " -oi -t >/dev/null";
outfile = popen(command.c_str(),"w");
if (outfile == NULL){
err.addWarning ("unable to open sendmail");
return;
}
fprintf(outfile,"From: %s\nSubject: %s\nTo: %s\n\n%s\n",
from.c_str(),subject.c_str(),to.c_str(),message.c_str());
pclose(outfile);
}
#endif
}
/*
@nodoc
This is a helper function to shorten the DBCONNECT initializaion of
parameters. It takes a prefix, a flag name, and an associative array
of flags. It looks for the flag name in the array. If not there,
it looks it up in the configuration under prefix+name. If not there
it looks it up under the flag name by itself.
*/
void BaseSBModule::dbconnectFlagLookup(string * result,
string & prefix, char * flag_name,
AssocArray & flags){
string tag;
if (flags.contains(flag_name)){
(*result) = interpretString(flags[flag_name]);
} else {
tag = downcase(prefix + flag_name);
if (config->contains(tag)){
(*result) = (*config)[tag];
} else {
tag = downcase(flag_name);
(*result) = (*config)[tag];
}
}
}
/*
@usehtml
@usage <DBCONNECT DB="connection_name"
@usage [USEDB="db_type"]
@usage [DBSYSTEM="system_name"]
@usage [DBUSERNAME="username"]
@usage [DBPASSWORD="password"]
@usage [DBNAME="db_name"]>
@IFLAG DB Name associated with the resulting database connection
@IFLAG USEDB Type of database (ORACLE MSQL MYSQL ODBC PGSQL SYBASE)
@IFLAG DBSYSTEM System on which database resides
@IFLAG DBUSERNAME Database username
@IFLAG DBPASSWORD Database password
@IFLAG DBNAME Name of tablespace or database to which to connect
@SECTIONS ALL
@INDEX Creates a new connection to a database.
Creates a new connection to a database. Other commands that use
datasources can use the resulting connection by setting the DB tag to
the connection name defined in the DBCONNECT command (for example:
_LOOP_, _SQL_, _EMAIL_, and _DBSELECT_). The initial SteelBlue
database connection is given the DB connection name "default".
<P>
The other flags correspond to their respective file configuration tag
names. (Please see the <a href="configuration.html">configuration</a>
section for more details.) If a flag is not given, the associated
file configuration option prepended with the connection name is used.
If the configuration option is not defined, then the associated file
configuration option with no prefix is used. For example, if the DB
connection name is "Magic", and the USEDB flag is not given, then the
file configuration option "MAGICUSEDB" is used. If "MAGICUSEDB" is
not defined, then the file configuration option "USEDB" is used.
Remember that file configuration options are case insensitive, so the
DB connection names "JOBS" and "Jobs" would use the same file
configuration options if the respective flags are not defined.
<P>
Please note that the SteelBlue executable must be compiled to use the
database given in the USEDB flag.
@ex If the file configuration had the options:
@ex
@ex UseDb = ORACLE
@ex DBSystem = localhost
@ex DBUsername = foo
@ex DBPassword = mypwd
@ex DBName = CALENDAR
@ex JOBSUseDb = MYSQL
@ex JOBSDBName = OURJOBS
@ex
@ex <DBCONNECT DB="Jobs">
@ex <!-- Connects to the OURJOBS MySQL database
@ex on localhost using the username "foo" and
@ex password "mypwd" -->
@ex
@ex <DBCONNECT DB="Magic" DBUseDB="MYSQL" DBUsername="www"
@ex DBPassword="" DBSystem="foo.com"
@ex DBName="Magic">
@ex <!-- Connects to the Magic MySQL database on
@ex foo.com using the username "www" and the
@ex password "" -->
@ex
@ex <SQL SQL="select * from Person where person_id=2"
@ex DB=Jobs NAME="people">
@ex <!-- Does a query on the Jobs database connection -->
*/
void BaseSBModule::processDBCONNECT (SGMLTag * t, Error & err) {
DBGate * newdbgate;
DBGateFactory dbgf;
AssocArray & flags = t->flags();
string db, dbsystem, dbusername, dbpassword, dbname, usedb;
int i;
db = interpretString(flags["DB"]);
if (db == ""){
err.addWarning ("No DB flag in DBCONNECT.");
return;
}
/* If dbgate already defined with that name, delete the old one */
if (dbgates.contains(db)){
delete(dbgates[db]);
dbgates[db] = NULL;
}
/* Lookup the various flags */
dbconnectFlagLookup(&dbsystem ,db,"DBSYSTEM" ,flags);
dbconnectFlagLookup(&dbusername,db,"DBUSERNAME",flags);
dbconnectFlagLookup(&dbpassword,db,"DBPASSWORD",flags);
dbconnectFlagLookup(&dbname ,db,"DBNAME" ,flags);
dbconnectFlagLookup(&usedb ,db,"USEDB" ,flags);
newdbgate = dbgf.makeDBGate (usedb.c_str());
if (newdbgate == NULL){
err.addWarning("DBCONNECT: Unable to access given DB type");
return;
}
try {
newdbgate->login( (char *) dbsystem.c_str(),
(char *) dbname.c_str(),
(char *) dbusername.c_str(),
(char *) dbpassword.c_str());
} catch (DBError dbe){
err.addWarning("DBCONNECT: Unable to login to database");
return;
} catch (char * errstr){
err.addWarning("DBCONNECT: Unable to login to database");
return;
}
/* We are connected, so add to dbgates array */
dbgates[db] = newdbgate;
}
/*
@usehtml
@usage <ABORT>
@SECTIONS ALL
@INDEX Aborts the currently executing section.
Aborts the currently executing section, and skips to the next (if applicable).
If ABORT occurs in an HTML section, the ending </HTML> tag
is sent and the processing ends. If the ABORT occurs in a CHECK
section, if an _ERROR_ has been trhown, the previous HTML section is re-executed
immediately, otherwise the ACTION section is executed.
If an ABORT occurs in an ACTION section, if an _ERROR_ has been
thrown, the previous HTML section is re-executed immediately, otherwise
the destination HTML section is executed.
@ex <HTML>
@ex <BODY>
@ex <if bool="!group('admin')">
@ex <h2>
@ex You must be an administrator to execute
@ex this page
@ex </h2>
@ex <ABORT>
@ex </if>
@ex Do something sensitive...
@ex </BODY>
@ex </HTML>
*/
void BaseSBModule::processABORT (SGMLTag * t, Error & err) {
ExecutorAbort ea;
throw(ea);
}
/*
@usehtml
@usage <LOGOUT>
@SECTIONS ALL
@INDEX Logs the user out by invalidating the ticket.
Logs the user out by invalidating the ticket. The user will then need
to log in again in order to access the SteelBlue application.
@ex <LOGOUT>
*/
void BaseSBModule::processLOGOUT (SGMLTag * t, Error & err) {
string sql;
/* Invalidate the ticket by placing it in the past (1/1/1980) */
sql = "update " + (*config)["tickettable"]
+ " set timeout = '19800101010101' where ticket = '"
+ security->getTicket() + "'";
dbgate->setSQL(sql.c_str());
dbgate->execute();
}
/*
@usehtml
@usage <INCLUDE FILE="filename" [JSESCAPE]>
@FLAG FILE SteelBlue code filename (relative to the BasePages directory)
@FLAG JSESCAPE Includes file as a constant escaped string for use in JavaScript
@SECTIONS ALL
@INDEX Includes SteelBlue code from a separate file.
Includes SteelBlue code from a separate file specified by FILE
(relative to the BasePages directory). The included file is treated
as if it were physically placed at the location of the INCLUDE
command. If JSESCAPE is specified, then the file is not interpreted,
but instead is escaped for use as a JavaScript string.
@ex <!-- The following will include a SteelBlue code
@ex file which contains a navigation bar common
@ex to many pages -->
@ex <include file="navigation_bar.sb">
*/
void BaseSBModule::processINCLUDE (SGMLTag * t, Error & err) {
AssocArray * flags = &(t->flags());
string fname = config->getBasePageDir() + (*flags)["FILE"];
string old_err_fname = err.getFilename();
if (flags->contains("JSESCAPE")){
char nc;
CharStream char_stream;
if (!char_stream.newFile(fname)){
string s("Unable to open " + fname + " to read");
err.addFatalError(s);
return;
}
while((nc = char_stream.getChar()) != '\0'){
if ((nc == '\'') || (nc == '"')){
cout << '\\' << nc;
} else if (nc == '\n'){
cout << "\\n";
} else {
cout << nc;
}
}
} else {
SGMLParser * incfile = new SGMLParser;
err.setFilename(fname);
incfile->newFile(fname);
SGMLTokenList & stl = incfile->tokens();
if (incfile->errors().numFatalErrors() > 0 ||
incfile->errors().numWarnings() > 0){
outputErrors(incfile->errors(),security->getDest());
}
recurseOnTokens (stl, err);
/* Above, stl was a copy instead of a reference. This was fixed, but
before declaring this fixed, it should be tested more. */
#ifdef NO_LEAK
delete (incfile);
#endif
err.setFilename(old_err_fname);
}
}
/*
@usehtml
@CONTAINER
@usage <EVAL>
@usage Eval Language Block
@usage </EVAL>
@SECTIONS ALL
@INDEX Allows a large block of Eval code to be executed.
Allows a large block of Eval code to be executed. The Eval language
is well suited to quickly execute series of conditional statements,
assignments, and loops.
@ex <!-- Prints 10 elements of the Fibinocci
@ex series -->
@ex <EVAL>
@ex $a = 0;
@ex $b = 1;
@ex for($i=0;$i<10;$i=$i+1){
@ex $c = $a + $b;
@ex print("Fib[".$i."] = ".$c."<br>");
@ex $a = $b;
@ex $b = $c;
@ex }
@ex </EVAL>
*/
void BaseSBModule::processEVAL ( SGMLContainer *t, Error &err) {
SGMLTokenList * tl;
SGMLTag * tag;
SGMLText * evalstring;
EvalParser evalparser;
evalparser.debug = 0;
tl = &(t->contents());
SGMLTokenList::iterator i = tl->begin();
tag = (SGMLTag *) (*i);
cout.flush();
if (tag->type() == SGML_TEXT_TYPE) {
evalstring = (SGMLText *) tag;
evalparser.newString(evalstring->text(), err, tag->linebegin());
} else {
err.addWarning ("EVAL tag doesn't (plain) contain text.");
}
}
void BaseSBModule::evalString(string & str, string & result, Error & err,
int linebegin){
EvalParser evalparser;
evalparser.newString(str, err, linebegin);
result = evalparser.returnValue();
}
int BaseSBModule::evalSimpleBool (SGMLTag * t, Error & err) {
AssocArray flags = t->flags();
static string boolstr = string ("BOOL");
return (evalBoolString( flags[boolstr], err, t->linebegin() ));
}
int BaseSBModule::evalBoolString (string & boolstr, Error & err,
int linebegin) {
string result;
EvalParser evalparser;
evalparser.newString(boolstr, err, linebegin);
result = evalparser.returnValue();
if (result.length() == 0){ // Empty string is false
return (0);
}
if (isdigit((result.c_str())[0])){ // If number, false if == 0.0
return(atol(result.c_str()) != 0.0);
} else {
return(1); // True if non null string
}
}
string BaseSBModule::makeHref (string screen) {
string result;
string url = (*config)["sburl"];
if (url.length() == 0) {
url = (*config)["daturl"];
}
result = url + "?" + screen + "+" + security->getTicket() + "+" + security->getDest();
return (result);
}
int BaseSBModule::hasDataSource (SGMLTag * t) {
AssocArray flags = t->flags();
if (flags.contains("SQL") || flags.contains("VALUE") ||
flags.contains("FOR") || flags.contains("ARRAY") ||
flags.contains("BOOL") || flags.contains("WHILE")) {
return (1);
} else {
return (0);
}
}