mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-26 09:03:37 +00:00
Nobody used them, and they were cluttering PDA Topic() needlessly. Rest in peace, my child. You were too good for this world.
380 lines
13 KiB
Plaintext
380 lines
13 KiB
Plaintext
////////////
|
|
//SECURITY//
|
|
////////////
|
|
#define UPLOAD_LIMIT 1048576 //Restricts client uploads to the server to 1MB //Could probably do with being lower.
|
|
#define MIN_CLIENT_VERSION 0 //Just an ambiguously low version for now, I don't want to suddenly stop people playing.
|
|
//I would just like the code ready should it ever need to be used.
|
|
/*
|
|
When somebody clicks a link in game, this Topic is called first.
|
|
It does the stuff in this proc and then is redirected to the Topic() proc for the src=[0xWhatever]
|
|
(if specified in the link). ie locate(hsrc).Topic()
|
|
|
|
Such links can be spoofed.
|
|
|
|
Because of this certain things MUST be considered whenever adding a Topic() for something:
|
|
- Can it be fed harmful values which could cause runtimes?
|
|
- Is the Topic call an admin-only thing?
|
|
- If so, does it have checks to see if the person who called it (usr.client) is an admin?
|
|
- Are the processes being called by Topic() particularly laggy?
|
|
- If so, is there any protection against somebody spam-clicking a link?
|
|
If you have any questions about this stuff feel free to ask. ~Carn
|
|
*/
|
|
/client/Topic(href, href_list, hsrc)
|
|
if(!usr || usr != mob) //stops us calling Topic for somebody else's client. Also helps prevent usr=null
|
|
return
|
|
|
|
//Admin PM
|
|
if(href_list["priv_msg"])
|
|
if (href_list["ahelp_reply"])
|
|
cmd_ahelp_reply(href_list["priv_msg"])
|
|
return
|
|
cmd_admin_pm(href_list["priv_msg"],null)
|
|
return
|
|
|
|
//Logs all hrefs
|
|
if(config && config.log_hrefs && href_logfile)
|
|
href_logfile << "<small>[time2text(world.timeofday,"hh:mm")] [src] (usr:[usr])</small> || [hsrc ? "[hsrc] " : ""][href]<br>"
|
|
|
|
switch(href_list["_src_"])
|
|
if("holder") hsrc = holder
|
|
if("usr") hsrc = mob
|
|
if("prefs") return prefs.process_link(usr,href_list)
|
|
if("vars") return view_var_Topic(href,href_list,hsrc)
|
|
|
|
..() //redirect to hsrc.Topic()
|
|
|
|
/client/proc/is_content_unlocked()
|
|
if(!prefs.unlock_content)
|
|
src << "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. Only 10 bucks for 3 months! <a href='http://www.byond.com/membership'>Click Here to find out more</a>."
|
|
return 0
|
|
return 1
|
|
|
|
/client/proc/handle_spam_prevention(message, mute_type)
|
|
if(config.automute_on && !holder && src.last_message == message)
|
|
src.last_message_count++
|
|
if(src.last_message_count >= SPAM_TRIGGER_AUTOMUTE)
|
|
src << "<span class='danger'>You have exceeded the spam filter limit for identical messages. An auto-mute was applied.</span>"
|
|
cmd_admin_mute(src, mute_type, 1)
|
|
return 1
|
|
if(src.last_message_count >= SPAM_TRIGGER_WARNING)
|
|
src << "<span class='danger'>You are nearing the spam filter limit for identical messages.</span>"
|
|
return 0
|
|
else
|
|
last_message = message
|
|
src.last_message_count = 0
|
|
return 0
|
|
|
|
//This stops files larger than UPLOAD_LIMIT being sent from client to server via input(), client.Import() etc.
|
|
/client/AllowUpload(filename, filelength)
|
|
if(filelength > UPLOAD_LIMIT)
|
|
src << "<font color='red'>Error: AllowUpload(): File Upload too large. Upload Limit: [UPLOAD_LIMIT/1024]KiB.</font>"
|
|
return 0
|
|
/* //Don't need this at the moment. But it's here if it's needed later.
|
|
//Helps prevent multiple files being uploaded at once. Or right after eachother.
|
|
var/time_to_wait = fileaccess_timer - world.time
|
|
if(time_to_wait > 0)
|
|
src << "<font color='red'>Error: AllowUpload(): Spam prevention. Please wait [round(time_to_wait/10)] seconds.</font>"
|
|
return 0
|
|
fileaccess_timer = world.time + FTPDELAY */
|
|
return 1
|
|
|
|
|
|
///////////
|
|
//CONNECT//
|
|
///////////
|
|
#if (PRELOAD_RSC == 0)
|
|
var/list/external_rsc_urls
|
|
var/next_external_rsc = 0
|
|
#endif
|
|
|
|
|
|
/client/New(TopicData)
|
|
|
|
TopicData = null //Prevent calls to client.Topic from connect
|
|
|
|
if(connection != "seeker" && connection != "web")//Invalid connection type.
|
|
return null
|
|
if(byond_version < MIN_CLIENT_VERSION) //Out of date client.
|
|
return null
|
|
|
|
#if (PRELOAD_RSC == 0)
|
|
if(external_rsc_urls && external_rsc_urls.len)
|
|
next_external_rsc = Wrap(next_external_rsc+1, 1, external_rsc_urls.len+1)
|
|
preload_rsc = external_rsc_urls[next_external_rsc]
|
|
#endif
|
|
|
|
clients += src
|
|
directory[ckey] = src
|
|
|
|
//Admin Authorisation
|
|
if(protected_config.autoadmin)
|
|
if(!admin_datums[ckey])
|
|
var/datum/admin_rank/autorank
|
|
for(var/datum/admin_rank/R in admin_ranks)
|
|
if(R.name == protected_config.autoadmin_rank)
|
|
autorank = R
|
|
break
|
|
if(!autorank)
|
|
world << "Autoadmin rank not found"
|
|
else
|
|
var/datum/admins/D = new(autorank, ckey)
|
|
admin_datums[ckey] = D
|
|
holder = admin_datums[ckey]
|
|
if(holder)
|
|
admins += src
|
|
holder.owner = src
|
|
|
|
//preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum)
|
|
prefs = preferences_datums[ckey]
|
|
if(!prefs)
|
|
prefs = new /datum/preferences(src)
|
|
preferences_datums[ckey] = prefs
|
|
prefs.last_ip = address //these are gonna be used for banning
|
|
prefs.last_id = computer_id //these are gonna be used for banning
|
|
|
|
. = ..() //calls mob.Login()
|
|
|
|
if (connection == "web")
|
|
if (!config.allowwebclient)
|
|
src << "Web client is disabled"
|
|
del(src)
|
|
return 0
|
|
if (config.webclientmembersonly && !IsByondMember())
|
|
src << "Sorry, but the web client is restricted to byond members only."
|
|
del(src)
|
|
return 0
|
|
|
|
if( (world.address == address || !address) && !host )
|
|
host = key
|
|
world.update_status()
|
|
|
|
if(holder)
|
|
add_admin_verbs()
|
|
admin_memo_output("Show")
|
|
adminGreet()
|
|
if((global.comms_key == "default_pwd" || length(global.comms_key) <= 6) && global.comms_allowed) //It's the default value or less than 6 characters long, but it somehow didn't disable comms.
|
|
src << "<span class='danger'>The server's API key is either too short or is the default value! Consider changing it immediately!</span>"
|
|
|
|
add_verbs_from_config()
|
|
set_client_age_from_db()
|
|
|
|
if (isnum(player_age) && player_age == -1) //first connection
|
|
if (config.panic_bunker && !holder && !(ckey in deadmins))
|
|
log_access("Failed Login: [key] - New account attempting to connect during panic bunker")
|
|
message_admins("<span class='adminnotice'>Failed Login: [key] - New account attempting to connect during panic bunker</span>")
|
|
src << "Sorry but the server is currently not accepting connections from never before seen players."
|
|
del(src)
|
|
return 0
|
|
|
|
if (config.notify_new_player_age >= 0)
|
|
message_admins("New user: [key_name_admin(src)] is connecting here for the first time.")
|
|
if (config.irc_first_connection_alert)
|
|
send2irc_adminless_only("New-user", "[key_name(src)] is connecting for the first time!")
|
|
|
|
player_age = 0 // set it from -1 to 0 so the job selection code doesn't have a panic attack
|
|
|
|
else if (isnum(player_age) && player_age < config.notify_new_player_age)
|
|
message_admins("New user: [key_name_admin(src)] just connected with an age of [player_age] day[(player_age==1?"":"s")]")
|
|
|
|
|
|
if (!ticker || ticker.current_state == GAME_STATE_PREGAME)
|
|
spawn (rand(10,150))
|
|
if (src)
|
|
sync_client_with_db()
|
|
else
|
|
sync_client_with_db()
|
|
|
|
send_resources()
|
|
|
|
if(!void)
|
|
void = new()
|
|
|
|
screen += void
|
|
|
|
if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates.
|
|
src << "<span class='info'>You have unread updates in the changelog.</span>"
|
|
if(config.aggressive_changelog)
|
|
src.changes()
|
|
else
|
|
winset(src, "rpane.changelogb", "background-color=#eaeaea;font-style=bold")
|
|
|
|
if (ckey in clientmessages)
|
|
for (var/message in clientmessages[ckey])
|
|
src << message
|
|
clientmessages.Remove(ckey)
|
|
|
|
if (config && config.autoconvert_notes)
|
|
convert_notes_sql(ckey)
|
|
|
|
|
|
//This is down here because of the browse() calls in tooltip/New()
|
|
if(!tooltips)
|
|
tooltips = new /datum/tooltip(src)
|
|
|
|
|
|
//////////////
|
|
//DISCONNECT//
|
|
//////////////
|
|
|
|
/client/Del()
|
|
if(holder)
|
|
adminGreet(1)
|
|
holder.owner = null
|
|
admins -= src
|
|
directory -= ckey
|
|
clients -= src
|
|
return ..()
|
|
|
|
|
|
/client/proc/set_client_age_from_db()
|
|
if (IsGuestKey(src.key))
|
|
return
|
|
|
|
establish_db_connection()
|
|
if(!dbcon.IsConnected())
|
|
return
|
|
|
|
var/sql_ckey = sanitizeSQL(src.ckey)
|
|
|
|
var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
|
|
if (!query.Execute())
|
|
return
|
|
|
|
while (query.NextRow())
|
|
player_age = text2num(query.item[2])
|
|
return
|
|
|
|
//no match mark it as a first connection for use in client/New()
|
|
player_age = -1
|
|
|
|
|
|
/client/proc/sync_client_with_db()
|
|
if (IsGuestKey(src.key))
|
|
return
|
|
|
|
establish_db_connection()
|
|
if (!dbcon.IsConnected())
|
|
return
|
|
|
|
var/sql_ckey = sanitizeSQL(src.ckey)
|
|
|
|
var/DBQuery/query_ip = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ip = '[address]' AND ckey != '[sql_ckey]'")
|
|
query_ip.Execute()
|
|
related_accounts_ip = ""
|
|
while(query_ip.NextRow())
|
|
related_accounts_ip += "[query_ip.item[1]], "
|
|
|
|
var/DBQuery/query_cid = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]' AND ckey != '[sql_ckey]'")
|
|
query_cid.Execute()
|
|
related_accounts_cid = ""
|
|
while (query_cid.NextRow())
|
|
related_accounts_cid += "[query_cid.item[1]], "
|
|
|
|
var/watchreason = check_watchlist(sql_ckey)
|
|
if(watchreason)
|
|
message_admins("<font color='red'><B>Notice: </B></font><font color='blue'>[key_name_admin(src)] is on the watchlist and has just connected - Reason: [watchreason]</font>")
|
|
send2irc_adminless_only("Watchlist", "[key_name(src)] is on the watchlist and has just connected - Reason: [watchreason]")
|
|
|
|
var/admin_rank = "Player"
|
|
if (src.holder && src.holder.rank)
|
|
admin_rank = src.holder.rank.name
|
|
|
|
var/sql_ip = sanitizeSQL(src.address)
|
|
var/sql_computerid = sanitizeSQL(src.computer_id)
|
|
var/sql_admin_rank = sanitizeSQL(admin_rank)
|
|
|
|
|
|
var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[sql_ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]') ON DUPLICATE KEY UPDATE lastseen = VALUES(lastseen), ip = VALUES(ip), computerid = VALUES(computerid), lastadminrank = VALUES(lastadminrank)")
|
|
query_insert.Execute()
|
|
|
|
//Logging player access
|
|
var/serverip = "[world.internet_address]:[world.port]"
|
|
var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]` (`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[sql_ckey]','[sql_ip]','[sql_computerid]');")
|
|
query_accesslog.Execute()
|
|
|
|
/client/proc/add_verbs_from_config()
|
|
if(config.see_own_notes)
|
|
verbs += /client/proc/self_notes
|
|
|
|
|
|
#undef TOPIC_SPAM_DELAY
|
|
#undef UPLOAD_LIMIT
|
|
#undef MIN_CLIENT_VERSION
|
|
|
|
//checks if a client is afk
|
|
//3000 frames = 5 minutes
|
|
/client/proc/is_afk(duration=3000)
|
|
if(inactivity > duration) return inactivity
|
|
return 0
|
|
|
|
// Byond seemingly calls stat, each tick.
|
|
// Calling things each tick can get expensive real quick.
|
|
// So we slow this down a little.
|
|
// See: http://www.byond.com/docs/ref/info.html#/client/proc/Stat
|
|
/client/Stat()
|
|
. = ..()
|
|
sleep(1)
|
|
|
|
//send resources to the client. It's here in its own proc so we can move it around easiliy if need be
|
|
/client/proc/send_resources()
|
|
|
|
spawn
|
|
// Preload the HTML interface. This needs to be done due to BYOND bug http://www.byond.com/forum/?post=1487244
|
|
var/datum/html_interface/hi
|
|
for (var/type in typesof(/datum/html_interface))
|
|
hi = new type(null)
|
|
hi.sendResources(src)
|
|
|
|
// Preload the crew monitor. This needs to be done due to BYOND bug http://www.byond.com/forum/?post=1487244
|
|
spawn
|
|
if (crewmonitor && crewmonitor.initialized)
|
|
crewmonitor.sendResources(src)
|
|
|
|
//Send nanoui files to client
|
|
SSnano.send_resources(src)
|
|
getFiles(
|
|
'html/search.js',
|
|
'html/panels.css',
|
|
'html/browser/common.css',
|
|
'html/browser/scannernew.css',
|
|
'html/browser/playeroptions.css',
|
|
'icons/pda_icons/pda_atmos.png',
|
|
'icons/pda_icons/pda_back.png',
|
|
'icons/pda_icons/pda_bell.png',
|
|
'icons/pda_icons/pda_blank.png',
|
|
'icons/pda_icons/pda_boom.png',
|
|
'icons/pda_icons/pda_bucket.png',
|
|
'icons/pda_icons/pda_medbot.png',
|
|
'icons/pda_icons/pda_floorbot.png',
|
|
'icons/pda_icons/pda_cleanbot.png',
|
|
'icons/pda_icons/pda_crate.png',
|
|
'icons/pda_icons/pda_cuffs.png',
|
|
'icons/pda_icons/pda_eject.png',
|
|
'icons/pda_icons/pda_exit.png',
|
|
'icons/pda_icons/pda_flashlight.png',
|
|
'icons/pda_icons/pda_honk.png',
|
|
'icons/pda_icons/pda_mail.png',
|
|
'icons/pda_icons/pda_medical.png',
|
|
'icons/pda_icons/pda_menu.png',
|
|
'icons/pda_icons/pda_mule.png',
|
|
'icons/pda_icons/pda_notes.png',
|
|
'icons/pda_icons/pda_power.png',
|
|
'icons/pda_icons/pda_rdoor.png',
|
|
'icons/pda_icons/pda_reagent.png',
|
|
'icons/pda_icons/pda_refresh.png',
|
|
'icons/pda_icons/pda_scanner.png',
|
|
'icons/pda_icons/pda_signaler.png',
|
|
'icons/pda_icons/pda_status.png',
|
|
'icons/stamp_icons/large_stamp-clown.png',
|
|
'icons/stamp_icons/large_stamp-deny.png',
|
|
'icons/stamp_icons/large_stamp-ok.png',
|
|
'icons/stamp_icons/large_stamp-hop.png',
|
|
'icons/stamp_icons/large_stamp-cmo.png',
|
|
'icons/stamp_icons/large_stamp-ce.png',
|
|
'icons/stamp_icons/large_stamp-hos.png',
|
|
'icons/stamp_icons/large_stamp-rd.png',
|
|
'icons/stamp_icons/large_stamp-cap.png',
|
|
'icons/stamp_icons/large_stamp-qm.png',
|
|
'icons/stamp_icons/large_stamp-law.png'
|
|
)
|