openclonk/src/lib/C4InputValidation.cpp

193 lines
7.3 KiB
C++
Raw Normal View History

2009-05-08 13:28:41 +00:00
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2007-2009, RedWolf Design GmbH, http://www.clonk.de/
* Copyright (c) 2009-2013, The OpenClonk Team and contributors
2009-05-08 13:28:41 +00:00
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
2009-05-08 13:28:41 +00:00
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
2009-05-08 13:28:41 +00:00
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
2009-05-08 13:28:41 +00:00
*/
// user input validation functions
2009-10-20 03:39:24 +00:00
#include "C4Include.h"
2009-05-08 13:28:41 +00:00
#include <C4InputValidation.h>
#include <C4Log.h>
2011-08-27 19:13:15 +00:00
#include "C4Markup.h"
2009-05-08 13:28:41 +00:00
2009-05-12 16:37:39 +00:00
#include <cctype>
2009-05-08 13:28:41 +00:00
namespace C4InVal
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool ValidateString(char *szString, ValidationOption eOption, size_t iMaxSize)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// validate in a StdStrBuf. Does one alloc and copy :(
StdStrBuf buf; buf.Copy(szString);
bool fInvalid = ValidateString(buf, eOption);
if (fInvalid) SCopy(buf.getData(), szString, iMaxSize);
return fInvalid;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool ValidateString(StdStrBuf &rsString, ValidationOption eOption)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
bool fValid = true;
// validation depending on option
// check min length
if (!rsString.getLength())
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// empty if not allowed?
if (eOption != VAL_NameAllowEmpty && eOption != VAL_NameExAllowEmpty && eOption != VAL_Comment)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
rsString.Copy("empty");
fValid = false;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
switch (eOption)
2010-03-28 18:58:01 +00:00
{
case VAL_Filename: // regular filenames only
// absolutely no directory traversal
if (rsString.ReplaceChar('/', '_')) fValid = false;
if (rsString.ReplaceChar('\\', '_')) fValid = false;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
// fallthrough to general file name validation
case VAL_SubPathFilename: // filenames and optional subpath
// do not traverse upwards in file hierarchy
if (rsString.Replace("..", "__")) fValid = false;
if (*rsString.getData() == '/' || *rsString.getData() == '\\') { *rsString.getMData() = '_'; fValid = false; }
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
// fallthrough to general file name validation
case VAL_FullPath: // full filename paths
// some characters are prohibited in filenames in general
if (rsString.ReplaceChar('*', '_')) fValid = false;
if (rsString.ReplaceChar('?', '_')) fValid = false;
if (rsString.ReplaceChar('<', '_')) fValid = false;
if (rsString.ReplaceChar('>', '_')) fValid = false;
// ';' and '|' is never allowed in filenames, because it would cause problems in many engine internal file lists
if (rsString.ReplaceChar(';', '_')) fValid = false;
if (rsString.ReplaceChar('|', '_')) fValid = false;
// the colon is generally prohibited except at pos 2 (C:\...), because it could lead to creation of (invisible) streams on NTFS
if (rsString.ReplaceChar(':', '_'/*, 2*/)) fValid = false;
2010-03-28 18:58:01 +00:00
if (*rsString.getData() == ':') { *rsString.getMData() = '_'; fValid = false; }
// validate drive letter
if (rsString.getLength()>=2 && *rsString.getPtr(1) == ':')
{
if (eOption != VAL_FullPath)
{
*rsString.getMPtr(1)='_'; fValid = false;
}
else if (!isalpha((unsigned char)*rsString.getData()) || (*rsString.getPtr(2)!='\\' && *rsString.getPtr(2)!='/'))
{
*rsString.getMData()=*rsString.getMPtr(1)='_'; fValid = false;
}
}
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case VAL_NameNoEmpty:
case VAL_NameAllowEmpty:
// no markup
2011-08-27 19:13:15 +00:00
if (C4Markup::StripMarkup(&rsString)) { fValid = false; }
2010-03-28 18:58:01 +00:00
// trim spaces
if (rsString.TrimSpaces()) fValid = false;
// min length
if (eOption == VAL_NameNoEmpty) if (!rsString.getLength()) { fValid = false; rsString.Copy("Unknown"); }
// max length
if (rsString.getLength() > C4MaxName) { fValid = false; rsString.SetLength(C4MaxName); }
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case VAL_NameExNoEmpty:
case VAL_NameExAllowEmpty:
// trim spaces
if (rsString.TrimSpaces()) fValid = false;
// min length
if (eOption == VAL_NameExNoEmpty) if (!rsString.getLength()) { fValid = false; rsString.Copy("Unknown"); }
// max length
if (rsString.getLength() > C4MaxLongName) { fValid = false; rsString.SetLength(C4MaxLongName); }
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case VAL_IRCName: // nickname for IRC. a-z, A-Z, _^{[]} only; 0-9|- inbetween; max 30 characters
if (rsString.getLength() > 30) fValid = false;
if (rsString.getLength() < 2) fValid = false;
if (!rsString.ValidateChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_^{[]}", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_^{[]}0123456789|-")) { fValid = false; rsString.Copy("Guest"); }
if (SEqualNoCase(rsString.getData(), "NickServ")
|| SEqualNoCase(rsString.getData(), "ChanServ")
|| SEqualNoCase(rsString.getData(), "MemoServ")
|| SEqualNoCase(rsString.getData(), "OperServ")
|| SEqualNoCase(rsString.getData(), "HelpServ")) fValid = false;
if (!fValid) rsString.Copy("Guest");
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case VAL_IRCPass: // password for IRC; max 31 characters
// max length; no spaces
if (rsString.getLength() > 31) { fValid = false; rsString.SetLength(31); }
if (rsString.getLength() < 2) { fValid = false; rsString.Copy("secret"); }
if (rsString.ReplaceChar(' ', '_')) fValid = false;
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case VAL_IRCChannel: // IRC channel name
2011-03-21 21:39:36 +00:00
{ // needed for the vector
2011-03-21 22:26:21 +00:00
std::vector<StdCopyStrBuf> chans;
2011-03-21 21:39:36 +00:00
StdStrBuf SplitPart;
2011-03-21 22:26:21 +00:00
StdCopyStrBuf tmp; tmp.Copy(rsString);
while(tmp.SplitAtChar(',', &SplitPart)) // Split
2011-03-21 21:39:36 +00:00
{
2011-03-21 22:26:21 +00:00
chans.push_back(tmp);
tmp.Copy(SplitPart);
2011-03-21 21:39:36 +00:00
}
2011-03-21 22:26:21 +00:00
chans.push_back(tmp);
2011-03-21 21:39:36 +00:00
rsString.Clear();
2011-03-21 22:26:21 +00:00
for(std::vector<StdCopyStrBuf>::iterator it = chans.begin(); it < chans.end(); ++it) // Reassemble clean
2011-03-21 21:39:36 +00:00
{
if (it->getLength() > 32) { fValid = false; it->SetLength(32); }
else if (it->getLength() < 2) { fValid = false; it->Clear(); }
else if (*it->getData() != '#' && *it->getData() != '+') { fValid = false; it->InsertChar('#', 0); }
if (it->ReplaceChar(' ', '_')) fValid = false;
rsString.Append(*it);
if(it+1 < chans.end() && it->getLength() > 0) rsString.Append(",");
}
if(rsString.getLength() < 2) rsString.Copy("#openclonk");
2010-03-28 18:58:01 +00:00
break;
2011-03-21 21:39:36 +00:00
}
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
case VAL_Comment: // comment - just limit length
if (rsString.getLength() > C4MaxComment) { fValid = false; rsString.SetLength(C4MaxComment); }
break;
2009-05-08 13:28:41 +00:00
2010-03-28 18:58:01 +00:00
default:
assert(!"not yet implemented");
}
2009-05-08 13:28:41 +00:00
// issue warning for invalid adjustments
if (0) if (!fValid)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
const char *szOption = "unknown";
switch (eOption)
2010-03-28 18:58:01 +00:00
{
case VAL_Filename: szOption = "filename"; break;
case VAL_SubPathFilename: szOption = "(sub-)filename"; break;
case VAL_FullPath: szOption = "free filename"; break;
case VAL_NameNoEmpty: szOption = "strict name"; break;
case VAL_NameExNoEmpty: szOption = "name"; break;
case VAL_NameAllowEmpty: szOption = "strict name*"; break;
case VAL_NameExAllowEmpty: szOption = "name*"; break;
case VAL_IRCName: szOption = "IRC nick"; break;
case VAL_IRCPass: szOption = "IRC password"; break;
case VAL_IRCChannel: szOption = "IRC channel"; break;
case VAL_Comment: szOption = "Comment"; break;
2009-05-08 13:28:41 +00:00
}
LogF("WARNING: Adjusted invalid user input for \"%s\" to \"%s\"", szOption, rsString.getData());
2009-05-08 13:28:41 +00:00
}
2010-03-28 18:58:01 +00:00
return !fValid;
}
2009-05-08 13:28:41 +00:00
bool ValidateInt(int32_t &riVal, int32_t iMinVal, int32_t iMaxVal)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (riVal < iMinVal) { riVal = iMinVal; return false; }
else if (riVal > iMaxVal) { riVal = iMaxVal; return false; }
else return true;
2010-01-25 04:00:59 +00:00
}
2010-03-28 18:58:01 +00:00
}