Newer
Older
/* Synchronet terminal server thread and related functions */
/****************************************************************************
* @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 *
* *
* 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. *
****************************************************************************/
#include "sbbs.h"
#include "ident.h"
#include "telnet.h"
#include "petdefs.h"
#include "filedat.h"
#include "js_rtpool.h"
#include "js_request.h"
#include "ver.h"
#ifdef __unix__
#include <sys/un.h>
#endif
//#define SBBS_TELNET_ENVIRON_SUPPORT 1
//---------------------------------------------------------------------------
#define TELNET_SERVER "Synchronet Terminal Server"
static const char* server_abbrev = "term";
static const char* eventIniSection = "Events";
static ini_style_t eventIniStyle = {
/* key_len: */LEN_CODE
static ini_style_t qhubIniStyle = {
/* key_len: */LEN_QWKID
};
static const char* qhubIniSection = "QWK_NetworkHubs";
#define TIMEOUT_THREAD_WAIT 60 // Seconds (was 15)
#define IO_THREAD_BUF_SIZE 20000 // Bytes
#define TIMEOUT_MUTEX_FILE 12*60*60
#ifdef USE_CRYPTLIB
static protected_uint32_t ssh_sessions;
void ssh_session_destroy(SOCKET sock, CRYPT_SESSION session, int line)
{
int result = cryptDestroySession(session);
if(result != 0)
lprintf(LOG_ERR, "%04d SSH ERROR %d destroying Cryptlib Session %d from line %d"
uint32_t remain = protected_uint32_adjust_fetch(&ssh_sessions, -1);
lprintf(LOG_DEBUG, "%04d SSH Cryptlib Session: %d destroyed from line %d (%u remain)"
, sock, session, line, remain);
}
}
#define SSH_END(sock) do { \
if(ssh) { \
pthread_mutex_lock(&sbbs->ssh_mutex); \
ssh_session_destroy(sock, sbbs->ssh_session, __LINE__); \
sbbs->ssh_mode = false; \
#endif
volatile time_t uptime=0;
static protected_uint32_t node_threads_running;
static volatile uint32_t client_highwater=0;
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 scfg_t scfg;
static char * text[TOTAL_TEXT];
static scfg_t node_scfg[MAX_NODES];
static char * node_text[MAX_NODES][TOTAL_TEXT];
static WORD first_node;
static WORD last_node;
static bool terminate_server=false;
static str_list_t pause_semfiles;
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
static str_list_t clear_attempts_semfiles;
static link_list_t current_logins;
static link_list_t current_connections;
#ifdef _THREAD_SUID_BROKEN
int thread_suid_broken=true; /* NPTL is no longer broken */
/* convenient space-saving global variables */
extern "C" {
const char* crlf="\r\n";
const char* nulstr="";
};
char *GCES_estr; \
int GCES_level; \
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);\
if (GCES_level < startup->ssh_error_level) \
GCES_level = startup->ssh_error_level; \
lprintf(GCES_level, "Node %d SSH %s from %s", node, GCES_estr, __FUNCTION__); \
char *GCES_estr; \
int GCES_level; \
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);\
if (GCES_level < startup->ssh_error_level) \
GCES_level = startup->ssh_error_level; \
lprintf(GCES_level, "SSH %s from %s", GCES_estr, __FUNCTION__); \
char *GCES_estr; \
int GCES_level; \
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);\
if (GCES_level < startup->ssh_error_level) \
GCES_level = startup->ssh_error_level; \
lprintf(GCES_level, "%04d SSH %s from %s", sock, GCES_estr, __FUNCTION__); \
#define GCESSTR(status, str, sess, action) do { \
char *GCES_estr; \
int GCES_level; \
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level);\
if (GCES_estr) { \
if (GCES_level < startup->ssh_error_level) \
GCES_level = startup->ssh_error_level; \
lprintf(GCES_level, "%s SSH %s from %s (session %d)", str, GCES_estr, __FUNCTION__, sess); \
free_crypt_attrstr(GCES_estr); \
} \
} while (0)
extern "C" {
static bbs_startup_t* startup=NULL;
static void set_state(enum server_state state)
static int curr_state;
if(state == curr_state)
return;
if(startup != NULL) {
if(startup->set_state != NULL)
startup->set_state(startup->cbdata, state);
}
static void update_clients()
{
if(startup != NULL) {
if(startup->clients != NULL)
startup->clients(startup->cbdata,protected_uint32_value(node_threads_running));
}
}
void client_on(SOCKET sock, client_t* client, BOOL update)
{
if(!update)
listAddNodeData(¤t_connections, client->addr, strlen(client->addr)+1, sock, LAST_NODE);
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,true,sock,client,update);
mqtt_client_on(&mqtt, true, sock, client, update);
}
static void client_off(SOCKET sock)
{
listRemoveTaggedNode(¤t_connections, sock, /* free_data */true);
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,false,sock,NULL,false);
mqtt_client_on(&mqtt, 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)
mqtt_lputs(&mqtt, TOPIC_SERVER, level, str);
if(level <= LOG_ERR) {
char errmsg[1024];
SAFEPRINTF2(errmsg, "%-4s %s", server_abbrev, str);
errorlog(&scfg, &mqtt, level, 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 || 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 eputs(int level, const char *str)
{
if(*str == 0)
return 0;
mqtt_lputs(&mqtt, TOPIC_HOST_EVENT, level, str);
if(level <= LOG_ERR) {
char errmsg[1024];
SAFEPRINTF(errmsg, "evnt %s", str);
errorlog(&scfg, &mqtt, level, startup==NULL ? NULL:startup->host_name, errmsg);
if(startup!=NULL && startup->errormsg!=NULL)
startup->errormsg(startup->cbdata, level, errmsg);
}
if(startup==NULL || startup->event_lputs==NULL || level > startup->log_level)
return(0);
}
int lprintf(int level, const char *fmt, ...)
{
va_list argptr;
char sbuf[1024];
sbuf[sizeof(sbuf)-1]=0;
void
sbbs_t::log_crypt_error_status_sock(int status, const char *action)
{
char *estr;
int level;
if (cryptStatusError(status)) {
get_crypt_error_string(status, ssh_session, &estr, action, &level);
if (estr) {
if (level < startup->ssh_error_level)
level = startup->ssh_error_level;
lprintf(level, "%04d SSH %s", client_socket, estr);
free_crypt_attrstr(estr);
}
/* Picks the right log callback function (event or term) based on the sbbs->cfg.node_num value */
/* Prepends the current node number and user alias (if applicable) */
int sbbs_t::lputs(int level, const char* str)
{
char msg[2048];
char prefix[32] = "";
char user_str[64] = "";
if(is_event_thread && event_code != NULL && *event_code)
SAFEPRINTF(prefix, "%s ", event_code);
else if(cfg.node_num && !is_event_thread)
SAFEPRINTF(prefix, "Node %d ", cfg.node_num);
else if(client_name[0])
SAFEPRINTF(prefix, "%s ", client_name);
if(useron.number)
SAFEPRINTF(user_str, "<%s> ", useron.alias);
SAFEPRINTF3(msg, "%s%s%s", prefix, user_str, str);
strip_ctrl(msg, msg);
if(is_event_thread)
return ::eputs(level, msg);
return ::lputs(level, msg);
int sbbs_t::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));
}
struct main_sock_cb_data {
bbs_startup_t *startup;
const char *protocol;
};
void sock_cb(SOCKET sock, void *cb_data)
{
char error_str[256];
struct main_sock_cb_data *cb=(struct main_sock_cb_data *)cb_data;
if(cb->startup && cb->startup->socket_open)
cb->startup->socket_open(cb->startup->cbdata, true);
if(set_socket_options(&scfg, sock, cb->protocol, error_str, sizeof(error_str)))
lprintf(LOG_ERR,"%04d !ERROR %s",sock,error_str);
}
void sock_close_cb(SOCKET sock, void *cb_data)
{
bbs_startup_t *su=(bbs_startup_t *)cb_data;
if(su && su->socket_open)
su->socket_open(su->cbdata, false);
void call_socket_open_callback(bool open)
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata, open);
}
SOCKET open_socket(int domain, int type, const char* protocol)
{
SOCKET sock;
char error[256];
sock=socket(domain, type, IPPROTO_IP);
if(sock!=INVALID_SOCKET)
call_socket_open_callback(true);
if(sock!=INVALID_SOCKET && set_socket_options(&scfg, sock, protocol, error, sizeof(error)))
lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
return(sock);
}
// Used by sbbs_t::ftp_put() and js_accept()
SOCKET accept_socket(SOCKET s, union xp_sockaddr* addr, socklen_t* addrlen)
{
SOCKET sock;
if(sock!=INVALID_SOCKET)
call_socket_open_callback(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);
call_socket_open_callback(false);
if(result!=0 && ERROR_VALUE!=ENOTSOCK)
lprintf(LOG_WARNING,"!ERROR %d closing socket %d",ERROR_VALUE,sock);
return(result);
}
in_addr_t resolve_ip(char *addr)
{
HOSTENT* host;
char* p;
if(*addr==0)
return INADDR_NONE;
for(p=addr;*p;p++)
if(*p!='.' && !IS_DIGIT(*p))
break;
if(!(*p))
return(inet_addr(addr));
if((host=gethostbyname(addr))==NULL)
return INADDR_NONE;
if(host->h_addr_list[0] == NULL)
return INADDR_NONE;
return *((in_addr_t*)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);
}
#else /* No WINSOCK */
#define winsock_startup() (true)
#define SOCKLIB_DESC NULL
#endif
DWORD seed;
xp_randomize();
#if defined(HAS_DEV_RANDOM) && defined(RANDOM_DEV)
int rf,rd=0;
if((rf=open(RANDOM_DEV, O_RDONLY|O_NONBLOCK))!=-1) {
rd=read(rf, &seed, sizeof(seed));
close(rf);
}
#endif
seed = time32(NULL) ^ (uintmax_t)GetCurrentThreadId();
srand(seed);
sbbs_random(10); /* Throw away first number */
}
{
return(xp_random(n));
}
#ifdef JAVASCRIPT
static js_server_props_t js_server_props;
void* js_GetClassPrivate(JSContext *cx, JSObject *obj, JSClass* cls)
{
void *ret = JS_GetInstancePrivate(cx, obj, cls, NULL);
/*
* NOTE: Any changes here should also be added to the same function in jsdoor.c
* (ie: anything not Synchronet specific).
*/
if(ret == NULL)
JS_ReportError(cx, "'%s' instance: No Private Data or Class Mismatch"
, cls == NULL ? "???" : cls->name);
return ret;
}
js_CreateArrayOfStrings(JSContext* cx, JSObject* parent, const char* name, const 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(array == NULL || !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
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_FALSE
&& JS_DefineProperty(cx,obj,"_ver"
,INT_TO_JSVAL(ver),NULL,NULL,JSPROP_READONLY) != JS_FALSE);
}
JSBool
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* property_array_name = "_property_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
js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
uint i;
jsval val;
JSObject* array;
if((array=JS_NewObject(cx, NULL, NULL, obj))==NULL)
return(JS_FALSE);
for(i=0; props[i].name != NULL; ++i) {
if (props[i].tinyid < 256 && props[i].tinyid > -129) {
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);
}
else {
if(!JS_DefineProperty(cx, obj, props[i].name, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
return(JS_FALSE);
}
if (!(props[i].flags & JSPROP_ENUMERATE)) /* No need to document invisible props */
continue;
prop = JS_NewObject(cx, NULL, NULL, array);
if (prop == NULL)
return JS_FALSE;
if((ver=props[i].ver) < 10000) /* auto convert 313 to 31300 */
ver*=100;
val = INT_TO_JSVAL(ver);
JS_SetProperty(cx, prop, "ver", &val);
if (props[i].desc != NULL) {
if ((js_str = JS_NewStringCopyZ(cx, props[i].desc)) == NULL)
return JS_FALSE;
val = STRING_TO_JSVAL(js_str);
JS_SetProperty(cx, prop, "desc", &val);
if (!JS_DefineProperty(cx, array, props[i].name, OBJECT_TO_JSVAL(prop), NULL, NULL, JSPROP_READONLY | JSPROP_ENUMERATE))
return JS_FALSE;
}
return JS_DefineProperty(cx, obj, property_array_name, OBJECT_TO_JSVAL(array), NULL, NULL, JSPROP_READONLY);
}
js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
{
int i;
jsuint len=0;
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);
if (method_array == NULL || !JS_GetArrayLength(cx, method_array, &len))
for (i = 0; i < (int)len; i++) {
if (JS_GetElement(cx, method_array, i, &val) != JS_TRUE || val == JSVAL_VOID)
continue;
JS_GetProperty(cx, JSVAL_TO_OBJECT(val), "name", &val);
JSVALUE_TO_RASTRING(cx, val, str, &str_len, NULL);
if (strcmp(str, funcs[0].name) == 0)
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)
return(JS_FALSE);
for(i=0; funcs[i].name != NULL; ++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);
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
*/
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(consts) {
if(!js_DefineConstIntegers(cx, obj, consts, flags))
ret=JS_FALSE;
}
#else // NON-JSDOCS
JSBool
js_DefineSyncProperties(JSContext *cx, JSObject *obj, jsSyncPropertySpec* props)
{
uint i;
/*
* NOTE: Any changes here should also be added to the same function in jsdoor.c
* (ie: anything not Synchronet specific).
*/
for(i=0;props[i].name;i++) {
if (props[i].tinyid < 256 && props[i].tinyid > -129) {
if(!JS_DefinePropertyWithTinyId(cx, obj,
props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
return(JS_FALSE);
}
else {
if(!JS_DefineProperty(cx, obj, props[i].name, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
return(JS_FALSE);
}
}
return(JS_TRUE);
}
js_DefineSyncMethods(JSContext* cx, JSObject* obj, jsSyncMethodSpec *funcs)
uint i;
/*
* NOTE: Any changes here should also be added to the same function in jsdoor.c
* (ie: anything not Synchronet specific).
*/
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);
}
js_SyncResolve(JSContext* cx, JSObject* obj, char *name, jsSyncPropertySpec* props, jsSyncMethodSpec* funcs, jsConstIntSpec* consts, int flags)
/*
* NOTE: Any changes here should also be added to the same function in jsdoor.c
* (ie: anything not Synchronet specific).
*/
if(props) {
for(i=0;props[i].name;i++) {
if(name==NULL || strcmp(name, props[i].name)==0) {
if (props[i].tinyid < 256 && props[i].tinyid > -129) {
if(!JS_DefinePropertyWithTinyId(cx, obj,
props[i].name,props[i].tinyid, JSVAL_VOID, NULL, NULL, props[i].flags|JSPROP_SHARED))
return(JS_FALSE);
}
else {
if(!JS_DefineProperty(cx, obj, props[i].name, 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
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);
}
static JSBool
js_log(JSContext *cx, uintN argc, jsval *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])) {
if(!JS_ValueToInt32(cx,argv[i++],&level))
return JS_FALSE;
}
for(; i<argc; i++) {
if((str=JS_ValueToString(cx, argv[i]))==NULL) {
FREE_AND_NULL(line);
JSSTRING_TO_RASTRING(cx, str, line, &line_sz, NULL);
return(JS_FALSE);
rc=JS_SUSPENDREQUEST(cx);
sbbs->lputs(level, 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)
{
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) {
if(!JS_ValueToInt32(cx,argv[0],&len))
return JS_FALSE;
}
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)
{
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) {
if(!JS_ValueToInt32(cx,argv[0],&len))
return JS_FALSE;
}
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)
jsval *argv=JS_ARGV(cx, arglist);
JSString* str=NULL;
sbbs_t* sbbs;
jsrefcount rc;
char *cstr=NULL;
size_t cstr_sz=0;
JS_SET_RVAL(cx, arglist, JSVAL_VOID);
if((sbbs=(sbbs_t*)JS_GetContextPrivate(cx))==NULL)
return(JS_FALSE);