From: Rudolf Polzer <divverent@alientrap.org>
Date: Thu, 14 Jun 2012 08:05:40 +0000 (+0200)
Subject: implement vote command restrictions
X-Git-Tag: xonotic-v0.7.0~312^2~21^2~4
X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=aa456d152d681da1f268a61615964655844f4beb;p=xonotic%2Fxonotic-data.pk3dir.git

implement vote command restrictions
---

diff --git a/commands.cfg b/commands.cfg
index 9386b3c09..6b82c32ea 100644
--- a/commands.cfg
+++ b/commands.cfg
@@ -239,7 +239,9 @@ alias unban                "qc_cmd_sv     unban                ${* ?}" // Remove
 // other aliases for ban commands
 alias bans "banlist"
 
-// character classes (intersected with 32..126 minus ", $, ;, ^, \ - if you want these, include them explicitly)
+// character classes (intersected with 32..126 minus ", $, ;, ^, \ - if you
+// want these, include them explicitly)
+// note that QC code always forbids $ and ; in VoteCommand_checknasty
 set _iscntrl ""
 set _isblank " "
 set _ispunct "!#%&'()*+,-./:<=>?@[]_`{|}~"
@@ -259,8 +261,6 @@ set _isspace "$_isblank"
 // restriction is specified as <minargs> followed by <maxargs> instances of ';'
 // and the optional character class to verify the argument by (no checking if
 // empty)
-// as we use the semicolon as separator, we cannot include it directly
-// so ; is written as ^^ and ;^ is written as ^^^
 set sv_vote_command_restriction_restart "0"
 set sv_vote_command_restriction_fraglimit "1;$_isdigit"
 set sv_vote_command_restriction_chmap "1;$_isgraph"
diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc
index aea89933a..3604f6fb1 100644
--- a/qcsrc/server/command/vote.qc
+++ b/qcsrc/server/command/vote.qc
@@ -507,30 +507,78 @@ string ValidateMap(string validated_map, entity caller)
 	return validated_map;
 }
 
-float VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, float argc)
+float VoteCommand_checkargs(float startpos, float argc)
 {
-	string first_command;
-	
-	first_command = argv(startpos);
+	float p, q, check;
+	string cvarname = strcat("sv_vote_command_cmdrestriction_", argv(startpos));
+	string cmdrestriction = cvar_string(cvarname); // note: this warns on undefined cvar. We want that.
+	string charlist, arg;
+	float checkmate;
 
-	if not(VoteCommand_checkinlist(first_command, vote_list))
+	if(cmdrestriction == "")
+		return TRUE;
+
+	++startpos; // skip command name
+
+	// check minimum arg count
+
+	// 0 args: argc == startpos
+	// 1 args: argc == startpos + 1
+	// ...
+
+	if((argc - startpos) < stof(cmdrestriction))
 		return FALSE;
 
-	if(argc < startpos) // These commands won't work without arguments
+	p = strstrofs(cmdrestriction, ";", 0); // find first semicolon
+
+	for(;;)
 	{
-		switch(first_command)
+		if(startpos >= argc) // all args checked? GOOD
+			return TRUE;
+
+		if(p < 0) // no more args? FAIL
+			return FALSE;
+
+		// cut to next semicolon
+		q = strstrofs(cmdrestriction, ";", p+1); // find next semicolon
+		if(q < 0)
+			charlist = substring(cmdrestriction, p+1, -1);
+		else
+			charlist = substring(cmdrestriction, p+1, q - (p+1));
+
+		// in case we ever want to allow semicolons in VoteCommand_checknasty
+		// charlist = strreplace("^^", ";", charlist);
+
+		if(charlist != "")
 		{
-			case "map":
-			case "chmap":
-			case "gotomap":
-			case "kick":
-			case "kickban":
-				return FALSE;
-				
-			default: { break; }
+			// verify the arg only contains allowed chars
+			arg = argv(startpos);
+			checkmate = strlen(arg);
+			for(check = 0; check < checkmate; ++check)
+				if(strstrofs(charlist, substring(arg, check, 1), 0) < 0)
+					return FALSE; // not allowed character
+			// all characters are allowed. FINE.
 		}
+
+		++startpos;
+		p = q;
 	}
+
+	return TRUE;
+}
+
+float VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, float argc)
+{
+	string first_command;
 	
+	first_command = argv(startpos);
+
+	if not(VoteCommand_checkinlist(first_command, vote_list))
+		return FALSE;
+
+	if not(VoteCommand_checkargs(startpos, argc))
+		return FALSE;
+
 	switch(first_command) // now go through and parse the proper commands to adjust as needed.
 	{
 		case "kick":