", Tcl_GetVar(slaveInterp, "errorInfo", TCL_GLOBAL_ONLY), "
", (char *) NULL); if (rr != NULL) ap_destroy_sub_req (rr); return TCL_ERROR; } } if (rr != NULL) ap_destroy_sub_req (rr); return TCL_OK; } /*----------------------------------------------------------------------------- * Neo_FlushBufferCmd -- * Implements the flush_page TCL command: * flush_page * * Results: * Standard TCL result. *----------------------------------------------------------------------------- */ int Neo_FlushBufferCmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { if (argc != 1) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], "\"", (char *) NULL); return TCL_ERROR; } ap_bflush (Tcl_request_rec->connection->client); return TCL_OK; } /*----------------------------------------------------------------------------- * Neo_AbortPageCmd -- * Implements the abort_page TCL command: * abort_page * * Results: * Standard TCL result. *----------------------------------------------------------------------------- */ int Neo_AbortPageCmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { if (argc != 1) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], "\"", (char *) NULL); return TCL_ERROR; } ap_bflush (Tcl_request_rec->connection->client); /*------------- * removed from http_main.c in 1.2b8 - Eugene * abort_connection(Tcl_request_rec->connection); *------------- */ Tcl_request_rec->connection->aborted = 1; return TCL_OK; } /*----------------------------------------------------------------------------- * Neo_HttpdCmd -- * Make the child apache process do things * httpd child_terminate -- Exit child when this page is done * * Results * Standard TCL result. *----------------------------------------------------------------------------- */ int Neo_HttpdCmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { if (argc != 2 || strncmp("child_terminate", argv[1], strlen(argv[1]))) { interp->result = "Usage: httpd child_terminate"; return TCL_ERROR; } /* It looks like Win32 doesn't do this... -gporter ap_child_terminate(Tcl_request_rec); */ return TCL_OK; } /*----------------------------------------------------------------------------- * Neo_UnescapeUrlCmd -- * Implements the unescape_url TCL command: * unescape_url pathname * * Results: * Standard TCL result. *----------------------------------------------------------------------------- */ int Neo_UnescapeUrlCmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { if (argc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " url\"", (char *) NULL); return TCL_ERROR; } if (ap_unescape_url(argv[1]) == OK) { interp->result = argv[1]; } return TCL_OK; } /* *----------------------------------------------------------------------------- * * Neo_RequestInfoCmd -- * Implements the Neo TCL request_info command: * request_info args * * Results: * Standard TCL results. * *----------------------------------------------------------------------------- */ int Neo_RequestInfoCmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { char intbuf[20]; char *varName; int depth; request_rec *r = Tcl_request_rec; if (argc != 2 && argc != 4) { Tcl_AppendResult (interp, "wrong # args: should be \"", argv[0], " [next|prev|main depth] arrayVarName\"", (char *) NULL); return TCL_ERROR; } if (argc == 2) { varName = argv[1]; } else { varName = argv[3]; strcpy(interp->result,"0"); if (Tcl_GetInt (interp, argv[2], &depth) == TCL_ERROR) return TCL_ERROR; if (strcmp(argv[1], "prev") == 0) { while (depth--) { if (r->prev) { r = r->prev; } else { return TCL_OK; } } } else if (strcmp(argv[1], "next") == 0) { while (depth--) { if (r->next) { r = r->next; } else { return TCL_OK; } } } else if (strcmp(argv[1], "main") == 0) { while (depth--) { if (r->main) { r = r->main; } else { return TCL_OK; } } } else { Tcl_AppendResult (interp, "bad arg: should be \"", argv[0], " [next|prev|main depth] arrayVarName\"", (char *) NULL); return TCL_ERROR; } } #define REQUEST_CHAR_VAR(X) if (Tcl_SetVar2(interp, varName, #X, r->X == (char *)NULL ? "" : (char *)(r->X), TCL_LEAVE_ERR_MSG) == NULL) return TCL_ERROR #define REQUEST_INT_VAR(X) {sprintf(intbuf, "%d", r->X); if (Tcl_SetVar2(interp, varName, #X, intbuf, TCL_LEAVE_ERR_MSG) == NULL) return TCL_ERROR;} #define REQUEST_LONG_VAR(X) {sprintf(intbuf, "%ld", r->X); if (Tcl_SetVar2(interp, varName, #X, intbuf, TCL_LEAVE_ERR_MSG) == NULL) return TCL_ERROR;} REQUEST_CHAR_VAR(the_request); REQUEST_INT_VAR(assbackwards); REQUEST_INT_VAR(header_only); REQUEST_CHAR_VAR(protocol); REQUEST_CHAR_VAR(status_line); REQUEST_INT_VAR(status); REQUEST_CHAR_VAR(method); REQUEST_INT_VAR(method_number); REQUEST_LONG_VAR(bytes_sent); REQUEST_CHAR_VAR(content_type); REQUEST_CHAR_VAR(content_encoding); REQUEST_CHAR_VAR(content_language); REQUEST_INT_VAR(no_cache); REQUEST_CHAR_VAR(uri); REQUEST_CHAR_VAR(filename); REQUEST_CHAR_VAR(path_info); REQUEST_CHAR_VAR(args); if (Tcl_SetVar2(interp, varName, "main", r->main ? "1" : "0", TCL_LEAVE_ERR_MSG) == NULL) return TCL_ERROR; if (Tcl_SetVar2(interp, varName, "prev", r->prev ? "1" : "0", TCL_LEAVE_ERR_MSG) == NULL) return TCL_ERROR; if (Tcl_SetVar2(interp, varName, "next", r->next ? "1" : "0", TCL_LEAVE_ERR_MSG) == NULL) return TCL_ERROR; sprintf(intbuf, "%d", (int)(r->finfo.st_uid)); if (Tcl_SetVar2(interp, varName, "file_uid", intbuf, TCL_LEAVE_ERR_MSG) == NULL) return TCL_ERROR; interp->result = "1"; return TCL_OK; } /* *----------------------------------------------------------------------------- * * Neo_SetHeaderCmd -- * Implements the NeoWebScript set_header command: * set_header header-name header-value ... * * Results: * Standard TCL results. * *----------------------------------------------------------------------------- */ int Tcl_SetHeaderCmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { int i, forceHeaders = 1; table *h = Tcl_request_rec->headers_out; if ((argc % 2 == 0) && !(strcmp(argv[argc - 1], "-force"))) { forceHeaders = 0; argc--; } if (argc % 2 != 1) { sprintf(interp->result, "usage: %s header value ...", argv[0]); return TCL_ERROR; } if (forceHeaders && headersSent) { sprintf(interp->result, "headers have already gone out"); return TCL_ERROR; } for (i = 1; i < argc; i += 2) ap_table_set(h, argv[i], argv[i+1]); return TCL_OK; } int NWS_MD5Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { char *digest; if (argc != 2) { sprintf(interp->result, "usage: md5 string"); return TCL_ERROR; } digest = ap_md5(Tcl_request_rec->pool, argv[1]); Tcl_SetResult(interp, digest, TCL_STATIC); return TCL_OK; } int Tcl_gm_timestr_822Cmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { char *ascii_time; int clock; if (argc != 2) { sprintf(interp->result, "usage: gm_timestr_822 time"); return TCL_ERROR; } if (Tcl_GetInt(interp, argv[1], &clock) == TCL_ERROR) return TCL_ERROR; ascii_time = ap_gm_timestr_822(Tcl_request_rec->pool, clock); Tcl_SetResult(interp, ascii_time, TCL_STATIC); return TCL_OK; } int Tcl_ExtendSafeSlaveCmd(dummy, interp, argc, argv) ClientData dummy; /* Not used. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { Tcl_Interp *slaveInterp; if (argc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " slaveInterpName\"", (char *)NULL); return TCL_ERROR; } slaveInterp = Tcl_GetSlave(interp, argv[1]); if (slaveInterp == (Tcl_Interp *)NULL) { return TCL_ERROR; } /* Do this in the setup using load so that unsafe version can be used */ /* if (Tclx_SafeInit (slaveInterp) == TCL_ERROR) { return TCL_ERROR; } */ Tcl_CreateCommand (slaveInterp, "www_request_info", Neo_RequestInfoCmd, (ClientData) NULL, (void (*)()) NULL); return TCL_OK; } /*----------------------------------------------------------------------------- * Neo_SimplifyPathnameCmd -- * Implements the simplify_pathname TCL command: * simplify_pathname pathname * * Results: * Standard TCL result. *----------------------------------------------------------------------------- */ int Neo_SimplifyPathnameCmd (clientData, interp, argc, argv) ClientData clientData; Tcl_Interp *interp; int argc; char **argv; { if (argc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " pathname\"", (char *) NULL); return TCL_ERROR; } ap_getparents(argv[1]); interp->result = argv[1]; return TCL_OK; } /* *---------------------------------------------------------------------- * * Tcl_HtmlCmd -- * * This procedure is invoked to process the "html" Tcl command. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ /* ARGSUSED */ int Tcl_HtmlCmd(dummy, interp, argc, argv) ClientData dummy; /* Not used. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */ { int i = 1, tagstart, newline = 0; if ((argc >= 2) && (argv[i][0] == '-') && ((strcmp(argv[i], "-newline") == 0) || (strcmp(argv[i], "-n") == 0))) { newline = 1; i++; } if (i >= argc) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ?-newline? string ?tag ...?\"", (char *)NULL); return TCL_ERROR; } NEO_SEND_HEADERS(Tcl_request_rec); /* argv[i] == string * argv[tagstart] = 1st tag */ tagstart = i+1; if (tagstart == argc) { ap_rprintf(Tcl_request_rec, "%s", argv[i]); } else { int c, t; for (t = tagstart; t < argc; t++) { ap_rprintf(Tcl_request_rec, "<%s>", argv[t]); } ap_rprintf (Tcl_request_rec, "%s", argv[i]); for (t--; t >= tagstart; t--) { for (c = 0; (argv[t][c] != ' ') && (argv[t][c] != '\0'); c++) {} ap_rprintf(Tcl_request_rec, "%.*s>", c, argv[t]); } } if (newline) { ap_rprintf(Tcl_request_rec, "%c", '\n'); } return TCL_OK; } /* ----------------------- Initialization function ------------------------- */ void init_nws(server_rec *s, pool *p) { time_t date; table *t; table_entry *elts; array_header *arr; int i, nelts; nws_server_config *ns = (nws_server_config *)ap_get_module_config(s->module_config ,&neoscript_module); ap_add_version_component( NWS_VERSION_COMPONENT ); if (interp) { Tcl_DeleteInterp(interp); } interp = Tcl_CreateInterp(); /* get the current time (startup time) * so we can return server uptime info */ time(&date); sprintf(softwareStartTimeString, "%ld", date); /* Initialize core Tcl components and extensions */ /* Setup a library Path */ TclpInitLibraryPath("."); /* Tcl */ if (Tcl_Init(interp) == TCL_ERROR) { fprintf(stderr, "failed to init NeoWebScript Tcl component: %s\n", interp->result); exit(1); } /* Extended Tcl */ if (Tclx_Init(interp) == TCL_ERROR) { fprintf(stderr, "failed to init NeoWebScript Tclx component: %s\n", interp->result); exit(1); } Tcl_StaticPackage(interp, "Tclx", Tclx_Init, Tclx_SafeInit); /* Tclx does its own call to Tcl_StaticPackage */ #ifdef POSTGRESQL /* PostgreSQL (v6.0+) Database Interface */ if (Pgtcl_Init(interp) == TCL_ERROR) { fprintf(stderr, "failed to init NeoWebScript PostgreSQL component: %s\n", interp->result); exit(1); } Tcl_StaticPackage(interp, "Pgtcl", Pgtcl_Init, Pgtcl_Init); #endif #ifdef POSTGRES95 /* Postgres95 Database Interface */ if (Pg_Init(interp) == TCL_ERROR) { fprintf(stderr, "failed to init NeoWebScript Postgres95 component: %s\n", interp->result); exit(1); } Tcl_StaticPackage(interp, "Pg", Pg_Init, Pg_Init); #endif #ifdef PQATCL /* Another Postgres95 Database Interface */ if (Pqa_Init(interp) == TCL_ERROR) { fprintf(stderr, "failed to init NeoWebScript Postgres-Pqa component: %s\n", interp->result); exit(1); } Tcl_StaticPackage(interp, "Pqa", Pqa_Init, Pqa_Init); #endif #ifdef GDTCL /* GIF generation*/ if (Gd_Init(interp) == TCL_ERROR) { fprintf(stderr, "failed to init NeoWebScript Gd component: %s\n", interp->result); exit(1); } Tcl_StaticPackage(interp, "Gd", Gd_Init, Gd_Init); #endif /* NeoSoft Extensions */ if (Neo_Init(interp) == TCL_ERROR) { fprintf(stderr, "failed to init NeoWebScript Neo component: %s\n", interp->result); exit(1); } Tcl_StaticPackage(interp, "Neo", Neo_Init, NULL); /* * copy any variables defined with neowebscript server config commands * into a Tcl array */ t = ns->nws_server_vars; arr = ap_table_elts(t); elts = (table_entry *)arr->elts; nelts = arr->nelts; for (i = 0; i < nelts; ++i) Tcl_SetVar2(interp, "NeoWebServerConf", elts[i].key, elts[i].val, TCL_GLOBAL_ONLY); /* Tcl_InitMath (interp); */ Tcl_InitExtensions(interp); Tcl_SetVar2(interp, "server", "SERVER_ROOT", ap_server_root_relative(p, "."), TCL_GLOBAL_ONLY); Tcl_SetVar2(interp, "server", "SERVER_CONF", ap_server_confname, TCL_GLOBAL_ONLY); if (Tcl_VarEval(interp, "source ", ap_server_root_relative(p, "neowebscript/init.tcl"), (char *)NULL) == TCL_ERROR) { char *errorInfo; errorInfo = Tcl_GetVar (interp, "errorInfo", TCL_GLOBAL_ONLY); fprintf(stderr,"NeoWebScript startup failed: %s\n", errorInfo); exit(1); } } void Tcl_InitExtensions(Tcl_Interp *interp) { Tcl_CreateCommand(interp, "SAFE_include_file", Neo_IncludeCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, "SAFE_include_virtual", Neo_IncludeCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, "SAFE_load_file", Neo_IncludeCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, "SAFE_load_virtual", Neo_IncludeCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, "flush_page", Neo_FlushBufferCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, "abort_page", Neo_AbortPageCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, "httpd", Neo_HttpdCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, "extend_slave", Tcl_ExtendSafeSlaveCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, "html", Tcl_HtmlCmd, (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateCommand(interp, "www_simplify_pathname", Neo_SimplifyPathnameCmd, (ClientData) NULL, (void (*)()) NULL); Tcl_CreateCommand(interp, "www_unescape_url", Neo_UnescapeUrlCmd, (ClientData) NULL, (void (*)()) NULL); Tcl_CreateCommand(interp, "www_request_info", Neo_RequestInfoCmd, (ClientData) NULL, (void (*)()) NULL); Tcl_CreateCommand(interp, "set_header", Tcl_SetHeaderCmd, (ClientData) NULL, (void (*)()) NULL); Tcl_CreateCommand(interp, "md5", NWS_MD5Cmd, (ClientData) NULL, (void (*)()) NULL); Tcl_CreateCommand(interp, "gm_timestr_822", Tcl_gm_timestr_822Cmd, (ClientData) NULL, (void (*)()) NULL); } /* ------------------------ Environment function -------------------------- */ static void add_include_vars(request_rec *r, char *timefmt) { #ifndef WIN32 struct passwd *pw; #endif /* WIN32 */ table *e = r->subprocess_env; char *t; time_t date = time(NULL); ap_table_setn(e, "DATE_LOCAL", ap_ht_time(r->pool, date, timefmt, 0)); ap_table_setn(e, "DATE_GMT", ap_ht_time(r->pool, date, timefmt, 1)); ap_table_setn(e, "LAST_MODIFIED", ap_ht_time(r->pool, r->finfo.st_mtime, timefmt, 0)); ap_table_setn(e, "DOCUMENT_URI", r->uri); ap_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info); #ifndef WIN32 pw = getpwuid(r->finfo.st_uid); if (pw) { ap_table_setn(e, "USER_NAME", ap_pstrdup(r->pool, pw->pw_name)); } else { ap_table_setn(e, "USER_NAME", ap_psprintf(r->pool, "user#%lu", (unsigned long) r->finfo.st_uid)); } #endif /* ndef WIN32 */ if ((t = strrchr(r->filename, '/'))) { ap_table_setn(e, "DOCUMENT_NAME", ++t); } else { ap_table_setn(e, "DOCUMENT_NAME", r->uri); } if (r->args) { char *arg_copy = ap_pstrdup(r->pool, r->args); ap_unescape_url(arg_copy); ap_table_setn(e, "QUERY_STRING_UNESCAPED", ap_escape_shell_cmd(r->pool, arg_copy)); } } /* --------------------------- Parser functions --------------------------- */ static char *http2env(pool *a, char *w) { char *res = ap_pstrcat (a, "HTTP_", w, NULL); char *cp = res; while (*++cp) if (*cp == '-') *cp = '_'; else *cp = toupper(*cp); return res; } #define OUTBUFSIZE 4096 /* PUT_CHAR and FLUSH_BUF currently only work within the scope of * find_string(); they are hacks to avoid calling rputc for each and * every character output. A common set of buffering calls for this * type of output SHOULD be implemented. */ #define PUT_CHAR(c,r) \ { \ outbuf[outind++] = c; \ if (outind == OUTBUFSIZE) { \ FLUSH_BUF(r) \ }; \ } /* there SHOULD be some error checking on the return value of * rwrite, however it is unclear what the API for rwrite returning * errors is and little can really be done to help the error in * any case. */ #define FLUSH_BUF(r) \ { \ ap_rwrite(outbuf, outind, r); \ outind = 0; \ } /* * f: file handle being read from * c: character to read into * ret: return value to use if input fails * r: current request_rec * * This macro is redefined after find_string() for historical reasons * to avoid too many code changes. This is one of the many things * that should be fixed. */ #define GET_CHAR(f,c,ret,r) \ { \ int i = getc(f); \ if (i == EOF) { /* either EOF or error -- needs error handling if latter */ \ if (ferror(f)) { \ fprintf(stderr, "encountered error in GET_CHAR macro, " \ "mod_neoscript.\n"); \ } \ FLUSH_BUF(r); \ ap_pfclose(r->pool, f); \ return ret; \ } \ c = (char)i; \ } static int find_string(FILE *in, const char *str, request_rec *r, int printing) { int x, l = strlen(str), p; char outbuf[OUTBUFSIZE]; int outind = 0; char c; p = 0; while (1) { GET_CHAR(in, c, 1, r); if (c == str[p]) { if ((++p) == l) { FLUSH_BUF(r); return 0; } } else { if (printing) { for (x = 0; x < p; x++) { PUT_CHAR(str[x], r); } PUT_CHAR(c, r); } p = 0; } } } /* A hack of find_string to find both the normal SSI starting sequence, and * NeoWebScript's new tag-like HTML sequence. It may be more efficient on * servers with more RAM to slurp the entire buffer into a Tcl interpreter. * - Eugene */ int find_string2(FILE *in, char *str1, char *str2, request_rec *r, int *result, int printing) { int x, l1 = strlen(str1), l2 = strlen(str2), p, p1, p2, m1, m2; char c, *str, outbuf[OUTBUFSIZE]; int outind = 0; p1 = p2 = 0; while(1) { GET_CHAR(in, c, 1, r); m1 = m2 = 0; if(c == str1[p1]) { ++m1; if ((++p1) == l1) { FLUSH_BUF(r); *result = 1; return 0; } } if (c == str2[p2]) { ++m2; if ((++p2) == l2) { FLUSH_BUF(r); *result = 2; return 0; } } if (!m1 && !m2) { NEO_SEND_HEADERS(r); p = (p2 > p1) ? p2 : p1; str = (p2 > p1) ? str2 : str1; if (printing) { for (x=0; x
= 11 && val <= 31) ||
(val >= 127 && val <= 160) || val >= 256) {
p--; /* no data to output */
}
else {
*p = RAW_ASCII_CHAR(val);
}
}
else {
j = i - 1;
if (j > MAXENTLEN || entlist[j] == NULL) {
/* wrong length */
*p = '&';
continue; /* skip it */
}
for (ents = entlist[j]; *ents != '\0'; ents += i) {
if (strncmp(s + 1, ents, j) == 0) {
break;
}
}
if (*ents == '\0') {
*p = '&'; /* unknown */
}
else {
*p = RAW_ASCII_CHAR(((const unsigned char *) ents)[j]);
s += i;
}
}
}
*p = '\0';
}
/*
* extract the next tag name and value.
* if there are no more tags, set the tag name to 'done'
* the tag value is html decoded if dodecode is non-zero
*/
static char *get_tag(pool *p, FILE *in, char *tag, int tagbuf_len, int dodecode)
{
char *t = tag, *tag_val, c, term;
/* makes code below a little less cluttered */
--tagbuf_len;
do { /* skip whitespace */
GET_CHAR(in, c, NULL, p);
} while (ap_isspace(c));
/* tags can't start with - */
if (c == '-') {
GET_CHAR(in, c, NULL, p);
if (c == '-') {
do {
GET_CHAR(in, c, NULL, p);
} while (ap_isspace(c));
if (c == '>') {
ap_cpystrn(tag, "done", tagbuf_len);
return tag;
}
}
return NULL; /* failed */
}
/* find end of tag name */
while (1) {
if (t - tag == tagbuf_len) {
*t = '\0';
return NULL;
}
if (c == '=' || ap_isspace(c)) {
break;
}
*(t++) = ap_tolower(c);
GET_CHAR(in, c, NULL, p);
}
*t++ = '\0';
tag_val = t;
while (ap_isspace(c)) {
GET_CHAR(in, c, NULL, p); /* space before = */
}
if (c != '=') {
ungetc(c, in);
return NULL;
}
do {
GET_CHAR(in, c, NULL, p); /* space after = */
} while (ap_isspace(c));
/* we should allow a 'name' as a value */
if (c != '"' && c != '\'') {
return NULL;
}
term = c;
while (1) {
GET_CHAR(in, c, NULL, p);
if (t - tag == tagbuf_len) {
*t = '\0';
return NULL;
}
/* Want to accept \" as a valid character within a string. */
if (c == '\\') {
*(t++) = c; /* Add backslash */
GET_CHAR(in, c, NULL, p);
if (c == term) { /* Only if */
*(--t) = c; /* Replace backslash ONLY for terminator */
}
}
else if (c == term) {
break;
}
*(t++) = c;
}
*t = '\0';
if (dodecode) {
decodehtml(tag_val);
}
return ap_pstrdup(p, tag_val);
}
static char *
get_nws_code(pool *p, FILE *in, char *dummy, int codebuf_len, char *sequence) {
char *t = dummy, *code_val, c;
int n, l=(strlen(sequence)-1), cp;
n = 0;
do { /* skip whitespace */
GET_CHAR(in,c,NULL,p);
} while (ap_isspace(c));
cp=0;
code_val = t;
while(1) {
if(++n == codebuf_len) {
t[codebuf_len - 1] = '\0';
return NULL;
}
if(c == sequence[cp]) {
*(t++) = c;
if(cp == l)
break;
else
cp++;
GET_CHAR(in,c,NULL,p);
continue;
}
else if(cp > 0)
cp=0;
*(t++) = c;
GET_CHAR(in,c,NULL,p);
}
for(n=0;n<=l;n++) {
t--;
*t = '\0';
}
return ap_pstrdup (p, code_val);
}
static int get_directive(FILE *in, char *dest, size_t len, pool *p)
{
char *d = dest;
char c;
/* make room for nul terminator */
--len;
/* skip initial whitespace */
while (1) {
GET_CHAR(in, c, 1, p);
if (!ap_isspace(c)) {
break;
}
}
/* now get directive */
while (1) {
if (d - dest == len) {
return 1;
}
*d++ = ap_tolower(c);
GET_CHAR(in, c, 1, p);
if (ap_isspace(c)) {
break;
}
}
*d = '\0';
return 0;
}
/*
* Do variable substitution on strings
*/
static void parse_string(request_rec *r, const char *in, char *out,
size_t length, int leave_name)
{
char ch;
char *next = out;
char *end_out;
/* leave room for nul terminator */
end_out = out + length - 1;
while ((ch = *in++) != '\0') {
switch (ch) {
case '\\':
if (next == end_out) {
/* truncated */
*next = '\0';
return;
}
if (*in == '$') {
*next++ = *in++;
}
else {
*next++ = ch;
}
break;
case '$':
{
char var[MAX_STRING_LEN];
const char *start_of_var_name;
const char *end_of_var_name; /* end of var name + 1 */
const char *expansion;
const char *val;
size_t l;
/* guess that the expansion won't happen */
expansion = in - 1;
if (*in == '{') {
++in;
start_of_var_name = in;
in = strchr(in, '}');
if (in == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Missing '}' on variable \"%s\"",
expansion);
*next = '\0';
return;
}
end_of_var_name = in;
++in;
}
else {
start_of_var_name = in;
while (ap_isalnum(*in) || *in == '_') {
++in;
}
end_of_var_name = in;
}
/* what a pain, too bad there's no ap_table_getn where you can
* pass a non-nul terminated string */
l = end_of_var_name - start_of_var_name;
if (l != 0) {
l = (l > sizeof(var) - 1) ? (sizeof(var) - 1) : l;
memcpy(var, start_of_var_name, l);
var[l] = '\0';
val = ap_table_get(r->subprocess_env, var);
if (val) {
expansion = val;
l = strlen(expansion);
}
else if (leave_name) {
l = in - expansion;
}
else {
break; /* no expansion to be done */
}
}
else {
/* zero-length variable name causes just the $ to be copied */
l = 1;
}
l = (l > end_out - next) ? (end_out - next) : l;
memcpy(next, expansion, l);
next += l;
break;
}
default:
if (next == end_out) {
/* truncated */
*next = '\0';
return;
}
*next++ = ch;
break;
}
}
*next = '\0';
return;
}
/* ------------------------ Environment function -------------------------- */
void propagate_vars_to_nws(Tcl_Interp *interp, request_rec *r)
{
server_rec *s = r->server;
conn_rec *c = r->connection;
char *t, *tmpbuf;
char timeTextBuf[16], portbuf[10];
array_header *hdrs_arr = ap_table_elts (r->headers_in);
table_entry *hdrs = (table_entry *)hdrs_arr->elts;
int i;
/* First, add environment vars from headers... this is as per
* CGI specs, though other sorts of scripting interfaces see
* the same vars...
*/
Tcl_UnsetVar(interp, "webenv", TCL_GLOBAL_ONLY);
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (!hdrs[i].key) continue;
/* A few headers are special cased --- Authorization to prevent
* rogue scripts from capturing passwords; content-type and -length
* for no particular reason.
*/
if (!strcasecmp (hdrs[i].key, "Content-type"))
Tcl_SetVar2(interp, "webenv", "CONTENT_TYPE",
hdrs[i].val, TCL_GLOBAL_ONLY);
else if (!strcasecmp (hdrs[i].key, "Content-length"))
Tcl_SetVar2(interp, "webenv", "CONTENT_LENGTH",
hdrs[i].val, TCL_GLOBAL_ONLY);
else if (!strcasecmp (hdrs[i].key, "Authorization"))
continue;
else
Tcl_SetVar2(interp, "webenv", http2env (r->pool, hdrs[i].key),
hdrs[i].val, TCL_GLOBAL_ONLY);
}
Tcl_SetVar2(interp, "webenv", "SERVER_SOFTWARE",
(char *)ap_get_server_version(), TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "SERVER_ADMIN",
s->server_admin, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "SERVER_NAME",
s->server_hostname, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "NEOSCRIPT_VERSION",
NEOWEBSCRIPT_VERSION, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "NEOWEBSCRIPT_VERSION",
NEOWEBSCRIPT_VERSION, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "NEO_SOFTWARE_START",
softwareStartTimeString, TCL_GLOBAL_ONLY);
sprintf(portbuf, "%u", ap_get_server_port(r));
Tcl_SetVar2(interp, "webenv", "SERVER_PORT",
portbuf, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "SERVER_ROOT",
ap_server_root, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "REMOTE_HOST",
(char *)ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME),
TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "REMOTE_ADDR",
c->remote_ip, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "DOCUMENT_ROOT",
(char *)ap_document_root(r), TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "SCRIPT_FILENAME",
r->filename, TCL_GLOBAL_ONLY);
if (c->user)
Tcl_SetVar2(interp, "webenv", "REMOTE_USER",
c->user, TCL_GLOBAL_ONLY);
if (c->ap_auth_type)
Tcl_SetVar2(interp, "webenv", "AUTH_TYPE",
c->ap_auth_type, TCL_GLOBAL_ONLY);
if (c->remote_logname)
Tcl_SetVar2(interp, "webenv", "REMOTE_IDENT",
c->remote_logname, TCL_GLOBAL_ONLY);
/* Apache custom error responses. If we have redirected set two new vars */
if (r->prev) {
if (r->prev->args)
Tcl_SetVar2(interp, "webenv",
"REDIRECT_QUERY_STRING", r->prev->args, TCL_GLOBAL_ONLY);
if (r->prev->uri)
Tcl_SetVar2(interp, "webenv",
"REDIRECT_URL", r->prev->uri, TCL_GLOBAL_ONLY);
}
/* To prevent "discards const" warning */
tmpbuf = ckalloc((int) sizeof(r->method) + 1);
strcpy(tmpbuf, r->method);
/* these four are normally for CGI's */
Tcl_SetVar2(interp, "webenv", "GATEWAY_INTERFACE",
"CGI/1.1", TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "SERVER_PROTOCOL",
r->protocol, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "REQUEST_METHOD",
tmpbuf, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "DOCUMENT_URI",
r->uri, TCL_GLOBAL_ONLY);
if ((t = strrchr(r->filename, '/')))
Tcl_SetVar2(interp, "webenv", "DOCUMENT_NAME",
++t, TCL_GLOBAL_ONLY);
else
Tcl_SetVar2(interp, "webenv", "DOCUMENT_NAME",
r->uri, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "DOCUMENT_PATH_INFO",
r->path_info ? r->path_info : "", TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "QUERY_STRING",
r->args ? r->args : "", TCL_GLOBAL_ONLY);
sprintf (timeTextBuf, "%ld", r->finfo.st_mtime);
Tcl_SetVar2(interp, "webenv", "NEO_LAST_MODIFIED",
timeTextBuf, TCL_GLOBAL_ONLY);
sprintf (timeTextBuf, "%ld", r->finfo.st_uid);
Tcl_SetVar2(interp, "webenv", "NEO_DOCUMENT_UID",
timeTextBuf, TCL_GLOBAL_ONLY);
Tcl_SetVar2(interp, "webenv", "NEO_TIME_FORMAT",
DEFAULT_TIME_FORMAT, TCL_GLOBAL_ONLY);
ckfree(tmpbuf);
}
/* --------------------------- Action handlers ---------------------------- */
int run_pickfile_req (request_rec *r)
{
int errstatus;
FILE *f;
char buf[IOBUFSIZE];
int nLines;
int whichLine;
if (r->method_number != M_GET) return DECLINED;
if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"access to %s failed for %s, reason %s", r->filename,
ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_NAME),
"File does not exist");
return HTTP_NOT_FOUND;
}
ap_set_last_modified(r);
if ((errstatus = ap_set_content_length (r, r->finfo.st_size))
|| ((errstatus = ap_meets_conditions (r)) != OK))
return errstatus;
f = fopen (r->filename, "r");
if (f == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"access to %s failed for %s, reason %s", r->filename,
ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_NAME),
"file permissions deny server access");
return HTTP_FORBIDDEN;
}
if ((fgets(buf, IOBUFSIZE, f) != NULL) || (errno == EINTR)) {
nLines = atoi(buf);
if (nLines <= 0) {
fclose (f);
return OK;
}
ap_soft_timeout("send-pick", r);
srand((int)(getpid() * 17 + time((long *) 0)));
for (whichLine = rand() % nLines; whichLine-- >= 0; ) {
if ((fgets(buf,IOBUFSIZE,f)) == NULL) {
if (errno != EINTR) break;
}
}
ap_rprintf (r, "%s", buf);
}
fclose(f);
return OK;
}
static int include_cgi(char *s, request_rec *r)
{
request_rec *rr = ap_sub_req_lookup_uri(s, r);
int rr_status;
if (rr->status != HTTP_OK) {
return -1;
}
/* No hardwired path info or query allowed */
if ((rr->path_info && rr->path_info[0]) || rr->args) {
return -1;
}
if (rr->finfo.st_mode == 0) {
return -1;
}
/* Script gets parameters of the *document*, for back compatibility */
rr->path_info = r->path_info; /* hard to get right; see mod_cgi.c */
rr->args = r->args;
/* Force sub_req to be treated as a CGI request, even if ordinary
* typing rules would have called it something else.
*/
rr->content_type = CGI_MAGIC_TYPE;
/* Run it. */
rr_status = ap_run_sub_req(rr);
if (ap_is_HTTP_REDIRECT(rr_status)) {
const char *location = ap_table_get(rr->headers_out, "Location");
location = ap_escape_html(rr->pool, location);
ap_rvputs(r, "", location, "", NULL);
}
ap_destroy_sub_req(rr);
#ifndef WIN32
ap_chdir_file(r->filename);
#endif
return 0;
}
/* ensure that path is relative, and does not contain ".." elements
* ensentially ensure that it does not match the regex:
* (^/|(^|/)\.\.(/|$))
* XXX: this needs os abstraction... consider c:..\foo in win32
*/
static int is_only_below(const char *path)
{
#if WIN32
if (path[1] == ':')
return 0;
#endif
if (path[0] == '/') {
return 0;
}
if (path[0] == '.' && path[1] == '.'
&& (path[2] == '\0' || path[2] == '/')) {
return 0;
}
while (*path) {
if (*path == '/' && path[1] == '.' && path[2] == '.'
&& (path[3] == '\0' || path[3] == '/')) {
return 0;
}
++path;
}
return 1;
}
static int handle_include(FILE *in, request_rec *r, const char *error, int noexec)
{
char tag[MAX_STRING_LEN];
char parsed_string[MAX_STRING_LEN];
char *tag_val;
while(1) {
if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
return 1;
}
if (!strcmp(tag, "file") || !strcmp (tag, "virtual")) {
request_rec *rr=NULL;
char *error_fmt = NULL;
parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
if (tag[0] == 'f') {
/* be safe; only files in this directory or below allowed */
if (!is_only_below(parsed_string)) {
error_fmt = "unable to include file \"%s\" "
"in parsed file %s";
}
else {
rr = ap_sub_req_lookup_file(parsed_string, r);
}
}
else {
rr = ap_sub_req_lookup_uri (parsed_string, r);
}
if (!error_fmt && noexec && rr->content_type
&& (strncmp(rr->content_type, "text/", 5))) {
error_fmt = "unable to include potential exec \"%s\" "
"in parsed file %s";
}
if (error_fmt == NULL) {
request_rec *p;
for (p = r; p != NULL; p = p->main) {
if (strcmp(p->filename, rr->filename) == 0) {
break;
}
}
if (p != NULL) {
error_fmt = "Recursive include of \"%s\" "
"in parsed file %s";
}
}
/* see the Kludge in send_parsed_file for why */
if (rr) {
ap_set_module_config(rr->request_config, &neoscript_module, r);
}
if (!error_fmt && ap_run_sub_req(rr)) {
error_fmt = "unable to include \"%s\" in parsed file %s";
}
if (!error_fmt && ap_run_sub_req (rr)) {
error_fmt = "unable to include \"%s\" in parsed file %s";
}
#ifndef WIN32
ap_chdir_file(r->filename);
#endif
if (error_fmt) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
error_fmt, tag_val, r->filename);
ap_rputs(error, r);
}
/* destroy the sub request if it's not a nested include */
if (rr != NULL
&& ap_get_module_config(rr->request_config, &neoscript_module)
!= NESTED_INCLUDE_MAGIC) {
ap_destroy_sub_req (rr);
}
}
else if (!strcmp(tag, "pickfile") || !strcmp(tag, "pickvirtual")) {
request_rec *rr=NULL;
char *error_fmt = NULL;
if (tag[4] == 'f')
{ /* be safe; only files in this directory or below allowed */
char tmp[MAX_STRING_LEN+2];
sprintf(tmp, "/%s/", tag_val);
if (tag_val[0] == '/' || strstr(tmp, "/../") != NULL)
error_fmt = "unable to include file %s in parsed file %s";
else
rr = ap_sub_req_lookup_file (tag_val, r);
} else
rr = ap_sub_req_lookup_uri (tag_val, r);
if (!error_fmt && rr->status != 200)
error_fmt = "unable to include %s in parsed file %s";
if (!error_fmt && noexec && rr->content_type
&& (strncmp (rr->content_type, "text/", 5)))
error_fmt =
"unable to include potential exec %s in parsed file %s";
if (!error_fmt && run_pickfile_req (rr))
error_fmt = "unable to pick from %s in parsed file %s";
if (error_fmt) {
ap_log_printf(r->server, error_fmt, tag_val, r->filename);
ap_rputs(error, r);
}
if (rr != NULL) ap_destroy_sub_req (rr);
}
else if (!strcmp(tag, "counter")) {
extern Tcl_Interp *interp;
if (Tcl_VarEval(interp, "db_include_counter ", r->filename,
" ", tag_val, (char *)NULL) == TCL_ERROR)
{
char *errorInfo;
errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
fprintf(stderr,"error with include counter: %s\n", errorInfo);
} else {
ap_rprintf(Tcl_request_rec, "%s", interp->result);
}
/*
nws_server_config *ns;
table *t;
array_header *arr;
table_entry *elts;
int nelts, i;
char *dbfname = NULL;
ns = (nws_server_config *)ap_get_module_config(r->server->module_config ,&neoscript_module);
t = ns->nws_server_vars;
arr = ap_table_elts(t);
elts = (table_entry *)arr->elts;
nelts = arr->nelts;
for (i = 0; i < nelts; ++i)
{
if (!strcmp(elts[i].key, "UrlAccessCounter")) {
dbfname = ap_server_root_relative(r->pool, elts[i].val);
}
}
if (!dbfname) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, r,
"UrlAccessCounter path undefined for parsed file %s",
r->filename);
} else {
extern int errno;
int count;
u_int32_t locker;
char newNumberText[16];
char *error_fmt = NULL;
DB *db;
DBT key;
DBT data;
DBT lock_dbt;
ino_t *ino;
struct stat lock_stat;
stat(dbfname, &lock_stat);
ino = malloc(sizeof(ino_t));
*ino = lock_stat.st_ino;
lock_dbt.data = ino;
lock_dbt.size = sizeof(lock_stat.st_ino);
locker = (u_int32_t) getpid();
if ((errno = db_open(dbfname, DB_HASH, DB_CREATE, 0644, NULL,
NULL, &db))) {
error_fmt = "[%s: %s]";
ap_log_printf(r->server, error_fmt, dbfname,
strerror(errno));
ap_rprintf(r, error_fmt, dbfname, strerror(errno));
} else {
key.data = r->filename;
key.size = strlen(r->filename) + 1;
if (db->get(db, NULL, &key, &data, 0) != 0) {
count = 0;
} else {
count = atoi(data.data);
}
if (ap_isdigit(tag_val[0])) {
int defcount = atoi(tag_val);
if (count < defcount)
count = defcount;
}
sprintf(newNumberText, "%d", ++count);
data.data = newNumberText;
data.size = strlen(newNumberText) + 1;
if (db->put(db, NULL, &key, &data, 0) != 0) {
db->close(db, NULL);
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"dbput failed! %s in %s, errno=%d",
key, data, errno);
ap_rprintf(r,"%s",error);
}
if (db->sync(db, 0) < 0) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"dbsync failed! errno=%d", errno);
ap_rprintf(r,"%s",error);
}
if (db->close(db, NULL) < 0) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"dbclose failed! errno=%d", errno);
ap_rprintf(r,"%s",error);
}
ap_rprintf(r, "%s", newNumberText);
}
free(ino);
}
*/
}
else if (!strcmp(tag, "done")) {
return 0;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag include in %s",
tag, r->filename);
ap_rputs(error, r);
}
}
}
typedef struct {
request_rec *r;
char *s;
} include_cmd_arg;
static int include_cmd_child (void *arg, child_info *pinfo)
{
request_rec *r = ((include_cmd_arg *)arg)->r;
char *s = ((include_cmd_arg *)arg)->s;
table *env = r->subprocess_env;
int child_pid=0;
#ifdef DEBUG_INCLUDE_CMD
FILE *dbg = fopen("/dev/tty", "w");
#endif
#ifndef WIN32
char err_string [MAX_STRING_LEN];
#endif
#ifdef DEBUG_INCLUDE_CMD
#ifdef __EMX__
/* under OS/2 /dev/tty is referenced as con */
FILE *dbg = fopen("con", "w");
#else
fprintf (dbg, "Attempting to include command '%s'\n", s);
#endif
#endif
if (r->path_info && r->path_info[0] != '\0') {
request_rec *pa_req;
ap_table_setn(env, "PATH_INFO", ap_escape_shell_cmd(r->pool, r->path_info));
pa_req = ap_sub_req_lookup_uri(ap_escape_uri(r->pool, r->path_info), r);
if (pa_req->filename)
ap_table_setn(env, "PATH_TRANSLATED",
ap_pstrcat(r->pool, pa_req->filename, pa_req->path_info,
NULL));
}
if (r->args) {
char *arg_copy = ap_pstrdup(r->pool, r->args);
ap_table_setn(env, "QUERY_STRING", r->args);
ap_unescape_url(arg_copy);
ap_table_setn(env, "QUERY_STRING_UNESCAPED",
ap_escape_shell_cmd(r->pool, arg_copy));
}
ap_error_log2stderr(r->server);
#ifdef DEBUG_INCLUDE_CMD
fprintf(dbg, "Attempting to exec '%s'\n", s);
#endif
ap_cleanup_for_exec();
/* set shellcmd flag to pass arg to SHELL_PATH */
child_pid = ap_call_exec(r, pinfo, s, ap_create_environment(r->pool, env),
1);
#ifdef WIN32
return (child_pid);
#else
/* Oh, drat. We're still here. The log file descriptors are closed,
* so we have to whimper a complaint onto stderr...
*/
#ifdef DEBUG_NEOINCLUDE_CMD
fprintf(dbg, "Exec failed\n");
#endif
ap_snprintf(err_string, sizeof(err_string),
"httpd: exec of %s failed, reason: %s (errno = %d)\n",
SHELL_PATH, strerror(errno), errno);
write (STDERR_FILENO, err_string, strlen(err_string));
exit(0);
/* NOT REACHED */
return (child_pid);
#endif /* WIN32 */
}
static int include_cmd(char *s, request_rec *r)
{
include_cmd_arg arg;
BUFF *script_in;
arg.r = r;
arg.s = s;
if (!ap_bspawn_child(r->pool, include_cmd_child, &arg,
kill_after_timeout, NULL, &script_in, NULL)) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"couldn't spawn include command");
return -1;
}
ap_send_fb(script_in, r);
ap_bclose(script_in);
return 0;
}
static int handle_exec(FILE *in, request_rec *r, const char *error)
{
char tag[MAX_STRING_LEN];
char *tag_val;
char *file = r->filename;
char parsed_string[MAX_STRING_LEN];
while (1) {
if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
return 1;
}
if (!strcmp(tag, "cmd")) {
parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1);
if (include_cmd(parsed_string, r) == -1) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"execution failure for parameter \"%s\" "
"to tag exec in file %s", tag, r->filename);
ap_rputs(error, r);
}
/* just in case some stooge changed directories */
#ifndef WIN32
ap_chdir_file(r->filename);
#endif
}
else if (!strcmp(tag, "cgi")) {
parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
if (include_cgi(parsed_string, r) == -1) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"invalid CGI ref \"%s\" in %s", tag_val, file);
ap_rputs(error, r);
}
/* grumble groan */
#ifndef WIN32
ap_chdir_file(r->filename);
#endif
}
else if (!strcmp(tag, "done")) {
return 0;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag exec in %s",
tag, file);
ap_rputs(error, r);
}
}
}
static int handle_echo (FILE *in, request_rec *r, const char *error) {
char tag[MAX_STRING_LEN];
char *tag_val;
while (1) {
if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
return 1;
}
if (!strcmp(tag, "var")) {
const char *val = ap_table_get(r->subprocess_env, tag_val);
if (val) {
ap_rputs(val, r);
}
else {
ap_rputs("(none)", r);
}
} else if (!strcmp(tag, "done")) {
return 0;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag echo in %s",
tag, r->filename);
ap_rputs(error, r);
}
}
}
#ifdef USE_PERL_SSI
static int handle_perl (FILE *in, request_rec *r, const char *error)
{
char tag[MAX_STRING_LEN];
char parsed_string[MAX_STRING_LEN];
char *tag_val;
SV *sub = Nullsv;
AV *av = newAV();
if (!(ap_allow_options(r) & OPT_INCLUDES)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"httpd: #perl SSI disallowed by IncludesNoExec in %s",
r->filename);
return DECLINED;
}
while (1) {
if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
break;
}
if (strnEQ(tag, "sub", 3)) {
sub = newSVpv(tag_val, 0);
}
else if (strnEQ(tag, "arg", 3)) {
parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
av_push(av, newSVpv(parsed_string, 0));
}
else if (strnEQ(tag, "done", 4)) {
break;
}
}
perl_stdout2client(r);
perl_call_handler(sub, r, av);
return OK;
}
#endif
/* error and tf must point to a string with room for at
* least MAX_STRING_LEN characters
*/
static int handle_config(FILE *in, request_rec *r, char *error, char *tf,
int *sizefmt)
{
char tag[MAX_STRING_LEN];
char *tag_val;
char parsed_string[MAX_STRING_LEN];
table *env = r->subprocess_env;
while (1) {
if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0))) {
return 1;
}
if (!strcmp(tag, "errmsg")) {
parse_string(r, tag_val, error, MAX_STRING_LEN, 0);
}
else if (!strcmp(tag, "timefmt")) {
time_t date = r->request_time;
parse_string(r, tag_val, tf, MAX_STRING_LEN, 0);
ap_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, tf, 0));
ap_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, tf, 1));
ap_table_setn(env, "LAST_MODIFIED",
ap_ht_time(r->pool, r->finfo.st_mtime, tf, 0));
}
else if (!strcmp(tag, "sizefmt")) {
parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
decodehtml(parsed_string);
if (!strcmp(parsed_string, "bytes")) {
*sizefmt = SIZEFMT_BYTES;
}
else if (!strcmp(parsed_string, "abbrev")) {
*sizefmt = SIZEFMT_KMG;
}
}
else if (!strcmp(tag, "done")) {
return 0;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag config in %s",
tag, r->filename);
ap_rputs(error, r);
}
}
}
static int find_file(request_rec *r, const char *directive, const char *tag,
char *tag_val, struct stat *finfo, const char *error)
{
char *to_send;
if (!strcmp(tag, "file")) {
ap_getparents(tag_val); /* get rid of any nasties */
to_send = ap_make_full_path(r->pool, "./", tag_val);
if (stat(to_send, finfo) == -1) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"unable to get information about \"%s\" "
"in parsed file %s",
to_send, r->filename);
ap_rputs(error, r);
return -1;
}
return 0;
}
else if (!strcmp(tag, "virtual")) {
request_rec *rr = ap_sub_req_lookup_uri(tag_val, r);
if (rr->status == HTTP_OK && rr->finfo.st_mode != 0) {
memcpy((char *) finfo, (const char *) &rr->finfo,
sizeof(struct stat));
ap_destroy_sub_req(rr);
return 0;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unable to get information about \"%s\" "
"in parsed file %s",
tag_val, r->filename);
ap_rputs(error, r);
ap_destroy_sub_req(rr);
return -1;
}
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag %s in %s",
tag, directive, r->filename);
ap_rputs(error, r);
return -1;
}
}
static int handle_fsize(FILE *in, request_rec *r, const char *error, int sizefmt)
{
char tag[MAX_STRING_LEN];
char *tag_val;
struct stat finfo;
char parsed_string[MAX_STRING_LEN];
while (1) {
if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
return 1;
}
else if (!strcmp(tag, "done")) {
return 0;
}
else {
parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
if (!find_file(r, "fsize", tag, parsed_string, &finfo, error)) {
if (sizefmt == SIZEFMT_KMG) {
ap_send_size(finfo.st_size, r);
}
else {
int l, x;
#if defined(BSD) && BSD > 199305
/* ap_snprintf can't handle %qd */
sprintf(tag, "%qd", finfo.st_size);
#else
ap_snprintf(tag, sizeof(tag), "%ld", finfo.st_size);
#endif
l = strlen(tag); /* grrr */
for(x = 0; x < l; x++) {
if (x && (!((l - x) % 3))) {
ap_rputc(',', r);
}
ap_rputc(tag[x], r);
}
}
}
}
}
}
static int handle_flastmod(FILE *in, request_rec *r, const char *error, const char *tf)
{
char tag[MAX_STRING_LEN];
char *tag_val;
struct stat finfo;
char parsed_string[MAX_STRING_LEN];
while (1) {
if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
return 1;
}
else if (!strcmp(tag, "done")) {
return 0;
}
else {
parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) {
ap_rputs(ap_ht_time(r->pool, finfo.st_mtime, tf, 0), r);
}
}
}
}
static int re_check(request_rec *r, char *string, char *rexp)
{
regex_t *compiled;
int regex_error;
compiled = ap_pregcomp(r->pool, rexp, REG_EXTENDED | REG_NOSUB);
if (compiled == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unable to compile pattern \"%s\"", rexp);
return -1;
}
regex_error = regexec(compiled, string, 0, (regmatch_t *) NULL, 0);
ap_pregfree (r->pool, compiled);
return(!regex_error);
}
enum token_type {
token_string,
token_and, token_or, token_not, token_eq, token_ne,
token_rbrace, token_lbrace, token_group,
token_ge, token_le, token_gt, token_lt
};
struct token {
enum token_type type;
char value[MAX_STRING_LEN];
};
/* there is an implicit assumption here that string is at most MAX_STRING_LEN-1
* characters long...
*/
static const char *get_ptoken(request_rec *r, const char *string, struct token *token)
{
char ch;
int next = 0;
int qs = 0;
/* Skip leading white space */
if (string == (char *) NULL) {
return (char *) NULL;
}
while ((ch = *string++)) {
if (!ap_isspace(ch)) {
break;
}
}
if (ch == '\0') {
return (char *) NULL;
}
token->type = token_string; /* the default type */
switch (ch) {
case '(':
token->type = token_lbrace;
return (string);
case ')':
token->type = token_rbrace;
return (string);
case '=':
token->type = token_eq;
return (string);
case '!':
if (*string == '=') {
token->type = token_ne;
return (string + 1);
}
else {
token->type = token_not;
return (string);
}
case '\'':
token->type = token_string;
qs = 1;
break;
case '|':
if (*string == '|') {
token->type = token_or;
return (string + 1);
}
break;
case '&':
if (*string == '&') {
token->type = token_and;
return (string + 1);
}
break;
case '>':
if (*string == '=') {
token->type = token_ge;
return (string + 1);
}
else {
token->type = token_gt;
return (string);
}
case '<':
if (*string == '=') {
token->type = token_le;
return (string + 1);
}
else {
token->type = token_lt;
return (string);
}
default:
token->type = token_string;
break;
}
/* We should only be here if we are in a string */
if (!qs) {
token->value[next++] = ch;
}
/*
* Yes I know that goto's are BAD. But, c doesn't allow me to
* exit a loop from a switch statement. Yes, I could use a flag,
* but that is (IMHO) even less readable/maintainable than the goto.
*/
/*
* I used the ++string throughout this section so that string
* ends up pointing to the next token and I can just return it
*/
for (ch = *string; ch != '\0'; ch = *++string) {
if (ch == '\\') {
if ((ch = *++string) == '\0') {
goto TOKEN_DONE;
}
token->value[next++] = ch;
continue;
}
if (!qs) {
if (ap_isspace(ch)) {
goto TOKEN_DONE;
}
switch (ch) {
case '(':
goto TOKEN_DONE;
case ')':
goto TOKEN_DONE;
case '=':
goto TOKEN_DONE;
case '!':
goto TOKEN_DONE;
case '|':
if (*(string + 1) == '|') {
goto TOKEN_DONE;
}
break;
case '&':
if (*(string + 1) == '&') {
goto TOKEN_DONE;
}
break;
case '<':
goto TOKEN_DONE;
case '>':
goto TOKEN_DONE;
}
token->value[next++] = ch;
}
else {
if (ch == '\'') {
qs = 0;
++string;
goto TOKEN_DONE;
}
token->value[next++] = ch;
}
}
TOKEN_DONE:
/* If qs is still set, I have an unmatched ' */
if (qs) {
ap_rputs("\nUnmatched '\n", r);
next = 0;
}
token->value[next] = '\0';
return (string);
}
/*
* Hey I still know that goto's are BAD. I don't think that I've ever
* used two in the same project, let alone the same file before. But,
* I absolutely want to make sure that I clean up the memory in all
* cases. And, without rewriting this completely, the easiest way
* is to just branch to the return code which cleans it up.
*/
/* there is an implicit assumption here that expr is at most MAX_STRING_LEN-1
* characters long...
*/
static int parse_expr(request_rec *r, const char *expr, const char *error)
{
struct parse_node {
struct parse_node *left, *right, *parent;
struct token token;
int value, done;
} *root, *current, *new;
const char *parse;
char buffer[MAX_STRING_LEN];
pool *expr_pool;
int retval = 0;
if ((parse = expr) == (char *) NULL) {
return (0);
}
root = current = (struct parse_node *) NULL;
expr_pool = ap_make_sub_pool(r->pool);
/* Create Parse Tree */
while (1) {
new = (struct parse_node *) ap_palloc(expr_pool,
sizeof(struct parse_node));
new->parent = new->left = new->right = (struct parse_node *) NULL;
new->done = 0;
if ((parse = get_ptoken(r, parse, &new->token)) == (char *) NULL) {
break;
}
switch (new->token.type) {
case token_string:
#ifdef DEBUG_INCLUDE
ap_rvputs(r, " Token: string (", new->token.value, ")\n", NULL);
#endif
if (current == (struct parse_node *) NULL) {
root = current = new;
break;
}
switch (current->token.type) {
case token_string:
if (current->token.value[0] != '\0') {
strncat(current->token.value, " ",
sizeof(current->token.value)
- strlen(current->token.value) - 1);
}
strncat(current->token.value, new->token.value,
sizeof(current->token.value)
- strlen(current->token.value) - 1);
current->token.value[sizeof(current->token.value) - 1] = '\0';
break;
case token_eq:
case token_ne:
case token_and:
case token_or:
case token_lbrace:
case token_not:
case token_ge:
case token_gt:
case token_le:
case token_lt:
new->parent = current;
current = current->right = new;
break;
default:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
break;
case token_and:
case token_or:
#ifdef DEBUG_INCLUDE
ap_rputs(" Token: and/or\n", r);
#endif
if (current == (struct parse_node *) NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
/* Percolate upwards */
while (current != (struct parse_node *) NULL) {
switch (current->token.type) {
case token_string:
case token_group:
case token_not:
case token_eq:
case token_ne:
case token_and:
case token_or:
case token_ge:
case token_gt:
case token_le:
case token_lt:
current = current->parent;
continue;
case token_lbrace:
break;
default:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
break;
}
if (current == (struct parse_node *) NULL) {
new->left = root;
new->left->parent = new;
new->parent = (struct parse_node *) NULL;
root = new;
}
else {
new->left = current->right;
current->right = new;
new->parent = current;
}
current = new;
break;
case token_not:
#ifdef DEBUG_INCLUDE
ap_rputs(" Token: not\n", r);
#endif
if (current == (struct parse_node *) NULL) {
root = current = new;
break;
}
/* Percolate upwards */
while (current != (struct parse_node *) NULL) {
switch (current->token.type) {
case token_not:
case token_eq:
case token_ne:
case token_and:
case token_or:
case token_lbrace:
case token_ge:
case token_gt:
case token_le:
case token_lt:
break;
default:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
break;
}
if (current == (struct parse_node *) NULL) {
new->left = root;
new->left->parent = new;
new->parent = (struct parse_node *) NULL;
root = new;
}
else {
new->left = current->right;
current->right = new;
new->parent = current;
}
current = new;
break;
case token_eq:
case token_ne:
case token_ge:
case token_gt:
case token_le:
case token_lt:
#ifdef DEBUG_INCLUDE
ap_rputs(" Token: eq/ne/ge/gt/le/lt\n", r);
#endif
if (current == (struct parse_node *) NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
/* Percolate upwards */
while (current != (struct parse_node *) NULL) {
switch (current->token.type) {
case token_string:
case token_group:
current = current->parent;
continue;
case token_lbrace:
case token_and:
case token_or:
break;
case token_not:
case token_eq:
case token_ne:
case token_ge:
case token_gt:
case token_le:
case token_lt:
default:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
break;
}
if (current == (struct parse_node *) NULL) {
new->left = root;
new->left->parent = new;
new->parent = (struct parse_node *) NULL;
root = new;
}
else {
new->left = current->right;
current->right = new;
new->parent = current;
}
current = new;
break;
case token_rbrace:
#ifdef DEBUG_INCLUDE
ap_rputs(" Token: rbrace\n", r);
#endif
while (current != (struct parse_node *) NULL) {
if (current->token.type == token_lbrace) {
current->token.type = token_group;
break;
}
current = current->parent;
}
if (current == (struct parse_node *) NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Unmatched ')' in \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
break;
case token_lbrace:
#ifdef DEBUG_INCLUDE
ap_rputs(" Token: lbrace\n", r);
#endif
if (current == (struct parse_node *) NULL) {
root = current = new;
break;
}
/* Percolate upwards */
while (current != (struct parse_node *) NULL) {
switch (current->token.type) {
case token_not:
case token_eq:
case token_ne:
case token_and:
case token_or:
case token_lbrace:
case token_ge:
case token_gt:
case token_le:
case token_lt:
break;
case token_string:
case token_group:
default:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
break;
}
if (current == (struct parse_node *) NULL) {
new->left = root;
new->left->parent = new;
new->parent = (struct parse_node *) NULL;
root = new;
}
else {
new->left = current->right;
current->right = new;
new->parent = current;
}
current = new;
break;
default:
break;
}
}
/* Evaluate Parse Tree */
current = root;
while (current != (struct parse_node *) NULL) {
switch (current->token.type) {
case token_string:
#ifdef DEBUG_INCLUDE
ap_rputs(" Evaluate string\n", r);
#endif
parse_string(r, current->token.value, buffer, sizeof(buffer), 0);
ap_cpystrn(current->token.value, buffer, sizeof(current->token.value));
current->value = (current->token.value[0] != '\0');
current->done = 1;
current = current->parent;
break;
case token_and:
case token_or:
#ifdef DEBUG_INCLUDE
ap_rputs(" Evaluate and/or\n", r);
#endif
if (current->left == (struct parse_node *) NULL ||
current->right == (struct parse_node *) NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
if (!current->left->done) {
switch (current->left->token.type) {
case token_string:
parse_string(r, current->left->token.value,
buffer, sizeof(buffer), 0);
ap_cpystrn(current->left->token.value, buffer,
sizeof(current->left->token.value));
current->left->value = (current->left->token.value[0] != '\0');
current->left->done = 1;
break;
default:
current = current->left;
continue;
}
}
if (!current->right->done) {
switch (current->right->token.type) {
case token_string:
parse_string(r, current->right->token.value,
buffer, sizeof(buffer), 0);
ap_cpystrn(current->right->token.value, buffer,
sizeof(current->right->token.value));
current->right->value = (current->right->token.value[0] != '\0');
current->right->done = 1;
break;
default:
current = current->right;
continue;
}
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r, " Left: ", current->left->value ? "1" : "0",
"\n", NULL);
ap_rvputs(r, " Right: ", current->right->value ? "1" : "0",
"\n", NULL);
#endif
if (current->token.type == token_and) {
current->value = current->left->value && current->right->value;
}
else {
current->value = current->left->value || current->right->value;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r, " Returning ", current->value ? "1" : "0",
"\n", NULL);
#endif
current->done = 1;
current = current->parent;
break;
case token_eq:
case token_ne:
#ifdef DEBUG_INCLUDE
ap_rputs(" Evaluate eq/ne\n", r);
#endif
if ((current->left == (struct parse_node *) NULL) ||
(current->right == (struct parse_node *) NULL) ||
(current->left->token.type != token_string) ||
(current->right->token.type != token_string)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
parse_string(r, current->left->token.value,
buffer, sizeof(buffer), 0);
ap_cpystrn(current->left->token.value, buffer,
sizeof(current->left->token.value));
parse_string(r, current->right->token.value,
buffer, sizeof(buffer), 0);
ap_cpystrn(current->right->token.value, buffer,
sizeof(current->right->token.value));
if (current->right->token.value[0] == '/') {
int len;
len = strlen(current->right->token.value);
if (current->right->token.value[len - 1] == '/') {
current->right->token.value[len - 1] = '\0';
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid rexp \"%s\" in file %s",
current->right->token.value, r->filename);
ap_rputs(error, r);
goto RETURN;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r, " Re Compare (", current->left->token.value,
") with /", ¤t->right->token.value[1], "/\n", NULL);
#endif
current->value =
re_check(r, current->left->token.value,
¤t->right->token.value[1]);
}
else {
#ifdef DEBUG_INCLUDE
rvputs(r, " Compare (", current->left->token.value,
") with (", current->right->token.value, ")\n", NULL);
#endif
current->value =
(strcmp(current->left->token.value,
current->right->token.value) == 0);
}
if (current->token.type == token_ne) {
current->value = !current->value;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r, " Returning ", current->value ? "1" : "0",
"\n", NULL);
#endif
current->done = 1;
current = current->parent;
break;
case token_ge:
case token_gt:
case token_le:
case token_lt:
#ifdef DEBUG_INCLUDE
ap_rputs(" Evaluate ge/gt/le/lt\n", r);
#endif
if ((current->left == (struct parse_node *) NULL) ||
(current->right == (struct parse_node *) NULL) ||
(current->left->token.type != token_string) ||
(current->right->token.type != token_string)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid expression \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
}
parse_string(r, current->left->token.value,
buffer, sizeof(buffer), 0);
ap_cpystrn(current->left->token.value, buffer,
sizeof(current->left->token.value));
parse_string(r, current->right->token.value,
buffer, sizeof(buffer), 0);
ap_cpystrn(current->right->token.value, buffer,
sizeof(current->right->token.value));
#ifdef DEBUG_INCLUDE
ap_rvputs(r, " Compare (", current->left->token.value,
") with (", current->right->token.value, ")\n", NULL);
#endif
current->value =
strcmp(current->left->token.value,
current->right->token.value);
if (current->token.type == token_ge) {
current->value = current->value >= 0;
}
else if (current->token.type == token_gt) {
current->value = current->value > 0;
}
else if (current->token.type == token_le) {
current->value = current->value <= 0;
}
else if (current->token.type == token_lt) {
current->value = current->value < 0;
}
else {
current->value = 0; /* Don't return -1 if unknown token */
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r, " Returning ", current->value ? "1" : "0",
"\n", NULL);
#endif
current->done = 1;
current = current->parent;
break;
case token_not:
if (current->right != (struct parse_node *) NULL) {
if (!current->right->done) {
current = current->right;
continue;
}
current->value = !current->right->value;
}
else {
current->value = 0;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r, " Evaluate !: ", current->value ? "1" : "0",
"\n", NULL);
#endif
current->done = 1;
current = current->parent;
break;
case token_group:
if (current->right != (struct parse_node *) NULL) {
if (!current->right->done) {
current = current->right;
continue;
}
current->value = current->right->value;
}
else {
current->value = 1;
}
#ifdef DEBUG_INCLUDE
ap_rvputs(r, " Evaluate (): ", current->value ? "1" : "0",
"\n", NULL);
#endif
current->done = 1;
current = current->parent;
break;
case token_lbrace:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Unmatched '(' in \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
case token_rbrace:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Unmatched ')' in \"%s\" in file %s",
expr, r->filename);
ap_rputs(error, r);
goto RETURN;
default:
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"bad token type");
ap_rputs(error, r);
goto RETURN;
}
}
retval = (root == (struct parse_node *) NULL) ? 0 : root->value;
RETURN:
ap_destroy_pool(expr_pool);
return (retval);
}
static int handle_if(FILE *in, request_rec *r, const char *error,
int *conditional_status, int *printing)
{
char tag[MAX_STRING_LEN];
char *tag_val;
char *expr;
expr = NULL;
while (1) {
tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0);
if (*tag == '\0') {
return 1;
}
else if (!strcmp(tag, "done")) {
if (expr == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"missing expr in if statement: %s",
r->filename);
ap_rputs(error, r);
return 1;
}
*printing = *conditional_status = parse_expr(r, expr, error);
#ifdef DEBUG_INCLUDE
ap_rvputs(r, "**** if conditional_status=\"",
*conditional_status ? "1" : "0", "\"\n", NULL);
#endif
return 0;
}
else if (!strcmp(tag, "expr")) {
expr = tag_val;
#ifdef DEBUG_INCLUDE
ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL);
#endif
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag if in %s",
tag, r->filename);
ap_rputs(error, r);
}
}
}
static int handle_elif(FILE *in, request_rec *r, const char *error,
int *conditional_status, int *printing)
{
char tag[MAX_STRING_LEN];
char *tag_val;
char *expr;
expr = NULL;
while (1) {
tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0);
if (*tag == '\0') {
return 1;
}
else if (!strcmp(tag, "done")) {
#ifdef DEBUG_INCLUDE
ap_rvputs(r, "**** elif conditional_status=\"",
*conditional_status ? "1" : "0", "\"\n", NULL);
#endif
if (*conditional_status) {
*printing = 0;
return (0);
}
if (expr == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"missing expr in elif statement: %s",
r->filename);
ap_rputs(error, r);
return 1;
}
*printing = *conditional_status = parse_expr(r, expr, error);
#ifdef DEBUG_INCLUDE
ap_rvputs(r, "**** elif conditional_status=\"",
*conditional_status ? "1" : "0", "\"\n", NULL);
#endif
return 0;
}
else if (!strcmp(tag, "expr")) {
expr = tag_val;
#ifdef DEBUG_INCLUDE
ap_rvputs(r, "**** if expr=\"", expr, "\"\n", NULL);
#endif
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"unknown parameter \"%s\" to tag if in %s",
tag, r->filename);
ap_rputs(error, r);
}
}
}
static int handle_else(FILE *in, request_rec *r, const char *error,
int *conditional_status, int *printing)
{
char tag[MAX_STRING_LEN];
if (!get_tag(r->pool, in, tag, sizeof(tag), 1)) {
return 1;
}
else if (!strcmp(tag, "done")) {
#ifdef DEBUG_INCLUDE
ap_rvputs(r, "**** else conditional_status=\"",
*conditional_status ? "1" : "0", "\"\n", NULL);
#endif
*printing = !(*conditional_status);
*conditional_status = 1;
return 0;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"else directive does not take tags in %s",
r->filename);
if (*printing) {
ap_rputs(error, r);
}
return -1;
}
}
static int handle_endif(FILE *in, request_rec *r, const char *error,
int *conditional_status, int *printing)
{
char tag[MAX_STRING_LEN];
if (!get_tag(r->pool, in, tag, sizeof(tag), 1)) {
return 1;
}
else if (!strcmp(tag, "done")) {
#ifdef DEBUG_INCLUDE
ap_rvputs(r, "**** endif conditional_status=\"",
*conditional_status ? "1" : "0", "\"\n", NULL);
#endif
*printing = 1;
*conditional_status = 1;
return 0;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"endif directive does not take tags in %s",
r->filename);
ap_rputs(error, r);
return -1;
}
}
static int handle_set(FILE *in, request_rec *r, const char *error)
{
char tag[MAX_STRING_LEN];
char parsed_string[MAX_STRING_LEN];
char *tag_val;
char *var;
var = (char *) NULL;
while (1) {
if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
return 1;
}
else if (!strcmp(tag, "done")) {
return 0;
}
else if (!strcmp(tag, "var")) {
var = tag_val;
}
else if (!strcmp(tag, "value")) {
if (var == (char *) NULL) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"variable must precede value in set directive in %s",
r->filename);
ap_rputs(error, r);
return -1;
}
parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0);
ap_table_setn(r->subprocess_env, var, ap_pstrdup(r->pool, parsed_string));
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid tag for set directive in %s", r->filename);
ap_rputs(error, r);
return -1;
}
}
}
static int handle_printenv(FILE *in, request_rec *r, const char *error)
{
char tag[MAX_STRING_LEN];
char *tag_val;
array_header *arr;
table_entry *elts;
int i, nelts;
arr = ap_table_elts(r->subprocess_env);
elts = (table_entry *) arr->elts;
nelts = arr->nelts;
if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 1))) {
return 1;
}
else if (!strcmp(tag, "done")) {
for (i = 0; i < nelts; ++i) {
ap_rvputs(r, elts[i].key, "=", elts[i].val, "\n", NULL);
}
return 0;
}
else {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"printenv directive does not take tags in %s",
r->filename);
ap_rputs(error, r);
return -1;
}
}
Tcl_Interp *get_slave_interp (request_rec *r, char *handler_name, char *name) {
extern Tcl_Interp *interp;
Tcl_Interp *safeInterp;
struct request_rec *q;
char *safeInterpName;
table *t;
array_header *arr;
table_entry *elts;
int i, nelts;
char script[101];
/* look into the request record and find out if we already have a safe
* interpreter running with the right user ID.
*
* if not, create one and register it. if so, hook up to it.
*
*/
for (q = r, safeInterpName = (char *)NULL; q != (struct request_rec *)NULL; q = q->main) {
if (r->finfo.st_uid != q->finfo.st_uid) break;
if ((safeInterpName = ap_get_module_config(q->request_config, &neoscript_module)) != NULL) break;
}
if (safeInterpName) {
safeInterp = Tcl_GetSlave(interp, safeInterpName);
assert (safeInterp != NULL);
} else {
/* Set array for the config information here, once, when the slave
* interp is created. Saves repeating work when several script blocks
* exist within a page.
*/
nws_dir_config *ns = (nws_dir_config *)ap_get_module_config(r->per_dir_config,&neoscript_module);
t = ns->nws_dir_vars;
arr = ap_table_elts(t);
elts = (table_entry *)arr->elts;
nelts = arr->nelts;
/* Make sure it doesn't exist from a previous page served by this
* process.
*/
Tcl_UnsetVar (interp, "NeoWebDirConf", TCL_GLOBAL_ONLY);
for (i = 0; i < nelts; ++i)
{
Tcl_SetVar2(interp, "NeoWebDirConf",
elts[i].key, elts[i].val, TCL_GLOBAL_ONLY);
}
Tcl_SetVar2(interp, "webenv",
"NEO_HANDLER", handler_name, TCL_GLOBAL_ONLY);
strcpy (script, "setup_safe_interpreter");
if (Tcl_GlobalEval(interp, script) != TCL_OK) {
char *errorInfo;
errorInfo = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "%s", errorInfo);
fprintf(stderr, "setup_safe_interpreter: %s\n", errorInfo);
exit(1);
} else {
safeInterpName = (char *) ap_palloc(r->pool,
strlen(interp->result) + 1);
strcpy(safeInterpName, interp->result);
safeInterp = Tcl_GetSlave(interp, safeInterpName);
/*assert (safeInterp != NULL);*/
if (safeInterp == NULL) {
exit(1);
}
t = ns->nws_user_vars;
arr = ap_table_elts(t);
elts = (table_entry *)arr->elts;
nelts = arr->nelts;
for (i = 0; i < nelts; ++i)
{
Tcl_SetVar2(safeInterp, "NeoWebUserConf",
elts[i].key, elts[i].val, TCL_GLOBAL_ONLY);
}
}
ap_register_cleanup(r->pool, (void *)safeInterp,
(void (*)())Tcl_DeleteInterp,
(void (*)())Tcl_DeleteInterp);
ap_set_module_config(r->request_config, &neoscript_module,
safeInterpName);
/*
propagate_vars_to_nws(safeInterp, r);
*/
}
if (name != NULL) {
strcpy(name, safeInterpName);
}
return safeInterp;
}
int handle_old_nws(FILE *in, request_rec *r, char *error, char *safeInterpName)
{
char tag[MAX_STRING_LEN];
char *tag_val;
extern Tcl_Interp *interp;
/* parse the tag=value pairs. build up a neoscript command
* including name of the safe interp we're running against.
*/
while(1) {
Tcl_DString userCommand;
char *commandString;
if (!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1))) {
return 1;
}
if (!strcmp(tag, "done"))
break;
Tcl_DStringInit(&userCommand);
Tcl_DStringAppendElement(&userCommand, "handle_neowebscript_request");
Tcl_DStringAppendElement(&userCommand, safeInterpName);
Tcl_DStringAppendElement(&userCommand, tag);
Tcl_DStringAppendElement(&userCommand, tag_val);
commandString = Tcl_DStringValue(&userCommand);
if (Tcl_GlobalEval (interp, commandString) == TCL_ERROR) {
ap_rprintf (r, "[%s error %s]", tag, interp->result);
}
Tcl_DStringFree(&userCommand);
}
return 0;
}
int handle_nws(FILE *in, request_rec *r, char *error,
char *safeInterpName)
{
char code[MAX_STRING_LEN];
char *nws_code;
extern Tcl_Interp *interp;
request_rec *Tcl_saved_request_rec;
Tcl_DString userCommand;
char *commandString;
Tcl_saved_request_rec = Tcl_request_rec;
Tcl_request_rec = r;
if(!(nws_code = get_nws_code (r->pool, in, code, MAX_STRING_LEN, ENDING_NWS_SEQUENCE))) {
Tcl_request_rec = Tcl_saved_request_rec;
return 1;
}
Tcl_DStringInit(&userCommand);
Tcl_DStringAppendElement(&userCommand, "handle_neowebscript_request");
Tcl_DStringAppendElement(&userCommand, safeInterpName);
Tcl_DStringAppendElement(&userCommand, "code");
Tcl_DStringAppendElement(&userCommand, nws_code);
commandString = Tcl_DStringValue(&userCommand);
if (Tcl_GlobalEval (interp, commandString) == TCL_ERROR) {
ap_rprintf (r, "[%s error %s]", "code", interp->result);
}
Tcl_DStringFree(&userCommand);
Tcl_request_rec = Tcl_saved_request_rec;
return 0;
}
int handle_nws_config(FILE *in, request_rec *r, char *error, char *tf,
int *sizefmt) {
char tag[MAX_STRING_LEN];
char *tag_val;
table *env = r->subprocess_env;
while(1) {
if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 0)))
return 1;
if(!strcmp(tag,"errmsg"))
strcpy(error,tag_val);
else if(!strcmp(tag,"timefmt")) {
time_t date = time(NULL);
strcpy(tf,tag_val);
ap_table_set (env, "DATE_LOCAL", ap_ht_time(r->pool,date,tf,0));
ap_table_set (env, "DATE_GMT", ap_ht_time(r->pool,date,tf,1));
ap_table_set (env, "LAST_MODIFIED", ap_ht_time(r->pool,r->finfo.st_mtime,tf,0));
}
else if(!strcmp(tag,"sizefmt")) {
decodehtml(tag_val);
if(!strcmp(tag_val,"bytes"))
*sizefmt = SIZEFMT_BYTES;
else if(!strcmp(tag_val,"abbrev"))
*sizefmt = SIZEFMT_KMG;
}
else if(!strcmp(tag,"done"))
return 0;
else {
char errstr[MAX_STRING_LEN];
sprintf(errstr,"unknown parameter \"%s\" to tag \"config\" in %s",
tag, r->filename);
ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "%s", errstr);
ap_rprintf (r,"%s",error);
}
}
}
void add_include_environment(request_rec *r)
{
ap_add_common_vars(r);
ap_add_cgi_vars(r);
add_include_vars(r, DEFAULT_TIME_FORMAT);
}
/* -------------------------- The main function --------------------------- */
/* This is a stub which parses a file descriptor. */
int send_parsed_content(FILE *f, request_rec *r)
{
char directive[MAX_STRING_LEN], error[MAX_STRING_LEN];
char timefmt[MAX_STRING_LEN];
int noexec = ap_allow_options(r) & OPT_INCNOEXEC;
int ret, sizefmt, seqtype=0;
int init_environment, if_nesting, printing, conditional_status;
Tcl_Interp *safeInterp = 0;
int has_includes = 0;
char safeInterpName[20];
request_rec *Tcl_saved_request_rec;
Tcl_saved_request_rec = Tcl_request_rec;
Tcl_request_rec = r;
ap_cpystrn(error, DEFAULT_ERROR_MSG, sizeof(error));
ap_cpystrn(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt));
sizefmt = SIZEFMT_KMG;
/* Turn printing on */
printing = conditional_status = 1;
if_nesting = 0;
init_environment = 0;
#ifndef WIN32
ap_chdir_file(r->filename);
#endif
if (r->args) { /* add QUERY stuff to env cause it ain't yet */
char *arg_copy = ap_pstrdup(r->pool, r->args);
ap_table_setn(r->subprocess_env, "QUERY_STRING", r->args);
ap_unescape_url(arg_copy);
ap_table_setn(r->subprocess_env, "QUERY_STRING_UNESCAPED",
ap_escape_shell_cmd(r->pool, arg_copy));
}
/* find_string2 returns 0 if there is an SSI, non-0 if not.
* look in seqtype for whether it is apache-style or