mirror of
https://github.com/goonstation/goonstation-2016.git
synced 2026-05-19 06:59:07 +01:00
dc4217b498
this is all very alpha please don't hate me too much if i fucked it up
656 lines
19 KiB
Plaintext
656 lines
19 KiB
Plaintext
//CONTENTS:
|
|
//Mainframe program parent type
|
|
//Mainframe user account datum
|
|
|
|
|
|
/*
|
|
* Mainframe Software
|
|
*/
|
|
|
|
/datum/computer/file/mainframe_program
|
|
name = "mainframe program"
|
|
extension = "MPG"
|
|
var/obj/machinery/networked/mainframe/master = null
|
|
var/executable = 1
|
|
var/needs_holder = 1
|
|
var/tmp/datum/computer/file/mainframe_program/parent_task = null
|
|
var/tmp/progid = 0
|
|
var/tmp/initialized = 0
|
|
var/tmp/datum/mainframe2_user_data/useracc = null
|
|
|
|
os
|
|
name = "Base OS"
|
|
size = 16
|
|
extension = "SYS"
|
|
executable = 0
|
|
var/tmp/setup_string = null
|
|
|
|
proc
|
|
//Called by the mainframe when a new terminal connection is made so as to alert the OS
|
|
new_connection(datum/terminal_connection/conn)
|
|
return
|
|
|
|
//Called by the mainframe upon termination of a connection, conn is deleted afterwards
|
|
closed_connection(datum/terminal_connection/conn)
|
|
return
|
|
|
|
//The data string (And, optionally, file) sent to us by a connected terminal (Identified by network ID in termid)
|
|
//Note: The passed file will be completely temporary and is deleted after the function returns.
|
|
term_input(var/data, var/termid, var/datum/computer/file/file)
|
|
if(!src.master || !data || !termid)
|
|
return 1
|
|
|
|
if(master.stat & (NOPOWER|BROKEN|MAINT))
|
|
return 1
|
|
|
|
if(src.needs_holder)
|
|
if(!(holder in src.master.contents))
|
|
return 1
|
|
|
|
if(!src.holder.root)
|
|
src.holder.root = new /datum/computer/folder
|
|
src.holder.root.holder = src
|
|
src.holder.root.name = "root"
|
|
|
|
return 0
|
|
|
|
//Called by the mainframe upon receipt of a ping_reply network signal. This does not a terminal_connection because we are not necessarily connected to the responding device.
|
|
ping_reply(var/senderid,var/sendertype)
|
|
return (!master || !senderid || !sendertype)
|
|
|
|
|
|
//Send a message to a connected terminal device (Using term_message)
|
|
message_term(var/message, var/termid, var/render=null)
|
|
|
|
if(!istype(master) || !message || !termid)
|
|
return 1
|
|
|
|
if(master.stat & (NOPOWER|BROKEN|MAINT))
|
|
return 1
|
|
|
|
if(src.needs_holder)
|
|
if (!holder || !(holder in src.master.contents))
|
|
return 1
|
|
|
|
if(!src.holder.root)
|
|
src.holder.root = new /datum/computer/folder
|
|
src.holder.root.holder = src
|
|
src.holder.root.name = "root"
|
|
|
|
spawn(1)
|
|
src.master.post_status(termid, "command", "term_message", "data", message, "render", render)
|
|
return 0
|
|
|
|
//Send a file and message to a connected terminal device (Using term_file)
|
|
file_term(var/datum/computer/file/file, var/termid, var/exdata)
|
|
|
|
|
|
if(!istype(master) || !istype(file) || !termid)
|
|
return 1
|
|
|
|
if(master.stat & (NOPOWER|BROKEN|MAINT))
|
|
return 1
|
|
|
|
if(src.needs_holder)
|
|
if (!holder || !(holder in src.master.contents))
|
|
return 1
|
|
|
|
if(!src.holder.root)
|
|
src.holder.root = new /datum/computer/folder
|
|
src.holder.root.holder = src
|
|
src.holder.root.name = "root"
|
|
|
|
spawn(1)
|
|
src.master.post_file(termid, "data", exdata, file)
|
|
return 0
|
|
|
|
/*
|
|
* The parse_* functions parse the filesystem to locate a computer datum of some sort (Be it directory, file, or either)
|
|
* with a supplied filepath string, origin point, and user. Create_if_missing determines whether intermediary folders in the path should be created if not present.
|
|
* Notes: A '/' prefixing the filepath will cause the search to start at the origin point
|
|
* '.' refers to the current folder in the search, while '..' refers to its parent folder
|
|
* Results will be filtered based on the supplied user's permissions -- If they do not have permission to read a file, they will not find said file.
|
|
* If a user datum is not supplied, it is assumed that the system made the call desiring full access.
|
|
*/
|
|
|
|
parse_directory(string, var/datum/computer/folder/origin, var/create_if_missing, var/datum/mainframe2_user_data/user)
|
|
if(!string)
|
|
return null
|
|
|
|
var/datum/computer/folder/current = origin
|
|
|
|
if(!origin)
|
|
origin = src.holder.root
|
|
|
|
if(dd_hasprefix(string , "/")) //if it starts with a /
|
|
if (string == "/")
|
|
return origin
|
|
current = origin
|
|
string = copytext(string,2)
|
|
|
|
var/list/sort1 = list()
|
|
sort1 = splittext(string,"/")
|
|
if (sort1.len && !sort1[sort1.len])
|
|
sort1.len--
|
|
|
|
while(current)
|
|
|
|
if(!sort1.len)
|
|
return current
|
|
|
|
if(sort1[1] == "..")
|
|
if (current == origin)
|
|
return null
|
|
current = current.holding_folder
|
|
sort1 -= sort1[1]
|
|
continue
|
|
else if (sort1[1] == ".")
|
|
sort1 -= sort1[1]
|
|
continue
|
|
|
|
else if (!sort1[1] && !create_if_missing)
|
|
return current
|
|
|
|
var/new_current = 0
|
|
for(var/datum/computer/folder/F in current.contents)
|
|
if(ckey(F.name) == ckey(sort1[1]) && (!user || check_read_permission(F, user)))
|
|
sort1 -= sort1[1]
|
|
current = F
|
|
new_current = 1
|
|
break
|
|
|
|
if(!new_current)
|
|
if (!create_if_missing)
|
|
return null
|
|
|
|
var/datum/computer/folder/F = new /datum/computer/folder( )
|
|
F.name = sort1[1]
|
|
|
|
if (is_name_invalid(F.name))
|
|
//qdel(F)
|
|
F.dispose()
|
|
return null
|
|
|
|
. = current.add_file(F)
|
|
if (!.)
|
|
if (F)
|
|
F.dispose()
|
|
return null
|
|
|
|
else if (istype(., /datum/computer/folder))
|
|
F = .
|
|
|
|
sort1 -= sort1[1]
|
|
current = F
|
|
new_current = 1
|
|
|
|
return null
|
|
|
|
//Find a file at the end of a given dirstring.
|
|
parse_file_directory(string, var/datum/computer/folder/origin, var/create_if_missing, var/datum/mainframe2_user_data/user)
|
|
if(!string)
|
|
return null
|
|
|
|
var/datum/computer/folder/current = origin
|
|
|
|
if(!origin)
|
|
origin = src.holder.root
|
|
|
|
if(dd_hasprefix(string , "/")) //if it starts with a /
|
|
current = origin
|
|
string = copytext(string,2)
|
|
|
|
var/list/sort1 = list()
|
|
sort1 = splittext(string,"/")
|
|
|
|
var/file_name = sort1[sort1.len]
|
|
if(!file_name)
|
|
return null
|
|
|
|
sort1 -= sort1[sort1.len]
|
|
|
|
while(current)
|
|
|
|
if(!sort1.len)
|
|
var/datum/computer/file/check = get_file_name(file_name, current, user)
|
|
if(check && istype(check))
|
|
return check
|
|
else
|
|
return null
|
|
|
|
if(sort1[1] == "..")
|
|
if (current == origin)
|
|
return null
|
|
current = current.holding_folder
|
|
sort1 -= sort1[1]
|
|
continue
|
|
else if (sort1[1] == ".")
|
|
sort1 -= sort1[1]
|
|
continue
|
|
|
|
var/new_current = 0
|
|
for(var/datum/computer/folder/F in current.contents)
|
|
if(ckey(F.name) == ckey(sort1[1]) && (!user || check_read_permission(F, user)))
|
|
sort1 -= sort1[1]
|
|
current = F
|
|
new_current = 1
|
|
break
|
|
|
|
if(!new_current)
|
|
if (!create_if_missing)
|
|
return null
|
|
|
|
var/datum/computer/folder/F = new /datum/computer/folder
|
|
F.name = sort1[1]
|
|
|
|
if (is_name_invalid(F.name) || !current.add_file(F))
|
|
//qdel(F)
|
|
F.dispose()
|
|
return null
|
|
|
|
sort1 -= sort1[1]
|
|
current = F
|
|
new_current = 1
|
|
|
|
return null
|
|
|
|
//If we are willing to accept either a file or folder as the return value.
|
|
parse_datum_directory(string, var/datum/computer/folder/origin, var/create_if_missing, var/datum/mainframe2_user_data/user)
|
|
if(!string)
|
|
return null
|
|
|
|
var/datum/computer/folder/current = origin
|
|
|
|
if(!origin)
|
|
origin = src.holder.root
|
|
|
|
if(dd_hasprefix(string , "/")) //if it starts with a /
|
|
if (string == "/")
|
|
return origin
|
|
current = origin
|
|
string = copytext(string,2)
|
|
|
|
var/list/sort1 = list()
|
|
sort1 = splittext(string,"/")
|
|
|
|
var/datum_name = sort1[sort1.len]
|
|
if(!datum_name)
|
|
//return null
|
|
sort1.len--
|
|
while (sort1.len)
|
|
datum_name = sort1[sort1.len]
|
|
if (!datum_name)
|
|
sort1.len--
|
|
continue
|
|
break
|
|
|
|
sort1.len = max(0, sort1.len-1)
|
|
|
|
while(current)
|
|
|
|
if(!sort1.len)
|
|
switch(datum_name)
|
|
if ("..")
|
|
if (current == origin)
|
|
return null
|
|
return current.holding_folder
|
|
if (".")
|
|
return current
|
|
var/datum/computer/check = get_computer_datum(datum_name, current, user)
|
|
if(check && istype(check))
|
|
return check
|
|
else
|
|
return null
|
|
|
|
if(sort1[1] == "..")
|
|
current = current.holding_folder
|
|
sort1 -= sort1[1]
|
|
continue
|
|
else if (sort1[1] == ".")
|
|
sort1 -= sort1[1]
|
|
continue
|
|
|
|
var/new_current = 0
|
|
for(var/datum/computer/folder/F in current.contents)
|
|
if(ckey(F.name) == ckey(sort1[1]) && (!user || check_read_permission(F, user)))
|
|
sort1 -= sort1[1]
|
|
current = F
|
|
new_current = 1
|
|
break
|
|
|
|
if(!new_current)
|
|
if (!create_if_missing)
|
|
return null
|
|
|
|
var/datum/computer/folder/F = new /datum/computer/folder
|
|
F.name = sort1[1]
|
|
|
|
if (is_name_invalid(F.name) || !current.add_file(F))
|
|
//qdel(F)
|
|
F.dispose()
|
|
return null
|
|
|
|
sort1 -= sort1[1]
|
|
current = F
|
|
new_current = 1
|
|
|
|
return null
|
|
|
|
New(obj/holding as obj)
|
|
if(holding)
|
|
src.holder = holding
|
|
|
|
if(istype(src.holder.loc,/obj/machinery/networked/mainframe))
|
|
src.master = src.holder.loc
|
|
|
|
disposing()
|
|
if(master && (src in master.processing))
|
|
master.processing[src] = null
|
|
master = null
|
|
..()
|
|
|
|
asText()
|
|
return initialized ? "[progid]" : ..()
|
|
|
|
proc
|
|
input_text(var/text)
|
|
if(!istype(src.master) || !text || !useracc)
|
|
return 1
|
|
|
|
if(src.needs_holder)
|
|
if(!istype(holder))
|
|
return 1
|
|
|
|
if(!(holder in src.master.contents))
|
|
return 1
|
|
|
|
if(master.stat & (NOPOWER|BROKEN|MAINT))
|
|
return 1
|
|
|
|
if(src.needs_holder && !src.holder.root)
|
|
src.holder.root = new /datum/computer/folder
|
|
src.holder.root.holder = src
|
|
src.holder.root.name = "root"
|
|
|
|
return 0
|
|
|
|
initialize(var/initparams) //Called when a program starts running.
|
|
if(src.initialized || !master)
|
|
return 1
|
|
|
|
src.initialized = 1
|
|
return 0
|
|
|
|
//Note: If you want your application to end inteionally, send the OS an "exit" signal.
|
|
//Use the mainframe_prog_exit macro. The program will not exit otherwise, even if nothing bothers to send it more input
|
|
handle_quit()
|
|
if(src.master)
|
|
src.master.unload_program(src)
|
|
return
|
|
|
|
process()
|
|
if (!istype(master))
|
|
return 1
|
|
|
|
if (src.needs_holder)
|
|
if (!istype(holder))
|
|
return 1
|
|
|
|
if(!(holder in src.master.contents))
|
|
master.processing.Remove(src)
|
|
return 1
|
|
|
|
if(!src.holder.root)
|
|
src.holder.root = new /datum/computer/folder
|
|
src.holder.root.holder = src
|
|
src.holder.root.name = "root"
|
|
|
|
return 0
|
|
|
|
parse_string(string, var/list/replaceList = null)
|
|
var/list/sorted = list()
|
|
sorted = command2list(string, " ", replaceList)
|
|
if (!sorted.len) sorted.len++
|
|
return sorted
|
|
|
|
//Find a folder with a given name
|
|
get_folder_name(string, var/datum/computer/folder/check_folder, var/datum/mainframe2_user_data/user)
|
|
if(!string || !istype(check_folder))
|
|
return null
|
|
|
|
var/datum/computer/taken = null
|
|
for(var/datum/computer/folder/F in check_folder.contents)
|
|
if(ckey(string) == ckey(F.name) && (!user || check_read_permission(F, user)))
|
|
taken = F
|
|
break
|
|
|
|
return taken
|
|
|
|
//Find a file with a given name
|
|
get_file_name(string, var/datum/computer/folder/check_folder, var/datum/mainframe2_user_data/user)
|
|
if(!string || !istype(check_folder))
|
|
return null
|
|
|
|
var/datum/computer/taken = null
|
|
for(var/datum/computer/file/F in check_folder.contents)
|
|
if(ckey(string) == ckey(F.name) && (!user || check_read_permission(F, user)))
|
|
taken = F
|
|
break
|
|
|
|
return taken
|
|
|
|
//Just find any computer datum with this name, gosh
|
|
get_computer_datum(string, var/datum/computer/folder/check_folder, var/datum/mainframe2_user_data/user)
|
|
if(!string || !istype(check_folder))
|
|
return null
|
|
|
|
var/datum/computer/taken = null
|
|
for(var/datum/computer/C in check_folder.contents)
|
|
if(ckey(string) == ckey(C.name) && (!user || check_read_permission(C, user)))
|
|
taken = C
|
|
break
|
|
|
|
return taken
|
|
|
|
is_name_invalid(string) //Check if a filename is invalid somehow
|
|
if(!string)
|
|
return 1
|
|
//ckeyEx because it allows for - and _ and we love those!!
|
|
if(lowertext(ckeyEx(string)) != replacetext(lowertext(string), " ", null))
|
|
return 1
|
|
|
|
if(findtext(string, "/"))
|
|
return 1
|
|
|
|
|
|
return 0
|
|
|
|
//Pass an output string to the user terminal, with optional render value.
|
|
//Notes: The string is not immediately set to the user! It is instead passed by signal to the parent task of the program.
|
|
//This allows for piping operations, etc. It is the duty of the OS to actually tranmit the information to the terminal.
|
|
message_user(var/msg, var/render, var/file)
|
|
if (!useracc)
|
|
return ESIG_NOTARGET
|
|
|
|
if (parent_task)
|
|
if (render)
|
|
return signal_program(parent_task.progid, list("command"=DWAINE_COMMAND_MSG_TERM, "data" = msg, "term" = useracc.user_id, "render" = render), file )
|
|
else
|
|
return signal_program(parent_task.progid, list("command"=DWAINE_COMMAND_MSG_TERM, "data" = msg, "term" = useracc.user_id), file )
|
|
|
|
return ESIG_GENERIC
|
|
|
|
read_user_field(var/field)
|
|
if (!useracc || (!istype(useracc.user_file) && !useracc.reload_user_file()))
|
|
return null
|
|
|
|
return useracc.user_file.fields[field]
|
|
|
|
write_user_field(var/field, var/data)
|
|
if (!useracc || !field || (!istype(useracc.user_file) && !useracc.reload_user_file()) || !useracc.user_file.fields)
|
|
return 0
|
|
|
|
useracc.user_file.fields[field] = data
|
|
return 1
|
|
|
|
signal_program(var/progid, var/list/data, var/datum/computer/file/file)
|
|
if(!data || !master)
|
|
return 1
|
|
|
|
if(useracc && useracc.user_file && ("id" in useracc.user_file.fields))
|
|
data["user"] = useracc.user_file.fields["id"]
|
|
|
|
return master.relay_progsignal(src, progid, data, file)
|
|
|
|
receive_progsignal(var/sendid, var/list/data, var/datum/computer/file/file)
|
|
return (!src.master || !(src in master.processing))
|
|
|
|
|
|
unloaded()
|
|
return
|
|
|
|
check_read_permission(var/datum/computer/cdatum, var/datum/mainframe2_user_data/usdat)
|
|
if (!usdat)
|
|
return 0
|
|
|
|
if (istype(cdatum, /datum/computer/folder/link) && cdatum:target)
|
|
cdatum = cdatum:target
|
|
|
|
if (!istype(cdatum) || !cdatum.metadata)
|
|
return 0
|
|
|
|
var/permissions = COMP_ALLACC
|
|
if (isnum(cdatum.metadata["permission"]))
|
|
permissions = cdatum.metadata["permission"]
|
|
|
|
if (istype(usdat.user_file) || usdat.reload_user_file())
|
|
if (!usdat.user_file.fields)
|
|
usdat.user_file.fields = list()
|
|
|
|
if (usdat.user_file.fields["group"] == 0) //Sysop usergroup
|
|
return 1
|
|
|
|
if (cdatum.metadata["owner"] && (usdat.user_file.fields["name"] == cdatum.metadata["owner"]) && (permissions & COMP_ROWNER))
|
|
return 1
|
|
|
|
if (cdatum.metadata["group"] && (usdat.user_file.fields["group"] == cdatum.metadata["group"]) && (permissions & COMP_RGROUP))
|
|
return 1
|
|
|
|
return (permissions & COMP_ROTHER)
|
|
|
|
check_write_permission(var/datum/computer/cdatum, var/datum/mainframe2_user_data/usdat)
|
|
if (!cdatum || !usdat)
|
|
return 0
|
|
|
|
var/permissions = COMP_ALLACC
|
|
|
|
if (istype(cdatum, /datum/computer/folder/link) && cdatum:target)
|
|
cdatum = cdatum:target
|
|
|
|
if (!istype(cdatum) || !cdatum.metadata)
|
|
return 0
|
|
|
|
if (istype(cdatum.metadata, /list) && isnum(cdatum.metadata["permission"]))
|
|
permissions = cdatum.metadata["permission"]
|
|
|
|
if (istype(usdat.user_file) || usdat.reload_user_file())
|
|
|
|
if (usdat.user_file.fields["group"] == 0) //Sysop usergroup can ~do anything~
|
|
return 1
|
|
|
|
if (cdatum.metadata["owner"] && (usdat.user_file.fields["name"] == cdatum.metadata["owner"]) && (permissions & COMP_WOWNER))
|
|
return 1
|
|
|
|
if (cdatum.metadata["group"] && (usdat.user_file.fields["group"] == cdatum.metadata["group"]) && (permissions & COMP_WGROUP))
|
|
return 1
|
|
|
|
return (permissions & COMP_WOTHER)
|
|
|
|
return 0
|
|
|
|
check_mode_permission(var/datum/computer/cdatum, var/datum/mainframe2_user_data/usdat)
|
|
if (!cdatum || !usdat)
|
|
return 0
|
|
|
|
if (istype(cdatum, /datum/computer/folder/link) && cdatum:target)
|
|
cdatum = cdatum:target
|
|
|
|
if (!istype(cdatum) || !cdatum.metadata)
|
|
return 0
|
|
|
|
var/permissions = COMP_ALLACC
|
|
if (istype(cdatum.metadata, /list) && isnum(cdatum.metadata["permission"]))
|
|
permissions = cdatum.metadata["permission"]
|
|
|
|
if (istype(usdat.user_file) || usdat.reload_user_file())
|
|
|
|
if (usdat.user_file.fields["group"] == 0) //Sysop usergroup can ~do anything~
|
|
return 1
|
|
|
|
if (cdatum.metadata["owner"] && (usdat.user_file.fields["name"] == cdatum.metadata["owner"]) && (permissions & COMP_DOWNER && permissions & COMP_WOWNER) )
|
|
return 1
|
|
|
|
if (cdatum.metadata["group"] && (usdat.user_file.fields["group"] == cdatum.metadata["group"]) && (permissions & COMP_DGROUP && permissions & COMP_WGROUP) )
|
|
return 1
|
|
|
|
return ((permissions & COMP_DOTHER) && (permissions & COMP_WOTHER))
|
|
|
|
return 0
|
|
|
|
//Command2list is a modified version of dd_text2list() designed to eat empty list entries generated by superfluous whitespace.
|
|
//It also can insert shell alias/variables if provided with a replacement value list.
|
|
#define QUOTE_SYMBOL "\""
|
|
#define QUOTE_SYMBOL_LENGTH 1
|
|
/proc/command2list(text, separator, list/replaceList, list/substitution_feedback_thing)
|
|
var/textlength = length(text)
|
|
var/separatorlength = length(separator)
|
|
var/list/textList = new()
|
|
var/searchPosition = 1
|
|
var/findPosition = 1
|
|
var/buggyText
|
|
|
|
|
|
//substitution_feedback_thing = list() //debug
|
|
while(1)
|
|
findPosition = findtext(text, separator, searchPosition, 0)
|
|
var/quotePoint = findtext(text, QUOTE_SYMBOL, searchPosition, findPosition)
|
|
if (quotePoint)
|
|
text = copytext(text, 1, quotePoint) + copytext(text, quotePoint + QUOTE_SYMBOL_LENGTH, 0)
|
|
var/quotePointEnd = findtext(text, QUOTE_SYMBOL, quotePoint, 0)
|
|
buggyText = copytext(text, searchPosition, quotePointEnd)
|
|
findPosition = quotePointEnd+QUOTE_SYMBOL_LENGTH
|
|
else
|
|
var/subStartPoint = findtext(text, "$(", searchPosition, findPosition)
|
|
if (substitution_feedback_thing && subStartPoint)
|
|
var/subEndPoint = findtext(text, ")", subStartPoint)
|
|
substitution_feedback_thing[++substitution_feedback_thing.len] = copytext(text, subStartPoint+2, subEndPoint)
|
|
|
|
text = copytext(text, 1, subStartPoint) + "_sub[substitution_feedback_thing.len]" + copytext(text, subEndPoint ? subEndPoint + 1 : 0)
|
|
//boutput(world, "text changed to \"[text]\"")
|
|
|
|
//boutput(world, "added: \[[substitution_feedback_thing[substitution_feedback_thing.len]]]")
|
|
|
|
continue
|
|
|
|
else
|
|
buggyText = trim(copytext(text, searchPosition, findPosition))
|
|
|
|
if(buggyText)
|
|
|
|
if (replaceList && dd_hasprefix(buggyText, "$") && (copytext(buggyText,2) in replaceList))
|
|
textList += "[replaceList[copytext(buggyText, 2)]]"
|
|
else
|
|
textList += "[buggyText]"
|
|
if(!findPosition)
|
|
//boutput(world, english_list(textList))
|
|
return textList
|
|
searchPosition = findPosition + separatorlength
|
|
if(searchPosition > textlength)
|
|
//boutput(world, english_list(textList))
|
|
return textList
|
|
return
|
|
|
|
#undef QUOTE_SYMBOL
|
|
#undef QUOTE_SYMBOL_LENGTH
|
|
|