/* * The contents of this file are subject to the AOLserver Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://aolserver.com/. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * The Original Code is AOLserver Code and related documentation * distributed by AOL. * * The Initial Developer of the Original Code is America Online, * Inc. Portions created by AOL are Copyright (C) 1999 America Online, * Inc. All Rights Reserved. * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public License (the "GPL"), in which case the * provisions of GPL are applicable instead of those above. If you wish * to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the * License, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the GPL. * If you do not delete the provisions above, a recipient may use your * version of this file under either the License or the GPL. */ /* * tclrequest.c -- * * Routines for Tcl proc and ADP registered requests. */ static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsd/tclrequest.c,v 1.13 2005/08/23 22:05:04 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; #include "nsd.h" #define ARGS_UNKNOWN (-1) #define ARGS_FAILED (-2) /* * The following structure defines the proc callback context. */ typedef struct { char *name; char *args; int nargs; } Proc; /* * Local functions defined in this file */ static Ns_OpProc ProcRequest; static Ns_OpProc AdpRequest; static Ns_FilterProc ProcFilter; static Proc *NewProc(char *name, char *args); static Ns_Callback FreeProc; static void AppendConnId(Tcl_DString *dsPtr, Ns_Conn *conn); static int RegisterFilterObj(NsInterp *itPtr, int when, int objc, Tcl_Obj *CONST objv[]); static int GetNumArgs(Tcl_Interp *interp, Proc *procPtr); /* *---------------------------------------------------------------------- * * Ns_TclRequest -- * * Dummy up a direct call to TclProcRequest for a connection. * * Results: * See TclDoOp. * * Side effects: * None. * *---------------------------------------------------------------------- */ int Ns_TclRequest(Ns_Conn *conn, char *name) { Proc proc; proc.name = name; proc.args = NULL; proc.nargs = 0; return ProcRequest(&proc, conn); } /* *---------------------------------------------------------------------- * * NsTclRegisterProcObjCmd -- * * Implements ns_register_proc as obj command. * * Results: * Tcl result. * * Side effects: * See docs. * *---------------------------------------------------------------------- */ int NsTclRegisterProcObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], int adp) { int flags, idx; Proc *procPtr; NsInterp *itPtr = arg; char *server, *method, *url, *name, *args; if (objc < 4 || objc > 7) { badargs: Tcl_WrongNumArgs(interp, 1, objv, "?-noinherit? method url proc ?args?"); return TCL_ERROR; } if (STREQ(Tcl_GetString(objv[1]), "-noinherit")) { if (objc < 5) { goto badargs; } flags = NS_OP_NOINHERIT; idx = 2; } else { if (objc == 7) { goto badargs; } flags = 0; idx = 1; } if (NsTclGetServer(itPtr, &server) != TCL_OK) { return TCL_ERROR; } method = Tcl_GetString(objv[idx++]); url = Tcl_GetString(objv[idx++]); name = Tcl_GetString(objv[idx++]); args = (idx < objc ? Tcl_GetString(objv[idx]) : NULL); procPtr = NewProc(name, args); Ns_RegisterRequest(server, method, url, ProcRequest, FreeProc, procPtr, flags); return TCL_OK; } /* *---------------------------------------------------------------------- * * NsTclRegisterAdpObjCmd -- * * Implements ns_register_adp as obj command. * * Results: * Tcl result. * * Side effects: * See docs. * *---------------------------------------------------------------------- */ int NsTclRegisterAdpObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsInterp *itPtr = arg; char *server, *method, *url, *file; if (objc != 4 && objc != 5) { badargs: Tcl_WrongNumArgs(interp, 1, objv, "?-noinherit? method url file"); return TCL_ERROR; } if (objc == 5 && !STREQ(Tcl_GetString(objv[1]), "-noinherit")) { goto badargs; } if (NsTclGetServer(itPtr, &server) != TCL_OK) { return TCL_ERROR; } method = Tcl_GetString(objv[objc-3]); url = Tcl_GetString(objv[objc-2]); file = ns_strdup(Tcl_GetString(objv[objc-1])); Ns_RegisterRequest(server, method, url, AdpRequest, ns_free, file, objc == 5 ? NS_OP_NOINHERIT : 0); return TCL_OK; } /* *---------------------------------------------------------------------- * * NsTclUnRegisterObjCmd -- * * Implements ns_unregister_proc and ns_unregister_adp commands. * * Results: * Tcl result. * * Side effects: * See docs. * *---------------------------------------------------------------------- */ int NsTclUnRegisterObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsInterp *itPtr = arg; char *server; if (objc != 3 && objc != 4) { Tcl_WrongNumArgs(interp, 1, objv, "?-noinherit? method url"); return TCL_ERROR; } if (objc == 4 && !STREQ(Tcl_GetString(objv[1]), "-noinherit")) { Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "unknown flag \"", Tcl_GetString(objv[1]), "\": should be -noinherit", NULL); return TCL_ERROR; } if (NsTclGetServer(itPtr, &server) != TCL_OK) { return TCL_ERROR; } Ns_UnRegisterRequest(server, Tcl_GetString(objv[objc-2]), Tcl_GetString(objv[objc-1]), objc == 3 ? 1 : 0); return TCL_OK; } /* *---------------------------------------------------------------------- * * NsTclRegisterFilterObjCmd -- * * Implements ns_register_filter. * * Results: * Tcl result. * * Side effects: * See docs. * *---------------------------------------------------------------------- */ int NsTclRegisterFilterObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsInterp *itPtr = arg; int lobjc; Tcl_Obj **lobjv; int when, i; static CONST char *wopt[] = { "prequeue", "preauth", "postauth", "trace", NULL }; enum { PQIdx, PRIdx, POIdx, TRIdx, } widx; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?args?"); return TCL_ERROR; } if (objc != 5 && objc != 6) { Tcl_WrongNumArgs(interp, 1, objv, "when method url script ?arg?"); return TCL_ERROR; } if (Tcl_ListObjGetElements(interp, objv[1], &lobjc, &lobjv) != TCL_OK) { return TCL_ERROR; } when = 0; for (i = 0; i < lobjc; ++i) { if (Tcl_GetIndexFromObj(interp, lobjv[i], wopt, "when", 0, (int *) &widx) != TCL_OK) { return TCL_ERROR; } switch (widx) { case PQIdx: when |= NS_FILTER_PRE_QUEUE; break; case PRIdx: when |= NS_FILTER_PRE_AUTH; break; case POIdx: when |= NS_FILTER_POST_AUTH; break; case TRIdx: when |= NS_FILTER_TRACE; break; } } if (when == 0) { Tcl_SetResult(interp, "blank filter when specification", TCL_STATIC); return TCL_ERROR; } return RegisterFilterObj(itPtr, when, objc - 2, objv + 2); } /* *---------------------------------------------------------------------- * * NsTclRegisterTraceObjCmd -- * * Implements ns_register_trace as obj command. * * Results: * Tcl result. * * Side effects: * See docs. * *---------------------------------------------------------------------- */ int NsTclRegisterTraceObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsInterp *itPtr = arg; if (objc != 4 && objc != 5) { Tcl_WrongNumArgs(interp, 1, objv, "method urlPattern script ?arg?"); return TCL_ERROR; } return RegisterFilterObj(itPtr, NS_FILTER_VOID_TRACE, objc - 1, objv + 1); } /* *---------------------------------------------------------------------- * * NsTclRegisterFastPathObjCmd -- * * Implements ns_register_fastpath as obj command. * * Results: * Tcl result. * * Side effects: * See docs. * *---------------------------------------------------------------------- */ int NsTclRegisterFastPathObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { NsInterp *itPtr = arg; NsServer *servPtr = itPtr->servPtr; char *server, *method, *url; int flags, idx; if (objc < 3 || objc > 4) { badargs: Tcl_WrongNumArgs(interp, 1, objv, "?-noinherit? method url"); return TCL_ERROR; } flags = 0; idx = 1; if (objc == 4) { if (!STREQ(Tcl_GetString(objv[1]), "-noinherit")) { goto badargs; } flags = NS_OP_NOINHERIT; idx++; } server = servPtr->server; method = Tcl_GetString(objv[idx++]); url = Tcl_GetString(objv[idx++]); Ns_RegisterRequest(server, method, url, Ns_FastPathOp, NULL, servPtr, flags); return TCL_OK; } /* *---------------------------------------------------------------------- * * AdpRequest -- * * Ns_OpProc for registered ADP's. * * Results: * See Ns_AdpRequest. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int AdpRequest(void *arg, Ns_Conn *conn) { return Ns_AdpRequest(conn, (char *) arg); } /* *---------------------------------------------------------------------- * * RequstProc -- * * Ns_OpProc for ns_register_proc callbacks. Constructs and * evaluates the given script. * * Results: * Standard request result. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ProcRequest(void *arg, Ns_Conn *conn) { Proc *procPtr = arg; Tcl_Interp *interp = Ns_GetConnInterp(conn); int result, cnt; Tcl_DString script; /* * Construct and evaluate the script. The legacy connId parameter is * no longer required so the code below supports three cases: * * 1. No args: Append nothing. * 2. One arg: Append the callback arg only. * 3. Two or more args: Append the connId and callback arg. */ Tcl_DStringInit(&script); Tcl_DStringAppendElement(&script, procPtr->name); cnt = GetNumArgs(interp, procPtr); if (cnt != 0) { if (cnt > 1) { AppendConnId(&script, conn); } Tcl_DStringAppendElement(&script, procPtr->args ? procPtr->args : ""); } result = Tcl_EvalEx(interp, script.string, script.length, 0); Tcl_DStringFree(&script); /* * On script error, generate an internal error response if * no content has been sent on the connection. */ if (result != TCL_OK) { Ns_TclLogError(interp); if (Ns_ConnResetReturn(conn) == NS_OK) { return Ns_ConnReturnInternalError(conn); } } return NS_OK; } /* *---------------------------------------------------------------------- * * ProcFilter -- * * Callback for Tcl-based connection filters. * * Results: * Standard filter result. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int ProcFilter(void *arg, Ns_Conn *conn, int why) { Proc *procPtr = arg; Tcl_Interp *interp = Ns_GetConnInterp(conn); Tcl_DString script; int status, cnt; CONST char *result; /* * Construct and evaluate the script. The filter arg and legacy connId * args will be appended before the "why" argument if present. */ Tcl_DStringInit(&script); Tcl_DStringAppendElement(&script, procPtr->name); cnt = GetNumArgs(interp, procPtr); if (cnt > 1) { if (cnt > 2) { AppendConnId(&script, conn); } Tcl_DStringAppendElement(&script, procPtr->args ? procPtr->args : ""); } switch (why) { case NS_FILTER_PRE_AUTH: Tcl_DStringAppendElement(&script, "preauth"); break; case NS_FILTER_POST_AUTH: Tcl_DStringAppendElement(&script, "postauth"); break; case NS_FILTER_TRACE: Tcl_DStringAppendElement(&script, "trace"); break; default: /* NB: Do not append a why argument. */ break; } Tcl_AllowExceptions(interp); status = Tcl_EvalEx(interp, script.string, script.length, 0); Tcl_DStringFree(&script); if (status != TCL_OK) { Ns_TclLogError(interp); } /* * Determine the filter return code from the Tcl result string. */ if (why == NS_FILTER_VOID_TRACE) { /* NB: Result string ignored for traces. */ status = NS_OK; } else if (status != TCL_OK) { /* NB: Filter error on script error. */ status = NS_ERROR; } else { result = Tcl_GetStringResult(interp); if (STREQ(result, "filter_ok")) { status = NS_OK; } else if (STREQ(result, "filter_break")) { status = NS_FILTER_BREAK; } else if (STREQ(result, "filter_return")) { status = NS_FILTER_RETURN; } else { Ns_Log(Warning, "tclfilter: %s return invalid result: %s", procPtr->name, result); status = NS_ERROR; } } return status; } /* *---------------------------------------------------------------------- * * GetNumArgs -- * * Get the number of arguments for a given callback, invoked * the first time the callback is used. * * Results: * Number of args or -1 on error. * * Side effects: * Will invoke script to determine arg count. * *---------------------------------------------------------------------- */ static int GetNumArgs(Tcl_Interp *interp, Proc *procPtr) { Tcl_Obj *objPtr; Tcl_DString ds; if (procPtr->nargs == ARGS_UNKNOWN) { Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, "llength [info args ", -1); Tcl_DStringAppendElement(&ds, procPtr->name); Tcl_DStringAppend(&ds, "]", 1); if (Tcl_Eval(interp, ds.string) != TCL_OK) { procPtr->nargs = ARGS_FAILED; } else { objPtr = Tcl_GetObjResult(interp); if (Tcl_GetIntFromObj(interp, objPtr, &procPtr->nargs) != TCL_OK) { procPtr->nargs = ARGS_FAILED; } } Tcl_DStringFree(&ds); } return procPtr->nargs; } /* *---------------------------------------------------------------------- * * RegisterFilterObj -- * * Register a Tcl filter. * * Results: * TCL_ERROR if no server, TCL_OK otherwise. * * Side effects: * Will register a filter to run later. * *---------------------------------------------------------------------- */ static int RegisterFilterObj(NsInterp *itPtr, int when, int objc, Tcl_Obj *CONST objv[]) { Proc *procPtr; char *server, *method, *url, *name, *args; if (NsTclGetServer(itPtr, &server) != TCL_OK) { return TCL_ERROR; } method = Tcl_GetString(objv[0]); url = Tcl_GetString(objv[1]); name = Tcl_GetString(objv[2]); args = (objc > 3 ? Tcl_GetString(objv[3]) : NULL); procPtr = NewProc(name, args); Ns_RegisterFilter(server, method, url, ProcFilter, when, procPtr); return TCL_OK; } /* *---------------------------------------------------------------------- * * AppendConnId -- * * Append the Tcl conn handle to a dstring. * * Results: * None. * * Side effects: * Appends to the dstring. * *---------------------------------------------------------------------- */ static void AppendConnId(Tcl_DString *dsPtr, Ns_Conn *conn) { Conn *connPtr = (Conn *) conn; Tcl_DStringAppendElement(dsPtr, connPtr->idstr); } /* *---------------------------------------------------------------------- * * NewProc, FreeProc -- * * Create or delete a Proc structure. * * Results: * For NewProc, pointer to new Proc. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Proc * NewProc(char *name, char *args) { Proc *procPtr; procPtr = ns_malloc(sizeof(Proc)); procPtr->name = ns_strdup(name); procPtr->args = ns_strcopy(args); procPtr->nargs = ARGS_UNKNOWN; return procPtr; } static void FreeProc(void *arg) { Proc *procPtr = arg; ns_free(procPtr->name); if (procPtr->args != NULL) { ns_free(procPtr->args); } ns_free(procPtr); }