mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 16:07:40 +00:00
486 lines
14 KiB
Plaintext
486 lines
14 KiB
Plaintext
/*
|
||
* Holds procs designed to help with filtering text
|
||
* Contains groups:
|
||
* SQL sanitization/formating
|
||
* Text sanitization
|
||
* Text searches
|
||
* Text modification
|
||
* Misc
|
||
*/
|
||
|
||
|
||
/*
|
||
* SQL sanitization
|
||
*/
|
||
|
||
// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts.
|
||
/proc/sanitizeSQL(t as text)
|
||
var/sqltext = dbcon.Quote(t);
|
||
return copytext(sqltext, 2, lentext(sqltext));//Quote() adds quotes around input, we already do that
|
||
|
||
/proc/format_table_name(table as text)
|
||
return sqlfdbktableprefix + table
|
||
|
||
/*
|
||
* Text sanitization
|
||
*/
|
||
|
||
//Simply removes < and > and limits the length of the message
|
||
/proc/strip_html_simple(t,limit=MAX_MESSAGE_LEN)
|
||
var/list/strip_chars = list("<",">")
|
||
t = copytext(t,1,limit)
|
||
for(var/char in strip_chars)
|
||
var/index = findtext(t, char)
|
||
while(index)
|
||
t = copytext(t, 1, index) + copytext(t, index+1)
|
||
index = findtext(t, char)
|
||
return t
|
||
|
||
//Removes a few problematic characters
|
||
/proc/sanitize_simple(t,list/repl_chars = list("\n"="#","\t"="#"))
|
||
for(var/char in repl_chars)
|
||
var/index = findtext(t, char)
|
||
while(index)
|
||
t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+1)
|
||
index = findtext(t, char, index+1)
|
||
return t
|
||
|
||
//Runs byond's sanitization proc along-side sanitize_simple
|
||
/proc/sanitize(t,list/repl_chars = null)
|
||
return html_encode(sanitize_simple(t,repl_chars))
|
||
|
||
//Runs sanitize and strip_html_simple
|
||
//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' after sanitize() calls byond's html_encode()
|
||
/proc/strip_html(t,limit=MAX_MESSAGE_LEN)
|
||
return copytext((sanitize(strip_html_simple(t))),1,limit)
|
||
|
||
//Runs byond's sanitization proc along-side strip_html_simple
|
||
//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause
|
||
/proc/adminscrub(t,limit=MAX_MESSAGE_LEN)
|
||
return copytext((html_encode(strip_html_simple(t))),1,limit)
|
||
|
||
|
||
//Returns null if there is any bad text in the string
|
||
/proc/reject_bad_text(text, max_length=512)
|
||
if(length(text) > max_length)
|
||
return //message too long
|
||
var/non_whitespace = 0
|
||
for(var/i=1, i<=length(text), i++)
|
||
switch(text2ascii(text,i))
|
||
if(62,60,92,47)
|
||
return //rejects the text if it contains these bad characters: <, >, \ or /
|
||
if(127 to 255)
|
||
return //rejects weird letters like <20>
|
||
if(0 to 31)
|
||
return //more weird stuff
|
||
if(32)
|
||
continue //whitespace
|
||
else
|
||
non_whitespace = 1
|
||
if(non_whitespace)
|
||
return text //only accepts the text if it has some non-spaces
|
||
|
||
// Used to get a properly sanitized input, of max_length
|
||
/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN)
|
||
var/name = input(user, message, title, default) as text|null
|
||
return trim(html_encode(name), max_length) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <)
|
||
|
||
// Used to get a properly sanitized multiline input, of max_length
|
||
/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN)
|
||
var/name = input(user, message, title, default) as message|null
|
||
return html_encode(trim(name, max_length))
|
||
|
||
//Filters out undesirable characters from names
|
||
/proc/reject_bad_name(t_in, allow_numbers=0, max_length=MAX_NAME_LEN)
|
||
if(!t_in || length(t_in) > max_length)
|
||
return //Rejects the input if it is null or if it is longer then the max length allowed
|
||
|
||
var/number_of_alphanumeric = 0
|
||
var/last_char_group = 0
|
||
var/t_out = ""
|
||
|
||
for(var/i=1, i<=length(t_in), i++)
|
||
var/ascii_char = text2ascii(t_in,i)
|
||
switch(ascii_char)
|
||
// A .. Z
|
||
if(65 to 90) //Uppercase Letters
|
||
t_out += ascii2text(ascii_char)
|
||
number_of_alphanumeric++
|
||
last_char_group = 4
|
||
|
||
// a .. z
|
||
if(97 to 122) //Lowercase Letters
|
||
if(last_char_group<2)
|
||
t_out += ascii2text(ascii_char-32) //Force uppercase first character
|
||
else
|
||
t_out += ascii2text(ascii_char)
|
||
number_of_alphanumeric++
|
||
last_char_group = 4
|
||
|
||
// 0 .. 9
|
||
if(48 to 57) //Numbers
|
||
if(!last_char_group)
|
||
continue //suppress at start of string
|
||
if(!allow_numbers)
|
||
continue
|
||
t_out += ascii2text(ascii_char)
|
||
number_of_alphanumeric++
|
||
last_char_group = 3
|
||
|
||
// ' - .
|
||
if(39,45,46) //Common name punctuation
|
||
if(!last_char_group)
|
||
continue
|
||
t_out += ascii2text(ascii_char)
|
||
last_char_group = 2
|
||
|
||
// ~ | @ : # $ % & * +
|
||
if(126,124,64,58,35,36,37,38,42,43) //Other symbols that we'll allow (mainly for AI)
|
||
if(!last_char_group)
|
||
continue //suppress at start of string
|
||
if(!allow_numbers)
|
||
continue
|
||
t_out += ascii2text(ascii_char)
|
||
last_char_group = 2
|
||
|
||
//Space
|
||
if(32)
|
||
if(last_char_group <= 1)
|
||
continue //suppress double-spaces and spaces at start of string
|
||
t_out += ascii2text(ascii_char)
|
||
last_char_group = 1
|
||
else
|
||
return
|
||
|
||
if(number_of_alphanumeric < 2)
|
||
return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '"
|
||
|
||
if(last_char_group == 1)
|
||
t_out = copytext(t_out,1,length(t_out)) //removes the last character (in this case a space)
|
||
|
||
for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai")) //prevents these common metagamey names
|
||
if(cmptext(t_out,bad_name))
|
||
return //(not case sensitive)
|
||
|
||
return t_out
|
||
|
||
//html_encode helper proc that returns the smallest non null of two numbers
|
||
//or 0 if they're both null (needed because of findtext returning 0 when a value is not present)
|
||
/proc/non_zero_min(a, b)
|
||
if(!a)
|
||
return b
|
||
if(!b)
|
||
return a
|
||
return (a < b ? a : b)
|
||
|
||
/*
|
||
* Text searches
|
||
*/
|
||
|
||
//Checks the beginning of a string for a specified sub-string
|
||
//Returns the position of the substring or 0 if it was not found
|
||
/proc/dd_hasprefix(text, prefix)
|
||
var/start = 1
|
||
var/end = length(prefix) + 1
|
||
return findtext(text, prefix, start, end)
|
||
|
||
//Checks the beginning of a string for a specified sub-string. This proc is case sensitive
|
||
//Returns the position of the substring or 0 if it was not found
|
||
/proc/dd_hasprefix_case(text, prefix)
|
||
var/start = 1
|
||
var/end = length(prefix) + 1
|
||
return findtextEx(text, prefix, start, end)
|
||
|
||
//Checks the end of a string for a specified substring.
|
||
//Returns the position of the substring or 0 if it was not found
|
||
/proc/dd_hassuffix(text, suffix)
|
||
var/start = length(text) - length(suffix)
|
||
if(start)
|
||
return findtext(text, suffix, start, null)
|
||
return
|
||
|
||
//Checks the end of a string for a specified substring. This proc is case sensitive
|
||
//Returns the position of the substring or 0 if it was not found
|
||
/proc/dd_hassuffix_case(text, suffix)
|
||
var/start = length(text) - length(suffix)
|
||
if(start)
|
||
return findtextEx(text, suffix, start, null)
|
||
|
||
//Adds 'u' number of zeros ahead of the text 't'
|
||
/proc/add_zero(t, u)
|
||
while (length(t) < u)
|
||
t = "0[t]"
|
||
return t
|
||
|
||
//Adds 'u' number of spaces ahead of the text 't'
|
||
/proc/add_lspace(t, u)
|
||
while(length(t) < u)
|
||
t = " [t]"
|
||
return t
|
||
|
||
//Adds 'u' number of spaces behind the text 't'
|
||
/proc/add_tspace(t, u)
|
||
while(length(t) < u)
|
||
t = "[t] "
|
||
return t
|
||
|
||
//Returns a string with reserved characters and spaces before the first letter removed
|
||
/proc/trim_left(text)
|
||
for (var/i = 1 to length(text))
|
||
if (text2ascii(text, i) > 32)
|
||
return copytext(text, i)
|
||
return ""
|
||
|
||
//Returns a string with reserved characters and spaces after the last letter removed
|
||
/proc/trim_right(text)
|
||
for (var/i = length(text), i > 0, i--)
|
||
if (text2ascii(text, i) > 32)
|
||
return copytext(text, 1, i + 1)
|
||
|
||
return ""
|
||
|
||
//Returns a string with reserved characters and spaces before the first word and after the last word removed.
|
||
/proc/trim(text, max_length)
|
||
if(max_length)
|
||
text = copytext(text, 1, max_length)
|
||
return trim_left(trim_right(text))
|
||
|
||
//Returns a string with the first element of the string capitalized.
|
||
/proc/capitalize(t as text)
|
||
return uppertext(copytext(t, 1, 2)) + copytext(t, 2)
|
||
|
||
//Centers text by adding spaces to either side of the string.
|
||
/proc/dd_centertext(message, length)
|
||
var/new_message = message
|
||
var/size = length(message)
|
||
var/delta = length - size
|
||
if(size == length)
|
||
return new_message
|
||
if(size > length)
|
||
return copytext(new_message, 1, length + 1)
|
||
if(delta == 1)
|
||
return new_message + " "
|
||
if(delta % 2)
|
||
new_message = " " + new_message
|
||
delta--
|
||
var/spaces = add_lspace("",delta/2-1)
|
||
return spaces + new_message + spaces
|
||
|
||
//Limits the length of the text. Note: MAX_MESSAGE_LEN and MAX_NAME_LEN are widely used for this purpose
|
||
/proc/dd_limittext(message, length)
|
||
var/size = length(message)
|
||
if(size <= length)
|
||
return message
|
||
return copytext(message, 1, length + 1)
|
||
|
||
|
||
/proc/stringmerge(text,compare,replace = "*")
|
||
//This proc fills in all spaces with the "replace" var (* by default) with whatever
|
||
//is in the other string at the same spot (assuming it is not a replace char).
|
||
//This is used for fingerprints
|
||
var/newtext = text
|
||
if(lentext(text) != lentext(compare))
|
||
return 0
|
||
for(var/i = 1, i < lentext(text), i++)
|
||
var/a = copytext(text,i,i+1)
|
||
var/b = copytext(compare,i,i+1)
|
||
//if it isn't both the same letter, or if they are both the replacement character
|
||
//(no way to know what it was supposed to be)
|
||
if(a != b)
|
||
if(a == replace) //if A is the replacement char
|
||
newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1)
|
||
else if(b == replace) //if B is the replacement char
|
||
newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1)
|
||
else //The lists disagree, Uh-oh!
|
||
return 0
|
||
return newtext
|
||
|
||
/proc/stringpercent(text,character = "*")
|
||
//This proc returns the number of chars of the string that is the character
|
||
//This is used for detective work to determine fingerprint completion.
|
||
if(!text || !character)
|
||
return 0
|
||
var/count = 0
|
||
for(var/i = 1, i <= lentext(text), i++)
|
||
var/a = copytext(text,i,i+1)
|
||
if(a == character)
|
||
count++
|
||
return count
|
||
|
||
/proc/reverse_text(text = "")
|
||
var/new_text = ""
|
||
for(var/i = length(text); i > 0; i--)
|
||
new_text += copytext(text, i, i+1)
|
||
return new_text
|
||
|
||
var/list/zero_character_only = list("0")
|
||
var/list/hex_characters = list("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f")
|
||
var/list/alphabet = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z")
|
||
var/list/binary = list("0","1")
|
||
/proc/random_string(length, list/characters)
|
||
. = ""
|
||
for(var/i=1, i<=length, i++)
|
||
. += pick(characters)
|
||
|
||
/proc/repeat_string(times, string="")
|
||
. = ""
|
||
for(var/i=1, i<=times, i++)
|
||
. += string
|
||
|
||
/proc/random_short_color()
|
||
return random_string(3, hex_characters)
|
||
|
||
/proc/random_color()
|
||
return random_string(6, hex_characters)
|
||
|
||
/proc/add_zero2(t, u)
|
||
var/temp1
|
||
while (length(t) < u)
|
||
t = "0[t]"
|
||
temp1 = t
|
||
if (length(t) > u)
|
||
temp1 = copytext(t,2,u+1)
|
||
return temp1
|
||
|
||
//merges non-null characters (3rd argument) from "from" into "into". Returns result
|
||
//e.g. into = "Hello World"
|
||
// from = "Seeya______"
|
||
// returns"Seeya World"
|
||
//The returned text is always the same length as into
|
||
//This was coded to handle DNA gene-splicing.
|
||
/proc/merge_text(into, from, null_char="_")
|
||
. = ""
|
||
if(!istext(into))
|
||
into = ""
|
||
if(!istext(from))
|
||
from = ""
|
||
var/null_ascii = istext(null_char) ? text2ascii(null_char,1) : null_char
|
||
|
||
var/previous = 0
|
||
var/start = 1
|
||
var/end = length(into) + 1
|
||
|
||
for(var/i=1, i<end, i++)
|
||
var/ascii = text2ascii(from, i)
|
||
if(ascii == null_ascii)
|
||
if(previous != 1)
|
||
. += copytext(from, start, i)
|
||
start = i
|
||
previous = 1
|
||
else
|
||
if(previous != 0)
|
||
. += copytext(into, start, i)
|
||
start = i
|
||
previous = 0
|
||
|
||
if(previous == 0)
|
||
. += copytext(from, start, end)
|
||
else
|
||
. += copytext(into, start, end)
|
||
|
||
//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 :(
|
||
/proc/findchar(haystack, needles, start=1, end=0)
|
||
var/temp
|
||
var/len = length(needles)
|
||
for(var/i=1, i<=len, i++)
|
||
temp = findtextEx(haystack, ascii2text(text2ascii(needles,i)), start, end) //Note: ascii2text(text2ascii) is faster than copytext()
|
||
if(temp)
|
||
end = temp
|
||
return end
|
||
|
||
|
||
/proc/parsepencode(t, mob/user=null, signfont=SIGNFONT)
|
||
if(length(t) < 1) //No input means nothing needs to be parsed
|
||
return
|
||
|
||
t = replacetext(t, "\[center\]", "<center>")
|
||
t = replacetext(t, "\[/center\]", "</center>")
|
||
t = replacetext(t, "\[br\]", "<BR>")
|
||
t = replacetext(t, "\[b\]", "<B>")
|
||
t = replacetext(t, "\[/b\]", "</B>")
|
||
t = replacetext(t, "\[i\]", "<I>")
|
||
t = replacetext(t, "\[/i\]", "</I>")
|
||
t = replacetext(t, "\[u\]", "<U>")
|
||
t = replacetext(t, "\[/u\]", "</U>")
|
||
t = replacetext(t, "\[large\]", "<font size=\"4\">")
|
||
t = replacetext(t, "\[/large\]", "</font>")
|
||
if(user)
|
||
t = replacetext(t, "\[sign\]", "<font face=\"[signfont]\"><i>[user.real_name]</i></font>")
|
||
else
|
||
t = replacetext(t, "\[sign\]", "")
|
||
t = replacetext(t, "\[field\]", "<span class=\"paper_field\"></span>")
|
||
|
||
t = replacetext(t, "\[*\]", "<li>")
|
||
t = replacetext(t, "\[hr\]", "<HR>")
|
||
t = replacetext(t, "\[small\]", "<font size = \"1\">")
|
||
t = replacetext(t, "\[/small\]", "</font>")
|
||
t = replacetext(t, "\[list\]", "<ul>")
|
||
t = replacetext(t, "\[/list\]", "</ul>")
|
||
|
||
return t
|
||
|
||
/proc/char_split(t)
|
||
. = list()
|
||
for(var/x in 1 to length(t))
|
||
. += copytext(t,x,x+1)
|
||
|
||
var/list/rot13_lookup = list()
|
||
|
||
/proc/generate_rot13_lookup()
|
||
var/letters = alphabet.Copy()
|
||
for(var/c in alphabet)
|
||
letters += uppertext(c)
|
||
|
||
for(var/char in letters)
|
||
var/ascii_char = text2ascii(char, 1)
|
||
|
||
var/index
|
||
|
||
switch(ascii_char)
|
||
// A - Z
|
||
if(65 to 90)
|
||
index = 65
|
||
// a - z
|
||
if(97 to 122)
|
||
index = 97
|
||
|
||
var/d = ascii_char - index
|
||
d += 13
|
||
if(d >= 26)
|
||
d -= 26
|
||
ascii_char = index + d
|
||
var/translated_char = ascii2text(ascii_char)
|
||
|
||
rot13_lookup[char] = translated_char
|
||
|
||
/proc/rot13(t_in)
|
||
if(!rot13_lookup.len)
|
||
generate_rot13_lookup()
|
||
|
||
var/t_out = ""
|
||
|
||
for(var/i in 1 to length(t_in))
|
||
var/char = copytext(t_in, i, i + 1)
|
||
if(char in rot13_lookup)
|
||
t_out += rot13_lookup[char]
|
||
else
|
||
t_out += char
|
||
|
||
return t_out
|
||
|
||
//Used in preferences' SetFlavorText and human's set_flavor verb
|
||
//Previews a string of len or less length
|
||
|
||
/proc/copytext_preserve_html(var/text, var/first, var/last)
|
||
return html_encode(copytext(html_decode(text), first, last))
|
||
|
||
proc/TextPreview(var/string,var/len=40)
|
||
if(lentext(string) <= len)
|
||
if(!lentext(string))
|
||
return "\[...\]"
|
||
else
|
||
return string
|
||
else
|
||
return "[copytext(string, 1, 37)]..." |