Skip to content
Snippets Groups Projects
services.c 62.5 KiB
Newer Older

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

/* Platform-specific headers */
rswindell's avatar
rswindell committed
#ifdef __unix__
	#include <sys/param.h>	/* BSD? */
#endif

/* ANSI C Library headers */
#include <stdio.h>
#include <stdlib.h>			/* ltoa in GNU C lib */
#include <stdarg.h>			/* va_list */
#include <string.h>			/* strrchr */
#include <ctype.h>			/* isdigit */
#include <fcntl.h>			/* Open flags */
#include <errno.h>			/* errno */

/* Synchronet-specific headers */
#ifndef JAVASCRIPT
#define JAVASCRIPT	/* required to include JS API headers */
#endif
#define SERVICES_INI_BITDESC_TABLE	/* required to defined service_options */
#undef SBBS	/* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
#include "sbbs.h"
#include "services.h"
#include "ident.h"	/* identify() */
deuce's avatar
deuce committed
#include "js_socket.h"
#include "multisock.h"
#include "ssl.h"
#define MAX_UDP_BUF_LEN			8192	/* 8K */
#define DEFAULT_LISTEN_BACKLOG	5

static services_startup_t* startup=NULL;
static scfg_t	scfg;
static volatile BOOL	terminated=FALSE;
static time_t	uptime=0;
static ulong	served=0;
static char		revision[16];
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
static protected_uint32_t threads_pending_start;

typedef struct {
	/* These are sysop-configurable */
deuce's avatar
deuce committed
	uint32_t		interface_addr;
	uint16_t		port;
deuce's avatar
deuce committed
	struct in_addr		outgoing4;
	struct in6_addr	outgoing6;
	char			protocol[34];
	char			cmd[128];
	uint			max_clients;
	uint32_t		options;
	int				listen_backlog;
	int				log_level;
	uint32_t		stack_size;
	js_server_props_t js_server_props;
	/* These are run-time state and stat vars */
deuce's avatar
deuce committed
	uint32_t		clients;
	ulong			served;
	struct xpms_set	*set;
	int				running;
	BOOL			terminated;
} service_t;

typedef struct {
deuce's avatar
deuce committed
	SOCKET				socket;
	struct xpms_set		*set;
	union xp_sockaddr	addr;
	time_t				logintime;
	user_t				user;
	client_t*			client;
	service_t*			service;
	js_callback_t		callback;
	/* Initial UDP datagram */
deuce's avatar
deuce committed
	BYTE*				udp_buf;
	int					udp_len;
	subscan_t			*subscan;
	CRYPT_SESSION		tls_sess;
static uint32_t		services=0;
static int lprintf(int level, const char *fmt, ...)
{
	va_list argptr;
	char sbuf[1024];

	va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);

		char errmsg[sizeof(sbuf)+16];
		SAFEPRINTF(errmsg, "srvc %s", sbuf);
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, errmsg);
		if(startup!=NULL && startup->errormsg!=NULL)
			startup->errormsg(startup->cbdata,level,errmsg);
    if(startup==NULL || startup->lputs==NULL || level > startup->log_level)
#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

    return(startup->lputs(startup->cbdata,level,sbuf));
}

#ifdef _WINSOCKAPI_

static WSADATA WSAData;
#define SOCKLIB_DESC WSAData.szDescription
static BOOL WSAInitialized=FALSE;

static BOOL winsock_startup(void)
{
	int		status;             /* Status Code */

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
		lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
		WSAInitialized=TRUE;
		return (TRUE);
	}

    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
	return (FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)
rswindell's avatar
rswindell committed
static ulong active_clients(void)
{
	ulong i;
	ulong total_clients=0;

	for(i=0;i<services;i++) 
		total_clients+=service[i].clients;

	return(total_clients);
}

static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
		startup->clients(startup->cbdata,active_clients());
static void client_on(SOCKET sock, client_t* client, BOOL update)
{
	if(startup!=NULL && startup->client_on!=NULL)
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
static void thread_up(BOOL setuid)
{
	if(startup!=NULL && startup->thread_up!=NULL)
		startup->thread_up(startup->cbdata,TRUE,setuid);
}

static void thread_down(void)
{
	if(startup!=NULL && startup->thread_up!=NULL)
		startup->thread_up(startup->cbdata,FALSE,FALSE);
deuce's avatar
deuce committed
void open_socket_cb(SOCKET sock, void *serv_ptr)
deuce's avatar
deuce committed
	service_t	*serv=(service_t *)serv_ptr;
deuce's avatar
deuce committed
	if(startup!=NULL && startup->socket_open!=NULL)
		startup->socket_open(startup->cbdata,TRUE);
deuce's avatar
deuce committed
	SAFEPRINTF(section,"services|%s", serv->protocol);
	if(set_socket_options(&scfg, sock, section, error, sizeof(error)))
		lprintf(LOG_ERR,"%04d !ERROR %s",sock, error);
}

void close_socket_cb(SOCKET sock, void *serv_ptr)
{
	if(startup!=NULL && startup->socket_open!=NULL)
		startup->socket_open(startup->cbdata,FALSE);
}

static SOCKET open_socket(int family, int type, service_t* serv)
{
	SOCKET	sock;

	sock=socket(family, type, IPPROTO_IP);
	if(sock!=INVALID_SOCKET)
		open_socket_cb(sock, serv);
	return(sock);
}

static int close_socket(SOCKET sock)
{
	int		result;

	if(sock==INVALID_SOCKET)
		return(-1);

	shutdown(sock,SHUT_RDWR);	/* required on Unix */
	result=closesocket(sock);
	if(startup!=NULL && startup->socket_open!=NULL) 
		startup->socket_open(startup->cbdata,FALSE);
		lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",sock, ERROR_VALUE);

	return(result);
}

static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
	    startup->status(startup->cbdata,str);
rswindell's avatar
rswindell committed
/* Global JavaScript Methods */
rswindell's avatar
rswindell committed
static JSBool
js_log(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
{
	jsval *argv=JS_ARGV(cx, arglist);
rswindell's avatar
rswindell committed
	char		str[512];
rswindell's avatar
rswindell committed
	service_client_t* client;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

rswindell's avatar
rswindell committed
	if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

	if(startup==NULL || startup->lputs==NULL)
		return(JS_FALSE);
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
rswindell's avatar
rswindell committed
	str[0]=0;
    for(;i<argc && strlen(str)<(sizeof(str)/2);i++) {
deuce's avatar
deuce committed
		JSVALUE_TO_MSTRING(cx, argv[i], line, NULL);
		HANDLE_PENDING(cx, line);
deuce's avatar
deuce committed
		if(line==NULL)
rswindell's avatar
rswindell committed
		    return(JS_FALSE);
deuce's avatar
deuce committed
		strncat(str,line,sizeof(str)/2);
deuce's avatar
deuce committed
		free(line);
rswindell's avatar
rswindell committed
		strcat(str," ");
	}

		lprintf(level,"%04d %s",client->socket,str);
	else if(level <= client->service->log_level)
		lprintf(level,"%04d %s %s",client->socket,client->service->protocol,str);
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)));
rswindell's avatar
rswindell committed
    return(JS_TRUE);
}

deuce's avatar
deuce committed
static void badlogin(SOCKET sock, char* prot, char* user, char* passwd, char* host, union xp_sockaddr* addr)
deuce's avatar
deuce committed
	char addr_ip[INET6_ADDRSTRLEN];
	ulong count;

	SAFEPRINTF(reason,"%s LOGIN", prot);
	count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd);
rswindell's avatar
rswindell committed
	if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold)
		hacklog(&scfg, reason, user, passwd, host, addr);
rswindell's avatar
rswindell committed
	if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold) {
deuce's avatar
deuce committed
		inet_addrtop(addr, addr_ip, sizeof(addr_ip));
		filter_ip(&scfg, prot, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS"
deuce's avatar
deuce committed
			,host, addr_ip, user, /* fname: */NULL);
	}
rswindell's avatar
rswindell committed
	mswait(startup->login_attempt.delay);
js_login(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
	JSBool		inc_logons=JS_FALSE;
	jsval		val;
	service_client_t* client;
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(JS_FALSE));

	if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

rswindell's avatar
rswindell committed
	JSVALUE_TO_ASTRING(cx, argv[0], user, 128, NULL);
deuce's avatar
deuce committed
	if(user==NULL)
deuce's avatar
deuce committed
	JSVALUE_TO_ASTRING(cx, argv[1], pass, LEN_PASS+2, NULL);
	if(pass==NULL) 
	memset(&client->user,0,sizeof(user_t));
	/* ToDo Deuce: did you mean to do this *before* the above memset(0) ? */
	if(client->user.number) {
		if(client->subscan!=NULL)
			putmsgptrs(&scfg, &client->user, client->subscan);
	if(isdigit(*user))
		client->user.number=atoi(user);
	else if(*user)
		client->user.number=matchuser(&scfg,user,FALSE);
	if(getuserdat(&scfg,&client->user)!=0) {
		lprintf(LOG_NOTICE,"%04d %s !USER NOT FOUND: '%s'"
			,client->socket,client->service->protocol,user);
		badlogin(client->socket, client->service->protocol, user, pass, client->client->host, &client->addr);
	if(client->user.misc&(DELETED|INACTIVE)) {
		lprintf(LOG_WARNING,"%04d %s !DELETED OR INACTIVE USER #%d: %s"
			,client->socket,client->service->protocol,client->user.number,user);
	if(client->user.pass[0] && stricmp(client->user.pass,pass)) { /* Wrong password */
		lprintf(LOG_WARNING,"%04d %s !INVALID PASSWORD ATTEMPT FOR USER: %s"
			,client->socket,client->service->protocol,client->user.alias);
		badlogin(client->socket, client->service->protocol, user, pass, client->client->host, &client->addr);
		JS_RESUMEREQUEST(cx, rc);
		return(JS_TRUE);
		JS_ValueToBoolean(cx,argv[2],&inc_logons);
deuce's avatar
deuce committed
		SAFECOPY(client->user.ipaddr,client->client->addr);
		SAFECOPY(client->user.comp,client->client->host);
		SAFECOPY(client->user.modem,client->service->protocol);
		client->user.logons++;
		client->user.ltoday++;
	putuserdat(&scfg,&client->user);
	if(client->subscan==NULL) {
		client->subscan=(subscan_t*)malloc(sizeof(subscan_t)*scfg.total_subs);
		if(client->subscan==NULL)
			lprintf(LOG_CRIT,"!MALLOC FAILURE");
	}
	if(client->subscan!=NULL) {
		getmsgptrs(&scfg,&client->user,client->subscan,NULL,NULL);
	if(!js_CreateUserObjects(cx, obj, &scfg, &client->user, client->client, NULL, client->subscan))
		lprintf(LOG_ERR,"%04d %s !JavaScript ERROR creating user objects"
			,client->socket,client->service->protocol);

		client->client->user=client->user.alias;
		client_on(client->socket,client->client,TRUE /* update */);
	}
	client->logintime=time(NULL);

	lprintf(LOG_INFO,"%04d %s Logging in %s"
		,client->socket,client->service->protocol,client->user.alias);

	val = BOOLEAN_TO_JSVAL(JS_TRUE);
	JS_SetProperty(cx, obj, "logged_in", &val);

		loginSuccess(startup->login_attempt_list, &client->addr);
	JS_SET_RVAL(cx, arglist,BOOLEAN_TO_JSVAL(JS_TRUE));
js_logout(JSContext *cx, uintN argc, jsval *arglist)
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval val;
	service_client_t* client;
	JS_SET_RVAL(cx, arglist, 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);

	if(!logoutuserdat(&scfg,&client->user,time(NULL),client->logintime))
		lprintf(LOG_ERR,"%04d !ERROR in logoutuserdat",client->socket);
	lprintf(LOG_INFO,"%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);

	JS_SET_RVAL(cx, arglist,BOOLEAN_TO_JSVAL(JS_TRUE));
/*
 * This macro is used to expose a function from the global
 * client.socket object in the global namespace.
 */
#define SOCKET_WRAPPER(funcname) \
static JSBool \
js_##funcname (JSContext *cx, uintN argc, jsval *arglist) \
{ \
	JSObject *obj=JS_THIS_OBJECT(cx, arglist); \
	JSObject *tmpobj; \
	jsval val; \
	jsval rval; \
	JSObject*	socket_obj; \
	jsval *argv=JS_ARGV(cx, arglist); \
	JSBool retval; \
\
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(JS_FALSE)); \
\
	if (!JS_GetProperty(cx, obj, "client", &val) || val == JSVAL_VOID) \
		return JS_FALSE; \
	tmpobj=JSVAL_TO_OBJECT(val); \
	if (!JS_GetProperty(cx, tmpobj, "socket", &val) || val == JSVAL_VOID) \
		return JS_FALSE; \
	socket_obj=JSVAL_TO_OBJECT(val); \
	retval = JS_CallFunctionName(cx, socket_obj, #funcname, argc, argv, &rval); \
	JS_SET_RVAL(cx, arglist, rval); \
	return retval; \
}

SOCKET_WRAPPER(read)
SOCKET_WRAPPER(readln)
SOCKET_WRAPPER(write)
SOCKET_WRAPPER(writeln)
SOCKET_WRAPPER(print)

rswindell's avatar
rswindell committed
static JSFunctionSpec js_global_functions[] = {
	{"read",			js_read,			0},		/* Read from socket */
	{"readln",			js_readln,			0},		/* Read line from socket */
	{"write",			js_write,			1},		/* Write to socket */
	{"writeln",			js_writeln,			0},		/* Write line to socket */
	{"print",			js_print,			0},		/* Write line to socket */
	{"log",				js_log,				0},		/* Log a string */
 	{"login",			js_login,			2},		/* Login specified username and password */
	{"logout",			js_logout,			0},		/* Logout user */
static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
	char	line[64];
	char	file[MAX_PATH+1];
	char*	prot="???";
	SOCKET	sock=0;
	service_client_t* client;

	if((client=(service_client_t*)JS_GetContextPrivate(cx))!=NULL) {
		prot=client->service->protocol;
		sock=client->socket;
	}
		lprintf(LOG_ERR,"%04d %s !JavaScript: %s", sock,prot,message);
		return;
    }

	if(report->filename)
		sprintf(file," %s",report->filename);
	else
		file[0]=0;

	if(report->lineno)
		sprintf(line," line %d",report->lineno);
	else
		line[0]=0;

	if(JSREPORT_IS_WARNING(report->flags)) {
		if(JSREPORT_IS_STRICT(report->flags))
			warning="strict warning";
		else
			warning="warning";
		log_level=LOG_WARNING;
	} else {
		log_level=LOG_ERR;
	lprintf(log_level,"%04d %s !JavaScript %s%s%s: %s",sock,prot,warning,file,line,message);
js_client_add(JSContext *cx, uintN argc, jsval *arglist)
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
	client_t		client;
	SOCKET			sock=INVALID_SOCKET;
	socklen_t		addr_len;
	union xp_sockaddr	addr;
deuce's avatar
deuce committed
	jsrefcount		rc;
	char			*cstr=NULL;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((service_client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

	service_client->service->clients++;
	update_clients();
	service_client->service->served++;
	served++;
	memset(&client,0,sizeof(client));
	client.size=sizeof(client);
	client.protocol=service_client->service->protocol;
deuce's avatar
deuce committed

	sock=js_socket(cx,argv[0]);
deuce's avatar
deuce committed

deuce's avatar
deuce committed
	if(getpeername(sock, &addr.addr, &addr_len)==0) {
		inet_addrtop(&addr, client.addr, sizeof(client.addr));
		client.port=inet_addrport(&addr);
deuce's avatar
deuce committed
	if(argc>1) {
deuce's avatar
deuce committed
		JSVALUE_TO_MSTRING(cx, argv[1], cstr, NULL);
		HANDLE_PENDING(cx, cstr);
deuce's avatar
deuce committed
		client.user=cstr;
	}
deuce's avatar
deuce committed
	if(argc>2)
		JSVALUE_TO_STRBUF(cx, argv[2], client.host, sizeof(client.host), NULL);
	client_on(sock, &client, /* update? */ FALSE);
deuce's avatar
deuce committed
	lprintf(LOG_DEBUG,"%s client_add(%04u,%s,%s)"
		,service_client->service->protocol
deuce's avatar
deuce committed
	if(cstr)
		free(cstr);
js_client_update(JSContext *cx, uintN argc, jsval *arglist)
	jsval *argv=JS_ARGV(cx, arglist);
	client_t	client;
	SOCKET		sock=INVALID_SOCKET;
	socklen_t	addr_len;
deuce's avatar
deuce committed
	union xp_sockaddr	addr;
	service_client_t*	service_client;
deuce's avatar
deuce committed
	char		*cstr=NULL;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((service_client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);

	memset(&client,0,sizeof(client));
	client.size=sizeof(client);
	client.protocol=service_client->service->protocol;
	sock=js_socket(cx,argv[0]);
deuce's avatar
deuce committed
	if(getpeername(sock, &addr.addr, &addr_len)==0) {
		inet_addrtop(&addr, client.addr, sizeof(client.addr));
		client.port=inet_addrport(&addr);
deuce's avatar
deuce committed
	if(argc>1) {
deuce's avatar
deuce committed
		JSVALUE_TO_MSTRING(cx, argv[1], cstr, NULL);
deuce's avatar
deuce committed
		client.user=cstr;
	}
deuce's avatar
deuce committed
	if(argc>2)
		JSVALUE_TO_STRBUF(cx, argv[2], client.host, sizeof(client.host), NULL);
	client_on(sock, &client, /* update? */ TRUE);
deuce's avatar
deuce committed
	lprintf(LOG_DEBUG,"%s client_update(%04u,%s,%s)"
		,service_client->service->protocol
deuce's avatar
deuce committed
	if(cstr)
		free(cstr);
js_client_remove(JSContext *cx, uintN argc, jsval *arglist)
	jsval *argv=JS_ARGV(cx, arglist);
	service_client_t* service_client;
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

	if((service_client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
		return(JS_FALSE);
	sock=js_socket(cx,argv[0]);
		if(service_client->service->clients==0)
deuce's avatar
deuce committed
			lprintf(LOG_WARNING,"%s !client_remove() called with 0 service clients"
				,service_client->service->protocol);
		else {
			service_client->service->clients--;
			update_clients();
		}
deuce's avatar
deuce committed
	lprintf(LOG_DEBUG,"%s client_remove(%04u)"
		,service_client->service->protocol, sock);
js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, JSObject** glob)
	BOOL		success=FALSE;
    if((js_cx = JS_NewContext(js_runtime, service_client->service->js.cx_stack))==NULL)

    JS_SetErrorReporter(js_cx, js_ErrorReporter);

	/* ToDo: call js_CreateCommonObjects() instead */

rswindell's avatar
rswindell committed
		JS_SetContextPrivate(js_cx, service_client);

		if(!js_CreateGlobalObject(js_cx, &scfg, NULL, &service_client->service->js, glob))
		if (!JS_DefineFunctions(js_cx, *glob, js_global_functions))
rswindell's avatar
rswindell committed
			break;

		if(js_CreateInternalJsObject(js_cx, *glob, &service_client->callback, &service_client->service->js)==NULL)
		/* Client Object */
deuce's avatar
deuce committed
		if(service_client->client!=NULL) {
			if(js_CreateClientObject(js_cx, *glob, "client", service_client->client, sock, service_client->tls_sess)==NULL)
deuce's avatar
deuce committed
		}
		if(js_CreateUserClass(js_cx, *glob, &scfg)==NULL) 
		if(js_CreateSocketClass(js_cx, *glob)==NULL)
		/* MsgBase Class */
		if(js_CreateMsgBaseClass(js_cx, *glob, &scfg)==NULL)
		if(js_CreateFileClass(js_cx, *glob)==NULL)
		/* Queue Class */
		if(js_CreateQueueClass(js_cx, *glob)==NULL)
		if(js_CreateCOMClass(js_cx, *glob)==NULL)
		/* CryptContext Class */
		if(js_CreateCryptContextClass(js_cx, *glob)==NULL)
			break;

deuce's avatar
deuce committed
		/* CryptKeyset Class */
		if(js_CreateCryptKeysetClass(js_cx, *glob)==NULL)
			break;

		/* CryptCert Class */
		if(js_CreateCryptCertClass(js_cx, *glob)==NULL)
			break;

		if(!js_CreateUserObjects(js_cx, *glob, &scfg, /*user: */NULL, service_client->client, NULL, service_client->subscan)) 
		if(js_CreateSystemObject(js_cx, *glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL) 

		if(service_client->service->js_server_props.version[0]==0) {
			SAFEPRINTF(service_client->service->js_server_props.version
				,"Synchronet Services %s",revision);
			service_client->service->js_server_props.version_detail=
				services_ver();
			service_client->service->js_server_props.clients=
				&service_client->service->clients;
			service_client->service->js_server_props.interfaces=
				&service_client->service->interfaces;
			service_client->service->js_server_props.options=
				&service_client->service->options;
		}

		if((server=js_CreateServerObject(js_cx,*glob
			,&service_client->service->js_server_props))==NULL)
			break;

deuce's avatar
deuce committed
		if(service_client->client==NULL) {	/* static service */
			if(js_CreateSocketObjectFromSet(js_cx, server, "socket", service_client->set)==NULL)
deuce's avatar
deuce committed
		}

		JS_DefineFunction(js_cx, server, "client_add"	, js_client_add,	1, 0);
		JS_DefineFunction(js_cx, server, "client_update", js_client_update,	1, 0);
		JS_DefineFunction(js_cx, server, "client_remove", js_client_remove, 1, 0);

		success=TRUE;

	} while(0);


	if(!success) {
		if(rooted)
			JS_RemoveObjectRoot(js_cx, glob);
		JS_DestroyContext(js_cx);
		return(NULL);
	}

	return(js_cx);
}

js_OperationCallback(JSContext *cx)
	JS_SetOperationCallback(cx, NULL);
	if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) {
		JS_SetOperationCallback(cx, js_OperationCallback);
	if(client->callback.auto_terminate && terminated) {
		JS_ReportWarning(cx,"Terminated");
		JS_SetOperationCallback(cx, js_OperationCallback);
	ret=js_CommonOperationCallback(cx,&client->callback);
	JS_SetOperationCallback(cx, js_OperationCallback);
static void js_init_args(JSContext* js_cx, JSObject* js_obj, const char* cmdline)
	int						argc=0;
	JSString*				arg_str;
	JSObject*				argv;
	jsval					val;

	argv=JS_NewArrayObject(js_cx, 0, NULL);
	JS_DefineProperty(js_cx, js_obj, "argv", OBJECT_TO_JSVAL(argv)
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
	p=(char*)cmdline;
	while(*p && *p>' ') p++;	/* find end of filename */
	while(*p && *p<=' ') p++;	/* find first arg */
	SAFECOPY(argbuf,p);

	args=argbuf;
	while(*args && argv!=NULL) {
		p=strchr(args,' ');
		if(p!=NULL)
			*p=0;
		while(*args && *args<=' ') args++; /* Skip spaces */
		arg_str = JS_NewStringCopyZ(js_cx, args);
		if(arg_str==NULL)
			break;
		val=STRING_TO_JSVAL(arg_str);
		if(!JS_SetElement(js_cx, argv, argc, &val))
			break;
		argc++;
		if(p==NULL)	/* last arg */
			break;
		args+=(strlen(args)+1);
	}
	JS_DefineProperty(js_cx, js_obj, "argc", INT_TO_JSVAL(argc)
		,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE);
deuce's avatar
deuce committed
#define HANDLE_CRYPT_CALL(status, service_client)  handle_crypt_call(status, service_client, __FILE__, __LINE__)

static BOOL handle_crypt_call(int status, service_client_t *service_client, const char *file, int line)
{
deuce's avatar
deuce committed
	int		sock = 0;

	if (status == CRYPT_OK)
		return TRUE;
	if (service_client != NULL) {
		if (service_client->service->options & SERVICE_OPT_TLS)
			estr = get_crypt_error(service_client->tls_sess);
deuce's avatar
deuce committed
		sock = service_client->socket;
	}
deuce's avatar
deuce committed
		lprintf(LOG_ERR, "%04d cryptlib error %d at %s:%d (%s)", sock, status, file, line, estr);
deuce's avatar
deuce committed
	else
		lprintf(LOG_ERR, "%04d cryptlib error %d at %s:%d", sock, status, file, line);
	return FALSE;
}

static void js_service_failure_cleanup(service_t *service, SOCKET socket)
{
	close_socket(socket);
	if(service->clients)
		service->clients--;
	thread_down();
	return;
}

static void js_service_thread(void* arg)
{
deuce's avatar
deuce committed
	char					host_name[256];
	SOCKET					socket;
	client_t				client;
	service_t*				service;
	service_client_t		service_client;
	ulong					login_attempts;
	/* JavaScript-specific */
	JSString*				datagram;
	JSObject*				js_glob;
	JSRuntime*				js_runtime;
rswindell's avatar
rswindell committed
	jsval					val;
	/* Copy service_client arg */
	service_client=*(service_client_t*)arg;
	/* Free original */
	free(arg);

	socket=service_client.socket;
	service=service_client.service;

	lprintf(LOG_DEBUG,"%04d %s JavaScript service thread started", socket, service->protocol);
	SetThreadName("sbbs/jsService");
	protected_uint32_adjust(&threads_pending_start, -1);

	/* Host name lookup and filtering */
	if(service->options&BBS_OPT_NO_HOST_LOOKUP 
deuce's avatar
deuce committed
			|| startup->options&BBS_OPT_NO_HOST_LOOKUP)
		strcpy(host_name, "<no name>");
	else {
		if(getnameinfo(&service_client.addr.addr, xp_sockaddr_len(&service_client), host_name, sizeof(host_name), NULL, 0, NI_NAMEREQD) != 0)
			strcpy(host_name, "<no name>");
	}