[MIRROR] Server logs (#10486)

Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-03-21 16:39:07 -07:00
committed by GitHub
parent 37ad599827
commit 11a5a81208
8 changed files with 151 additions and 135 deletions

View File

@@ -12,52 +12,112 @@
return text
//Sends resource files to client cache
/client/proc/getFiles()
for(var/file in args)
src << browse_rsc(file)
/**
* For FTP requests. (i.e. downloading runtime logs.)
*
* However it'd be ok to use for accessing attack logs and such too, which are even laggier.
*/
GLOBAL_VAR_INIT(fileaccess_timer, 0)
/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm"))
/client/proc/browse_files(root_type=BROWSE_ROOT_ALL_LOGS, max_iterations=10, list/valid_extensions=list("txt","log","htm", "html", "gz", "json"))
// wow why was this ever a parameter
var/root = "data/logs/"
switch(root_type)
if(BROWSE_ROOT_ALL_LOGS)
root = "data/logs/"
if(BROWSE_ROOT_CURRENT_LOGS)
root = log_path
var/path = root
for(var/i=0, i<max_iterations, i++)
var/list/choices = sortList(flist(path))
for(var/i in 1 to max_iterations)
var/list/choices = flist(path)
if(path != root)
choices.Insert(1,"/")
choices = sortList(choices) + "Download Folder"
var/choice = tgui_input_list(src, "Choose a file to access:", "Download", choices)
var/choice = tgui_input_list(src,"Choose a file to access:","Download",choices)
switch(choice)
if(null)
return
if("/")
path = root
continue
if("Download Folder")
var/list/comp_flist = flist(path)
var/confirmation = tgui_alert(src, "Are you SURE you want to download all the files in this folder? (This will open [length(comp_flist)] prompt[length(comp_flist) == 1 ? "" : "s"])", "Confirmation", list("Yes", "No"))
if(confirmation != "Yes")
continue
for(var/file in comp_flist)
src << ftp(path + file)
return
path += choice
if(copytext(path,-1,0) != "/") //didn't choose a directory, no need to iterate again
if(copytext_char(path, -1) != "/") //didn't choose a directory, no need to iterate again
break
var/extension = copytext(path,-4,0)
if( !fexists(path) || !(extension in valid_extensions) )
var/extensions
for(var/i in valid_extensions)
if(extensions)
extensions += "|"
extensions += "[i]"
var/regex/valid_ext = new("\\.([extensions])$", "i")
if( !fexists(path) || !(valid_ext.Find(path)) )
to_chat(src, span_red("Error: browse_files(): File not found/Invalid file([path])."))
return
return path
#define FTPDELAY 200 //200 tick delay to discourage spam
/* This proc is a failsafe to prevent spamming of file requests.
#define FTPDELAY 200 //200 tick delay to discourage spam
#define ADMIN_FTPDELAY_MODIFIER 0.5 //Admins get to spam files faster since we ~trust~ them!
/* This proc is a failsafe to prevent spamming of file requests.
It is just a timer that only permits a download every [FTPDELAY] ticks.
This can be changed by modifying FTPDELAY's value above.
PLEASE USE RESPONSIBLY, Some log files canr each sizes of 4MB! */
PLEASE USE RESPONSIBLY, Some log files can reach sizes of 4MB! */
/client/proc/file_spam_check()
var/time_to_wait = fileaccess_timer - world.time
var/time_to_wait = GLOB.fileaccess_timer - world.time
if(time_to_wait > 0)
to_chat(src, span_red("Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds."))
return 1
fileaccess_timer = world.time + FTPDELAY
return 0
to_chat(src, span_red("Error: file_spam_check(): Spam. Please wait [DisplayTimeText(time_to_wait)]."))
return TRUE
var/delay = FTPDELAY
if(holder)
delay *= ADMIN_FTPDELAY_MODIFIER
GLOB.fileaccess_timer = world.time + delay
return FALSE
#undef FTPDELAY
#undef ADMIN_FTPDELAY_MODIFIER
/**
* Takes a directory and returns every file within every sub directory.
* If extensions_filter is provided then only files that end in that extension are given back.
* If extensions_filter is a list, any file that matches at least one entry is given back.
*/
/proc/pathwalk(path, extensions_filter)
var/list/jobs = list(path)
var/list/filenames = list()
while(jobs.len)
var/current_dir = pop(jobs)
var/list/new_filenames = flist(current_dir)
for(var/new_filename in new_filenames)
// if filename ends in / it is a directory, append to currdir
if(findtext(new_filename, "/", -1))
jobs += "[current_dir][new_filename]"
continue
// filename extension filtering
if(extensions_filter)
if(islist(extensions_filter))
for(var/allowed_extension in extensions_filter)
if(endswith(new_filename, allowed_extension))
filenames += "[current_dir][new_filename]"
break
else if(endswith(new_filename, extensions_filter))
filenames += "[current_dir][new_filename]"
else
filenames += "[current_dir][new_filename]"
return filenames
/proc/pathflatten(path)
return replacetext(path, "/", "_")
/// Returns the md5 of a file at a given path.
/proc/md5filepath(path)
@@ -65,6 +125,7 @@
/// Save file as an external file then md5 it.
/// Used because md5ing files stored in the rsc sometimes gives incorrect md5 results.
/// https://www.byond.com/forum/post/2611357
/proc/md5asfile(file)
var/static/notch = 0
// its importaint this code can handle md5filepath sleeping instead of hard blocking, if it's converted to use rust_g.
@@ -73,3 +134,23 @@
fcopy(file, filename)
. = md5filepath(filename)
fdel(filename)
/**
* Sanitizes the name of each node in the path.
*
* Im case you are wondering when to use this proc and when to use SANITIZE_FILENAME,
*
* You use SANITIZE_FILENAME to sanitize the name of a file [e.g. example.txt]
*
* You use sanitize_filepath sanitize the path of a file [e.g. root/node/example.txt]
*
* If you use SANITIZE_FILENAME to sanitize a file path things will break.
*/
/proc/sanitize_filepath(path)
. = ""
var/delimiter = "/" //Very much intentionally hardcoded
var/list/all_nodes = splittext(path, delimiter)
for(var/node in all_nodes)
if(.)
. += delimiter // Add the delimiter before each successive node.
. += SANITIZE_FILENAME(node)

View File

@@ -661,8 +661,7 @@ GLOBAL_LIST_EMPTY(cached_examine_icons)
if (!isicon(icon2collapse))
if (isfile(thing)) //special snowflake
//var/name = SANITIZE_FILENAME("[generate_asset_name(thing)].png")
var/name = "[generate_asset_name(thing)].png"
var/name = SANITIZE_FILENAME("[generate_asset_name(thing)].png")
if (!SSassets.cache[name])
SSassets.transport.register_asset(name, thing)
for (var/thing2 in targets)

View File

@@ -632,6 +632,18 @@ GLOBAL_LIST_EMPTY(text_tag_cache)
var/static/regex/regex = new(@"[^a-zA-Z0-9]","g")
return replacetext(name, regex, "")
/// Returns TRUE if the input_text ends with the ending
/proc/endswith(input_text, ending)
var/input_length = LAZYLEN(ending)
return !!findtext(input_text, ending, -input_length)
/// Returns TRUE if the input_text starts with any of the beginnings
/proc/starts_with_any(input_text, list/beginnings)
for(var/beginning in beginnings)
if(!!findtext(input_text, beginning, 1, LAZYLEN(beginning)+1))
return TRUE
return FALSE
//finds the first occurrence of one of the characters from needles argument inside haystack
//it may appear this can be optimised, but it really can't. findtext() is so much faster than anything you can do in byondcode.
//stupid byond :(