services.c 62.7 KB
Newer Older
1
/* Synchronet Services */
2 3

/* $Id$ */
4
// vi: tabstop=4
5 6 7 8 9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
 *																			*
 * 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
38 39 40
#ifdef __unix__
	#include <sys/param.h>	/* BSD? */
#endif
41 42 43 44 45 46 47 48 49 50 51

/* 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 */
rswindell's avatar
rswindell committed
52
#ifndef JAVASCRIPT
53
#define JAVASCRIPT	/* required to include JS API headers */
rswindell's avatar
rswindell committed
54
#endif
55
#define SERVICES_INI_BITDESC_TABLE	/* required to defined service_options */
56
#undef SBBS	/* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
57 58 59
#include "sbbs.h"
#include "services.h"
#include "ident.h"	/* identify() */
60
#include "sbbs_ini.h"
61
#include "js_rtpool.h"
62
#include "js_request.h"
deuce's avatar
deuce committed
63 64 65
#include "js_socket.h"
#include "multisock.h"
#include "ssl.h"
66 67 68

/* Constants */

rswindell's avatar
rswindell committed
69
#define MAX_UDP_BUF_LEN			8192	/* 8K */
70
#define DEFAULT_LISTEN_BACKLOG	5
71 72 73

static services_startup_t* startup=NULL;
static scfg_t	scfg;
74
static volatile BOOL	terminated=FALSE;
75 76
static time_t	uptime=0;
static ulong	served=0;
77
static char		revision[16];
78 79
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
80
static protected_uint32_t threads_pending_start;
81 82 83

typedef struct {
	/* These are sysop-configurable */
deuce's avatar
deuce committed
84 85
	uint32_t		interface_addr;
	uint16_t		port;
86
	str_list_t		interfaces;
deuce's avatar
deuce committed
87 88 89 90 91 92 93 94 95
	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;
96
	js_startup_t	js;
97
	js_server_props_t js_server_props;
98
	/* These are run-time state and stat vars */
deuce's avatar
deuce committed
99 100 101 102 103
	uint32_t		clients;
	ulong			served;
	struct xpms_set	*set;
	int				running;
	BOOL			terminated;
104 105 106
} service_t;

typedef struct {
deuce's avatar
deuce committed
107 108 109 110 111 112 113 114
	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
115
	/* Initial UDP datagram */
deuce's avatar
deuce committed
116 117 118 119
	BYTE*				udp_buf;
	int					udp_len;
	subscan_t			*subscan;
	CRYPT_SESSION		tls_sess;
120 121
} service_client_t;

122
static service_t	*service=NULL;
123
static uint32_t		services=0;
124

125
static int lprintf(int level, const char *fmt, ...)
126 127 128 129
{
	va_list argptr;
	char sbuf[1024];

130 131 132 133 134
	va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);

135
	if(level <= LOG_ERR) {
136 137 138
		char errmsg[sizeof(sbuf)+16];
		SAFEPRINTF(errmsg, "srvc %s", sbuf);
		errorlog(&scfg,startup==NULL ? NULL:startup->host_name, errmsg);
139
		if(startup!=NULL && startup->errormsg!=NULL)
140
			startup->errormsg(startup->cbdata,level,errmsg);
141
	}
142

143
    if(startup==NULL || startup->lputs==NULL || level > startup->log_level)
144 145
        return(0);

146 147 148 149 150
#if defined(_WIN32)
	if(IsBadCodePtr((FARPROC)startup->lputs))
		return(0);
#endif

151
    return(startup->lputs(startup->cbdata,level,sbuf));
152 153 154 155 156
}

#ifdef _WINSOCKAPI_

static WSADATA WSAData;
157
#define SOCKLIB_DESC WSAData.szDescription
158 159 160 161 162 163 164
static BOOL WSAInitialized=FALSE;

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

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

170
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
171 172 173 174 175 176
	return (FALSE);
}

#else /* No WINSOCK */

#define winsock_startup()	(TRUE)
177
#define SOCKLIB_DESC NULL
178 179 180

#endif

rswindell's avatar
rswindell committed
181
static ulong active_clients(void)
182 183 184 185 186 187 188 189 190 191
{
	ulong i;
	ulong total_clients=0;

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

	return(total_clients);
}

192 193 194
static void update_clients(void)
{
	if(startup!=NULL && startup->clients!=NULL)
195
		startup->clients(startup->cbdata,active_clients());
196 197
}

198
static void client_on(SOCKET sock, client_t* client, BOOL update)
199 200
{
	if(startup!=NULL && startup->client_on!=NULL)
201
		startup->client_on(startup->cbdata,TRUE,sock,client,update);
202 203 204 205 206
}

static void client_off(SOCKET sock)
{
	if(startup!=NULL && startup->client_on!=NULL)
207
		startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
208 209
}

210
static void thread_up(BOOL setuid)
211 212
{
	if(startup!=NULL && startup->thread_up!=NULL)
213
		startup->thread_up(startup->cbdata,TRUE,setuid);
214 215 216 217 218
}

static void thread_down(void)
{
	if(startup!=NULL && startup->thread_up!=NULL)
219
		startup->thread_up(startup->cbdata,FALSE,FALSE);
220 221
}

deuce's avatar
deuce committed
222
void open_socket_cb(SOCKET sock, void *serv_ptr)
223
{
224
	char	error[256];
225
	char	section[128];
deuce's avatar
deuce committed
226
	service_t	*serv=(service_t *)serv_ptr;
227

deuce's avatar
deuce committed
228
	if(startup!=NULL && startup->socket_open!=NULL)
229
		startup->socket_open(startup->cbdata,TRUE);
deuce's avatar
deuce committed
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	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);
248 249 250 251 252 253 254
	return(sock);
}

static int close_socket(SOCKET sock)
{
	int		result;

255 256 257
	if(sock==INVALID_SOCKET)
		return(-1);

258 259
	shutdown(sock,SHUT_RDWR);	/* required on Unix */
	result=closesocket(sock);
260
	if(startup!=NULL && startup->socket_open!=NULL) 
261
		startup->socket_open(startup->cbdata,FALSE);
262
	if(result!=0)
263
		lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",sock, ERROR_VALUE);
264 265 266 267 268 269 270

	return(result);
}

static void status(char* str)
{
	if(startup!=NULL && startup->status!=NULL)
271
	    startup->status(startup->cbdata,str);
272 273
}

rswindell's avatar
rswindell committed
274
/* Global JavaScript Methods */
275

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

287 288
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

292 293
	if(startup==NULL || startup->lputs==NULL)
		return(JS_FALSE);
rswindell's avatar
rswindell committed
294

295 296 297 298
	if(argc > 1 && JSVAL_IS_NUMBER(argv[i])) {
		if(!JS_ValueToInt32(cx,argv[i++],&level))
			return JS_FALSE;
	}
299

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

311
	rc=JS_SUSPENDREQUEST(cx);
312
	if(service==NULL)
313
		lprintf(level,"%04d %s",client->socket,str);
314
	else if(level <= client->service->log_level)
315
		lprintf(level,"%04d %s %s",client->socket,client->service->protocol,str);
316
	JS_RESUMEREQUEST(cx, rc);
rswindell's avatar
rswindell committed
317

318
	JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)));
319

rswindell's avatar
rswindell committed
320 321 322
    return(JS_TRUE);
}

deuce's avatar
deuce committed
323
static void badlogin(SOCKET sock, char* prot, char* user, char* passwd, char* host, union xp_sockaddr* addr)
324 325
{
	char reason[128];
deuce's avatar
deuce committed
326
	char addr_ip[INET6_ADDRSTRLEN];
327 328 329
	ulong count;

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

rswindell's avatar
rswindell committed
339
	mswait(startup->login_attempt.delay);
340 341
}

342
static JSBool
343
js_login(JSContext *cx, uintN argc, jsval *arglist)
344
{
345 346
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
347 348
	char*		user;
	char*		pass;
349 350
	JSBool		inc_logons=JS_FALSE;
	jsval		val;
351
	service_client_t* client;
352
	jsrefcount	rc;
353

354
	JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(JS_FALSE));
355 356 357 358

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

359
	/* User name or number */
rswindell's avatar
rswindell committed
360
	JSVALUE_TO_ASTRING(cx, argv[0], user, 128, NULL);
deuce's avatar
deuce committed
361
	if(user==NULL)
362 363
		return(JS_FALSE);

364
	/* Password */
deuce's avatar
deuce committed
365
	JSVALUE_TO_ASTRING(cx, argv[1], pass, LEN_PASS+2, NULL);
deuce's avatar
deuce committed
366
	if(pass==NULL) 
367 368
		return(JS_FALSE);

369
	rc=JS_SUSPENDREQUEST(cx);
370
	memset(&client->user,0,sizeof(user_t));
371

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

378 379 380 381
	if(isdigit(*user))
		client->user.number=atoi(user);
	else if(*user)
		client->user.number=matchuser(&scfg,user,FALSE);
382

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

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

398
	/* Password */
399 400 401 402 403 404
	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);
405
	}
406
	JS_RESUMEREQUEST(cx, rc);
407 408

	if(argc>2)
409
		JS_ValueToBoolean(cx,argv[2],&inc_logons);
410

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

	if(inc_logons) {
419 420
		client->user.logons++;
		client->user.ltoday++;
421 422
	}	

423
	putuserdat(&scfg,&client->user);
deuce's avatar
deuce committed
424 425 426 427 428 429
	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) {
430
		getmsgptrs(&scfg,&client->user,client->subscan,NULL,NULL);
deuce's avatar
deuce committed
431 432
	}

433
	JS_RESUMEREQUEST(cx, rc);
434

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

439
	if(client->client!=NULL) {
440
		client->client->user=client->user.alias;
441 442
		client_on(client->socket,client->client,TRUE /* update */);
	}
443

444 445
	client->logintime=time(NULL);

446
	lprintf(LOG_INFO,"%04d %s Logging in %s"
447
		,client->socket,client->service->protocol,client->user.alias);
448 449 450 451

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

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
	lprintf(LOG_INFO,"%04d %s Logging out %s"
481 482 483
		,client->socket,client->service->protocol,client->user.alias);

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

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

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

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

494 495 496 497
/*
 * This macro is used to expose a function from the global
 * client.socket object in the global namespace.
 */
498 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
#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
529
static JSFunctionSpec js_global_functions[] = {
530 531 532 533 534
	{"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 */
535
	{"log",				js_log,				0},		/* Log a string */
536
 	{"login",			js_login,			2},		/* Login specified username and password */
537
	{"logout",			js_logout,			0},		/* Logout user */
rswindell's avatar
rswindell committed
538 539 540
    {0}
};

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

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

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

584
	rc=JS_SUSPENDREQUEST(cx);
585
	lprintf(log_level,"%04d %s !JavaScript %s%s%s: %s",sock,prot,warning,file,line,message);
586
	JS_RESUMEREQUEST(cx, rc);
587 588
}

589 590 591
/* Server Methods */

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

603 604
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

621
	addr_len = sizeof(addr);
deuce's avatar
deuce committed
622 623 624
	if(getpeername(sock, &addr.addr, &addr_len)==0) {
		inet_addrtop(&addr, client.addr, sizeof(client.addr));
		client.port=inet_addrport(&addr);
625 626
	}

deuce's avatar
deuce committed
627
	if(argc>1) {
deuce's avatar
deuce committed
628
		JSVALUE_TO_MSTRING(cx, argv[1], cstr, NULL);
629
		HANDLE_PENDING(cx, cstr);
deuce's avatar
deuce committed
630 631
		client.user=cstr;
	}
632

deuce's avatar
deuce committed
633 634
	if(argc>2)
		JSVALUE_TO_STRBUF(cx, argv[2], client.host, sizeof(client.host), NULL);
635

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

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

661 662
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

663 664 665 666 667 668
	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;
669
	client.user=STR_UNKNOWN_USER;
670 671
	SAFECOPY(client.host,client.user);

672
	sock=js_socket(cx,argv[0]);
673 674

	addr_len = sizeof(addr);
deuce's avatar
deuce committed
675 676 677
	if(getpeername(sock, &addr.addr, &addr_len)==0) {
		inet_addrtop(&addr, client.addr, sizeof(client.addr));
		client.port=inet_addrport(&addr);
678 679
	}

deuce's avatar
deuce committed
680
	if(argc>1) {
deuce's avatar
deuce committed
681
		JSVALUE_TO_MSTRING(cx, argv[1], cstr, NULL);
deuce's avatar
deuce committed
682 683
		client.user=cstr;
	}
684

deuce's avatar
deuce committed
685 686
	if(argc>2)
		JSVALUE_TO_STRBUF(cx, argv[2], client.host, sizeof(client.host), NULL);
687

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


static JSBool
703
js_client_remove(JSContext *cx, uintN argc, jsval *arglist)
704
{
705
	jsval *argv=JS_ARGV(cx, arglist);
706
	SOCKET	sock=INVALID_SOCKET;
707
	service_client_t* service_client;
708
	jsrefcount	rc;
709

710 711
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

715
	sock=js_socket(cx,argv[0]);
716

717
	if(sock!=INVALID_SOCKET) {
718

719
		rc=JS_SUSPENDREQUEST(cx);
720
		client_off(sock);
721

722
		if(service_client->service->clients==0)
deuce's avatar
deuce committed
723 724
			lprintf(LOG_WARNING,"%s !client_remove() called with 0 service clients"
				,service_client->service->protocol);
725 726 727 728
		else {
			service_client->service->clients--;
			update_clients();
		}
729
		JS_RESUMEREQUEST(cx, rc);
730
	}
731

732
#ifdef _DEBUG
deuce's avatar
deuce committed
733 734
	lprintf(LOG_DEBUG,"%s client_remove(%04u)"
		,service_client->service->protocol, sock);
735
#endif
736 737 738
	return(JS_TRUE);
}

739
static JSContext* 
740
js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, JSObject** glob)
741 742
{
	JSContext*	js_cx;
743
	JSObject*	server;
744
	BOOL		success=FALSE;
745
	BOOL		rooted=FALSE;
746

747
    if((js_cx = JS_NewContext(js_runtime, service_client->service->js.cx_stack))==NULL)
748
		return(NULL);
749
	JS_BEGINREQUEST(js_cx);
750 751 752

    JS_SetErrorReporter(js_cx, js_ErrorReporter);

753 754
	/* ToDo: call js_CreateCommonObjects() instead */

755 756
	do {

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

759
		if(!js_CreateGlobalObject(js_cx, &scfg, NULL, &service_client->service->js, glob))
760
			break;
761
		rooted=TRUE;
762

763
		if (!JS_DefineFunctions(js_cx, *glob, js_global_functions))
rswindell's avatar
rswindell committed
764 765
			break;

766
		/* Internal JS Object */
767
		if(js_CreateInternalJsObject(js_cx, *glob, &service_client->callback, &service_client->service->js)==NULL)
768 769
			break;

770
		/* Client Object */
deuce's avatar
deuce committed
771
		if(service_client->client!=NULL) {
772
			if(js_CreateClientObject(js_cx, *glob, "client", service_client->client, sock, service_client->tls_sess)==NULL)
773
				break;
deuce's avatar
deuce committed
774
		}
775 776

		/* User Class */
777
		if(js_CreateUserClass(js_cx, *glob, &scfg)==NULL) 
778 779 780
			break;

		/* Socket Class */
781
		if(js_CreateSocketClass(js_cx, *glob)==NULL)
782 783
			break;

784
		/* MsgBase Class */
785
		if(js_CreateMsgBaseClass(js_cx, *glob, &scfg)==NULL)
786 787
			break;

788
		/* File Class */
789
		if(js_CreateFileClass(js_cx, *glob)==NULL)
790 791
			break;

792
		/* Queue Class */
793
		if(js_CreateQueueClass(js_cx, *glob)==NULL)
794 795 796
			break;

		/* COM Class */
797
		if(js_CreateCOMClass(js_cx, *glob)==NULL)
798 799
			break;

800 801 802 803
		/* CryptContext Class */
		if(js_CreateCryptContextClass(js_cx, *glob)==NULL)
			break;

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