Newer
Older
/* Synchronet Services */
// vi: tabstop=4
/****************************************************************************
* @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 */
#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 */
#define JAVASCRIPT /* required to include JS API headers */
#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() */
#include "sbbs_ini.h"
#include "js_rtpool.h"
#include "js_request.h"
#include "js_socket.h"
#include "multisock.h"
#include "ssl.h"
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 */
str_list_t interfaces;
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_startup_t js;
js_server_props_t js_server_props;
/* These are run-time state and stat vars */
uint32_t clients;
ulong served;
struct xpms_set *set;
int running;
BOOL terminated;
} service_t;
typedef struct {
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;
BYTE* udp_buf;
int udp_len;
subscan_t *subscan;
CRYPT_SESSION tls_sess;
static service_t *service=NULL;
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);
if(level <= LOG_ERR) {
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)

rswindell
committed
#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)
#define SOCKLIB_DESC NULL
{
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);
char error[256];
char section[128];
startup->socket_open(startup->cbdata,TRUE);
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);
if(result!=0)
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);
js_log(JSContext *cx, uintN argc, jsval *arglist)
jsval *argv=JS_ARGV(cx, arglist);
uintN i=0;
int32 level=LOG_INFO;
jsrefcount rc;
char *line = NULL;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
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;
}
for(;i<argc && strlen(str)<(sizeof(str)/2);i++) {
HANDLE_PENDING(cx, line);
rc=JS_SUSPENDREQUEST(cx);
if(service==NULL)
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_RESUMEREQUEST(cx, rc);
JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, str)));
static void badlogin(SOCKET sock, char* prot, char* user, char* passwd, char* host, union xp_sockaddr* addr)
{
char reason[128];
ulong count;
SAFEPRINTF(reason,"%s LOGIN", prot);
count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd);
if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold)
hacklog(&scfg, reason, user, passwd, host, addr);
if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold) {
filter_ip(&scfg, prot, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS"
}
js_login(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
char* user;
char* pass;
JSBool inc_logons=JS_FALSE;
jsval val;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(JS_FALSE));
if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
/* User name or number */
/* Password */
JSVALUE_TO_ASTRING(cx, argv[1], pass, LEN_PASS+2, NULL);
rc=JS_SUSPENDREQUEST(cx);
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);
JS_RESUMEREQUEST(cx, rc);
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);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
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_RESUMEREQUEST(cx, rc);
JS_ValueToBoolean(cx,argv[2],&inc_logons);
rc=JS_SUSPENDREQUEST(cx);
if(client->client!=NULL) {
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);
JS_RESUMEREQUEST(cx, rc);
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);
if(client->client!=NULL) {
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);
if(client->user.pass[0])
loginSuccess(startup->login_attempt_list, &client->addr);
JS_SET_RVAL(cx, arglist,BOOLEAN_TO_JSVAL(JS_TRUE));
return(JS_TRUE);
}
static JSBool
js_logout(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval val;
service_client_t* client;
jsrefcount rc;
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);
rc=JS_SUSPENDREQUEST(cx);
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));
JS_RESUMEREQUEST(cx, rc);
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.
*/
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)
{"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;
jsrefcount rc;
int log_level;
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;
rc=JS_SUSPENDREQUEST(cx);
lprintf(log_level,"%04d %s !JavaScript %s%s%s: %s",sock,prot,warning,file,line,message);
JS_RESUMEREQUEST(cx, rc);
/* Server Methods */
static JSBool
js_client_add(JSContext *cx, uintN argc, jsval *arglist)
{
jsval *argv=JS_ARGV(cx, arglist);
client_t client;
SOCKET sock=INVALID_SOCKET;
socklen_t addr_len;
union xp_sockaddr addr;
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);
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;
client.time=time32(NULL);
client.user=STR_UNKNOWN_USER;
SAFECOPY(client.host,client.user);
sock=js_socket(cx,argv[0]);
addr_len = sizeof(addr);
if(getpeername(sock, &addr.addr, &addr_len)==0) {
inet_addrtop(&addr, client.addr, sizeof(client.addr));
client.port=inet_addrport(&addr);
}
HANDLE_PENDING(cx, cstr);
if(argc>2)
JSVALUE_TO_STRBUF(cx, argv[2], client.host, sizeof(client.host), NULL);
rc=JS_SUSPENDREQUEST(cx);
client_on(sock, &client, /* update? */ FALSE);
#ifdef _DEBUG
lprintf(LOG_DEBUG,"%s client_add(%04u,%s,%s)"
,service_client->service->protocol
,sock,client.user,client.host);
#endif
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
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;
jsrefcount rc;
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;
client.user=STR_UNKNOWN_USER;
SAFECOPY(client.host,client.user);
sock=js_socket(cx,argv[0]);
addr_len = sizeof(addr);
if(getpeername(sock, &addr.addr, &addr_len)==0) {
inet_addrtop(&addr, client.addr, sizeof(client.addr));
client.port=inet_addrport(&addr);
}
if(argc>2)
JSVALUE_TO_STRBUF(cx, argv[2], client.host, sizeof(client.host), NULL);
rc=JS_SUSPENDREQUEST(cx);
client_on(sock, &client, /* update? */ TRUE);
#ifdef _DEBUG
lprintf(LOG_DEBUG,"%s client_update(%04u,%s,%s)"
,service_client->service->protocol
,sock,client.user,client.host);
#endif
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
js_client_remove(JSContext *cx, uintN argc, jsval *arglist)
{
jsval *argv=JS_ARGV(cx, arglist);
SOCKET sock=INVALID_SOCKET;
service_client_t* service_client;
jsrefcount rc;
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(sock!=INVALID_SOCKET) {
rc=JS_SUSPENDREQUEST(cx);
client_off(sock);
if(service_client->service->clients==0)
lprintf(LOG_WARNING,"%s !client_remove() called with 0 service clients"
,service_client->service->protocol);
else {
service_client->service->clients--;
update_clients();
}
JS_RESUMEREQUEST(cx, rc);
}
#ifdef _DEBUG
lprintf(LOG_DEBUG,"%s client_remove(%04u)"
,service_client->service->protocol, sock);
#endif
return(JS_TRUE);
}
js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, JSObject** glob)
JSObject* server;
BOOL rooted=FALSE;
if((js_cx = JS_NewContext(js_runtime, service_client->service->js.cx_stack))==NULL)
JS_BEGINREQUEST(js_cx);
JS_SetErrorReporter(js_cx, js_ErrorReporter);
/* ToDo: call js_CreateCommonObjects() instead */
if(!js_CreateGlobalObject(js_cx, &scfg, NULL, &service_client->service->js, glob))
rooted=TRUE;
if (!JS_DefineFunctions(js_cx, *glob, js_global_functions))
/* Internal JS Object */
if(js_CreateInternalJsObject(js_cx, *glob, &service_client->callback, &service_client->service->js)==NULL)
break;
if(js_CreateClientObject(js_cx, *glob, "client", service_client->client, sock, service_client->tls_sess)==NULL)
break;
if(js_CreateUserClass(js_cx, *glob, &scfg)==NULL)
break;
/* Socket Class */
if(js_CreateSocketClass(js_cx, *glob)==NULL)
if(js_CreateMsgBaseClass(js_cx, *glob, &scfg)==NULL)
if(js_CreateFileClass(js_cx, *glob)==NULL)
if(js_CreateQueueClass(js_cx, *glob)==NULL)
break;
/* COM Class */
if(js_CreateCOMClass(js_cx, *glob)==NULL)
/* CryptContext Class */
if(js_CreateCryptContextClass(js_cx, *glob)==NULL)
break;
/* CryptKeyset Class */
if(js_CreateCryptKeysetClass(js_cx, *glob)==NULL)
break;
/* CryptCert Class */
if(js_CreateCryptCertClass(js_cx, *glob)==NULL)
break;
/* user-specific objects */
if(!js_CreateUserObjects(js_cx, *glob, &scfg, /*user: */NULL, service_client->client, NULL, service_client->subscan))
break;
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;
if(service_client->client==NULL) { /* static service */
if(js_CreateSocketObjectFromSet(js_cx, server, "socket", service_client->set)==NULL)
break;
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_ENDREQUEST(js_cx);
JS_DestroyContext(js_cx);
return(NULL);
}
return(js_cx);
}
static JSBool
js_OperationCallback(JSContext *cx)
{
JSBool ret;
service_client_t* client;
JS_SetOperationCallback(cx, NULL);
if((client=(service_client_t*)JS_GetContextPrivate(cx))==NULL) {
JS_SetOperationCallback(cx, js_OperationCallback);
return(JS_FALSE);
/* Terminated? */
if(client->callback.auto_terminate && terminated) {
JS_ReportWarning(cx,"Terminated");
client->callback.counter=0;
JS_SetOperationCallback(cx, js_OperationCallback);
return(JS_FALSE);
}
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)
{
char argbuf[MAX_PATH+1];
char* p;
char* args;
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);
}
#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)
{
char *estr = NULL;
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);
if (estr) {
lprintf(LOG_ERR, "%04d cryptlib error %d at %s:%d (%s)", sock, status, file, line, estr);
free_crypt_attrstr(estr);
}
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)
{
SOCKET socket;
client_t client;
service_t* service;
service_client_t service_client;
/* JavaScript-specific */
char spath[MAX_PATH+1];
char fname[MAX_PATH+1];
JSObject* js_script;
JSRuntime* js_runtime;
service_client=*(service_client_t*)arg;
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");
thread_up(TRUE /* setuid */);
protected_uint32_adjust(&threads_pending_start, -1);
/* Host name lookup and filtering */
if(service->options&BBS_OPT_NO_HOST_LOOKUP
|| 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>");
}