blob: c9063f47464886170d75f4bc9f83554ddcae034f [file] [log] [blame]
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_core.h"
#include "http_protocol.h"
#include "http_log.h"
#include "http_main.h"
#include "util_script.h"
#include "http_conf_globals.h"
#include <tcl.h>
#include <string.h>
#include "tcl_commands.h"
#include "apache_request.h"
#include "apache_cookie.h"
#include "mod_dtcl.h"
extern request_rec *global_rr;
extern obuff obuffer;
extern int content_sent;
extern int buffer_output;
extern int headers_printed;
extern int cacheFreeSize;
/* Include and parse a file */
int Parse(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *filename;
struct stat finfo;
if (objc != 2)
{
Tcl_WrongNumArgs(interp, 1, objv, "filename");
return TCL_ERROR;
}
filename = Tcl_GetStringFromObj (objv[1], (int *)NULL);
if (!strcmp(filename, global_rr->filename))
{
Tcl_AddErrorInfo(interp, "Cannot recursively call the same file!");
return TCL_ERROR;
}
if (stat(filename, &finfo))
{
Tcl_AddErrorInfo(interp, Tcl_PosixError(interp));
return TCL_ERROR;
}
if (send_parsed_file(global_rr, filename, &finfo, 0) == OK)
return TCL_OK;
else
return TCL_ERROR;
}
/* Tcl command to include flat files */
int Include(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
Tcl_Channel fd;
int sz;
char buf[2000];
if (objc != 2)
{
Tcl_WrongNumArgs(interp, 1, objv, "filename");
return TCL_ERROR;
}
fd = Tcl_OpenFileChannel(interp,
Tcl_GetStringFromObj (objv[1], (int *)NULL), "r", 0664);
if (fd == NULL)
{
return TCL_ERROR;
} else {
Tcl_SetChannelOption(interp, fd, "-translation", "lf");
}
/* print_headers(global_rr);
flush_output_buffer(global_rr); */
while ((sz = Tcl_Read(fd, buf, sizeof(buf) - 1)))
{
if (sz == -1)
{
Tcl_AddErrorInfo(interp, Tcl_PosixError(interp));
return TCL_ERROR;
}
buf[sz] = '\0';
/* we could include code to either ap_pwrite this or memwrite
it, depending on buffering */
memwrite(&obuffer, buf, sz);
if (sz < sizeof(buf) - 1)
break;
}
return Tcl_Close(interp,fd);
/* close(fd); */
return TCL_OK;
}
/* Command to *only* add to the output buffer */
int Buffer_Add(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *arg1;
int len;
if (objc < 2)
{
Tcl_WrongNumArgs(interp, 1, objv, "string");
return TCL_ERROR;
}
arg1 = Tcl_GetByteArrayFromObj(objv[1], &len);
memwrite(&obuffer, arg1, len);
content_sent = 0;
return TCL_OK;
}
/* Tcl command to output some text to the web server */
int Hputs(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *arg1;
int length;
if (objc < 2)
{
Tcl_WrongNumArgs(interp, 1, objv, "?-error? string");
return TCL_ERROR;
}
arg1 = Tcl_GetByteArrayFromObj(objv[1], &length);
if (!strncmp("-error", arg1, 6))
{
if (objc != 3)
{
Tcl_WrongNumArgs(interp, 1, objv, "?-error? string");
return TCL_ERROR;
}
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE,
global_rr->server, "Mod_Dtcl Error: %s",
Tcl_GetStringFromObj (objv[2], (int *)NULL));
} else {
if (objc != 2)
{
Tcl_WrongNumArgs(interp, 1, objv, "?-error? string");
return TCL_ERROR;
}
if (buffer_output == 1)
{
memwrite(&obuffer, arg1, length);
} else {
print_headers(global_rr);
flush_output_buffer(global_rr);
ap_rwrite(arg1, length, global_rr);
}
}
return TCL_OK;
}
/* Tcl command to manipulate headers */
int Headers(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *opt;
if (objc < 2)
{
Tcl_WrongNumArgs(interp, 1, objv, "headers option arg ?arg ...?");
return TCL_ERROR;
}
if (headers_printed != 0)
{
Tcl_AddObjErrorInfo(interp, "Cannot manipulate headers - already sent", -1);
return TCL_ERROR;
}
opt = Tcl_GetStringFromObj(objv[1], NULL);
if (!strcmp("setcookie", opt)) /* ### setcookie ### */
{
int i;
ApacheCookie *cookie;
char *stringopts[12] = {NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL};
if (objc < 4 || objc > 14)
{
Tcl_WrongNumArgs(interp, 1, objv,
"headers setcookie -name cookie-name -value cookie-value ?-expires expires? ?-domain domain? ?-path path? ?-secure on/off?");
return TCL_ERROR;
}
/* SetCookie: foo=bar; EXPIRES=DD-Mon-YY HH:MM:SS; DOMAIN=domain; PATH=path; SECURE */
for (i = 0; i < objc - 2; i++)
{
stringopts[i] = Tcl_GetString(objv[i + 2]);
}
cookie = ApacheCookie_new(global_rr,
stringopts[0], stringopts[1],
stringopts[2], stringopts[3],
stringopts[4], stringopts[5],
stringopts[6], stringopts[7],
stringopts[8], stringopts[9],
stringopts[10], stringopts[11],
NULL);
ApacheCookie_bake(cookie);
}
else if (!strcmp("redirect", opt)) /* ### redirect ### */
{
if (objc != 3)
{
Tcl_WrongNumArgs(interp, 1, objv, "headers redirect new-url");
return TCL_ERROR;
}
ap_table_set(global_rr->headers_out, "Location", Tcl_GetStringFromObj (objv[2], (int *)NULL));
global_rr->status = 301;
return TCL_RETURN;
}
else if (!strcmp("set", opt)) /* ### set ### */
{
if (objc != 4)
{
Tcl_WrongNumArgs(interp, 1, objv, "set headername value");
return TCL_ERROR;
}
ap_table_set(global_rr->headers_out,
Tcl_GetStringFromObj (objv[2], (int *)NULL),
Tcl_GetStringFromObj (objv[3], (int *)NULL));
}
else if (!strcmp("type", opt)) /* ### set ### */
{
if (objc != 3)
{
Tcl_WrongNumArgs(interp, 1, objv, "type mime/type");
return TCL_ERROR;
}
set_header_type(global_rr, Tcl_GetStringFromObj(objv[2], (int *)NULL));
} else if (!strcmp("numeric", opt)) /* ### numeric ### */
{
int st = 200;
if (objc != 3)
{
Tcl_WrongNumArgs(interp, 1, objv, "numeric response code");
return TCL_ERROR;
}
if (Tcl_GetIntFromObj(interp, objv[2], &st) != TCL_ERROR)
global_rr->status = st;
else
return TCL_ERROR;
} else {
/* XXX Tcl_WrongNumArgs(interp, 1, objv, "headers option arg ?arg ...?"); */
return TCL_ERROR;
}
return TCL_OK;
}
/* turn buffering on and off */
int Buffered(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *opt = Tcl_GetStringFromObj(objv[1], NULL);
if (objc != 2)
{
Tcl_WrongNumArgs(interp, 1, objv, "on/off");
return TCL_ERROR;
}
if (!strncmp(opt, "on", 2))
{
buffer_output = 1;
} else if (!strncmp(opt, "off", 3)) {
buffer_output = 0;
print_headers(global_rr);
flush_output_buffer(global_rr);
} else {
return TCL_ERROR;
}
return TCL_OK;
}
/* Tcl command to flush the output stream */
int HFlush(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
if (objc != 1)
{
Tcl_WrongNumArgs(interp, 1, objv, NULL);
return TCL_ERROR;
}
print_headers(global_rr);
flush_output_buffer(global_rr);
ap_rflush(global_rr);
return TCL_OK;
}
/* Tcl command to get and parse any CGI and environmental variables */
/* Get the environmental variables, but do it from a tcl function, so
we can decide whether we wish to or not */
int HGetVars(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *timefmt = DEFAULT_TIME_FORMAT;
#ifndef WIN32
struct passwd *pw;
#endif /* ndef WIN32 */
char *t;
char *authorization = NULL;
time_t date = global_rr->request_time;
int i;
array_header *hdrs_arr;
table_entry *hdrs;
array_header *env_arr;
table_entry *env;
Tcl_Obj *EnvsObj = Tcl_NewStringObj("::request::ENVS", -1);
Tcl_IncrRefCount(EnvsObj);
/* ensure that the system area which holds the cgi variables is empty */
ap_clear_table(global_rr->subprocess_env);
/* retrieve cgi variables */
ap_add_cgi_vars(global_rr);
ap_add_common_vars(global_rr);
hdrs_arr = ap_table_elts(global_rr->headers_in);
hdrs = (table_entry *) hdrs_arr->elts;
env_arr = ap_table_elts(global_rr->subprocess_env);
env = (table_entry *) env_arr->elts;
/* Get the user/pass info for Basic authentication */
(const char*)authorization = ap_table_get(global_rr->headers_in, "Authorization");
if (authorization && !strcasecmp(ap_getword_nc(global_rr->pool, &authorization, ' '), "Basic"))
{
char *tmp;
char *user;
char *pass;
tmp = ap_pbase64decode(global_rr->pool, authorization);
user = ap_getword_nulls_nc(global_rr->pool, &tmp, ':');
pass = tmp;
Tcl_ObjSetVar2(interp, Tcl_NewStringObj("::request::USER", -1),
Tcl_NewStringObj("user", -1),
STRING_TO_UTF_TO_OBJ(user),
0);
Tcl_ObjSetVar2(interp, Tcl_NewStringObj("::request::USER", -1),
Tcl_NewStringObj("pass", -1),
STRING_TO_UTF_TO_OBJ(pass),
0);
}
/* These were the "include vars" */
Tcl_ObjSetVar2(interp, EnvsObj, Tcl_NewStringObj("DATE_LOCAL", -1), STRING_TO_UTF_TO_OBJ(ap_ht_time(global_rr->pool, date, timefmt, 0)), 0);
Tcl_ObjSetVar2(interp, EnvsObj, Tcl_NewStringObj("DATE_GMT", -1), STRING_TO_UTF_TO_OBJ(ap_ht_time(global_rr->pool, date, timefmt, 1)), 0);
Tcl_ObjSetVar2(interp, EnvsObj, Tcl_NewStringObj("LAST_MODIFIED", -1), STRING_TO_UTF_TO_OBJ(ap_ht_time(global_rr->pool, global_rr->finfo.st_mtime, timefmt, 0)), 0);
Tcl_ObjSetVar2(interp, EnvsObj, Tcl_NewStringObj("DOCUMENT_URI", -1), STRING_TO_UTF_TO_OBJ(global_rr->uri), 0);
Tcl_ObjSetVar2(interp, EnvsObj, Tcl_NewStringObj("DOCUMENT_PATH_INFO", -1), STRING_TO_UTF_TO_OBJ(global_rr->path_info), 0);
#ifndef WIN32
pw = getpwuid(global_rr->finfo.st_uid);
if (pw)
Tcl_ObjSetVar2(interp, EnvsObj, Tcl_NewStringObj("USER_NAME", -1), STRING_TO_UTF_TO_OBJ(ap_pstrdup(global_rr->pool, pw->pw_name)), 0);
else
Tcl_ObjSetVar2(interp, EnvsObj, Tcl_NewStringObj("USER_NAME", -1),
STRING_TO_UTF_TO_OBJ(ap_psprintf(global_rr->pool, "user#%lu", (unsigned long) global_rr->finfo.st_uid)), 0);
#endif
if ((t = strrchr(global_rr->filename, '/')))
Tcl_ObjSetVar2(interp, EnvsObj, Tcl_NewStringObj("DOCUMENT_NAME", -1), STRING_TO_UTF_TO_OBJ(++t), 0);
else
Tcl_ObjSetVar2(interp, EnvsObj, Tcl_NewStringObj("DOCUMENT_NAME", -1), STRING_TO_UTF_TO_OBJ(global_rr->uri), 0);
if (global_rr->args)
{
char *arg_copy = ap_pstrdup(global_rr->pool, global_rr->args);
ap_unescape_url(arg_copy);
Tcl_ObjSetVar2(interp, EnvsObj, Tcl_NewStringObj("QUERY_STRING_UNESCAPED", -1), STRING_TO_UTF_TO_OBJ(ap_escape_shell_cmd(global_rr->pool, arg_copy)), 0);
}
/* ---------------------------- */
/* transfer client request headers to TCL request namespace */
for (i = 0; i < hdrs_arr->nelts; ++i)
{
if (!hdrs[i].key)
continue;
else {
Tcl_ObjSetVar2(interp, EnvsObj, STRING_TO_UTF_TO_OBJ(hdrs[i].key), STRING_TO_UTF_TO_OBJ(hdrs[i].val), 0);
}
}
/* transfer apache internal cgi variables to TCL request namespace */
for (i = 0; i < env_arr->nelts; ++i)
{
if (!env[i].key)
continue;
Tcl_ObjSetVar2(interp, EnvsObj, STRING_TO_UTF_TO_OBJ(env[i].key), STRING_TO_UTF_TO_OBJ(env[i].val), 0);
}
do { /* I do this because I want some 'local' variables */
ApacheCookieJar *cookies = ApacheCookie_parse(global_rr, NULL);
Tcl_Obj *cookieobj = Tcl_NewStringObj("::request::COOKIES", -1);
for (i = 0; i < ApacheCookieJarItems(cookies); i++) {
ApacheCookie *c = ApacheCookieJarFetch(cookies, i);
int j;
for (j = 0; j < ApacheCookieItems(c); j++) {
char *name = c->name;
char *value = ApacheCookieFetch(c, j);
Tcl_ObjSetVar2(interp, cookieobj,
STRING_TO_UTF_TO_OBJ(name),
STRING_TO_UTF_TO_OBJ(value), 0);
}
}
} while (0);
/* cleanup system cgi variables */
ap_clear_table(global_rr->subprocess_env);
return TCL_OK;
}
/* Tcl command to get, and print some information about the current
state of affairs */
int Dtcl_Info(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *tble;
tble = ap_psprintf(global_rr->pool,
"<table border=0 bgcolor=green><tr><td>\n"
"<table border=0 bgcolor=\"#000000\">\n"
"<tr><td align=center bgcolor=blue><font color=\"#ffffff\" size=+2>dtcl_info</font><br></td></tr>\n"
"<tr><td><font color=\"#ffffff\">Free cache size: %d</font><br></td></tr>\n"
"<tr><td><font color=\"#ffffff\">PID: %d</font><br></td></tr>\n"
"</table>\n"
"</td></tr></table>\n", cacheFreeSize, getpid());
/* print_headers(global_rr);
flush_output_buffer(global_rr); */
memwrite(&obuffer, tble, strlen(tble));
return TCL_OK;
}
/* Tcl command to erase body, so that only header is returned.
Necessary for 304 responses */
int No_Body(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
if (content_sent == 1)
return TCL_ERROR;
print_headers(global_rr);
Tcl_Free(obuffer.buf);
obuffer.buf = NULL;
obuffer.len = 0;
return TCL_OK;
}