Newer
Older
/* main.cpp */
/* Synchronet terminal server thread and related functions */
/* $Id$ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright 2011 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. *
****************************************************************************/
#include "sbbs.h"
#include "ident.h"
#include "telnet.h"
#include "js_rtpool.h"
#include "js_request.h"
#ifdef __unix__
#include <sys/un.h>
#ifndef SUN_LEN
#define SUN_LEN(su) \
(sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
#endif
#endif
//---------------------------------------------------------------------------
#define TELNET_SERVER "Synchronet Terminal Server"
#define STATUS_WFC "Listening"
#define TIMEOUT_THREAD_WAIT 60 // Seconds (was 15)
#define IO_THREAD_BUF_SIZE 20000 // Bytes
// Globals
#ifdef _WIN32
HANDLE exec_mutex=NULL;
HINSTANCE hK32=NULL;
#if defined(_DEBUG) && defined(_MSC_VER)
HANDLE debug_log=INVALID_HANDLE_VALUE;
_CrtMemState mem_chkpoint;
#endif // _DEBUG && _MSC_VER
#endif // _WIN32
#ifdef USE_CRYPTLIB
#define SSH_END() if(ssh) cryptDestroySession(sbbs->ssh_session);
#else
#define SSH_END()
#endif
volatile time_t uptime=0;
volatile ulong served=0;
static protected_uint32_t node_threads_running;
char lastuseron[LEN_ALIAS+1]; /* Name of user last online */
RingBuf* node_inbuf[MAX_NODES];
SOCKET spy_socket[MAX_NODES];
#ifdef __unix__
SOCKET uspy_socket[MAX_NODES]; /* UNIX domain spy sockets */
#endif
SOCKET node_socket[MAX_NODES];
static SOCKET telnet_socket=INVALID_SOCKET;
static SOCKET rlogin_socket=INVALID_SOCKET;
#ifdef USE_CRYPTLIB
static SOCKET ssh_socket=INVALID_SOCKET;
#endif
static sbbs_t* sbbs=NULL;
static scfg_t scfg;
static char * text[TOTAL_TEXT];
static WORD first_node;
static WORD last_node;
static bool terminate_server=false;
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
#ifdef _THREAD_SUID_BROKEN
int thread_suid_broken=TRUE; /* NPTL is no longer broken */
#endif
extern "C" {
static bbs_startup_t* startup=NULL;
{
if(startup!=NULL && startup->status!=NULL)
startup->status(startup->cbdata,str);
}
static void update_clients()
{
if(startup!=NULL && startup->clients!=NULL)
startup->clients(startup->cbdata,node_threads_running.value);
}
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()
{
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,FALSE,FALSE);
int lputs(int level, const char* str)
if(level <= LOG_ERR) {
errorlog(&scfg,startup==NULL ? NULL:startup->host_name, str);
if(startup!=NULL && startup->errormsg!=NULL)
startup->errormsg(startup->cbdata,level,str);
}
if(startup==NULL || startup->lputs==NULL || str==NULL || level > startup->log_level)
return(0);
#if defined(_WIN32)
if(IsBadCodePtr((FARPROC)startup->lputs))
return(0);
#endif
return(startup->lputs(startup->cbdata,level,str));
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);
return(lputs(level,sbuf));
int eprintf(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) {
errorlog(&scfg,startup==NULL ? NULL:startup->host_name, sbuf);
if(startup!=NULL && startup->errormsg!=NULL)
startup->errormsg(startup->cbdata,level,sbuf);
}
if(startup==NULL || startup->event_lputs==NULL || level > startup->log_level)
return(0);
strip_ctrl(sbuf, sbuf);
return(startup->event_lputs(startup->event_cbdata,level,sbuf));
SOCKET open_socket(int type, const char* protocol)
{
SOCKET sock;
char error[256];
sock=socket(AF_INET, type, IPPROTO_IP);
if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,TRUE);
if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
return(sock);
}
SOCKET accept_socket(SOCKET s, SOCKADDR* addr, socklen_t* addrlen)
{
SOCKET sock;
sock=accept(s,addr,addrlen);
if(sock!=INVALID_SOCKET && startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,TRUE);
return(sock);
}
int close_socket(SOCKET sock)
{
int result;
if(sock==INVALID_SOCKET || sock==0)
return(0);
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 && ERROR_VALUE!=ENOTSOCK)
lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
return(result);
}
u_long resolve_ip(char *addr)
{
HOSTENT* host;
char* p;
if(*addr==0)
return((u_long)INADDR_NONE);
for(p=addr;*p;p++)
if(*p!='.' && !isdigit(*p))
break;
if(!(*p))
return(inet_addr(addr));
if((host=gethostbyname(addr))==NULL)
return((u_long)INADDR_NONE);
return(*((ulong*)host->h_addr_list[0]));
}
} /* extern "C" */
#ifdef _WINSOCKAPI_
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
#endif
DLLEXPORT void DLLCALL sbbs_srand()
{
DWORD seed;
xp_randomize();
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
int rf;
if((rf=open(RANDOM_DEV, O_RDONLY))!=-1) {
read(rf, &seed, sizeof(seed));
close(rf);
}
#else
seed = time(NULL) ^ (DWORD)GetCurrentThreadId();
#endif
srand(seed);
sbbs_random(10); /* Throw away first number */
}
int DLLCALL sbbs_random(int n)
{
return(xp_random(n));
}
#ifdef JAVASCRIPT
static js_server_props_t js_server_props;
JSBool
DLLCALL js_CreateArrayOfStrings(JSContext* cx, JSObject* parent, const char* name, char* str[],uintN flags)
{
JSObject* array;
JSString* js_str;
jsval val;
size_t i;
jsuint len=0;
if(JS_GetProperty(cx,parent,name,&val) && val!=JSVAL_VOID)
array=JSVAL_TO_OBJECT(val);
else
if((array=JS_NewArrayObject(cx, 0, NULL))==NULL) /* Assertion here, in _heap_alloc_dbg, June-21-2004 */
return(JS_FALSE); /* Caused by nntpservice.js? */
if(!JS_DefineProperty(cx, parent, name, OBJECT_TO_JSVAL(array)
,NULL,NULL,flags))
return(JS_FALSE);
if(!JS_GetArrayLength(cx, array, &len))
return(JS_FALSE);
for(i=0;str[i]!=NULL;i++) {
if((js_str = JS_NewStringCopyZ(cx, str[i]))==NULL)
break;
val = STRING_TO_JSVAL(js_str);
if(!JS_SetElement(cx, array, len+i, &val))
break;
}
return(JS_TRUE);
/* Convert from Synchronet-specific jsSyncMethodSpec to JSAPI's JSFunctionSpec */
JSBool
DLLCALL js_DescribeSyncObject(JSContext* cx, JSObject* obj, const char* str, int ver)
{
JSString* js_str = JS_NewStringCopyZ(cx, str);
if(js_str==NULL)
return(JS_FALSE);
if(ver < 10000) /* auto convert 313 to 31300 */
ver*=100;
return(JS_DefineProperty(cx,obj,"_description"
,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY)
&& JS_DefineProperty(cx,obj,"_ver"
,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY));
}
JSBool
DLLCALL js_DescribeSyncConstructor(JSContext* cx, JSObject* obj, const char* str)
{
JSString* js_str = JS_NewStringCopyZ(cx, str);
if(js_str==NULL)
return(JS_FALSE);
return(JS_DefineProperty(cx,obj,"_constructor"
,STRING_TO_JSVAL(js_str),NULL,NULL,JSPROP_READONLY));
}
#ifdef BUILD_JSDOCS
static const char* method_array_name = "_method_list";
static const char* propver_array_name = "_property_ver_list";
/*
* from jsatom.c:
* Keep this in sync with jspubtd.h -- an assertion below will insist that
* its length match the JSType enum's JSTYPE_LIMIT limit value.
*/
static const char *js_type_str[] = {
"void", // changed from "undefined"
"object",
"function",
"string",
"number",
"boolean",
"array",
"alias",
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
uint i;
long ver;
jsval val;
jsuint len=0;
JSObject* array;
if((array=JS_NewArrayObject(cx, 0, NULL))==NULL)
return(JS_FALSE);
if(!JS_DefineProperty(cx, obj, propver_array_name, OBJECT_TO_JSVAL(array)
,NULL,NULL,JSPROP_READONLY))
return(JS_FALSE);
for(i=0;props[i].name;i++) {
if(!JS_DefinePropertyWithTinyId(cx, obj, /* Never reserve any "slots" for properties */
props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
return(JS_FALSE);
if(props[i].flags&JSPROP_ENUMERATE) { /* No need to version invisible props */
if((ver=props[i].ver) < 10000) /* auto convert 313 to 31300 */
ver*=100;
val = INT_TO_JSVAL(ver);
if(!JS_SetElement(cx, array, len++, &val))
return(JS_FALSE);
}
}
return(JS_TRUE);
}
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
{
int i;
jsuint len=0;
long ver;
jsval val;
JSObject* method;
JSObject* method_array;
JSString* js_str;
/* Return existing method_list array if it's already been created */
if(JS_GetProperty(cx,obj,method_array_name,&val) && val!=JSVAL_VOID)
method_array=JSVAL_TO_OBJECT(val);
else
if((method_array=JS_NewArrayObject(cx, 0, NULL))==NULL)
return(JS_FALSE);
if(!JS_DefineProperty(cx, obj, method_array_name, OBJECT_TO_JSVAL(method_array)
, NULL, NULL, 0))
return(JS_FALSE);
if(append)
if(!JS_GetArrayLength(cx, method_array, &len))
return(JS_FALSE);
for(i=0;funcs[i].name;i++) {
if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
return(JS_FALSE);
if(funcs[i].type==JSTYPE_ALIAS)
continue;
method = JS_NewObject(cx, NULL, NULL, method_array); /* exception here June-7-2003 */
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
if(method==NULL)
return(JS_FALSE);
if(funcs[i].name!=NULL) {
if((js_str=JS_NewStringCopyZ(cx,funcs[i].name))==NULL)
return(JS_FALSE);
val = STRING_TO_JSVAL(js_str);
JS_SetProperty(cx, method, "name", &val);
}
val = INT_TO_JSVAL(funcs[i].nargs);
if(!JS_SetProperty(cx, method, "nargs", &val))
return(JS_FALSE);
if((js_str=JS_NewStringCopyZ(cx,js_type_str[funcs[i].type]))==NULL)
return(JS_FALSE);
val = STRING_TO_JSVAL(js_str);
JS_SetProperty(cx, method, "type", &val);
if(funcs[i].args!=NULL) {
if((js_str=JS_NewStringCopyZ(cx,funcs[i].args))==NULL)
return(JS_FALSE);
val = STRING_TO_JSVAL(js_str);
JS_SetProperty(cx, method, "args", &val);
}
if(funcs[i].desc!=NULL) {
if((js_str=JS_NewStringCopyZ(cx,funcs[i].desc))==NULL)
return(JS_FALSE);
val = STRING_TO_JSVAL(js_str);
JS_SetProperty(cx, method, "desc", &val);
}
if(funcs[i].ver) {
if((ver=funcs[i].ver) < 10000) /* auto convert 313 to 31300 */
ver*=100;
val = INT_TO_JSVAL(ver);
JS_SetProperty(cx,method, "ver", &val);
}
val=OBJECT_TO_JSVAL(method);
if(!JS_SetElement(cx, method_array, len+i, &val))
return(JS_FALSE);
}
return(JS_TRUE);
}
/*
* Always resolve all here since
* 1) We'll always be enumerating anyways
* 2) The speed penalty won't be seen in production code anyways
*/
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
if(!js_DefineSyncProperties(cx, obj, props))
ret=JS_FALSE;
if(funcs) {
if(!js_DefineSyncMethods(cx, obj, funcs, 0))
if(consts) {
if(!js_DefineConstIntegers(cx, obj, consts, flags))
ret=JS_FALSE;
}
#else // NON-JSDOCS
JSBool
DLLCALL js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
uint i;
for(i=0;props[i].name;i++)
if(!JS_DefinePropertyWithTinyId(cx, obj,
props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
return(JS_FALSE);
return(JS_TRUE);
}
DLLCALL js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs, BOOL append)
uint i;
for(i=0;funcs[i].name;i++)
if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
return(JS_FALSE);
return(JS_TRUE);
}
JSBool
DLLCALL js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
if(props) {
for(i=0;props[i].name;i++) {
if(name==NULL || strcmp(name, props[i].name)==0) {
if(!JS_DefinePropertyWithTinyId(cx, obj,
props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
return(JS_FALSE);
if(name)
return(JS_TRUE);
}
}
}
if(funcs) {
for(i=0;funcs[i].name;i++) {
if(name==NULL || strcmp(name, funcs[i].name)==0) {
if(!JS_DefineFunction(cx, obj, funcs[i].name, funcs[i].call, funcs[i].nargs, 0))
return(JS_FALSE);
if(name)
return(JS_TRUE);
}
}
}
if(name==NULL || strcmp(name, consts[i].name)==0) {
val=INT_TO_JSVAL(consts[i].val);
if(!JS_DefineProperty(cx, obj, consts[i].name, val ,NULL, NULL, flags))
return(JS_FALSE);
if(name)
return(JS_TRUE);
}
}
}
#endif
/* This is a stream-lined version of JS_DefineConstDoubles */
JSBool
DLLCALL js_DefineConstIntegers(JSContext* cx, JSObject* obj, jsConstIntSpec* ints, int flags)
{
uint i;
jsval val;
for(i=0;ints[i].name;i++) {
val=INT_TO_JSVAL(ints[i].val);
if(!JS_DefineProperty(cx, obj, ints[i].name, val ,NULL, NULL, flags))
return(JS_FALSE);
}
return(JS_TRUE);
}
char*
DLLCALL js_ValueToStringBytes(JSContext* cx, jsval val, size_t* len)
{
JSString* str;
if((str=JS_ValueToString(cx, val))==NULL)
return(NULL);
if(len!=NULL)
*len = JS_GetStringLength(str);
JSSTRING_TO_STRING(cx, str, leak, NULL);
}
static JSBool
js_log(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
uintN i=0;
int32 level=LOG_INFO;
JSString* str=NULL;
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
if(argc > 1 && JSVAL_IS_NUMBER(argv[i]))
JS_ValueToInt32(cx,argv[i++],&level);
for(; i<argc; i++) {
JSVALUE_TO_STRING(cx, argv[i], line, NULL);
JS_RESUMEREQUEST(cx, rc);
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
if(sbbs->online==ON_LOCAL) {
if(startup!=NULL && startup->event_lputs!=NULL && level <= startup->log_level) {
startup->event_lputs(startup->event_cbdata,level,line);
}
lprintf(level,"Node %d %s", sbbs->cfg.node_num, line);
JS_RESUMEREQUEST(cx, rc);
}
if(str==NULL)
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
else
JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
return(JS_TRUE);
}
static JSBool
js_read(JSContext *cx, uintN argc, jsval *arglist)
{
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
uchar* buf;
int32 len=128;
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
if(argc)
JS_ValueToInt32(cx,argv[0],&len);
if((buf=(uchar*)malloc(len))==NULL)
return(JS_TRUE);
rc=JS_SUSPENDREQUEST(cx);
len=RingBufRead(&sbbs->inbuf,buf,len);
JS_RESUMEREQUEST(cx, rc);
if(len>0)
JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyN(cx,(char*)buf,len)));
free(buf);
return(JS_TRUE);
}
static JSBool
js_readln(JSContext *cx, uintN argc, jsval *arglist)
{
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
char* buf;
int32 len=128;
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
if(argc)
JS_ValueToInt32(cx,argv[0],&len);
if((buf=(char*)malloc(len))==NULL)
return(JS_TRUE);
rc=JS_SUSPENDREQUEST(cx);
len=sbbs->getstr(buf,len,K_NONE);
JS_RESUMEREQUEST(cx, rc);
if(len>0)
JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,buf)));
free(buf);
return(JS_TRUE);
}
static JSBool
js_write(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
JSString* str=NULL;
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
for (i = 0; i < argc; i++) {
JSVALUE_TO_STRING(cx, argv[i], cstr, NULL);
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
if(sbbs->online==ON_LOCAL)
JS_RESUMEREQUEST(cx, rc);
}
if(str==NULL)
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
else
JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(str));
return(JS_TRUE);
}
static JSBool
js_write_raw(JSContext *cx, uintN argc, jsval *arglist)
{
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
uintN i;
char* str=NULL;
size_t len;
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
for (i = 0; i < argc; i++) {
if((str=js_ValueToStringBytes(cx, argv[i], &len))==NULL)
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
sbbs->putcom(str, len);
JS_RESUMEREQUEST(cx, rc);
}
return(JS_TRUE);
}
static JSBool
js_writeln(JSContext *cx, uintN argc, jsval *arglist)
{
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
js_write(cx,argc,arglist);
rc=JS_SUSPENDREQUEST(cx);
if(sbbs->online==ON_REMOTE)
sbbs->bputs(crlf);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
js_printf(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
char* p;
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
if((p = js_sprintf(cx, 0, argc, argv))==NULL) {
JS_ReportError(cx,"js_sprintf failed");
return(JS_FALSE);
}
rc=JS_SUSPENDREQUEST(cx);
if(sbbs->online==ON_LOCAL)
eprintf(LOG_INFO,"%s",p);
else
sbbs->bputs(p);
JS_RESUMEREQUEST(cx, rc);
JS_SET_RVAL(cx, arglist, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, p)));
js_sprintf_free(p);
return(JS_TRUE);
}
static JSBool
js_alert(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
JSString * str;
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
JSVALUE_TO_STRING(cx, argv[0], cstr, NULL);
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
sbbs->attr(sbbs->cfg.color[clr_err]);
sbbs->attr(LIGHTGRAY);
sbbs->bputs(crlf);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
js_confirm(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
JSString * str;
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
JSVALUE_TO_STRING(cx, argv[0], cstr, NULL);
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->yesno(cstr)));
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
js_deny(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
JSString * str;
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
JSVALUE_TO_STRING(cx, argv[0], cstr, NULL);
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
JS_SET_RVAL(cx, arglist, BOOLEAN_TO_JSVAL(sbbs->noyes(cstr)));
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
static JSBool
js_prompt(JSContext *cx, uintN argc, jsval *arglist)
JSObject *obj=JS_THIS_OBJECT(cx, arglist);
jsval *argv=JS_ARGV(cx, arglist);
char instr[81];
JSString * str;
sbbs_t* sbbs;
jsrefcount rc;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);
JSVALUE_TO_STRING(cx, argv[0], prompt, NULL);
return(JS_FALSE);
if(argc>1) {
JSVALUE_TO_STRING(cx, argv[1], cstr, NULL);
return(JS_FALSE);
} else
instr[0]=0;
rc=JS_SUSPENDREQUEST(cx);
if(!sbbs->getstr(instr,sizeof(instr)-1,K_EDIT)) {
JS_SET_RVAL(cx, arglist, JSVAL_NULL);
JS_RESUMEREQUEST(cx, rc);
return(JS_TRUE);
}
JS_RESUMEREQUEST(cx, rc);