mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-19 03:55:11 +01:00
Make NanoUI resistant to Topic spoofs
Move Topic() into a NanoUI-specific ui_act proc Update to @YotaXP's latest JSON code. Return focus to the mapwindow if a key is pressed in a NanoUI.
This commit is contained in:
@@ -173,6 +173,19 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
//DEFINITIONS FOR ASSET DATUMS START HERE.
|
||||
|
||||
|
||||
/datum/asset/simple/nanoui
|
||||
assets = list(
|
||||
"nanoui.lib.js" = 'nano/assets/nanoui.lib.js',
|
||||
"nanoui.main.js" = 'nano/assets/nanoui.main.js',
|
||||
"nanoui.templates.js" = 'nano/assets/nanoui.templates.js',
|
||||
"nanoui.lib.css" = 'nano/assets/nanoui.lib.css',
|
||||
"nanoui.common.css" = 'nano/assets/nanoui.common.css',
|
||||
"nanoui.generic.css" = 'nano/assets/nanoui.generic.css',
|
||||
"nanoui.nanotrasen.css" = 'nano/assets/nanoui.nanotrasen.css',
|
||||
"fontawesome-webfont.eot" = 'nano/assets/fontawesome-webfont.eot',
|
||||
"fontawesome-webfont.woff2" = 'nano/assets/fontawesome-webfont.woff2'
|
||||
)
|
||||
|
||||
/datum/asset/simple/pda
|
||||
assets = list(
|
||||
"pda_atmos.png" = 'icons/pda_icons/pda_atmos.png',
|
||||
@@ -219,18 +232,6 @@ You can set verify to TRUE if you want send() to sleep until the client has the
|
||||
"large_stamp-law.png" = 'icons/stamp_icons/large_stamp-law.png'
|
||||
)
|
||||
|
||||
/datum/asset/simple/nanoui
|
||||
assets = list(
|
||||
"nanoui.lib.js" = 'nano/assets/nanoui.lib.js',
|
||||
"nanoui.main.js" = 'nano/assets/nanoui.main.js',
|
||||
"nanoui.templates.js" = 'nano/assets/nanoui.templates.js',
|
||||
"nanoui.lib.css" = 'nano/assets/nanoui.lib.css',
|
||||
"nanoui.common.css" = 'nano/assets/nanoui.common.css',
|
||||
"nanoui.generic.css" = 'nano/assets/nanoui.generic.css',
|
||||
"nanoui.nanotrasen.css" = 'nano/assets/nanoui.nanotrasen.css',
|
||||
"fontawesome-webfont.eot" = 'nano/assets/fontawesome-webfont.eot',
|
||||
"fontawesome-webfont.woff2" = 'nano/assets/fontawesome-webfont.woff2'
|
||||
)
|
||||
|
||||
//Registers HTML Interface assets.
|
||||
/datum/asset/HTML_interface/register()
|
||||
|
||||
+201
-2
@@ -1,6 +1,14 @@
|
||||
/var/datum/jsonHelper/_jsonHelper = new // Solely as a namespace for procs.
|
||||
/* Usage:
|
||||
JSON.stringify(obj) - Converts lists and values into a JSON string.
|
||||
JSON.parse(json) - Converts a JSON string into lists and values.
|
||||
*/
|
||||
|
||||
/var/datum/jsonHelper/JSON = new // A namespace for procs.
|
||||
|
||||
// ************************************ WRITER ************************************
|
||||
/datum/jsonHelper/proc/stringify(value)
|
||||
return list2text(WriteValue(list(), value))
|
||||
|
||||
/datum/jsonHelper/proc/WriteValue(list/json, value)
|
||||
. = json
|
||||
if(isnum(value))
|
||||
@@ -82,4 +90,195 @@
|
||||
#undef Either
|
||||
#undef CannotBeFlat
|
||||
#undef CannotBeAssoc
|
||||
#undef BadList
|
||||
#undef BadList
|
||||
|
||||
// ************************************ READER ************************************
|
||||
#define aBackspace 0x08
|
||||
#define aTab 0x09
|
||||
#define aLineBreak 0x0A
|
||||
#define aVertTab 0x0B
|
||||
#define aFormFeed 0x0C
|
||||
#define aCarriageReturn 0x0D
|
||||
#define aSpace 0x20
|
||||
#define aZero 0x30
|
||||
#define aNonBreakSpace 0xA0
|
||||
|
||||
#define Advance if(++readPos > jsonLen) { curAscii = 0; curChar = "" } else { curAscii = text2ascii(json, readPos); curChar = ascii2text(curAscii) } // Deal with it.
|
||||
#define SkipWhitespace while(curAscii in whitespace) Advance
|
||||
#define AdvanceWS Advance; SkipWhitespace
|
||||
|
||||
/datum/jsonHelper/var
|
||||
readPos
|
||||
jsonLen
|
||||
json
|
||||
curAscii
|
||||
curChar
|
||||
static/list/whitespace = list(aTab, aLineBreak, aVertTab, aFormFeed, aCarriageReturn, aSpace, aNonBreakSpace)
|
||||
|
||||
/datum/jsonHelper/proc/parse(json)
|
||||
readPos = 0
|
||||
jsonLen = length(json)
|
||||
src.json = json
|
||||
curAscii = 0
|
||||
curChar = ""
|
||||
AdvanceWS
|
||||
var/value = ParseValue()
|
||||
if(readPos < jsonLen)
|
||||
throw EXCEPTION("Expected: End of JSON")
|
||||
return value
|
||||
|
||||
/datum/jsonHelper/proc/ParseValue()
|
||||
if(curChar == "\"")
|
||||
return ParseString()
|
||||
else if(curChar == "-" || (curAscii >= aZero && curAscii <= aZero + 9))
|
||||
return ParseNumber()
|
||||
else if(curChar == "{")
|
||||
return ParseObject()
|
||||
else if(curChar == "\[")
|
||||
return ParseArray()
|
||||
else if(curChar == "t")
|
||||
if(copytext(json, readPos, readPos+4) == "true")
|
||||
readPos += 3
|
||||
AdvanceWS
|
||||
return TRUE
|
||||
else
|
||||
throw EXCEPTION("Expected: 'true'")
|
||||
else if(curChar == "f")
|
||||
if(copytext(json, readPos, readPos+5) == "false")
|
||||
readPos += 4
|
||||
AdvanceWS
|
||||
return FALSE
|
||||
else
|
||||
throw EXCEPTION("Expected: 'false'")
|
||||
else if(curChar == "n")
|
||||
if(copytext(json, readPos, readPos+4) == "null")
|
||||
readPos += 3
|
||||
AdvanceWS
|
||||
return null
|
||||
else
|
||||
throw EXCEPTION("Expected: 'null'")
|
||||
else if(curChar == "")
|
||||
throw EXCEPTION("Unexpected: End of JSON")
|
||||
else
|
||||
throw EXCEPTION("Unexpected: '[curChar]'")
|
||||
|
||||
|
||||
|
||||
/datum/jsonHelper/proc/ParseString()
|
||||
ASSERT(curChar == "\"")
|
||||
Advance
|
||||
var/list/chars = list()
|
||||
while(readPos <= jsonLen)
|
||||
if(curChar == "\"")
|
||||
AdvanceWS
|
||||
return list2text(chars)
|
||||
else if(curChar == "\\")
|
||||
Advance
|
||||
switch(curChar)
|
||||
if("\"", "\\", "/")
|
||||
chars += ascii2text(curAscii)
|
||||
if("b")
|
||||
chars += ascii2text(aBackspace)
|
||||
if("f")
|
||||
chars += ascii2text(aFormFeed)
|
||||
if("n")
|
||||
chars += "\n"
|
||||
if("r")
|
||||
chars += ascii2text(aCarriageReturn) // Should we ignore these?
|
||||
if("t")
|
||||
chars += "\t"
|
||||
if("u")
|
||||
throw EXCEPTION("JSON \\uXXXX escape sequence not supported")
|
||||
else
|
||||
throw EXCEPTION("Invalid escape sequence")
|
||||
Advance
|
||||
else
|
||||
chars += ascii2text(curAscii)
|
||||
Advance
|
||||
throw EXCEPTION("Unterminated string")
|
||||
|
||||
/datum/jsonHelper/proc/ParseNumber()
|
||||
var/firstPos = readPos
|
||||
if(curChar == "-")
|
||||
Advance
|
||||
if(curAscii >= aZero + 1 && curAscii <= aZero + 9)
|
||||
do
|
||||
Advance
|
||||
while(curAscii >= aZero && curAscii <= aZero + 9)
|
||||
else if(curAscii == aZero)
|
||||
Advance
|
||||
else
|
||||
throw EXCEPTION("Expected: digit")
|
||||
|
||||
if(curChar == ".")
|
||||
Advance
|
||||
var/found = FALSE
|
||||
while(curAscii >= aZero && curAscii <= aZero + 9)
|
||||
found = TRUE
|
||||
Advance
|
||||
if(!found)
|
||||
throw EXCEPTION("Expected: digit")
|
||||
|
||||
if(curChar == "E" || curChar == "e")
|
||||
Advance
|
||||
var/found = FALSE
|
||||
if(curChar == "-")
|
||||
Advance
|
||||
else if(curChar == "+")
|
||||
Advance
|
||||
while(curAscii >= aZero && curAscii <= aZero + 9)
|
||||
found = TRUE
|
||||
Advance
|
||||
if(!found)
|
||||
throw EXCEPTION("Expected: digit")
|
||||
|
||||
SkipWhitespace
|
||||
return text2num(copytext(json, firstPos, readPos))
|
||||
|
||||
/datum/jsonHelper/proc/ParseObject()
|
||||
ASSERT(curChar == "{")
|
||||
var/list/object = list()
|
||||
AdvanceWS
|
||||
while(curChar == "\"")
|
||||
var/key = ParseString()
|
||||
if(curChar != ":")
|
||||
throw EXCEPTION("Expected: ':'")
|
||||
AdvanceWS
|
||||
object[key] = ParseValue()
|
||||
if(curChar == ",")
|
||||
AdvanceWS
|
||||
else
|
||||
break
|
||||
if(curChar != "}")
|
||||
throw EXCEPTION("Expected: string or '}'")
|
||||
AdvanceWS
|
||||
return object
|
||||
|
||||
/datum/jsonHelper/proc/ParseArray()
|
||||
ASSERT(curChar == "\[")
|
||||
var/list/array = list()
|
||||
AdvanceWS
|
||||
while(curChar != "]")
|
||||
array += list(ParseValue()) // Wrapped in a list in case ParseValue() returns a list.
|
||||
if(curChar == ",")
|
||||
AdvanceWS
|
||||
else
|
||||
break
|
||||
if(curChar != "]")
|
||||
throw EXCEPTION("Expected: ']'")
|
||||
AdvanceWS
|
||||
return array
|
||||
|
||||
#undef aBackspace
|
||||
#undef aTab
|
||||
#undef aLineBreak
|
||||
#undef aVertTab
|
||||
#undef aFormFeed
|
||||
#undef aCarriageReturn
|
||||
#undef aSpace
|
||||
#undef aZero
|
||||
#undef aNonBreakSpace
|
||||
|
||||
#undef Advance
|
||||
#undef SkipWhitespace
|
||||
#undef AdvanceWS
|
||||
@@ -27,6 +27,20 @@
|
||||
datum/topic_state/state = default_state)
|
||||
return -1 // Sorta implemented.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
* Called on a NanoUI when the UI receieves a href.
|
||||
* Think of this as Topic().
|
||||
*
|
||||
* required action string The action/button that has been invoked by the user.
|
||||
* required params list A list of parameters attached to the button.
|
||||
*
|
||||
* return bool If the UI should be updated or not.
|
||||
**/
|
||||
/atom/movable/proc/ui_act(action, list/params)
|
||||
return // Not implemented.
|
||||
|
||||
/**
|
||||
* public
|
||||
*
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
|
||||
// Generate JSON.
|
||||
var/list/send_data = get_send_data(initial_data)
|
||||
var/initial_data_json = replacetext(writeJson(send_data), "'", "\\'")
|
||||
var/initial_data_json = replacetext(JSON.stringify(send_data), "'", "\\'")
|
||||
|
||||
// Generate the HTML document.
|
||||
return {"
|
||||
@@ -318,7 +318,7 @@
|
||||
var/list/send_data = get_send_data(data) // Get the data to send.
|
||||
|
||||
// Send the new data to the recieveUpdate() Javascript function.
|
||||
user << output(list2params(list(writeJson(send_data))), "[window_id].browser:receiveUpdate")
|
||||
user << output(list2params(list(JSON.stringify(send_data))), "[window_id].browser:receiveUpdate")
|
||||
|
||||
/**
|
||||
* private
|
||||
@@ -332,7 +332,10 @@
|
||||
if (status != NANO_INTERACTIVE || user != usr)
|
||||
return // If UI is not interactive or usr calling Topic is not the UI user.
|
||||
|
||||
var/update = src_object.Topic(href, href_list, 0, state) // Call Topic() on the src_object.
|
||||
var/action = href_list["nano"] // Pull the action out.
|
||||
href_list -= "nano"
|
||||
|
||||
var/update = src_object.ui_act(action, href_list, state) // Call Topic() on the src_object.
|
||||
|
||||
if (src_object && update)
|
||||
SSnano.update_uis(src_object) // If we have a src_object and its Topic() told us to update.
|
||||
|
||||
+10
-32
@@ -721,22 +721,8 @@
|
||||
|
||||
|
||||
/obj/machinery/power/apc/proc/can_use(mob/user, loud = 0) //used by attack_hand() and Topic()
|
||||
if (IsAdminGhost(user))
|
||||
if(IsAdminGhost(user))
|
||||
return 1
|
||||
if (user.stat)
|
||||
user << "<span class='warning'>You must be conscious to use [src]!</span>"
|
||||
return 0
|
||||
if(!user.client)
|
||||
return 0
|
||||
if(!user.IsAdvancedToolUser())
|
||||
user << "<span class='warning'>You don't have the dexterity to use [src]!</span>"
|
||||
return 0
|
||||
if(user.restrained())
|
||||
user << "<span class='warning'>You must have free hands to use [src].</span>"
|
||||
return 0
|
||||
if(user.lying)
|
||||
user << "<span class='warning'>You must stand to use [src]!</span>"
|
||||
return 0
|
||||
if(user.has_unlimited_silicon_privilege)
|
||||
var/mob/living/silicon/ai/AI = user
|
||||
var/mob/living/silicon/robot/robot = user
|
||||
@@ -754,24 +740,16 @@
|
||||
else
|
||||
if ((!in_range(src, user) || !istype(src.loc, /turf)))
|
||||
return 0
|
||||
|
||||
var/mob/living/carbon/human/H = user
|
||||
if (istype(H))
|
||||
if(H.getBrainLoss() >= 60)
|
||||
H.visible_message("[H] stares cluelessly at [src] and drools.")
|
||||
return 0
|
||||
else if(prob(H.getBrainLoss()))
|
||||
user << "<span class='danger'>You momentarily forget how to use [src].</span>"
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/obj/machinery/power/apc/Topic(href, href_list)
|
||||
/obj/machinery/power/apc/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
if(!can_use(usr, 1))
|
||||
return
|
||||
|
||||
switch(href_list["nano"])
|
||||
switch(action)
|
||||
if("lock")
|
||||
coverlocked = !coverlocked
|
||||
if ("breaker")
|
||||
@@ -782,18 +760,18 @@
|
||||
charging = 0
|
||||
update_icon()
|
||||
if("channel")
|
||||
if (href_list["eqp"])
|
||||
var/val = text2num(href_list["eqp"])
|
||||
if (params["eqp"])
|
||||
var/val = text2num(params["eqp"])
|
||||
equipment = setsubsystem(val)
|
||||
update_icon()
|
||||
update()
|
||||
else if (href_list["lgt"])
|
||||
var/val = text2num(href_list["lgt"])
|
||||
else if (params["lgt"])
|
||||
var/val = text2num(params["lgt"])
|
||||
lighting = setsubsystem(val)
|
||||
update_icon()
|
||||
update()
|
||||
else if (href_list["env"])
|
||||
var/val = text2num(href_list["env"])
|
||||
else if (params["env"])
|
||||
var/val = text2num(params["env"])
|
||||
environ = setsubsystem(val)
|
||||
update_icon()
|
||||
update()
|
||||
|
||||
@@ -350,11 +350,11 @@
|
||||
)
|
||||
return data
|
||||
|
||||
/obj/machinery/power/smes/Topic(href, href_list)
|
||||
/obj/machinery/power/smes/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(href_list["nano"])
|
||||
switch(action)
|
||||
if("tryinput")
|
||||
input_attempt = !input_attempt
|
||||
log_smes(usr.ckey)
|
||||
@@ -364,7 +364,7 @@
|
||||
log_smes(usr.ckey)
|
||||
update_icon()
|
||||
if("input")
|
||||
switch(href_list["set"])
|
||||
switch(params["set"])
|
||||
if("custom")
|
||||
var/custom = input(usr, "What rate would you like this SMES to attempt to charge at? Max is [input_level_max].") as null|num
|
||||
if(custom)
|
||||
@@ -380,7 +380,7 @@
|
||||
input_level = Clamp(input_level, 0, input_level_max)
|
||||
log_smes(usr.ckey)
|
||||
if("output")
|
||||
switch(href_list["set"])
|
||||
switch(params["set"])
|
||||
if("custom")
|
||||
var/custom = input(usr, "What rate would you like this SMES to attempt to output at? Max is [output_level_max].") as null|num
|
||||
if(custom)
|
||||
|
||||
@@ -437,24 +437,24 @@
|
||||
targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it
|
||||
nexttime += 36000/abs(trackrate) //reset the counter for the next 1°
|
||||
|
||||
/obj/machinery/power/solar_control/Topic(href, href_list)
|
||||
/obj/machinery/power/solar_control/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(href_list["nano"])
|
||||
switch(action)
|
||||
if("control")
|
||||
if(href_list["cdir"])
|
||||
src.cdir = dd_range(0,359,(360+src.cdir+text2num(href_list["cdir"]))%360)
|
||||
if(params["cdir"])
|
||||
src.cdir = dd_range(0,359,(360+src.cdir+text2num(params["cdir"]))%360)
|
||||
src.targetdir = src.cdir
|
||||
if(track == 2) //manual update, so losing auto-tracking
|
||||
track = 0
|
||||
spawn(1)
|
||||
set_panels(cdir)
|
||||
if(href_list["tdir"])
|
||||
src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(href_list["tdir"]))
|
||||
if(params["tdir"])
|
||||
src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(params["tdir"]))
|
||||
if(src.trackrate) nexttime = world.time + 36000/abs(trackrate)
|
||||
if("tracking")
|
||||
track = text2num(href_list["mode"])
|
||||
track = text2num(params["mode"])
|
||||
if(track == 2)
|
||||
if(connected_tracker)
|
||||
connected_tracker.set_angle(SSsun.angle)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
icon_state = "dispenser"
|
||||
use_power = 1
|
||||
idle_power_usage = 40
|
||||
interact_offline = 1
|
||||
var/energy = 100
|
||||
var/max_energy = 100
|
||||
var/amount = 30
|
||||
@@ -122,27 +123,27 @@
|
||||
data["chemicals"] = chemicals
|
||||
return data
|
||||
|
||||
/obj/machinery/chem_dispenser/Topic(href, href_list)
|
||||
/obj/machinery/chem_dispenser/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(href_list["nano"])
|
||||
switch(action)
|
||||
if("amount")
|
||||
amount = round(text2num(href_list["set"]), 5) // round to nearest 5
|
||||
amount = round(text2num(params["set"]), 5) // round to nearest 5
|
||||
if (amount < 0) // Since the user can actually type the commands himself, some sanity checking
|
||||
amount = 0
|
||||
if (amount > 100)
|
||||
amount = 100
|
||||
if("dispense")
|
||||
if(beaker && dispensable_reagents.Find(href_list["reagent"]))
|
||||
if(beaker && dispensable_reagents.Find(params["reagent"]))
|
||||
var/datum/reagents/R = beaker.reagents
|
||||
var/space = R.maximum_volume - R.total_volume
|
||||
|
||||
R.add_reagent(href_list["reagent"], min(amount, energy * 10, space))
|
||||
R.add_reagent(params["reagent"], min(amount, energy * 10, space))
|
||||
energy = max(energy - min(amount, energy * 10, space) / 10, 0)
|
||||
if("remove")
|
||||
if(beaker)
|
||||
var/amount = text2num(href_list["amount"])
|
||||
var/amount = text2num(params["amount"])
|
||||
if(isnum(amount) && (amount > 0) && (amount in beaker.possible_transfer_amounts))
|
||||
beaker.reagents.remove_all(amount)
|
||||
if("eject")
|
||||
@@ -150,7 +151,6 @@
|
||||
beaker.loc = loc
|
||||
beaker = null
|
||||
overlays.Cut()
|
||||
add_fingerprint(usr)
|
||||
return 1
|
||||
|
||||
/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params)
|
||||
|
||||
@@ -77,18 +77,17 @@
|
||||
return
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/chem_heater/Topic(href, href_list)
|
||||
/obj/machinery/chem_heater/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
|
||||
switch(href_list["nano"])
|
||||
switch(action)
|
||||
if("power")
|
||||
on = !on
|
||||
if("temperature")
|
||||
desired_temp = Clamp(input("Please input the target temperature", name) as num, 0, 1000)
|
||||
if("eject")
|
||||
eject_beaker()
|
||||
add_fingerprint(usr)
|
||||
return 1
|
||||
|
||||
/obj/machinery/chem_heater/interact(mob/user)
|
||||
|
||||
Reference in New Issue
Block a user