/* * 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.lcs.mit.edu/. * * 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. * * Copyright (C) 2007 Tom Jackson * 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. */ /* * nsbgwrite.c -- Enables removal of Sock from Conn wrapped in channel. * * Author * Tom Jackson * * Based upon code written by Zoran Vasiljevic and * extended by Gustaf Neumann. * Module template design take from nszlib by Vlad Seryakov. */ #include "nsbgwrite.h" #define VERSION "4.5" static Tcl_Channel MakeConnChannel(Ns_Conn *conn, int spliceout); static int BgWriteObjCmd(ClientData ignored, Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[]); static int BgTrace(Tcl_Interp *interp, void *ignored); /* *---------------------------------------------------------------------- * * Nsbgwrite_Init-- * * Tcl load-command entry point. * * Results: * TCL_OK. * * Side effects: * Adds the ns_bgwrite command. * *---------------------------------------------------------------------- */ int Nsbgwrite_Init(Tcl_Interp *interp) { Tcl_CreateObjCommand(interp, "ns_bgwrite",BgWriteObjCmd ,NULL, NULL); return TCL_OK; } /* *---------------------------------------------------------------------- * * NsBgWriteModInit-- * * AOLserver module entry point. * * Results: * NS_OK. * * Side effects: * Registers a trace to create ns_bgwrite command in all new * interps. * *---------------------------------------------------------------------- */ int NsBgWriteModInit(char *server, char *module) { Ns_TclRegisterTrace(server, BgTrace, NULL, NS_TCL_TRACE_CREATE); return NS_OK; } /* *---------------------------------------------------------------------- * * BgWriteObjCmd -- * * Implements the ns_bgwrite command. * * Results: * Standard Tcl result. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int BgWriteObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[]) { Ns_Conn *conn; Tcl_Channel chan; Tcl_Obj *result; int nContentSent; static CONST char *opts[] = { "channel", "contentsentlength", NULL }; enum { CChannelIdx, CContentSentLenIdx } opt; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option ?arg?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0, (int *) &opt) != TCL_OK) { return TCL_ERROR; } result = Tcl_GetObjResult(interp); conn = Ns_GetConn(); if (conn == NULL) { Tcl_AppendResult(interp, "no current connection", NULL); return TCL_ERROR; } switch (opt) { case CChannelIdx: chan = MakeConnChannel(conn, 1); if (chan == NULL) { Tcl_AppendResult(interp, Tcl_PosixError(interp), NULL); return TCL_ERROR; } Tcl_RegisterChannel(interp, chan); Tcl_SetStringObj(result, Tcl_GetChannelName(chan), -1); break; case CContentSentLenIdx: if (objc == 2) { Tcl_SetIntObj(result, Ns_ConnContentSent(conn)); } else if (objc == 3) { if (Tcl_GetIntFromObj(interp, objv[2], &nContentSent) != TCL_OK || Ns_ConnSetContentSent(conn, nContentSent) != TCL_OK) { return TCL_ERROR; } } else { Tcl_WrongNumArgs(interp, 2, objv, "?value?"); return TCL_ERROR; } break; } return TCL_OK; } /*---------------------------------------------------------------------------- * MakeConnChannel -- * * Wraps a Tcl channel around the current connection socket * and returns the channel handle to the caller. * * Result: * Tcl_Channel handle or NULL. * * Side Effects: * Flushes the connection socket before dup'ing. * The resulting Tcl channel is set to blocking mode. * On error, connection socket is restored. * *---------------------------------------------------------------------------- */ static Tcl_Channel MakeConnChannel(Ns_Conn *conn, int spliceout) { Tcl_Channel chan; SOCKET sock; /* * Assures the socket is flushed */ Ns_WriteConn(conn, NULL, 0); /* * spliceout is always 1, why? */ if (spliceout) { sock = Ns_ConnSock(conn); if (Ns_ConnSetSockInvalid(conn) != NS_OK) { return NULL; } } else { sock = ns_sockdup(Ns_ConnSock(conn)); } if (sock == INVALID_SOCKET) { return NULL; } /* * At this point we may also set some other * chan config options (binary,encoding, etc) */ Ns_SockSetBlocking(sock); /* * Wrap a Tcl TCP channel around the socket. */ chan = Tcl_MakeTcpClientChannel((ClientData)sock); /* * Undo INVALID if unable to make channel, * otherwise close conn sock. * TJ: Note, spliceout is always 1, why? */ if (chan == NULL && spliceout) { Ns_ConnSetSock(conn,sock); } else if (chan != NULL) { ns_sockclose(Ns_ConnSock(conn)); } return chan; } /* *---------------------------------------------------------------------- * * BgTrace -- * * AOLserver Tcl trace to add ns_bgwrite command. * * Results: * NS_OK. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int BgTrace(Tcl_Interp *interp, void *ignored) { Nsbgwrite_Init(interp); return NS_OK; }