# BulkServer 1.1 by Filipe Dobreira, dobreira @ gmail.com
# March 2010.
@name BulkServer1
@inputs Scr:wirelink Input:string
@outputs
@persist [PRT STK HOLD UNPACK SEND LOG]:array [V BL SCHEDULE GLOBALS]:table
@persist Start End
@trigger
if(first())
{
# Server Variables +---------------------------+
# server variables should use an underscore prefix ('_') to prevent being overwritten.
# and are accessible in code unless declared in the blacklist.
# server variables (_prefix) cannot be written to by scripts.
V["_range",string] = "2-16" # Sweep range, where the server looks for connections.
V["_lookupPort",number] = 1 # Swap port, for server-use.
V["_serverVersion",string] = "1.1" # Server version/identification.
V["_serverName",string] = "Test Server." # Server identification, sent in the header.
V["_frequency",number] = 50 # Interval between port sweeps.
V["_group",string] = "fnet" # Default connection group.
V["_timeout",number] = 150 # Maximum operations allowed for client scripts.
V["_allowRun",number] = 1 # If set to 1, allows the use of the RUN command.
V["_refreshRate",number] = 1000 # Interval in MS between data refreshing.
V["_day",number] = time("day") # The current day.
V["_dir",string] = "/bulkserv/" # Directory used for file-functions.
V["_fileFunctions",string] = "RWL" # File-function flags to control access (R-read,W-write,L-load)
V["_log",number] = 1 # If 1, actions are logged to---...
V["_logfile",string] = "_log.txt" # Location of log file, relative to the _dir SVar.
# Black List +----------------------------------+
# the black list is a multi-purpose table used to block access to certain data.
# the KEY is the variable name, the VALUE can be 0(unlocked),1(locked) or a STRING(auth).
# May also be used to blacklist files, with FILENAME as the key, value follows same rules.
BL["_frequency",number] = 1
BL[V["_logfile",string],number]= 1
# Globals List +-------------------------------+
# The globals list marks variables that should not be dumped after the session ends.
# Scripts can set these globals, or you can set them manually here.
GLOBALS["bacon",number] = 1
# Identifiers +---------------------------------+
# used in the stack processor, identifiers tell the script who or what
# is using the server at that exact time. The optional SVar identifiers
# can be used to identify known sources, with a persistant ID.
V["_%kbrd",string] = "Keyboard Module."
V["_%"+owner():steamID(),string] = "BulkServer Administrator."
# Boot operations +-----------------------------+
# anything else that should be done at boot time goes here:
Scr[2041] = 1 V["_bootTime",number] = int(curtime())
timer("refresh",V["_refreshRate",number])
PRT:pushString("444:+----------------------------+")
PRT:pushString("444:BulkServer online.")
PRT:pushString("444:serverName - "+V["_serverName",string])
PRT:pushString("444:group - "+V["_group",string])
PRT:pushString("444:+----------------------------+")
LOG:pushString("SERVER Initiated - "+V["_serverName",string])
}
# Optional Input Module:
if(changed(Input) & Input != "") {
STK:pushString("{%kbrd%}"+Input)
}
# Frequency +-----------------------------+
interval(V["_frequency",number])
# Data Refresh:
if ( clk("refresh") ) {
V["_curtime",number] = int(curtime())
V["_hour",number] = time("hour")
V["_min",number] = time("min")
V["_sec",number] = time("sec")
V["_time",string] = time("hour")+":"+time("min")+":"+time("sec")
V["_uptime",number] = int(curtime()) - V["_bootTime",number]
timer("refresh",V["_refreshRate",number])
}
# Port Sweep +----------------------------+
if( first() | V["_switchPort",number] ) {
SweepRange = V["_range",string]:explode("-")
Start = SweepRange[1,string]:toNumber()
End = SweepRange[2,string]:toNumber()
V["_switchPort",number] = 0
}
gSetGroup(V["_group",string]) gShare(1)
for ( I = Start, End ) {
FetchVal = gDeleteStr(I)
if(FetchVal != ""){ UNPACK:pushString(FetchVal) }
}
# Unpack Buffer function:
while(UNPACK:count()>0)
{
# Unpack:
Package = glonDecodeTable(UNPACK[1,string])
UNPACK:remove(1)
# Get sender id:
SenderId = Package["_sender",string]
# Look for script:
Script = Package["_script",string]
# Save HOLD index:
V["_HOLD_"+SenderId,number] = HOLD:count()+1
# Unpack variables (skip hidden vars):
Keys = Package:keys()
Variables = array()
for(I=1,Keys:count())
{
if(Keys[I,string][1] != "_") {
K = Package[Keys[I,string],string]
N = Package[Keys[I,string],number]
}
if(K!="") {
Variables:pushString(Keys[I,string])
V[Keys[I,string],string] = Package[Keys[I,string],string]
}
elseif(N) {
Variables:pushString(Keys[I,string])
V[Keys[I,string],number] = Package[Keys[I,string],number]
}
}
# Store variable manifest:
Package["_vars",string] = Variables:concat(";")
# Store package:
HOLD:pushString(glonEncode(Package))
# Run script (if exists):
if(Script == "") { Script = "send" }
STK:pushString("{%"+SenderId+"%}run "+Script:trim())
}
# Schedule Service:
Curtime = int(curtime()):toString()
if( SCHEDULE[Curtime,string] !="" ) {
STK:pushString("run "+SCHEDULE[Curtime,string])
SCHEDULE[Curtime,string] = ""
}
# Stack Processor +-----------------------+
if(clk("continue") | STK:count()>0) {
while(STK:count()>0 & perf()) {
# Initiate error buffer:
ERROR = array()
# Fetch string:
P = STK[1,string]:explode(" ") STK:remove(1)
Line = P[1,string]
# Fetch identifier:
if( Line:find("{%") == 1) {
Id = Line:sub(3,Line:find("%}")-1)
P[1,string] = Line:sub(Line:find("%}")+2)
}
# Fetch package:
Pack = glonDecodeTable(HOLD[V["_HOLD_"+Id,number],string])
V["_id",string] = Id
V["_ident",string] = V["_%"+Id,string]
# Expand variables:
for( I = 1, P:count() ) {
Key = P[I,string]
if( Key[1] == "$" ) {
Key = Key:right(Key:length() - 1)
if( V[Key,number] & !BL[Key,number] ) { P[I,string] = V[Key,number]:toString() }
elseif( !BL[Key,number] ) { P[I,string] = V[Key,string] }
}
}
# Fetch command (command can be fetched from a variable):
C = P[1,string] P:remove(1)
# Helper PRT function, used to print a string to the console.
if( C == "prt" )
{
PRT:pushString(P:concat(" "))
}
# Allows scripts to write into the log file:
if( C == "log" ) {
LOG:pushString(V["_ident",string]+" || "+P:concat(" "))
}
# SET, used to set variables, server variables are blocked by default.
elseif( C == "set" ) {
TargetVar = P[1,string] P:remove(1)
if(TargetVar[1] != "_") {
Var = P:concat(" ")
if(Var:toNumber()) {
V[TargetVar,number] = Var:toNumber()
}
else{ V[TargetVar,string] = Var }
Pack["_vars",string] = Pack["_vars",string] +";"+TargetVar
}
else{
PRT:pushString("900:Attempt to (re)declare "+TargetVar+"!")
PRT:pushString("Cannot (re)declare server variables.")
}
}
# GLOBAL-ize a variable, may be done before it's even declared.
elseif( C == "global" ) { GLOBALS[P[1,string],number] = 1 }
# PUT, used to store variables in the package.
elseif( C == "put" ) {
TargetVar = P[1,string] P:remove(1)
Value = P:concat(" ")
if(Value:toNumber()) {
Pack[TargetVar,number] = Value:toNumber()
}
else {
Pack[TargetVar,string] = Value
}
}
# ROT13, for those that take security as seriously as...well, you get the idea.
elseif( C == "rot13" ) {
TargetVar = P[1,string] P:remove(1)
Offset = 13 % 26
S = P:concat(" "):explode("")
for(I = 1,S:count())
{
A = toByte(S[I,string])
if (A >= 97 & A <= 122) {
S[I,string] = toChar((A - 71 + Offset) % 26 + 97)
}
elseif (A >= 65 & A <= 90) {
S[I,string] = toChar((A - 39 + Offset) % 26 + 65)
}
}
V[TargetVar,string] = S:concat("")
}
# RUN command is used to parse a string of code into the stack.
elseif( C == "run" ) {
if(V["_allowRun",number]) {
Code = P:concat(" "):explode(";")
for(I=1,Code:count()) {
print(Code[I,string]:trim())
STK:pushString("{%"+Id+"%}" + Code[I,string]:trim() )
}
}
else{
PRT:pushString("900:RUN blocked by SVar.")
ERROR:pushString("RUN blocked by SVar.")
}
}
# Script scheduler, can be used to run functions in the future:
elseif( C == "schedule" )
{
ScheduleOffset = int(curtime()) + P[1,string]:toNumber()
P:remove(1) Script = P:concat(" ")
Sch = ScheduleOffset:toString()
SCHEDULE[Sch,string] = SCHEDULE[Sch,string] + Script
LOG:pushString("SCHEDULE: "+V["_id",string]+"|"+V["_ident",string])
}
# SEND command, packages and sends package to destination:
elseif( C == "send" )
{
PRT:pushString("--SENT!")
SEND:pushString(glonEncode(Pack))
}
# Re-package and store data:
HOLD[V["_HOLD_"+Id,number],string] = glonEncode(Pack)
}} # <- stack wrapper.
# Stack loopback +------------------------+
if(STK:count()>0) { timer("continue",10) }
# SEND buffer:
while( SEND:count() > 0 )
{
Data = SEND[1,string]
Package = glonDecodeTable(Data)
Target = Package["_target",number]
# If no target specified, look for target variable.
if(!Target) { Target = Package["target",number] }
# Connect to target group:
Group = Package["_group",string]
if(Group == "") {
Group = Package["group",string]
}
# Load variable manifest, dump session variables:
Keys = Package["_vars",string]:explode(";")
for(I=1,Keys:count()) {
K = Keys[I,string]
if(K[1] != "_" & !GLOBALS[K,number])
{ V[K,string] = ""
V[K,number] = 0
}
}
# Check and send:
if ( Target & Group !="") {
# Adios!
Package["_handler",string] = "BulkServer - "+V["_serverName",string]
gSetGroup(Group) gShare(1)
gSetStr(Target,glonEncode(Package))
}
}
# PRT output buffer: +--------------------+
while( PRT:count() > 0 )
{
Print = PRT[1,string] PRT:remove(1)
# Text color:
Color = 999
if(inrange(Print:find(":"),1,4)) {
Color = Print:sub(1,Print:find(":")-1):toNumber()
Print = Print:sub( Print:find(":")+1 )
}
Scr:writeString(Print,0,17,Color,Scr[2042])
Scr[2038] = 1
}
# LOG output buffer +---------------------+
while(LOG:count()>0)
{
Line = LOG[1,string] LOG:remove(1)
if(V["_log",number])
{
fileAppend(V["_dir",string]+V["_logfile",string],V["_time",string]+": "+Line+"\n")
}
}