1
0
mirror of https://github.com/stefanocasazza/ULib.git synced 2025-09-28 19:05:55 +08:00
ULib/src/ulib/net/server/plugin/mod_ssi.cpp
stefanocasazza 832fe2c988 some fix
2015-11-17 17:38:18 +01:00

1118 lines
36 KiB
C++

// ============================================================================
//
// = LIBRARY
// ULib - c++ library
//
// = FILENAME
// mod_ssi.cpp - this is a plugin SSI for userver
//
// = AUTHOR
// Stefano Casazza
//
// ============================================================================
#include <ulib/url.h>
#include <ulib/date.h>
#include <ulib/command.h>
#include <ulib/tokenizer.h>
#include <ulib/file_config.h>
#include <ulib/utility/uhttp.h>
#include <ulib/utility/escape.h>
#include <ulib/utility/services.h>
#include <ulib/net/server/server.h>
#include <ulib/utility/xml_escape.h>
#include <ulib/utility/string_ext.h>
#include <ulib/net/server/plugin/mod_ssi.h>
// Server Side Include (SSI) commands are executed by the server as it parses your HTML file.
// Server side includes can be used to include the value of various server environment variables
// within your HTML such as the local date and time. One might use a server side include to add
// a signature file to an HTML file or company logo.
U_CREAT_FUNC(server_plugin_ssi, USSIPlugIn)
int USSIPlugIn::alternative_response;
bool USSIPlugIn::use_size_abbrev;
time_t USSIPlugIn::last_modified;
UString* USSIPlugIn::errmsg;
UString* USSIPlugIn::timefmt;
UString* USSIPlugIn::docname;
UString* USSIPlugIn::body;
UString* USSIPlugIn::header;
UString* USSIPlugIn::environment;
UString* USSIPlugIn::alternative_include;
USSIPlugIn::USSIPlugIn()
{
U_TRACE_REGISTER_OBJECT(0, USSIPlugIn, "")
errmsg = U_NEW(UString);
timefmt = U_NEW(UString);
docname = U_NEW(UString);
UString::str_allocate(STR_ALLOCATE_SSI);
}
USSIPlugIn::~USSIPlugIn()
{
U_TRACE_UNREGISTER_OBJECT(0, USSIPlugIn)
delete errmsg;
delete timefmt;
delete docname;
if (body)
{
delete body;
delete header;
delete alternative_include;
}
if (environment) delete environment;
}
void USSIPlugIn::setAlternativeRedirect(const char* fmt, ...)
{
U_TRACE(0, "USSIPlugIn::setAlternativeRedirect(%S)", fmt)
char format[4096];
UString buffer(U_CAPACITY);
va_list argp;
va_start(argp, fmt);
(void) u__snprintf(format, sizeof(format), U_CTYPE_HTML "\r\nRefresh: 0; url=%s\r\n", fmt);
buffer.vsnprintf(format, argp);
va_end(argp);
alternative_response = 1;
U_http_info.nResponseCode = HTTP_OK;
UClientImage_Base::setCloseConnection();
UHTTP::setResponse(&buffer, 0);
}
void USSIPlugIn::setBadRequest()
{
U_TRACE_NO_PARAM(0, "USSIPlugIn::setBadRequest()")
alternative_response = 1;
UHTTP::setBadRequest();
}
void USSIPlugIn::setAlternativeResponse()
{
U_TRACE_NO_PARAM(0, "USSIPlugIn::setAlternativeResponse()")
alternative_response = 1;
U_http_info.nResponseCode = HTTP_NO_CONTENT;
UClientImage_Base::setCloseConnection();
UHTTP::setResponse(0, 0);
}
void USSIPlugIn::setAlternativeResponse(UString& _body)
{
U_TRACE(0, "USSIPlugIn::setAlternativeResponse(%V)", _body.rep)
alternative_response = 1;
UClientImage_Base::setCloseConnection();
if (_body.empty())
{
U_http_info.nResponseCode = HTTP_NO_CONTENT;
UHTTP::setResponse(0, 0);
}
else
{
U_http_info.nResponseCode = HTTP_OK;
UHTTP::setResponse(u_is_know(UHTTP::mime_index) ? UString::str_ctype_txt : UString::str_ctype_html, &_body);
}
}
void USSIPlugIn::setAlternativeInclude(const UString& tmpl, uint32_t estimated_size, bool bprocess, const char* title_txt, const char* ssi_head, const char* body_style, ...)
{
U_TRACE(0, "USSIPlugIn::setAlternativeInclude(%V,%u,%b,%S,%S,%S)", tmpl.rep, estimated_size, bprocess, title_txt, ssi_head, body_style)
U_INTERNAL_ASSERT_POINTER(title_txt)
U_INTERNAL_ASSERT(tmpl.isNullTerminated())
estimated_size += tmpl.size() + 4096;
alternative_include->setBuffer(estimated_size);
va_list argp;
va_start(argp, body_style);
alternative_include->vsnprintf(tmpl.data(), argp);
va_end(argp);
U_http_info.nResponseCode = HTTP_NO_CONTENT;
UString buffer(U_CAPACITY);
buffer.snprintf( "'TITLE_TXT=%s'\n", title_txt);
if (ssi_head) buffer.snprintf_add("'SSI_HEAD=%s'\n", ssi_head);
if (body_style) buffer.snprintf_add("BODY_STYLE=%s\n", body_style);
(void) UClientImage_Base::environment->append(buffer);
if (bprocess) *alternative_include = processSSIRequest(*alternative_include, 0);
UClientImage_Base::wbuffer->setBuffer(U_CAPACITY); // NB: to avoid append on output...
}
void USSIPlugIn::setMessagePageWithVar(const UString& tmpl, const char* title_txt, const char* fmt, ...)
{
U_TRACE(0, "USSIPlugIn::setMessagePageWithVar(%V,%S,%S)", tmpl.rep, title_txt, fmt)
char format[4096];
va_list argp;
va_start(argp, fmt);
(void) u__vsnprintf(format, sizeof(format), fmt, argp);
va_end(argp);
setMessagePage(tmpl, title_txt, format);
}
U_NO_EXPORT UString USSIPlugIn::getPathname(const UString& name, const UString& value, const UString& directory)
{
U_TRACE(0, "USSIPlugIn::getPathname(%V,%V,%V)", name.rep, value.rep, directory.rep)
/**
* "include file" looks in the current directory (the name must not start with /, ., or ..) and "include virtual" starts
* in the root directory of your kiosk (so the name must start with "/".) You might want to make a "/includes" directory
* in your Kiosk and then you can say "include virtual=/includes/file.txt" from any page. The "virtual" and "file"
* parameters are also used with "fsize" and "flastmod". With either method, you can only reference files that are within
* your Kiosk directory (apart if "direct"...)
*/
UString pathname;
if (name.equal(U_CONSTANT_TO_PARAM("direct"))) pathname = value;
else if (name.equal(U_CONSTANT_TO_PARAM("file"))) pathname = directory + '/' + value;
else if (name.equal(U_CONSTANT_TO_PARAM("virtual"))) pathname = "./" + value;
U_RETURN_STRING(pathname);
}
U_NO_EXPORT UString USSIPlugIn::getInclude(const UString& include, int include_level, bool bssi)
{
U_TRACE(0, "USSIPlugIn::getInclude(%V,%d,%b)", include.rep, include_level, bssi)
UString content = include;
U_INTERNAL_DUMP("file = %.*S", U_FILE_TO_TRACE(*UHTTP::file))
if (bssi ||
UStringExt::endsWith(U_FILE_TO_PARAM(*UHTTP::file), U_CONSTANT_TO_PARAM(".shtml")))
{
if (include_level < 16) content = processSSIRequest(content, include_level + 1);
else
{
U_SRV_LOG("WARNING: SSI #include level is too deep (%.*S)", U_FILE_TO_TRACE(*UHTTP::file));
}
}
U_RETURN_STRING(content);
}
U_NO_EXPORT bool USSIPlugIn::callService(const UString& name, const UString& value)
{
U_TRACE(0, "USSIPlugIn::callService(%V,%V)", name.rep, value.rep)
if (name.equal(U_CONSTANT_TO_PARAM("cmd")))
{
static int fd_stderr;
if (fd_stderr == 0) fd_stderr = UServices::getDevNull("/tmp/SSI.err");
*UClientImage_Base::wbuffer = UCommand::outputCommand(value, 0, -1, fd_stderr);
U_RETURN(true);
}
U_ASSERT(name == *UString::str_cgi ||
name.equal(U_CONSTANT_TO_PARAM("servlet")))
UClientImage_Base::wbuffer->setBuffer(U_CAPACITY); // NB: we need this to avoid to accumulate output from services...
bool result = UHTTP::callService(value);
#ifdef DEBUG // NB: to avoid DEAD OF SOURCE STRING WITH CHILD ALIVE...
UHTTP::file->getPath().clear();
#endif
if (result == false ||
alternative_response ||
U_ClientImage_parallelization == 2) // 2 => parent of parallelization
{
alternative_response = 1; // 1 => response already complete (nothing to do)
U_RETURN(false);
}
U_RETURN(true);
}
U_NO_EXPORT UString USSIPlugIn::processSSIRequest(const UString& content, int include_level)
{
U_TRACE(0, "USSIPlugIn::processSSIRequest(%V,%d)", content.rep, include_level)
U_INTERNAL_ASSERT(content)
UString tmp; // NB: must be here to avoid DEAD OF SOURCE STRING WITH CHILD ALIVE...
UVector<UString> name_value;
uint32_t size, sz = content.size();
enum {E_NONE, E_URL, E_ENTITY} encode;
int if_level = 0, if_is_false_level = 0;
bool bgroup, bfile, bvar, if_is_false = false, if_is_false_endif = false;
UString token, name, value, pathname, include, output(U_CAPACITY), x, encoded,
directory = UStringExt::dirname(UHTTP::file->getPath()).copy();
UTokenizer t(content);
t.setGroup(U_CONSTANT_TO_PARAM("<!--->"));
enum { SSI_NONE, SSI_INCLUDE, SSI_EXEC, SSI_ECHO, SSI_CONFIG, SSI_FLASTMOD,
SSI_FSIZE, SSI_PRINTENV, SSI_SET, SSI_IF, SSI_ELSE, SSI_ELIF, SSI_ENDIF };
do {
// Find next SSI tag
uint32_t distance = t.getDistance(),
pos = content.find("<!--#", distance);
if (pos)
{
if (pos == U_NOT_FOUND) pos = sz;
t.setDistance(pos);
size = pos - distance;
if (size &&
if_is_false == false)
{
(void) output.append(content.substr(distance, size)); // plain html block
}
}
if (t.next(token, &bgroup) == false) break;
U_INTERNAL_ASSERT(bgroup)
U_INTERNAL_DUMP("token = %V", token.rep)
U_ASSERT(token.size() >= 2)
int i = 2;
const char* directive = token.c_pointer(2); // "-#"...
U_INTERNAL_DUMP("directive = %.*S", 10, directive)
/**
* <!--#element attribute=value attribute=value ... -->
*
* echo DONE
* var DONE
* encoding DONE
* set DONE
* var DONE
* value DONE
* exec DONE
* cmd DONE
* cgi DONE
* servlet DONE
* include DONE
* file DONE
* direct DONE
* virtual DONE
* fsize DONE
* file DONE
* direct DONE
* virtual DONE
* flastmod DONE
* file DONE
* direct DONE
* virtual DONE
* config DONE
* errmsg DONE
* sizefmt DONE
* timefmt DONE
* printenv DONE
*
* if DONE
* elif DONE
* else DONE
* endif DONE
*
* expressions
* &&, || DONE
* comp DONE
* ${...} DONE
* $... DONE
* '...' DONE
* ( ... ) DONE
*/
// Check element
int op = SSI_NONE;
if (strncmp(directive, U_CONSTANT_TO_PARAM("include ")) == 0)
{
op = SSI_INCLUDE;
i += U_CONSTANT_SIZE("include ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("if ")) == 0)
{
op = SSI_IF;
i += U_CONSTANT_SIZE("if ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("else ")) == 0)
{
op = SSI_ELSE;
i += U_CONSTANT_SIZE("else ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("elif ")) == 0)
{
op = SSI_ELIF;
i += U_CONSTANT_SIZE("elif ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("endif ")) == 0)
{
op = SSI_ENDIF;
i += U_CONSTANT_SIZE("endif ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("exec ")) == 0)
{
op = SSI_EXEC;
i += U_CONSTANT_SIZE("exec ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("echo ")) == 0)
{
op = SSI_ECHO;
i += U_CONSTANT_SIZE("echo ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("config ")) == 0)
{
op = SSI_CONFIG;
i += U_CONSTANT_SIZE("config ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("flastmod ")) == 0)
{
op = SSI_FLASTMOD;
i += U_CONSTANT_SIZE("flastmod ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("fsize ")) == 0)
{
op = SSI_FSIZE;
i += U_CONSTANT_SIZE("fsize ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("printenv ")) == 0)
{
op = SSI_PRINTENV;
i += U_CONSTANT_SIZE("printenv ");
}
else if (strncmp(directive, U_CONSTANT_TO_PARAM("set ")) == 0)
{
op = SSI_SET;
i += U_CONSTANT_SIZE("set ");
}
int n = token.size() - i;
U_INTERNAL_DUMP("op = %d n = %u", op, n)
# ifdef DEBUG // NB: to avoid DEAD OF SOURCE STRING WITH CHILD ALIVE...
name.clear();
value.clear();
# endif
if (n)
{
name_value.clear();
tmp = UStringExt::simplifyWhiteSpace(token.substr(i, n));
n = UStringExt::getNameValueFromData(tmp, name_value, U_CONSTANT_TO_PARAM(" "));
}
# ifdef DEBUG // NB: to avoid DEAD OF SOURCE STRING WITH CHILD ALIVE...
token.clear();
# endif
U_INTERNAL_DUMP("if_is_false = %b if_is_false_level = %d if_level = %d if_is_false_endif = %b",
if_is_false, if_is_false_level, if_level, if_is_false_endif)
switch (op)
{
/*
<!--#if expr="${Sec_Nav}" -->
<!--#include virtual="secondary_nav.txt" -->
<!--#elif expr="${Pri_Nav}" -->
<!--#include virtual="primary_nav.txt" -->
<!--#endif -->
*/
case SSI_IF:
case SSI_ELIF:
{
if (n != 2) U_ERROR("SSI: syntax error for %S statement", op == SSI_IF ? "if" : "elif");
name = name_value[0];
if (name.equal(U_CONSTANT_TO_PARAM("expr")) == false)
{
U_ERROR("SSI: unknow attribute %V for %S statement", name.rep, op == SSI_IF ? "if" : "elif");
}
value = name_value[1];
if (op == SSI_IF)
{
if ( if_is_false == false &&
(if_is_false_level == 0 || (if_level < if_is_false_level)))
{
if_is_false = (UStringExt::evalExpression(value, *UClientImage_Base::environment).empty()
? (if_is_false_level = if_level, true)
: false);
}
}
else
{
--if_level;
if (if_level == if_is_false_level)
{
if (if_is_false &&
if_is_false_endif == false)
{
if_is_false = (UStringExt::evalExpression(value, *UClientImage_Base::environment).empty()
? (if_is_false_level = if_level, true)
: false);
}
else
{
if_is_false_level = if_level;
if_is_false = if_is_false_endif = true;
}
}
}
++if_level;
}
break;
case SSI_ELSE:
{
--if_level;
if (if_is_false)
{
if (if_level == if_is_false_level &&
if_is_false_endif == false)
{
if_is_false = false;
}
}
else
{
if_is_false = true;
if_is_false_level = if_level;
}
++if_level;
}
break;
case SSI_ENDIF:
{
--if_level;
if (if_level == if_is_false_level) if_is_false = if_is_false_endif = false;
}
break;
}
U_INTERNAL_DUMP("if_is_false = %b if_is_false_level = %d if_level = %d if_is_false_endif = %b",
if_is_false, if_is_false_level, if_level, if_is_false_endif)
if (if_is_false) continue;
switch (op)
{
/*
<!--#printenv -->
*/
case SSI_PRINTENV: (void) output.append(U_STRING_TO_PARAM(*UClientImage_Base::environment)); break;
/*
<!--#config sizefmt="bytes" -->
<!--#config timefmt="%y %m %d" -->
<!--#config errmsg="SSI command failed!" -->
*/
case SSI_CONFIG:
{
for (i = 0; i < n; i += 2)
{
name = name_value[i];
value = name_value[i+1];
if (name.equal(U_CONSTANT_TO_PARAM("errmsg"))) (*errmsg = value).duplicate();
else if (name.equal(U_CONSTANT_TO_PARAM("timefmt"))) (*timefmt = value).duplicate();
else if (name.equal(U_CONSTANT_TO_PARAM("sizefmt"))) use_size_abbrev = value.equal(U_CONSTANT_TO_PARAM("abbrev"));
}
}
break;
/*
<!--#echo var=$BODY_STYLE -->
<!--#echo [encoding="..."] var="..." [encoding="..."] var="..." ... -->
*/
case SSI_ECHO:
{
encode = E_NONE;
for (i = 0; i < n; i += 2)
{
name = name_value[i];
value = name_value[i+1];
if (name.equal(U_CONSTANT_TO_PARAM("encoding")))
{
if (value.equal(U_CONSTANT_TO_PARAM("none"))) encode = E_NONE;
else if (value.equal(U_CONSTANT_TO_PARAM("url"))) encode = E_URL;
else if (value.equal(U_CONSTANT_TO_PARAM("entity"))) encode = E_ENTITY;
}
else if (name == *UString::str_var)
{
U_INTERNAL_ASSERT_MAJOR(u_user_name_len,0)
/**
* DATE_GMT The current date in Greenwich Mean Time.
* DATE_LOCAL The current date in the local time zone.
* LAST_MODIFIED The last modification date of the document requested by the user.
* USER_NAME Contains the owner of the file which included it.
* DOCUMENT_NAME The filename (excluding directories) of the document requested by the user.
* DOCUMENT_URI The URL path of the document requested by the user. Note that in the case of
* nested include files, this is not then URL for the current document.
*/
if (value.equal(U_CONSTANT_TO_PARAM("DATE_GMT"))) x = UTimeDate::strftime(timefmt->data(), u_now->tv_sec);
else if (value.equal(U_CONSTANT_TO_PARAM("DATE_LOCAL"))) x = UTimeDate::strftime(timefmt->data(), u_now->tv_sec, true);
else if (value.equal(U_CONSTANT_TO_PARAM("LAST_MODIFIED"))) x = UTimeDate::strftime(timefmt->data(), last_modified);
else if (value.equal(U_CONSTANT_TO_PARAM("USER_NAME"))) (void) x.assign(u_user_name, u_user_name_len);
else if (value.equal(U_CONSTANT_TO_PARAM("DOCUMENT_URI"))) (void) x.assign(U_HTTP_URI_TO_PARAM);
else if (value.equal(U_CONSTANT_TO_PARAM("DOCUMENT_NAME"))) x = *docname;
else
{
x = UStringExt::expandEnvironmentVar(value, UClientImage_Base::environment);
}
if (x.empty()) continue;
if (encode == E_NONE) encoded = x;
else if (encode == E_URL)
{
encoded.setBuffer(x.size() * 3);
Url::encode(x, encoded);
}
else
{
U_INTERNAL_ASSERT_EQUALS(encode, E_ENTITY)
encoded.setBuffer(x.size());
UXMLEscape::encode(x, encoded);
}
(void) output.append(encoded);
}
x.clear();
}
}
break;
/*
<!--#fsize file="script.pl" -->
<!--#flastmod virtual="index.html" -->
*/
case SSI_FSIZE:
case SSI_FLASTMOD:
{
if (n == 2)
{
name = name_value[0];
value = name_value[1];
pathname = getPathname(name, value, directory);
}
bfile = false;
if (pathname)
{
UHTTP::file->setPath(pathname, UClientImage_Base::environment);
if (UHTTP::isFileInCache()) bfile = true;
else if (UHTTP::file->open())
{
bfile = true;
UHTTP::file->fstat();
}
pathname.clear();
}
if (bfile == false)
{
(void) output.append(*errmsg);
}
else if (op == SSI_FSIZE)
{
uint32_t file_size = UHTTP::file->getSize();
if (use_size_abbrev == false) UStringExt::appendNumber32(output, file_size);
else (void) output.append(UStringExt::printSize(file_size));
}
else
{
(void) output.append(UTimeDate::strftime(timefmt->data(), UHTTP::file->st_mtime)); // SSI_FLASTMOD
}
}
break;
/*
<!--#include file=footer.html -->
*/
case SSI_INCLUDE:
{
if (n == 2)
{
name = name_value[0];
value = name_value[1];
pathname = getPathname(name, value, directory);
}
bfile = false;
if (pathname)
{
// NB: we check if we are near to the end of ssi processing (include of services output file)...
bfile = u_startsWith(U_STRING_TO_PARAM(pathname), U_CONSTANT_TO_PARAM("$SSI_FILE_")); // HEAD|BODY
if (bfile &&
*alternative_include)
{
(void) output.append(*alternative_include);
alternative_include->clear();
break;
}
UHTTP::file->setPath(pathname, UClientImage_Base::environment);
if (bfile == false &&
UHTTP::isFileInCache() &&
UHTTP::isDataFromCache())
{
include = UHTTP::getBodyFromCache();
}
else if (UHTTP::file->open())
{
UHTTP::file->fstat();
include = UHTTP::file->getContent();
// NB: we want to clean the temporary file generate by shell script services...
if (bfile &&
(UStringExt::endsWith(U_FILE_TO_PARAM(*UHTTP::file), U_CONSTANT_TO_PARAM(":head.html")) ||
UStringExt::endsWith(U_FILE_TO_PARAM(*UHTTP::file), U_CONSTANT_TO_PARAM(":body.html"))))
{
(void) UHTTP::file->_unlink();
}
}
}
if (include.empty()) x = *errmsg;
else
{
x = getInclude(include, include_level, bfile);
include.clear();
}
(void) output.append(x);
x.clear();
pathname.clear();
UHTTP::file->getPath().clear();
}
break;
/*
<!--#exec cmd="ls -l" -->
<!--#exec cgi=/cgi-bin/foo.cgi -->
<!--#exec servlet=/servlet/mchat -->
*/
case SSI_EXEC:
{
if (n == 2)
{
if (callService(name_value[0], name_value[1]) == false) return UString::getStringNull();
(void) output.append(*UClientImage_Base::wbuffer);
}
}
break;
/*
<!--#set cmd="env -l" -->
<!--#set var="foo" value="bar" -->
<!--#set cgi=$VIRTUAL_HOST/cgi-bin/main.bash -->
*/
case SSI_SET:
{
bvar = false;
for (i = 0; i < n; i += 4)
{
if (name_value[i] == *UString::str_var &&
name_value[i+2].equal(U_CONSTANT_TO_PARAM("value")))
{
bvar = true;
(void) UClientImage_Base::environment->append(name_value[i+1]);
(void) UClientImage_Base::environment->append(1U, '=');
(void) UClientImage_Base::environment->append(UStringExt::expandEnvironmentVar(name_value[i+3], UClientImage_Base::environment));
(void) UClientImage_Base::environment->append(1U, '\n');
}
}
if (bvar == false)
{
name = name_value[0];
if (callService(name, name_value[1]) == false) return UString::getStringNull();
if (name == *UString::str_cgi)
{
// NB: check if we are in cgi shell script DEBUG mode...
if (u_startsWith(U_STRING_TO_PARAM(*UClientImage_Base::wbuffer), U_CONSTANT_TO_PARAM("ENVIRONMENT:\n-")))
{
pos = U_STRING_FIND(*UClientImage_Base::wbuffer, 100, "'TITLE_TXT=");
UClientImage_Base::wbuffer->erase(0, pos);
}
// NB: we check if there is an alternative response to elaborate...
UString rheader = UStringExt::getEnvironmentVar(U_CONSTANT_TO_PARAM("HTTP_RESPONSE_HEADER"), UClientImage_Base::wbuffer);
U_INTERNAL_DUMP("response_header(%u) = %V", rheader.size(), rheader.rep)
if (rheader)
{
// 2 => var environment 'HTTP_RESPONSE_HEADER' inserted in header response (var 'header')
alternative_response = 2;
uint32_t hsz = rheader.size();
UString _tmp(hsz);
UEscape::decode(rheader, _tmp);
// NB: we cannot use directly the body attribute because is bounded at the param 'content'...
UString rbody = UStringExt::getEnvironmentVar(U_CONSTANT_TO_PARAM("HTTP_RESPONSE_BODY"), UClientImage_Base::wbuffer);
U_INTERNAL_DUMP("response_body(%u) = %V", rbody.size(), rbody.rep)
if (rbody.empty()) (void) header->insert(0, _tmp);
else
{
// 3 => var environment 'HTTP_RESPONSE_HEADER' set as header response (var 'header') and
// var environment 'HTTP_RESPONSE_BODY' set as body response (var 'body')
alternative_response = 3;
*body = rbody;
// NB: with an alternative body response we cannot use the cached header response...
(void) header->replace(_tmp);
return UString::getStringNull();
}
UClientImage_Base::wbuffer->erase(0, U_CONSTANT_SIZE("HTTP_RESPONSE_HEADER=") + hsz + 3);
}
(void) UClientImage_Base::environment->append(*UClientImage_Base::wbuffer);
}
}
}
break;
}
}
while (t.atEnd() == false);
U_INTERNAL_DUMP("if_is_false = %b if_is_false_level = %d if_level = %d if_is_false_endif = %b",
if_is_false, if_is_false_level, if_level, if_is_false_endif)
if (if_is_false || if_is_false_endif || if_level || if_is_false_level) U_ERROR("SSI: syntax error for conditional statement");
U_RETURN_STRING(output);
}
// Server-wide hooks
int USSIPlugIn::handlerConfig(UFileConfig& cfg)
{
U_TRACE(0, "USSIPlugIn::handlerConfig(%p)", &cfg)
// --------------------------------------------------------------------------------------------------------------
// ENVIRONMENT path of file configuration environment for SSI
//
// SSI_AUTOMATIC_ALIASING special SSI HTML file that is recognized automatically as alias of all uri request
// without suffix (generally cause navigation directory not working)
// --------------------------------------------------------------------------------------------------------------
if (cfg.loadTable())
{
UString x = cfg.at(U_CONSTANT_TO_PARAM("ENVIRONMENT"));
if (x)
{
environment = U_NEW(UString(UStringExt::prepareForEnvironmentVar(UFile::contentOf(x))));
const char* home = U_SYSCALL(getenv, "%S", "HOME");
if (home) environment->snprintf_add("HOME=%s\n", home);
U_ASSERT_EQUALS(environment->isBinary(), false)
}
# ifdef U_ALIAS
x = cfg.at(U_CONSTANT_TO_PARAM("SSI_AUTOMATIC_ALIASING"));
if (x) UHTTP::setGlobalAlias(x); // NB: automatic alias of all uri request without suffix...
# endif
U_RETURN(U_PLUGIN_HANDLER_PROCESSED | U_PLUGIN_HANDLER_GO_ON);
}
U_RETURN(U_PLUGIN_HANDLER_GO_ON);
}
int USSIPlugIn::handlerInit()
{
U_TRACE_NO_PARAM(0, "USSIPlugIn::handlerInit()")
U_INTERNAL_ASSERT_EQUALS(body, 0)
U_INTERNAL_ASSERT_EQUALS(header, 0)
U_INTERNAL_ASSERT_EQUALS(alternative_include, 0)
body = U_NEW(UString);
header = U_NEW(UString);
alternative_include = U_NEW(UString);
U_RETURN(U_PLUGIN_HANDLER_PROCESSED | U_PLUGIN_HANDLER_GO_ON);
}
// Connection-wide hooks
int USSIPlugIn::handlerRequest()
{
U_TRACE_NO_PARAM(0, "USSIPlugIn::handlerRequest()")
U_INTERNAL_DUMP("uri = %.*S", U_HTTP_URI_TO_TRACE)
if (UClientImage_Base::isRequestNotFound() == false &&
UClientImage_Base::isRequestForbidden() == false)
{
bool bcache = UHTTP::file_data &&
u_is_ssi(UHTTP::file_data->mime_index);
U_DUMP("bcache = %b", bcache)
if (bcache ||
(UHTTP::setMimeIndex(), u_is_ssi(UHTTP::mime_index)))
{
// init
U_ASSERT(UClientImage_Base::environment->empty())
if (UHTTP::getCGIEnvironment(*UClientImage_Base::environment, U_SHELL) == false) U_RETURN(U_PLUGIN_HANDLER_ERROR);
if (environment) (void) UClientImage_Base::environment->append(*environment);
if (UHTTP::isMobile()) (void) UClientImage_Base::environment->append(U_CONSTANT_TO_PARAM("HTTP_USER_AGENT_MOBILE=1\n"));
(void) errmsg->assign(U_CONSTANT_TO_PARAM("Error"));
(void) timefmt->assign(U_CONSTANT_TO_PARAM("%A, %d-%b-%Y %T GMT"));
last_modified = UHTTP::file->st_mtime;
use_size_abbrev = true;
alternative_response = 0;
header->setBuffer(U_CAPACITY);
UClientImage_Base::setRequestNoCache();
*docname = UStringExt::basename(UHTTP::file->getPath()).copy();
// read the SSI file
if (bcache == false)
{
*body = UHTTP::file->getContent();
if (body->empty())
{
U_DEBUG("USSIPlugIn::handlerRequest() SSI file empty: %.*S", U_FILE_TO_TRACE(*UHTTP::file));
UClientImage_Base::environment->setEmpty();
UHTTP::setInternalError();
U_RETURN(U_PLUGIN_HANDLER_GO_ON);
}
}
else
{
U_INTERNAL_DUMP("UClientImage_Base::body(%u) = %V", UClientImage_Base::body->size(), UClientImage_Base::body->rep)
U_ASSERT(UHTTP::isDataFromCache())
U_INTERNAL_ASSERT_POINTER(UHTTP::file_data->array)
U_INTERNAL_ASSERT_EQUALS( UHTTP::file_data->array->size(), 2)
(void) header->append(UHTTP::getHeaderFromCache()); // NB: after now 'file_data' can change...
*body = (UHTTP::isGETorHEAD() &&
*UClientImage_Base::body
? *UClientImage_Base::body
: UHTTP::getBodyFromCache());
}
// process the SSI file
UString output = processSSIRequest(*body, 0);
U_INTERNAL_DUMP("alternative_response = %d output(%u) = %V", alternative_response, output.size(), output.rep)
if (alternative_response == 0)
{
uint32_t size = output.size();
U_INTERNAL_ASSERT_MAJOR(size,0)
# ifdef USE_LIBZ
U_INTERNAL_DUMP("U_http_is_accept_gzip = %C", U_http_is_accept_gzip)
if (U_http_is_accept_gzip &&
size > U_MIN_SIZE_FOR_DEFLATE)
# endif
{
output = UStringExt::deflate(output, 1);
size = output.size();
(void) header->insert(0, U_CONSTANT_TO_PARAM("Content-Encoding: gzip\r\n"));
}
*UHTTP::ext = *header;
if (bcache) (void) UHTTP::checkContentLength(size, U_NOT_FOUND); // NB: adjusting the size of response...
else
{
UHTTP::mime_index = U_unknow;
(void) UHTTP::ext->append(UHTTP::getHeaderMimeType(0, size, U_CTYPE_HTML));
}
U_http_info.nResponseCode = HTTP_OK;
*UClientImage_Base::body = output;
UHTTP::handlerResponse();
}
else if (alternative_response > 1)
{
// -----------------------------------------------------------------------------------------------------
// 1 => response already complete (nothing to do)
// ------------------------------------------------------------------------------------------------------
// 2 => var environment 'HTTP_RESPONSE_HEADER' inserted in header response (var 'header')
// ------------------------------------------------------------------------------------------------------
// 3 => var environment 'HTTP_RESPONSE_HEADER' set as header response (var 'header') and
// var environment 'HTTP_RESPONSE_BODY' set as body response (var 'body')
// ------------------------------------------------------------------------------------------------------
U_INTERNAL_DUMP("header(%u) = %V", header->size(), header->rep)
U_INTERNAL_DUMP(" body(%u) = %V", body->size(), body->rep)
(void) UClientImage_Base::wbuffer->replace(*header);
(void) UClientImage_Base::wbuffer->append(U_CONSTANT_TO_PARAM(U_CRLF));
U_http_info.endHeader = UClientImage_Base::wbuffer->size();
U_http_info.nResponseCode = HTTP_OK;
(void) UClientImage_Base::wbuffer->append(alternative_response == 2 ? output : *body);
if (UHTTP::processCGIOutput(true, true) == false) UHTTP::setInternalError();
}
UClientImage_Base::environment->setEmpty();
}
// U_RETURN(U_PLUGIN_HANDLER_PROCESSED | U_PLUGIN_HANDLER_GO_ON);
}
U_RETURN(U_PLUGIN_HANDLER_GO_ON);
}
// DEBUG
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
const char* USSIPlugIn::dump(bool reset) const
{
*UObjectIO::os << "last_modified " << last_modified << '\n'
<< "use_size_abbrev " << use_size_abbrev << '\n'
<< "body (UString " << (void*)body << ")\n"
<< "errmsg (UString " << (void*)errmsg << ")\n"
<< "header (UString " << (void*)header << ")\n"
<< "timefmt (UString " << (void*)timefmt << ")\n"
<< "docname (UString " << (void*)docname << ")\n"
<< "environment (UString " << (void*)environment << ")\n"
<< "alternative_include (UString " << (void*)alternative_include << ')';
if (reset)
{
UObjectIO::output();
return UObjectIO::buffer_output;
}
return 0;
}
#endif