rem: if (Tcl_Eval(interp, ds.string) != TCL_OK) {
add: if (Tcl_RecordAndEval(interp, ds.string, 0) != TCL_OK) {
context: static Tcl_CmdProc ExitCmd;static int Login(Sess *sessPtr);static int GetLine(SOCKET sock, char *prompt, Tcl_DString *dsPtr, int echo);static Ns_ArgProc ArgProc;/* * The following values are sent to the telnet client to enable * and disable password prompt echo. */#define TN_IAC  255#define TN_WILL 251#define TN_WONT 252#define TN_DO   253#define TN_DONT 254#define TN_EOF  236#define TN_IP   244#define TN_ECHO   1static unsigned char do_echo[]    = {TN_IAC, TN_DO,   TN_ECHO};static unsigned char dont_echo[]  = {TN_IAC, TN_DONT, TN_ECHO};static unsigned char will_echo[]  = {TN_IAC, TN_WILL, TN_ECHO};static unsigned char wont_echo[]  = {TN_IAC, TN_WONT, TN_ECHO};NS_EXPORT int Ns_ModuleVersion = 1;/* *---------------------------------------------------------------------- * * Ns_ModuleInit -- * *	Load the config parameters, setup the structures, and *	listen on the control port. * * Results: *	None. * * Side effects: *	Server will listen for control connections on specified *  	address and port. * *---------------------------------------------------------------------- */ NS_EXPORT intNs_ModuleInit(char *server, char *module){    Mod *modPtr;    char *path, *addr, *pass, *user, *key, *end;    int i, new, port;    SOCKET lsock;    Ns_Set *set;    Tcl_HashEntry *hPtr;    /*      * Create the listening socket and callback.     */    path = Ns_ConfigGetPath(server, module, NULL);    if (((addr = Ns_ConfigGet(path, "address")) == NULL)	 || (!Ns_ConfigGetInt(path, "port", &port)) )  {	Ns_Log(Error, "nscp: address and port must be specified in config");	return NS_ERROR;    }    lsock = Ns_SockListen(addr, port);    if (lsock == INVALID_SOCKET) {	Ns_Log(Error, "nscp: could not listen on %s:%d", addr, port);	return NS_ERROR;    }    Ns_Log(Notice, "nscp: listening on %s:%d", addr, port);    /*     * Create a new Mod structure for this instance.     */    modPtr = ns_malloc(sizeof(Mod));    modPtr->server = server;    modPtr->addr = addr;    modPtr->port = port;    if (!Ns_ConfigGetBool(path, "echopassword", &modPtr->echo)) {    	modPtr->echo = 1;    }    /*     * Initialize the hash table of authorized users.  Entry values     * are either NULL indicating authorization should be checked     * via the Ns_AuthorizeUser() API or contain a Unix crypt(3)     * sytle encrypted password.  For the later, the entry is     * compatible with /etc/passwd (i.e., username followed by     * password separated by colons).     */    Tcl_InitHashTable(&modPtr->users, TCL_STRING_KEYS);    path = Ns_ConfigGetPath(server, module, "users", NULL);    set = Ns_ConfigGetSection(path);    for (i = 0; set != NULL && i < Ns_SetSize(set); ++i) {	key = Ns_SetKey(set, i);	user = Ns_SetValue(set, i);	if (!STRIEQ(key, "user") || (pass = strchr(user, ':')) == NULL) {	    continue;	}	*pass = '\0';	hPtr = Tcl_CreateHashEntry(&modPtr->users, user, &new);	if (new) {	    Ns_Log(Notice, "nscp: added user: %s", user);	} else {	    Ns_Log(Warning, "nscp: duplicate user: %s", user);	    ns_free(Tcl_GetHashValue(hPtr));	}	*pass++ = ':';	end = strchr(pass, ':');	if (end != NULL) {	    *end = '\0';	}	pass = ns_strdup(pass);	if (end != NULL) {	    *end = ':';	}	Tcl_SetHashValue(hPtr, pass);    }    if (modPtr->users.numEntries == 0) {	Ns_Log(Warning, "nscp: no authorized users");    }    Ns_SockCallback(lsock, AcceptProc, modPtr, NS_SOCK_READ|NS_SOCK_EXIT);    Ns_RegisterProcInfo((void *)AcceptProc, "nscp", ArgProc);    return NS_OK;}/* *---------------------------------------------------------------------- * * ArgProc -- * *	Append listen port info for query callback. * * Results: *	None * * Side effects: *  	None. * *---------------------------------------------------------------------- */static voidArgProc(Tcl_DString *dsPtr, void *arg){    Mod *modPtr = arg;    char buf[20];    sprintf(buf, "%d", modPtr->port);    Tcl_DStringStartSublist(dsPtr);    Tcl_DStringAppendElement(dsPtr, modPtr->addr);    Tcl_DStringAppendElement(dsPtr, buf);    Tcl_DStringEndSublist(dsPtr);}/* *---------------------------------------------------------------------- * * AcceptProc -- * *	Socket callback to accept a new connection. * * Results: *	NS_TRUE to keep listening unless shutdown is in progress. * * Side effects: *  	New EvalThread will be created. * *---------------------------------------------------------------------- */static intAcceptProc(SOCKET lsock, void *arg, int why){    Mod *modPtr = arg;    Sess *sessPtr;    int len;    static int next;    if (why == NS_SOCK_EXIT) {	Ns_Log(Notice, "nscp: shutdown");	ns_sockclose(lsock);	return NS_FALSE;    }    sessPtr = ns_malloc(sizeof(Sess));    sessPtr->modPtr = modPtr;    len = sizeof(struct sockaddr_in);    sessPtr->sock = Ns_SockAccept(lsock, (struct sockaddr *) &sessPtr->sa, &len);    if (sessPtr->sock == INVALID_SOCKET) {	Ns_Log(Error, "nscp: accept() failed: %s",	       ns_sockstrerror(ns_sockerrno));	ns_free(sessPtr);    } else {	sessPtr->id = ++next;	Ns_ThreadCreate(EvalThread, sessPtr, 0, NULL);    }    return NS_TRUE;}/* *---------------------------------------------------------------------- * * EvalThread -- * *	Thread to read and evaluate commands from remote. * * Results: *	None. * * Side effects: *  	Depends on commands. * *---------------------------------------------------------------------- */static voidEvalThread(void *arg){    Tcl_Interp *interp;    Tcl_DString ds;    char buf[64], *res;    int n, len, ncmd, stop;    Sess *sessPtr = arg;    char *server = sessPtr->modPtr->server;    /*     * Initialize the thread and login the user.     */         Tcl_DStringInit(&ds);    sprintf(buf, "-nscp:%d-", sessPtr->id);    Ns_ThreadSetName(buf);    Ns_Log(Notice, "nscp: connect: %s", ns_inet_ntoa(sessPtr->sa.sin_addr));    if (!Login(sessPtr)) {	goto done;    }    /*     * Loop until the remote shuts down, evaluating complete     * commands.     */    interp = Ns_TclAllocateInterp(server);    /*     * Create a special exit command for this interp only.     */    stop = 0;    Tcl_CreateCommand(interp, "exit", ExitCmd, (ClientData) &stop, NULL);    ncmd = 0;    while (!stop) {	Tcl_DStringTrunc(&ds, 0);	++ncmd;retry:	sprintf(buf, "%s:nscp %d> ", server, ncmd);	while (1) {	    if (!GetLine(sessPtr->sock, buf, &ds, 1)) {		goto done;	    }	    if (Tcl_CommandComplete(ds.string)) {		break;	    }	    sprintf(buf, "%s:nscp %d>>> ", server, ncmd);	}	while (ds.length > 0 && ds.string[ds.length-1] == '\n') {	    Tcl_DStringTrunc(&ds, ds.length-1);	}	if (STREQ(ds.string, "")) {	    goto retry; /* Empty command - try again. */	}	if (Tcl_Eval(interp, ds.string) != TCL_OK) {	    Ns_TclLogError(interp);	}	Tcl_AppendResult(interp, "\r\n", NULL);	res = interp->result;	len = strlen(res);	while (len > 0) {	    if ((n = send(sessPtr->sock, res, len, 0)) <= 0) goto done;	    len -= n;	    res += n;	}    }done:    Tcl_DStringFree(&ds);    Ns_TclDeAllocateInterp(interp);    Ns_Log(Notice, "nscp: disconnect: %s", ns_inet_ntoa(sessPtr->sa.sin_addr));    ns_sockclose(sessPtr->sock);    ns_free(sessPtr);}/* *---------------------------------------------------------------------- * * GetLine -- * *	Prompt for a line of input from the remote.  \r\n sequences *  	are translated to \n. * * Results: *  	1 if line received, 0 if remote dropped. * * Side effects: *  	None. * *---------------------------------------------------------------------- */static intGetLine(SOCKET sock, char *prompt, Tcl_DString *dsPtr, int echo){    unsigned char buf[2048];    int n;    int result = 0;    /*     * Suppress output on things like password prompts.     */    if (!echo) {	send(sock, will_echo, 3, 0);	send(sock, dont_echo, 3, 0);	recv(sock, buf, sizeof(buf), 0); /* flush client ack thingies */    }    n = strlen(prompt);    if (send(sock, prompt, n, 0) != n) {	result = 0;	goto bail;    }    do {	if ((n = recv(sock, buf, sizeof(buf), 0)) <= 0) {	    result = 0;	    goto bail;	}	if (n > 1 && buf[n-1] == '\n' && buf[n-2] == '\r') {	    buf[n-2] = '\n';	    --n;	}	/*	 * This EOT checker cannot happen in the context of telnet.	 */	if (n == 1 && buf[0] == 4) {	    result = 0;	    goto bail;	}		/*	 * Deal with telnet IAC commands in some sane way.	 */	if (n > 1 && buf[0] == TN_IAC) {	    if ( buf[1] == TN_EOF) {		result = 0;		goto bail;	    } else if (buf[1] == TN_IP) {		result = 0;		goto bail;	    } else {		Ns_Log(Warning, "nscp: "		       "unsupported telnet IAC code received from client");		result = 0;		goto bail;	    }	}	Tcl_DStringAppend(dsPtr, buf, n);	result = 1;    } while (buf[n-1] != '\n'); bail:    if (!echo) {	send(sock, wont_echo, 3, 0);	send(sock, do_echo, 3, 0);	recv(sock, buf, sizeof(buf), 0); /* flush client ack thingies */    }    return result;}/* *---------------------------------------------------------------------- * * Login -- * *	Attempt to login the user. * * Results: *  	1 if login ok, 0 otherwise. * * Side effects: *  	None. * *---------------------------------------------------------------------- */static intLogin(Sess *sessPtr){    Tcl_HashEntry *hPtr;    Tcl_DString uds, pds;    char *encpass, *user, *pass, msg[255], buf[30];    int ok;    user = NULL;    ok = 0;    Tcl_DStringInit(&uds);    Tcl_DStringInit(&pds);    if (GetLine(sessPtr->sock, "login: ", &uds, 1) &&	GetLine(sessPtr->sock, "Password: ", &pds, sessPtr->modPtr->echo)) {	user = Ns_StrTrim(uds.string);	pass = Ns_StrTrim(pds.string);    	hPtr = Tcl_FindHashEntry(&sessPtr->modPtr->users, user);	if (hPtr != NULL) {    	    encpass = Tcl_GetHashValue(hPtr);	    Ns_Encrypt(pass, encpass, buf);    	    if (STREQ(buf, encpass)) {		ok = 1;	    }	}    }    if (ok) {	Ns_Log(Notice, "nscp: logged in: '%s'", user);	sprintf(msg, "\nWelcome to %s running at %s (pid %d)\n"		"%s/%s (%s) for %s built on %s\nCVS Tag: %s\n",		sessPtr->modPtr->server,		Ns_InfoNameOfExecutable(), Ns_InfoPid(),		Ns_InfoServerName(), Ns_InfoServerVersion(), Ns_InfoLabel(),		Ns_InfoPlatform(), Ns_InfoBuildDate(), Ns_InfoTag());    } else {	Ns_Log(Warning, "nscp: login failed: '%s'", user ? user : "?");	sprintf(msg, "Access denied!\n");    }    (void) send(sessPtr->sock, msg, strlen(msg), 0);    Tcl_DStringFree(&uds);    Tcl_DStringFree(&pds);    return ok;}/* *---------------------------------------------------------------------- * * ExitCmd -- * *	Special exit command for nscp. * * Results: *  	Standard Tcl result. * * Side effects: *  	None. * *---------------------------------------------------------------------- */static intExitCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv){    int *stopPtr;    if (argc != 1) {	Tcl_AppendResult(interp, "wrong # args: should be \"",	    argv[0], "\"", NULL);	return TCL_ERROR;    }    stopPtr = (int *) arg;    *stopPtr = 1;    Tcl_SetResult(interp, "\nGoodbye!", TCL_STATIC);    return TCL_OK;}