blob: 1f8d707e8ad0619a4367105365f1078f82e29c2b [file] [log] [blame]
/*
* weboutint.c --- output handler of websh3
* nca-073-9
*
* Copyright (c) 1996-2000 by Netcetera AG.
* Copyright (c) 2001 by Apache Software Foundation.
* All rights reserved.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* @(#) $Id$
*
*/
#include "tcl.h"
#include <stdio.h>
#include "webout.h" /* is member of output module of websh */
#include "args.h" /* arg processing */
#include "webutl.h"
#include "hashutl.h"
#include "paramlist.h" /* destroyParamList */
#include "varchannel.h"
/* ----------------------------------------------------------------------------
* getChannel
* ------------------------------------------------------------------------- */
Tcl_Channel getChannel(Tcl_Interp * interp, ResponseObj * responseObj)
{
int mode = 0;
Tcl_Channel channel = NULL;
if ((interp == NULL) || (responseObj == NULL))
return NULL;
channel = Web_GetChannelOrVarChannel(interp,
Tcl_GetString(responseObj->name),
&mode);
if (channel == NULL) {
LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__, __LINE__,
"response", WEBLOG_ERROR,
"error getting channel \"", Tcl_GetString(responseObj->name),
"\"", NULL);
return NULL;
}
if (!(mode & TCL_WRITABLE)) {
LOG_MSG(interp, WRITE_LOG, __FILE__, __LINE__,
"response", WEBLOG_ERROR,
"channel \"", Tcl_GetString(responseObj->name),
"\" not open for writing", NULL);
return NULL;
}
return channel;
}
/* ----------------------------------------------------------------------------
* getResponseObj
* ------------------------------------------------------------------------- */
ResponseObj *getResponseObj(Tcl_Interp * interp, OutData * outData,
char *name)
{
ResponseObj *responseObj = NULL;
if ((interp == NULL) || (outData == NULL))
return NULL;
/* --------------------------------------------------------------------------
* default ?
* ----------------------------------------------------------------------- */
if (name == NULL) {
responseObj = outData->defaultResponseObj;
} else {
/* ------------------------------------------------------------------------
* do we have it ?
* --------------------------------------------------------------------- */
responseObj =
(ResponseObj *) getFromHashTable(outData->responseObjHash, name);
/* ------------------------------------------------------------------------
* no such object. implicitely build one
* --------------------------------------------------------------------- */
if (responseObj == NULL) {
int err = 0;
responseObj =
createResponseObj(interp, name, &objectHeaderHandler);
if (responseObj == NULL)
err++;
else if (appendToHashTable(outData->responseObjHash,
Tcl_GetString(responseObj->name),
(ClientData) responseObj) != TCL_OK)
err++;
if (err) {
LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__, __LINE__,
"response", WEBLOG_ERROR,
"error creating response object", NULL);
return NULL;
}
}
}
if (responseObj == NULL) {
LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__, __LINE__,
"web::putx", WEBLOG_ERROR,
"error accessing response object", NULL);
return NULL;
}
return responseObj;
}
/* ----------------------------------------------------------------------------
* createResponseObj
* ------------------------------------------------------------------------- */
ResponseObj __declspec(dllexport) *createResponseObj(Tcl_Interp * interp, char *channelName,
ResponseHeaderHandler * headerHandler)
{
Tcl_HashTable *hash = NULL;
ResponseObj *responseObj;
char *defheaders[] = { HEADER, NULL };
int i;
if (channelName == NULL)
return NULL;
/* fprintf(stderr,"creating '%s'\n",channelName); fflush(stderr); */
/* --------------------------------------------------------------------------
* create response object
* ----------------------------------------------------------------------- */
responseObj = WebAllocInternalData(ResponseObj);
if (responseObj == NULL) {
LOG_MSG(interp, WRITE_LOG | SET_RESULT, __FILE__, __LINE__,
"createResponseObj", WEBLOG_ERROR,
"error creating internal data", NULL);
return NULL;
}
/* --------------------------------------------------------------------------
* headers
* ----------------------------------------------------------------------- */
HashUtlAllocInit(hash, TCL_STRING_KEYS);
if (hash == NULL)
return NULL;
i = 0;
while (defheaders[i]) {
char *key;
Tcl_Obj *val;
key = defheaders[i++];
val = Tcl_NewStringObj(defheaders[i++], -1);
paramListAdd(hash, key, val);
}
responseObj->sendHeader = 1;
responseObj->bytesSent = 0;
responseObj->headers = hash;
responseObj->name = Tcl_NewStringObj(channelName, -1);
responseObj->httpresponse = NULL;
responseObj->headerHandler = headerHandler;
Tcl_IncrRefCount(responseObj->name); /* it's mine */
return responseObj;
}
/* ----------------------------------------------------------------------------
* destroyResponseObj
* ------------------------------------------------------------------------- */
int destroyResponseObj(ClientData clientData, Tcl_Interp * interp)
{
ResponseObj *responseObj = NULL;
if (clientData == NULL)
return TCL_ERROR;
responseObj = (ResponseObj *) clientData;
/* unregister if was a varchannel */
/* printf("DBG destroyResponseObj '%s'\n",Tcl_GetString(responseObj->name)); fflush(stdout); */
Web_UnregisterVarChannel(interp, Tcl_GetString(responseObj->name), NULL);
WebDecrRefCountIfNotNull(responseObj->name);
WebDecrRefCountIfNotNull(responseObj->httpresponse);
if (responseObj->headers != NULL) {
destroyParamList(responseObj->headers);
responseObj->headers = NULL;
}
WebFreeIfNotNull(responseObj);
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* createResponseObjHash
* ------------------------------------------------------------------------- */
int createResponseObjHash(OutData * outData)
{
if (outData == NULL)
return TCL_ERROR;
if (outData->defaultResponseObj == NULL)
return TCL_ERROR;
HashUtlAllocInit(outData->responseObjHash, TCL_STRING_KEYS);
if (outData->responseObjHash != NULL) {
if (appendToHashTable(outData->responseObjHash,
Tcl_GetString(outData->defaultResponseObj->
name),
(ClientData) outData->defaultResponseObj)
!= TCL_OK) {
HashUtlDelFree(outData->responseObjHash);
outData->responseObjHash = NULL;
return TCL_ERROR;
}
}
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* destroyResponseObjHash
* ------------------------------------------------------------------------- */
int destroyResponseObjHash(OutData * outData, Tcl_Interp * interp)
{
HashTableIterator iterator;
ResponseObj *responseObj = NULL;
if (outData == NULL)
return TCL_ERROR;
if (outData->responseObjHash == NULL)
return TCL_ERROR;
/* delete all response Obj */
assignIteratorToHashTable(outData->responseObjHash, &iterator);
while (nextFromHashIterator(&iterator) != TCL_ERROR) {
responseObj = (ResponseObj *) valueOfCurrent(&iterator);
if (responseObj != NULL)
destroyResponseObj((ClientData) responseObj, interp);
}
HashUtlDelFree(outData->responseObjHash);
outData->responseObjHash = NULL;
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* createOutData
* ------------------------------------------------------------------------- */
OutData *createOutData(Tcl_Interp * interp)
{
OutData *outData = NULL;
outData = WebAllocInternalData(OutData);
if (outData != NULL) {
outData->defaultResponseObj = createDefaultResponseObj(interp);
if (outData->defaultResponseObj != NULL) {
if (createResponseObjHash(outData) != TCL_OK) {
destroyResponseObj((ClientData) outData->defaultResponseObj,
interp);
WebFreeIfNotNull(outData);
return NULL;
}
}
else {
WebFreeIfNotNull(outData);
return NULL;
}
outData->putxMarkup = PUTXMARKUPDEFAULT;
}
return outData;
}
/* ----------------------------------------------------------------------------
* reset
* ------------------------------------------------------------------------- */
int resetOutData(Tcl_Interp * interp, OutData * outData)
{
if ((interp == NULL) || (outData == NULL))
return TCL_ERROR;
outData->putxMarkup = 0;
if (destroyResponseObjHash(outData, interp) == TCL_ERROR)
return TCL_ERROR;
outData->responseObjHash = NULL;
/* create standard channel */
outData->defaultResponseObj = createDefaultResponseObj(interp);
if (outData->defaultResponseObj == NULL)
return TCL_ERROR;
/* create Hash (and add default channel) */
if (createResponseObjHash(outData) != TCL_OK)
return TCL_ERROR;
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* destroy internal data structure
* ------------------------------------------------------------------------- */
int destroyOutData(ClientData clientData, Tcl_Interp * interp)
{
OutData *outData;
/* HashTableIterator iterator; */
if (clientData == NULL)
return TCL_ERROR;
outData = (OutData *) clientData;
/* delete all response Obj */
destroyResponseObjHash(outData, interp);
WebFreeIfNotNull(outData);
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* webout_eval_tag (code in <? ?>)
* ------------------------------------------------------------------------- */
int webout_eval_tag(Tcl_Interp * interp, ResponseObj * responseObj,
Tcl_Obj * in, TCLCONST char *strstart, TCLCONST char *strend)
{
Tcl_Obj *outbuf;
Tcl_Obj *tclo;
char *next;
char *cur;
int endseqlen = strlen(strend);
int startseqlen = strlen(strstart);
int begin = 1;
int firstScan = 1;
int inside = 0;
int inLen = 0;
int res = 0;
next = Tcl_GetStringFromObj(in, &inLen);
outbuf = Tcl_NewStringObj("", -1);
Tcl_IncrRefCount(outbuf);
if (inLen == 0) {
Tcl_DecrRefCount(outbuf);
return 0;
}
while (*next != 0) {
cur = next;
next = (char *)Tcl_UtfNext(cur);
if (strncmp("\\", cur, 1) == 0) {
if (firstScan == 1) { firstScan = 0; }
if (strncmp(strstart, next, startseqlen) == 0) {
Tcl_AppendToObj(outbuf, "\\", 1);
Tcl_AppendToObj(outbuf, strstart, startseqlen);
next += startseqlen;
} else if (strncmp(strend, next, endseqlen) == 0) {
Tcl_AppendToObj(outbuf, "\\", 1);
Tcl_AppendToObj(outbuf, strend, endseqlen);
next += endseqlen;
} else if (inside < 1) {
Tcl_AppendToObj(outbuf, "\\\\", 2);
} else {
Tcl_AppendToObj(outbuf, "\\", 1);
}
} else if (strncmp(strstart, cur, startseqlen) == 0) {
if ((++inside) == 1) {
if (firstScan == 1) {
begin = 0;
firstScan = 0;
Tcl_AppendToObj(outbuf, "\n", 1);
} else {
Tcl_AppendToObj(outbuf, "\"\n", 2);
}
if (startseqlen > 1) {
next += startseqlen - 1;
}
} else {
Tcl_AppendToObj(outbuf, cur, startseqlen);
if (startseqlen > 1) {
next += startseqlen - 1;
}
}
} else if (strncmp(strend, cur, endseqlen) == 0) {
if (firstScan == 1) { firstScan = 0; }
if ((--inside) == 0) {
Tcl_AppendToObj(outbuf, "\nweb::put \"", -1);
if (endseqlen > 1) {
next += endseqlen - 1;
}
} else {
Tcl_AppendToObj(outbuf, cur, endseqlen);
if (endseqlen > 1) {
next += endseqlen - 1;
}
}
if (inside < 0) { inside = 0; }
} else if (inside < 1) {
if (firstScan == 1) { firstScan = 0; }
switch (*cur) {
case '{':
case '}':
case '$':
case '[':
case ']':
case '"':
Tcl_AppendToObj(outbuf, "\\", -1);
default:
Tcl_AppendToObj(outbuf, cur, next - cur);
break;
}
} else {
if (firstScan == 1) { firstScan = 0; }
Tcl_AppendToObj(outbuf, cur, next - cur);
}
}
if (begin) {
tclo = Tcl_NewStringObj("web::put \"", -1);
Tcl_IncrRefCount(tclo);
Tcl_AppendObjToObj(tclo, outbuf);
Tcl_DecrRefCount(outbuf);
} else {
tclo = outbuf;
}
Tcl_AppendToObj(tclo, "\"", -1);
res = Tcl_EvalObjEx(interp, tclo, TCL_EVAL_DIRECT);
Tcl_DecrRefCount(tclo);
return res;
}
/* ----------------------------------------------------------------------------
* putsCmdImpl -- do the work here
* ------------------------------------------------------------------------- */
int putsCmdImpl(Tcl_Interp * interp, ResponseObj * responseObj, Tcl_Obj * str)
{
Tcl_Obj *sendString = NULL;
long bytesSent = 0;
Tcl_Channel channel;
Tcl_DString translation;
/* --------------------------------------------------------------------------
* sanity
* ----------------------------------------------------------------------- */
if ((responseObj == NULL) || (str == NULL))
return TCL_ERROR;
/* printf("DBG putsCmdImpl - got '%s'\n",Tcl_GetString(str)); fflush(stdout); */
channel = getChannel(interp, responseObj);
if (channel == NULL)
return TCL_ERROR;
sendString = Tcl_NewObj();
Tcl_IncrRefCount(sendString);
if (responseObj->sendHeader) {
responseObj->headerHandler(interp, responseObj, sendString);
}
Tcl_AppendObjToObj(sendString, str);
/* make sure there is no additional newline translation */
Tcl_DStringInit(&translation);
Tcl_GetChannelOption(interp, channel, "-translation", &translation);
Tcl_SetChannelOption(interp, channel, "-translation", "lf");
if ((bytesSent = Tcl_WriteObj(channel, sendString)) == -1) {
LOG_MSG(interp, WRITE_LOG | SET_RESULT,
__FILE__, __LINE__,
"web::put", WEBLOG_ERROR,
"error writing to response object:",
Tcl_GetStringResult(interp), NULL);
Tcl_DecrRefCount(sendString);
Tcl_SetChannelOption(interp, channel, "-translation", Tcl_DStringValue(&translation));
Tcl_DStringFree(&translation);
return TCL_ERROR;
}
Tcl_SetChannelOption(interp, channel, "-translation", Tcl_DStringValue(&translation));
Tcl_DStringFree(&translation);
responseObj->bytesSent += bytesSent;
/* flush varchannel */
if (responseObj->name != NULL) {
char *channelName = Tcl_GetString(responseObj->name);
if (channelName != NULL)
if (channelName[0] == '#')
Tcl_Flush(channel);
}
Tcl_DecrRefCount(sendString);
return TCL_OK;
}
/* ----------------------------------------------------------------------------
* objectHeaderHandler -- send headers into a Tcl_Obj, used for variables and channels
* ------------------------------------------------------------------------- */
int objectHeaderHandler(Tcl_Interp * interp, ResponseObj * responseObj,
Tcl_Obj * out)
{
/* --------------------------------------------------------------------------
* sanity
* ----------------------------------------------------------------------- */
if ((out == NULL) || (responseObj == NULL))
return TCL_ERROR;
if (responseObj->sendHeader == 1) {
HashTableIterator iterator;
char *key;
Tcl_Obj *headerList;
if (responseObj->httpresponse != NULL) {
Tcl_AppendObjToObj(out, responseObj->httpresponse);
Tcl_AppendToObj(out, "\r\n", 2);
}
assignIteratorToHashTable(responseObj->headers, &iterator);
while (nextFromHashIterator(&iterator) != TCL_ERROR) {
key = keyOfCurrent(&iterator);
if (key != NULL) {
headerList = (Tcl_Obj *) valueOfCurrent(&iterator);
if (headerList != NULL) {
int lobjc = -1;
Tcl_Obj **lobjv = NULL;
int i;
if (Tcl_ListObjGetElements(interp, headerList,
&lobjc, &lobjv) == TCL_ERROR) {
LOG_MSG(interp, WRITE_LOG,
__FILE__, __LINE__,
"web::put", WEBLOG_ERROR,
(char *) Tcl_GetStringResult(interp), NULL);
return TCL_ERROR;
}
/* add all occurrences of this header */
for (i = 0; i < lobjc; i++) {
Tcl_AppendToObj(out, key, -1);
Tcl_AppendToObj(out, ": ", 2);
Tcl_AppendObjToObj(out, lobjv[i]);
Tcl_AppendToObj(out, "\r\n", 2);
}
}
}
}
Tcl_AppendToObj(out, "\r\n", 2);
responseObj->sendHeader = 0;
}
return TCL_OK;
}