Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

services.c 68 KB
Newer Older
1
/* Synchronet Services */
2 3 4 5 6

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *																			*
 * 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										*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

/* Platform-specific headers */
rswindell's avatar
rswindell committed
23 24 25
#ifdef __unix__
	#include <sys/param.h>	/* BSD? */
#endif
26 27 28 29 30 31 32 33 34 35

/* 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 <fcntl.h>			/* Open flags */
#include <errno.h>			/* errno */

/* Synchronet-specific headers */
rswindell's avatar
rswindell committed
36
#ifndef JAVASCRIPT
37
#define JAVASCRIPT	/* required to include JS API headers */
rswindell's avatar
rswindell committed
38
#endif
39
#define SERVICES_INI_BITDESC_TABLE	/* required to defined service_options */
40
#undef SBBS	/* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
41 42 43
#include "sbbs.h"
#include "services.h"
#include "ident.h"	/* identify() */
44
#include "sbbs_ini.h"
45
#include "js_rtpool.h"
46
#include "js_request.h"
deuce's avatar
deuce committed
47 48 49
#include "js_socket.h"
#include "multisock.h"
#include "ssl.h"
50 51
#include "git_branch.h"
#include "git_hash.h"
52 53 54

/* Constants */

rswindell's avatar
rswindell committed
55
#define MAX_UDP_BUF_LEN			8192	/* 8K */
56
#define DEFAULT_LISTEN_BACKLOG	5
57 58 59

static services_startup_t* startup=NULL;
static scfg_t	scfg;
60
static char*	text[TOTAL_TEXT];
61
static volatile BOOL	terminated=FALSE;
62 63
static time_t	uptime=0;
static ulong	served=0;
64 65
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
66
static protected_uint32_t threads_pending_start;
67 68 69

typedef struct {
	/* These are sysop-configurable */
deuce's avatar
deuce committed
70 71
	uint32_t		interface_addr;
	uint16_t		port;
72
	str_list_t		interfaces;
deuce's avatar
deuce committed
73 74 75 76 77 78 79 80 81
	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;
82
	js_startup_t	js;
83
	js_server_props_t js_server_props;
84
	/* These are run-time state and stat vars */
85
	protected_uint32_t clients;
deuce's avatar
deuce committed
86 87 88 89
	ulong			served;
	struct xpms_set	*set;
	int				running;
	BOOL			terminated;
90 91 92
} service_t;

typedef struct {
deuce's avatar
deuce committed
93 94 95 96 97 98 99 100
	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;
rswindell's avatar
rswindell committed
101
	/* Initial UDP datagram */
deuce's avatar
deuce committed
102 103 104 105
	BYTE*				udp_buf;
	int					udp_len;
	subscan_t			*subscan;
	CRYPT_SESSION		tls_sess;
106 107
} service_client_t;

108
static service_t	*service=NULL;
109
static unsigned int	services=0;
110

111 112 113
#if defined(__GNUC__)   // Catch printf-format errors with lprintf
static int lprintf(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
#endif
114
static int lprintf(int level, const char *fmt, ...)
115 116 117 118
{
	va_list argptr;
	char sbuf[1024];

119 120 121 122 123
	va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);

124
	if(level <= LOG_ERR) {
125 126
		char errmsg[sizeof(sbuf)+16];
		SAFEPRINTF(errmsg, "srvc %s", sbuf);
127
		errorlog(&scfg, level, startup==NULL ? NULL:startup->host_name, errmsg);
128
		if(startup!=NULL && startup->errormsg!=NULL)
129
			startup->errormsg(startup->cbdata,level,errmsg);
130
	}
131

132
    if(startup==NULL || startup->lputs==NULL || level > startup->log_level)
133 134
        return(0);

135 136 137 138 139
#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

140
    return(startup->lputs(startup->cbdata,level,sbuf));
141 142 143 144 145
}

#ifdef _WINSOCKAPI_

static WSADATA WSAData;
146
#define SOCKLIB_DESC WSAData.szDescription
147 148 149 150 151 152 153
static BOOL WSAInitialized=FALSE;

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

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

159
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
160 161 162 163 164 165
	return (FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)
166
#define SOCKLIB_DESC NULL
167 168 169

#endif

170 171 172 173 174
static char* server_host_name(void)
{
	return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
}

rswindell's avatar
rswindell committed
175
static ulong active_clients(void)
176 177 178 179 180
{
	ulong i;
	ulong total_clients=0;

	for(i=0;i<services;i++) 
181
		total_clients += protected_uint32_value(service[i].clients);
182 183 184 185

	return(total_clients);
}

186 187 188
static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
189
		startup->clients(startup->cbdata,active_clients());
190 191
}

192
static void client_on(SOCKET sock, client_t* client, BOOL update)
193 194
{
	if(startup!=NULL && startup->client_on!=NULL)
195
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
196 197 198 199 200
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
201
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
202 203
}

204
static void thread_up(BOOL setuid)
205 206
{
	if(startup!=NULL && startup->thread_up!=NULL)
207
		startup->thread_up(startup->cbdata,TRUE,setuid);
208 209 210 211 212
}

static void thread_down(void)
{
	if(startup!=NULL && startup->thread_up!=NULL)
213
		startup->thread_up(startup->cbdata,FALSE,FALSE);
214 215
}

deuce's avatar
deuce committed
216
void open_socket_cb(SOCKET sock, void *serv_ptr)
217
{
218
	char	error[256];
219
	char	section[128];
deuce's avatar
deuce committed
220
	service_t	*serv=(service_t *)serv_ptr;
221

deuce's avatar
deuce committed
222
	if(startup!=NULL && startup->socket_open!=NULL)
223
		startup->socket_open(startup->cbdata,TRUE);
deuce's avatar
deuce committed
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
	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);
242 243 244 245 246
	return(sock);
}

static int close_socket(SOCKET sock)
{
247
	char	err[128];
248 249
	int		result;

250 251 252
	if(sock==INVALID_SOCKET)
		return(-1);

253 254
	shutdown(sock,SHUT_RDWR);	/* required on Unix */
	result=closesocket(sock);
255
	if(startup!=NULL && startup->socket_open!=NULL) 
256
		startup->socket_open(startup->cbdata,FALSE);
257
	if(result!=0)
258
		lprintf(LOG_WARNING,"%04d !ERROR %d closing socket: %s",sock, ERROR_VALUE, socket_strerror(socket_errno, err, sizeof(err)));
259 260 261 262 263 264 265

	return(result);
}

static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
266
	    startup->status(startup->cbdata,str);
267 268
}

rswindell's avatar
rswindell committed
269
/* Global JavaScript Methods */
270

rswindell's avatar
rswindell committed
271
static JSBool
272
js_log(JSContext *cx, uintN argc, jsval *arglist)
rswindell's avatar
rswindell committed
273
{
274
	jsval *argv=JS_ARGV(cx, arglist);
rswindell's avatar
rswindell committed
275
	char		str[512];
276 277
    uintN		i=0;
	int32		level=LOG_INFO;
rswindell's avatar
rswindell committed
278
	service_client_t* client;
279
	jsrefcount	rc;
280
	char		*line = NULL;
rswindell's avatar
rswindell committed
281

282 283
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

287 288
	if(startup==NULL || startup->lputs==NULL)
		return(JS_FALSE);
rswindell's avatar
rswindell committed
289

290 291 292 293
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
294

rswindell's avatar
rswindell committed
295
	str[0]=0;
296
    for(;i<argc && strlen(str)<(sizeof(str)/2);i++) {
deuce's avatar
deuce committed
297
		JSVALUE_TO_MSTRING(cx, argv[i], line, NULL);
298
		HANDLE_PENDING(cx, line);
deuce's avatar
deuce committed
299
		if(line==NULL)
rswindell's avatar
rswindell committed
300
		    return(JS_FALSE);
deuce's avatar
deuce committed
301
		strncat(str,line,sizeof(str)/2);
deuce's avatar
deuce committed
302
		free(line);
rswindell's avatar
rswindell committed
303 304 305
		strcat(str," ");
	}

306
	rc=JS_SUSPENDREQUEST(cx);
307
	if(service==NULL)
308
		lprintf(level,"%04d %s",client->socket,str);
309
	else if(level <= client->service->log_level)
310
		lprintf(level,"%04d %s %s",client->socket,client->service->protocol,str);
311
	JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
312

313
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)));
314

rswindell's avatar
rswindell committed
315 316 317
    return(JS_TRUE);
}

deuce's avatar
deuce committed
318
static void badlogin(SOCKET sock, char* prot, char* user, char* passwd, char* host, union xp_sockaddr* addr)
319 320
{
	char reason[128];
deuce's avatar
deuce committed
321
	char addr_ip[INET6_ADDRSTRLEN];
322 323 324
	ulong count;

	SAFEPRINTF(reason,"%s LOGIN", prot);
325
	count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd);
rswindell's avatar
rswindell committed
326
	if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold)
327
		hacklog(&scfg, reason, user, passwd, host, addr);
rswindell's avatar
rswindell committed
328
	if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold) {
deuce's avatar
deuce committed
329
		inet_addrtop(addr, addr_ip, sizeof(addr_ip));
330 331
		SAFEPRINTF(reason, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS (%lu)", count);
		filter_ip(&scfg, prot, reason, host, addr_ip, user, /* fname: */NULL);
deuce's avatar
deuce committed
332
	}
333

rswindell's avatar
rswindell committed
334
	mswait(startup->login_attempt.delay);
335 336
}

337
static JSBool
338
js_login(JSContext *cx, uintN argc, jsval *arglist)
339
{
340 341
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
342
	char*		user;
343
	char*		pass = NULL;
344 345
	JSBool		inc_logons=JS_FALSE;
	jsval		val;
346
	service_client_t* client;
347
	jsrefcount	rc;
348

349
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(JS_FALSE));
350 351 352 353

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

354
	/* User name or number */
rswindell's avatar
rswindell committed
355
	JSVALUE_TO_ASTRING(cx, argv[0], user, 128, NULL);
deuce's avatar
deuce committed
356
	if(user==NULL)
357 358
		return(JS_FALSE);

359
	/* Password */
360 361 362 363 364
	if(argc > 1) {
		JSVALUE_TO_ASTRING(cx, argv[1], pass, LEN_PASS+2, NULL);
		if(pass==NULL) 
			return(JS_FALSE);
	}
365
	rc=JS_SUSPENDREQUEST(cx);
366
	memset(&client->user,0,sizeof(user_t));
367

368
	/* ToDo Deuce: did you mean to do this *before* the above memset(0) ? */
deuce's avatar
deuce committed
369 370
	if(client->user.number) {
		if(client->subscan!=NULL)
371
			putmsgptrs(&scfg, &client->user, client->subscan);
deuce's avatar
deuce committed
372 373
	}

374
	if(IS_DIGIT(*user))
375 376 377
		client->user.number=atoi(user);
	else if(*user)
		client->user.number=matchuser(&scfg,user,FALSE);
378

379
	if(getuserdat(&scfg,&client->user)!=0) {
380
		lprintf(LOG_NOTICE,"%04d %s !USER NOT FOUND: '%s'"
381 382
			,client->socket,client->service->protocol,user);
		badlogin(client->socket, client->service->protocol, user, pass, client->client->host, &client->addr);
383
		JS_RESUMEREQUEST(cx, rc);
384 385 386
		return(JS_TRUE);
	}

387
	if(client->user.misc&(DELETED|INACTIVE)) {
388
		lprintf(LOG_WARNING,"%04d %s !DELETED OR INACTIVE USER #%d: %s"
389
			,client->socket,client->service->protocol,client->user.number,user);
390
		JS_RESUMEREQUEST(cx, rc);
391 392 393
		return(JS_TRUE);
	}

394
	/* Password */
395
	if(client->user.pass[0] && (pass == NULL || stricmp(client->user.pass,pass))) { /* Wrong password */
396 397 398 399 400
		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);
401
	}
402
	JS_RESUMEREQUEST(cx, rc);
403 404

	if(argc>2)
405
		JS_ValueToBoolean(cx,argv[2],&inc_logons);
406

407
	rc=JS_SUSPENDREQUEST(cx);
408
	if(client->client!=NULL) {
deuce's avatar
deuce committed
409
		SAFECOPY(client->user.ipaddr,client->client->addr);
410 411
		SAFECOPY(client->user.comp,client->client->host);
		SAFECOPY(client->user.modem,client->service->protocol);
412
	}
413 414

	if(inc_logons) {
415 416
		client->user.logons++;
		client->user.ltoday++;
417 418
	}	

419
	putuserdat(&scfg,&client->user);
deuce's avatar
deuce committed
420
	if(client->subscan==NULL) {
421
		client->subscan=(subscan_t*)calloc(scfg.total_subs, sizeof(subscan_t));
deuce's avatar
deuce committed
422 423 424 425
		if(client->subscan==NULL)
			lprintf(LOG_CRIT,"!MALLOC FAILURE");
	}
	if(client->subscan!=NULL) {
426
		getmsgptrs(&scfg,&client->user,client->subscan,NULL,NULL);
deuce's avatar
deuce committed
427 428
	}

429
	JS_RESUMEREQUEST(cx, rc);
430

deuce's avatar
deuce committed
431
	if(!js_CreateUserObjects(cx, obj, &scfg, &client->user, client->client, NULL, client->subscan))
432
		lprintf(LOG_ERR,"%04d %s !JavaScript ERROR creating user objects"
433 434
			,client->socket,client->service->protocol);

435
	if(client->client!=NULL) {
436
		client->client->user=client->user.alias;
437
		client->client->usernum = client->user.number;
438 439
		client_on(client->socket,client->client,TRUE /* update */);
	}
440

441 442
	client->logintime=time(NULL);

443 444 445
	if(client->service->log_level >= LOG_INFO)
		lprintf(LOG_INFO,"%04d %s Logging in %s"
			,client->socket,client->service->protocol,client->user.alias);
446 447

	val = BOOLEAN_TO_JSVAL(JS_TRUE);
448
	if(!JS_SetProperty(cx, obj, "logged_in", &val))
449
		lprintf(LOG_ERR, "%04d %s Error setting logged in property for %s"
450
			,client->socket, client->service->protocol, client->user.alias);
451

452
	if(client->user.pass[0])
453
		loginSuccess(startup->login_attempt_list, &client->addr);
454

455
	JS_SET_RVAL(cx, arglist,BOOLEAN_TO_JSVAL(JS_TRUE));
456 457 458 459 460

	return(JS_TRUE);
}

static JSBool
461
js_logout(JSContext *cx, uintN argc, jsval *arglist)
462
{
463
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
464 465
	jsval val;
	service_client_t* client;
466
	jsrefcount	rc;
467

468
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(JS_FALSE));
469 470 471 472 473 474 475

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

	if(client->user.number<1)	/* Not logged in */
		return(JS_TRUE);

476
	rc=JS_SUSPENDREQUEST(cx);
477 478
	if(!logoutuserdat(&scfg,&client->user,time(NULL),client->logintime))
		lprintf(LOG_ERR,"%04d !ERROR in logoutuserdat",client->socket);
479

480 481 482
	if(client->service->log_level >= LOG_INFO)
		lprintf(LOG_INFO,"%04d %s Logging out %s"
			,client->socket,client->service->protocol,client->user.alias);
483 484

	memset(&client->user,0,sizeof(client->user));
485
	JS_RESUMEREQUEST(cx, rc);
486 487 488 489

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

490
	JS_SET_RVAL(cx, arglist,BOOLEAN_TO_JSVAL(JS_TRUE));
491 492 493

	return(JS_TRUE);
}
rswindell's avatar
rswindell committed
494

495 496 497 498
/*
 * This macro is used to expose a function from the global
 * client.socket object in the global namespace.
 */
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529
#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
530
static JSFunctionSpec js_global_functions[] = {
531 532 533 534 535
	{"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 */
536
	{"log",				js_log,				0},		/* Log a string */
537
 	{"login",			js_login,			2},		/* Login specified username and password */
538
	{"logout",			js_logout,			0},		/* Logout user */
rswindell's avatar
rswindell committed
539 540 541
    {0}
};

542 543 544 545 546
static void
js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report)
{
	char	line[64];
	char	file[MAX_PATH+1];
547 548
	char*	prot="???";
	SOCKET	sock=0;
549
	char*	warning;
550
	service_client_t* client;
551
	jsrefcount	rc;
552
	int		log_level;
553 554 555 556 557

	if((client=(service_client_t*)JS_GetContextPrivate(cx))!=NULL) {
		prot=client->service->protocol;
		sock=client->socket;
	}
558 559

	if(report==NULL) {
560
		lprintf(LOG_ERR,"%04d %s !JavaScript: %s", sock,prot,message);
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
		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";
579 580 581
		log_level=LOG_WARNING;
	} else {
		log_level=LOG_ERR;
582
		warning="";
583
	}
584

585
	rc=JS_SUSPENDREQUEST(cx);
586 587
	if(client == NULL || client->service == NULL || client->service->log_level >= log_level)
		lprintf(log_level,"%04d %s !JavaScript %s%s%s: %s",sock,prot,warning,file,line,message);
588
	JS_RESUMEREQUEST(cx, rc);
589 590
}

591 592 593
/* Server Methods */

static JSBool
594
js_client_add(JSContext *cx, uintN argc, jsval *arglist)
595
{
596
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
597 598 599 600
	client_t		client;
	SOCKET			sock=INVALID_SOCKET;
	socklen_t		addr_len;
	union xp_sockaddr	addr;
601
	service_client_t* service_client;
deuce's avatar
deuce committed
602 603
	jsrefcount		rc;
	char			*cstr=NULL;
604

605 606
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

610
	protected_uint32_adjust(&service_client->service->clients, 1);
611 612 613
	update_clients();
	service_client->service->served++;
	served++;
614 615 616
	memset(&client,0,sizeof(client));
	client.size=sizeof(client);
	client.protocol=service_client->service->protocol;
617
	client.time=time32(NULL);
618
	client.user=STR_UNKNOWN_USER;
619
	client.usernum = 0;
620
	SAFECOPY(client.host,client.user);
deuce's avatar
deuce committed
621

622
	sock=js_socket(cx,argv[0]);
623 624
	if(sock < 0)
		return JS_TRUE;
deuce's avatar
deuce committed
625

626
	addr_len = sizeof(addr);
deuce's avatar
deuce committed
627 628 629
	if(getpeername(sock, &addr.addr, &addr_len)==0) {
		inet_addrtop(&addr, client.addr, sizeof(client.addr));
		client.port=inet_addrport(&addr);
630 631
	}

deuce's avatar
deuce committed
632
	if(argc>1) {
deuce's avatar
deuce committed
633
		JSVALUE_TO_MSTRING(cx, argv[1], cstr, NULL);
634
		HANDLE_PENDING(cx, cstr);
deuce's avatar
deuce committed
635 636
		client.user=cstr;
	}
637

deuce's avatar
deuce committed
638 639
	if(argc>2)
		JSVALUE_TO_STRBUF(cx, argv[2], client.host, sizeof(client.host), NULL);
640

641
	rc=JS_SUSPENDREQUEST(cx);
642
	client_on(sock, &client, /* update? */ FALSE);
643
#ifdef _DEBUG
deuce's avatar
deuce committed
644 645
	lprintf(LOG_DEBUG,"%s client_add(%04u,%s,%s)"
		,service_client->service->protocol
646
		,sock,client.user,client.host);
647
#endif
deuce's avatar
deuce committed
648 649
	if(cstr)
		free(cstr);
650
	JS_RESUMEREQUEST(cx, rc);
651 652 653 654
	return(JS_TRUE);
}

static JSBool
655
js_client_update(JSContext *cx, uintN argc, jsval *arglist)
656
{
657
	jsval *argv=JS_ARGV(cx, arglist);
658 659 660
	client_t	client;
	SOCKET		sock=INVALID_SOCKET;
	socklen_t	addr_len;
deuce's avatar
deuce committed
661 662
	union xp_sockaddr	addr;
	service_client_t*	service_client;
663
	jsrefcount	rc;
deuce's avatar
deuce committed
664
	char		*cstr=NULL;
665

666 667
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

668 669 670 671 672 673
	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;
674
	client.user=STR_UNKNOWN_USER;
675 676
	SAFECOPY(client.host,client.user);

677
	sock=js_socket(cx,argv[0]);
678 679
	if(sock < 0)
		return JS_TRUE;
680 681

	addr_len = sizeof(addr);
deuce's avatar
deuce committed
682 683 684
	if(getpeername(sock, &addr.addr, &addr_len)==0) {
		inet_addrtop(&addr, client.addr, sizeof(client.addr));
		client.port=inet_addrport(&addr);
685 686
	}

deuce's avatar
deuce committed
687
	if(argc>1) {
deuce's avatar
deuce committed
688
		JSVALUE_TO_MSTRING(cx, argv[1], cstr, NULL);
deuce's avatar
deuce committed
689 690
		client.user=cstr;
	}
691

deuce's avatar
deuce committed
692 693
	if(argc>2)
		JSVALUE_TO_STRBUF(cx, argv[2], client.host, sizeof(client.host), NULL);
694

695
	rc=JS_SUSPENDREQUEST(cx);
696
	client_on(sock, &client, /* update? */ TRUE);
697
#ifdef _DEBUG
deuce's avatar
deuce committed
698 699
	lprintf(LOG_DEBUG,"%s client_update(%04u,%s,%s)"
		,service_client->service->protocol
700
		,sock,client.user,client.host);
701
#endif
deuce's avatar
deuce committed
702 703
	if(cstr)
		free(cstr);
704
	JS_RESUMEREQUEST(cx, rc);
705 706 707 708 709
	return(JS_TRUE);
}


static JSBool
710
js_client_remove(JSContext *cx, uintN argc, jsval *arglist)
711
{
712
	jsval *argv=JS_ARGV(cx, arglist);
713
	SOCKET	sock=INVALID_SOCKET;
714
	service_client_t* service_client;
715
	jsrefcount	rc;
716

717 718
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

722
	sock=js_socket(cx,argv[0]);
723 724
	if(sock < 0)
		return JS_TRUE;
725

726
	if(sock!=INVALID_SOCKET) {
727

728
		rc=JS_SUSPENDREQUEST(cx);
729
		client_off(sock);
730

731
		if(protected_uint32_value(service_client->service->clients) == 0)
deuce's avatar
deuce committed
732 733
			lprintf(LOG_WARNING,"%s !client_remove() called with 0 service clients"
				,service_client->service->protocol);
734
		else {
735
			protected_uint32_adjust(&service_client->service->clients, -1);
736 737
			update_clients();
		}
738
		JS_RESUMEREQUEST(cx, rc);
739
	}
740

741
#ifdef _DEBUG
deuce's avatar
deuce committed
742 743
	lprintf(LOG_DEBUG,"%s client_remove(%04u)"
		,service_client->service->protocol, sock);
744
#endif
745 746 747
	return(JS_TRUE);
}

748
static JSContext* 
749
js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, JSObject** glob)
750 751
{
	JSContext*	js_cx;
752
	JSObject*	server;
753
	BOOL		success=FALSE;
754
	BOOL		rooted=FALSE;
755

756
    if((js_cx = JS_NewContext(js_runtime, JAVASCRIPT_CONTEXT_STACK))==NULL)
757
		return(NULL);
758
	JS_SetOptions(js_cx, startup->js.options);
759
	JS_BEGINREQUEST(js_cx);
760 761 762

    JS_SetErrorReporter(js_cx, js_ErrorReporter);

763 764
	/* ToDo: call js_CreateCommonObjects() instead */

765 766
	do {

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

769
		if(!js_CreateGlobalObject(js_cx, &scfg, NULL, &service_client->service->js, glob))
770
			break;
771
		rooted=TRUE;
772

773
		if (!JS_DefineFunctions(js_cx, *glob, js_global_functions))
rswindell's avatar
rswindell committed
774 775
			break;

776
		/* Internal JS Object */
777
		if(js_CreateInternalJsObject(js_cx, *glob, &service_client->callback, &service_client->service->js)==NULL)
778 779
			break;

780
		/* Client Object */
deuce's avatar
deuce committed
781
		if(service_client->client!=NULL) {
782
			if(js_CreateClientObject(js_cx, *glob, "client", service_client->client, sock, service_client->tls_sess)==NULL)
783
				break;
deuce's avatar
deuce committed
784
		}