From 26f510204855c9240417bf392553a131c16f577b Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Thu, 15 Nov 2001 14:13:19 +0000
Subject: [PATCH] Added logout method and logged_in (boolean) property. Each
 client gets their own JS run-time to resolve concurrent GC blocking problems.

---
 src/sbbs3/services.c | 151 +++++++++++++++++++++++++++++++++----------
 1 file changed, 116 insertions(+), 35 deletions(-)

diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c
index 43c96aa6c0..614992e4e1 100644
--- a/src/sbbs3/services.c
+++ b/src/sbbs3/services.c
@@ -77,7 +77,6 @@ static scfg_t	scfg;
 static int		active_clients=0;
 static DWORD	sockets=0;
 static BOOL		terminated=FALSE;
-static JSRuntime* js_runtime=NULL;
 static time_t	uptime;
 
 typedef struct {
@@ -95,6 +94,8 @@ typedef struct {
 typedef struct {
 	SOCKET			socket;
 	SOCKADDR_IN		addr;
+	time_t			logintime;
+	user_t			user;
 	client_t*		client;
 	service_t*		service;
 } service_client_t;
@@ -287,7 +288,9 @@ static JSBool
 js_login(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
 	char*		p;
+	JSBool		inc_logons=JS_FALSE;
 	user_t		user;
+	jsval		val;
 	JSString*	js_str;
 	service_client_t* client;
 
@@ -328,10 +331,19 @@ js_login(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 			return(JS_TRUE);
 		}
 	}
+
+	if(argc>2)
+		inc_logons=JSVAL_TO_BOOLEAN(argv[2]);
+
 	sprintf(user.note,"%.*s",sizeof(user.note)-1,client->client->addr);
 	sprintf(user.comp,"%.*s",sizeof(user.comp)-1,client->client->host);
 	sprintf(user.modem,"%.*s",sizeof(user.modem)-1,client->service->protocol);
-	user.logons++;
+
+	if(inc_logons) {
+		user.logons++;
+		user.ltoday++;
+	}	
+
 	putuserdat(&scfg,&user);
 
 	/* user object */
@@ -352,6 +364,45 @@ js_login(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 	client->client->user=user.alias;
 	client_on(client->socket,client->client);
 
+	memcpy(&client->user,&user,sizeof(user));
+
+	client->logintime=time(NULL);
+
+	lprintf("%04d %s Logging in %s"
+		,client->socket,client->service->protocol,user.alias);
+
+	val = BOOLEAN_TO_JSVAL(JS_TRUE);
+	JS_SetProperty(cx, obj, "logged_in", &val);
+
+	*rval=BOOLEAN_TO_JSVAL(JS_TRUE);
+
+	return(JS_TRUE);
+}
+
+static JSBool
+js_logout(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+	jsval val;
+	service_client_t* client;
+
+	*rval = BOOLEAN_TO_JSVAL(JS_FALSE);
+
+	if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
+		return(JS_FALSE);
+
+	if(client->user.number<1)	/* Not logged in */
+		return(JS_TRUE);
+
+	logoutuserdat(&scfg,&client->user,time(NULL),client->logintime);
+
+	lprintf("%04d %s Logging out %s"
+		,client->socket,client->service->protocol,client->user.alias);
+
+	memset(&client->user,0,sizeof(client->user));
+
+	val = BOOLEAN_TO_JSVAL(JS_FALSE);
+	JS_SetProperty(cx, obj, "logged_in", &val);
+
 	*rval=BOOLEAN_TO_JSVAL(JS_TRUE);
 
 	return(JS_TRUE);
@@ -360,6 +411,7 @@ js_login(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 static JSFunctionSpec js_global_functions[] = {
 	{"log",				js_log,				1},		/* Log a string */
  	{"login",			js_login,			2},		/* Login specified username and password */
+	{"logout",			js_logout,			0},		/* Logout user */
     {0}
 };
 
@@ -368,10 +420,18 @@ js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
 {
 	char	line[64];
 	char	file[MAX_PATH+1];
+	char*	prot="???";
+	SOCKET	sock=0;
 	char*	warning;
+	service_client_t* client;
+
+	if((client=(service_client_t*)JS_GetContextPrivate(cx))!=NULL) {
+		prot=client->service->protocol;
+		sock=client->socket;
+	}
 
 	if(report==NULL) {
-		lprintf("!JavaScript: %s", message);
+		lprintf("%04d %s !JavaScript: %s", sock,prot,message);
 		return;
     }
 
@@ -393,11 +453,11 @@ js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
 	} else
 		warning="";
 
-	lprintf("!JavaScript %s%s%s: %s",warning,file,line,message);
+	lprintf("%04d %s !JavaScript %s%s%s: %s",sock,prot,warning,file,line,message);
 }
 
 static JSContext* 
-js_initcx(SOCKET sock, service_client_t* service_client, JSObject** glob)
+js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, JSObject** glob)
 {
 	JSContext*	js_cx;
 	JSObject*	js_glob;
@@ -410,8 +470,11 @@ js_initcx(SOCKET sock, service_client_t* service_client, JSObject** glob)
 
 //	lprintf("%04d JavaScript: Context created",sock);
 
+#if 0
+	lprintf("%04d %s JS_BeginRequest"
+		,sock,service_client->service->protocol);
 	JS_BeginRequest(js_cx);	/* Required for multi-thread support */
-
+#endif
     JS_SetErrorReporter(js_cx, js_ErrorReporter);
 
 	do {
@@ -458,6 +521,7 @@ js_initcx(SOCKET sock, service_client_t* service_client, JSObject** glob)
 
 
 	if(!success) {
+		lprintf("%04d JS_EndRequest",sock);
 		JS_EndRequest(js_cx);		/* Required for multi-thread support */
 		JS_DestroyContext(js_cx);
 		return(NULL);
@@ -475,7 +539,7 @@ static void js_service_thread(void* arg)
 	SOCKET					socket;
 	client_t				client;
 	service_t*				service;
-	service_client_t		service_client=*(service_client_t*)arg;
+	service_client_t		service_client;
 	/* JavaScript-specific */
 	char					spath[MAX_PATH];
 	char*					args=NULL;
@@ -484,10 +548,14 @@ static void js_service_thread(void* arg)
 	JSObject*				argv;
 	JSObject*				js_glob;
 	JSScript*				js_script;
+	JSRuntime*				js_runtime;
 	JSContext*				js_cx;
 	jsval					val;
 	jsval					rval;
 
+	// Copy service_client arg
+	service_client=*(service_client_t*)arg;
+	// Free original
 	free(arg);
 
 	socket=service_client.socket;
@@ -495,7 +563,6 @@ static void js_service_thread(void* arg)
 
 	lprintf("%04d %s JavaScript service thread started", socket, service->protocol);
 
-
 	thread_up();
 
 	/* Host name lookup and filtering */
@@ -517,6 +584,8 @@ static void js_service_thread(void* arg)
 		lprintf("%04d !%s CLIENT BLOCKED in host.can: %s"
 			,socket, service->protocol, host_name);
 		close_socket(socket);
+		if(service->clients)
+			service->clients--;
 		thread_down();
 		return;
 	}
@@ -546,20 +615,26 @@ static void js_service_thread(void* arg)
 	client.user="<unknown>";
 	service_client.client=&client;
 
-	if(((js_cx=js_initcx(socket,&service_client,&js_glob))==NULL)) {
+	active_clients++;
+	update_clients();
+
+	/* Initialize client display */
+	client_on(socket,&client);
+
+	if((js_runtime=JS_NewRuntime(JAVASCRIPT_RUNTIME_MEMORY))==NULL
+		|| (js_cx=js_initcx(js_runtime,socket,&service_client,&js_glob))==NULL) {
 		lprintf("%04d !%s ERROR initializing JavaScript context"
 			,socket,service->protocol);
 		close_socket(socket);
+		client_off(socket);
+		active_clients--;
+		update_clients();
+		if(service->clients)
+			service->clients--;
 		thread_down();
 		return;
 	}
 
-	active_clients++;
-	update_clients();
-
-	/* Initialize client display */
-	client_on(socket,&client);
-
 	/* RUN SCRIPT */
 	sprintf(spath,"%s%s",scfg.exec_dir,service->cmd);
 	p=spath;
@@ -594,25 +669,44 @@ static void js_service_thread(void* arg)
 	JS_DefineProperty(js_cx, js_glob, "argc", INT_TO_JSVAL(argc)
 		,NULL,NULL,JSPROP_READONLY);
 
+	val = BOOLEAN_TO_JSVAL(JS_FALSE);
+	JS_SetProperty(js_cx, js_glob, "logged_in", &val);
+
 	js_script=JS_CompileFile(js_cx, js_glob, spath);
 
 	if(js_script==NULL) 
 		lprintf("%04d !JavaScript FAILED to compile script (%s)",socket,spath);
 	else  {
 		JS_ExecuteScript(js_cx, js_glob, js_script, &rval);
+		lprintf("%04d %s JS_DestroyScript",socket,service->protocol);
 		JS_DestroyScript(js_cx, js_script);
+//		lprintf("%04d JS_GC",socket);
+//		JS_GC(js_cx);
 	}
 	close_socket(socket);
 
-	JS_EndRequest(js_cx);
+//	lprintf("%04d JS_EndRequest",socket);
+//	JS_EndRequest(js_cx);
 
-//	lprintf("%04d JavaScript: Destroying context",socket);
+
+	lprintf("%04d %s JS_DestroyContext",socket,service->protocol);
 	JS_DestroyContext(js_cx);	/* Free Context */
 
-	lprintf("%04d %s JavaScript service thread terminated", socket, service->protocol);
+	lprintf("%04d %s JS_DestroyRuntime",socket,service->protocol);
+	JS_DestroyRuntime(js_runtime);
+
+	if(service_client.user.number) {
+		lprintf("%04d %s Logging out %s"
+			,socket, service->protocol, service_client.user.alias);
+		logoutuserdat(&scfg,&service_client.user,time(NULL),service_client.logintime);
+	}
+
 	if(service->clients)
 		service->clients--;
 
+	lprintf("%04d %s JavaScript service thread terminated (%u clients remain)"
+		, socket, service->protocol, service->clients);
+
 	active_clients--;
 	update_clients();
 	client_off(socket);
@@ -724,10 +818,12 @@ static void native_service_thread(void* arg)
 	close_socket(socket);
 	closesocket(socket_dup);	/* close duplicate handle */
 
-	lprintf("%04d %s service thread terminated", socket, service->protocol);
 	if(service->clients)
 		service->clients--;
 
+	lprintf("%04d %s service thread terminated (%u clients remain)"
+		,socket, service->protocol, service->clients);
+
 	active_clients--;
 	update_clients();
 	client_off(socket);
@@ -806,12 +902,6 @@ static void cleanup(int code)
 		lprintf("0000 !WSACleanup ERROR %d",ERROR_VALUE);
 #endif
 
-	if(js_runtime!=NULL) {
-		lprintf("0000 JavaScript: Destroying runtime");
-		JS_DestroyRuntime(js_runtime);
-		js_runtime=NULL;
-	}
-
     lprintf("#### Services thread terminated");
 	status("Down");
 	if(startup!=NULL && startup->terminated!=NULL)
@@ -936,16 +1026,6 @@ void DLLCALL services_thread(void* arg)
 		return;
 	}
 
-	lprintf("JavaScript: Creating runtime: %lu bytes"
-		,JAVASCRIPT_RUNTIME_MEMORY);
-
-	if((js_runtime = JS_NewRuntime(JAVASCRIPT_RUNTIME_MEMORY))==NULL) {
-		lprintf("!JS_NewRuntime failed");
-		cleanup(1);
-		return;
-	}
-	lprintf("JavaScript: Context stack: %lu bytes", JAVASCRIPT_CONTEXT_STACK);
-
 	active_clients=0;
 	update_clients();
 
@@ -1078,6 +1158,7 @@ void DLLCALL services_thread(void* arg)
 			if(startup->socket_open!=NULL)
 				startup->socket_open(TRUE);	/* Callback */
 
+			memset(client,0,sizeof(service_client_t));
 			client->socket=client_socket;
 			client->addr=client_addr;
 			client->service=&service[i];
-- 
GitLab