OpenClonk
C4InputValidation.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2007-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // user input validation functions
17 
18 #include "C4Include.h"
19 #include "lib/C4InputValidation.h"
20 
21 #include "lib/C4Markup.h"
22 
23 namespace C4InVal
24 {
25  bool ValidateString(char *szString, ValidationOption eOption, size_t iMaxSize)
26  {
27  // validate in a StdStrBuf. Does one alloc and copy :(
28  StdStrBuf buf; buf.Copy(szString);
29  bool fInvalid = ValidateString(buf, eOption);
30  if (fInvalid) SCopy(buf.getData(), szString, iMaxSize);
31  return fInvalid;
32  }
33 
34  bool ValidateString(StdStrBuf &rsString, ValidationOption eOption)
35  {
36  bool fValid = true;
37  // validation depending on option
38  // check min length
39  if (!rsString.getLength())
40  {
41  // empty if not allowed?
42  if (eOption != VAL_NameAllowEmpty && eOption != VAL_NameExAllowEmpty && eOption != VAL_Comment)
43  {
44  rsString.Copy("empty");
45  fValid = false;
46  }
47  }
48  switch (eOption)
49  {
50  case VAL_Filename: // regular filenames only
51  // absolutely no directory traversal
52  if (rsString.ReplaceChar('/', '_')) fValid = false;
53  if (rsString.ReplaceChar('\\', '_')) fValid = false;
54 
55  // fallthrough to general file name validation
56  case VAL_SubPathFilename: // filenames and optional subpath
57  // do not traverse upwards in file hierarchy
58  if (rsString.Replace("..", "__")) fValid = false;
59  if (*rsString.getData() == '/' || *rsString.getData() == '\\') { *rsString.getMData() = '_'; fValid = false; }
60 
61  // fallthrough to general file name validation
62  case VAL_FullPath: // full filename paths
63  // some characters are prohibited in filenames in general
64  if (rsString.ReplaceChar('*', '_')) fValid = false;
65  if (rsString.ReplaceChar('?', '_')) fValid = false;
66  if (rsString.ReplaceChar('<', '_')) fValid = false;
67  if (rsString.ReplaceChar('>', '_')) fValid = false;
68  // ';' and '|' is never allowed in filenames, because it would cause problems in many engine internal file lists
69  if (rsString.ReplaceChar(';', '_')) fValid = false;
70  if (rsString.ReplaceChar('|', '_')) fValid = false;
71  // the colon is generally prohibited except at pos 2 (C:\...), because it could lead to creation of (invisible) streams on NTFS
72  if (rsString.ReplaceChar(':', '_')) fValid = false;
73  if (*rsString.getData() == ':') { *rsString.getMData() = '_'; fValid = false; }
74  // validate drive letter
75  if (rsString.getLength()>=2 && *rsString.getPtr(1) == ':')
76  {
77  if (eOption != VAL_FullPath)
78  {
79  *rsString.getMPtr(1)='_'; fValid = false;
80  }
81  else if (!isalpha((unsigned char)*rsString.getData()) || (*rsString.getPtr(2)!='\\' && *rsString.getPtr(2)!='/'))
82  {
83  *rsString.getMData()=*rsString.getMPtr(1)='_'; fValid = false;
84  }
85  }
86  break;
87 
88  case VAL_NameNoEmpty:
89  case VAL_NameAllowEmpty:
90  // no markup
91  if (C4Markup::StripMarkup(&rsString)) { fValid = false; }
92  // trim spaces
93  if (rsString.TrimSpaces()) fValid = false;
94  // min length
95  if (eOption == VAL_NameNoEmpty) if (!rsString.getLength()) { fValid = false; rsString.Copy("Unknown"); }
96  // max length
97  if (rsString.getLength() > C4MaxName) { fValid = false; rsString.SetLength(C4MaxName); }
98  break;
99 
100  case VAL_NameExNoEmpty:
102  // trim spaces
103  if (rsString.TrimSpaces()) fValid = false;
104  // min length
105  if (eOption == VAL_NameExNoEmpty) if (!rsString.getLength()) { fValid = false; rsString.Copy("Unknown"); }
106  // max length
107  if (rsString.getLength() > C4MaxLongName) { fValid = false; rsString.SetLength(C4MaxLongName); }
108  break;
109 
110  case VAL_IRCName: // nickname for IRC. a-z, A-Z, _^{[]} only; 0-9|- inbetween; max 30 characters
111  if (rsString.getLength() > 30) fValid = false;
112  if (rsString.getLength() < 2) fValid = false;
113  if (!rsString.ValidateChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_^{[]}", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_^{[]}0123456789|-")) { fValid = false; rsString.Copy("Guest"); }
114  if (SEqualNoCase(rsString.getData(), "NickServ")
115  || SEqualNoCase(rsString.getData(), "ChanServ")
116  || SEqualNoCase(rsString.getData(), "MemoServ")
117  || SEqualNoCase(rsString.getData(), "OperServ")
118  || SEqualNoCase(rsString.getData(), "HelpServ")) fValid = false;
119  if (!fValid) rsString.Copy("Guest");
120  break;
121 
122  case VAL_IRCPass: // password for IRC; max 31 characters
123  // max length; no spaces
124  if (rsString.getLength() > 31) { fValid = false; rsString.SetLength(31); }
125  if (rsString.getLength() < 2) { fValid = false; rsString.Copy("secret"); }
126  if (rsString.ReplaceChar(' ', '_')) fValid = false;
127  break;
128 
129  case VAL_IRCChannel: // IRC channel name
130  { // needed for the vector
131  std::vector<StdCopyStrBuf> chans;
132  StdStrBuf SplitPart;
133  StdCopyStrBuf tmp; tmp.Copy(rsString);
134  while(tmp.SplitAtChar(',', &SplitPart)) // Split
135  {
136  chans.push_back(tmp);
137  tmp.Copy(SplitPart);
138  }
139  chans.push_back(tmp);
140  rsString.Clear();
141  for(std::vector<StdCopyStrBuf>::iterator it = chans.begin(); it < chans.end(); ++it) // Reassemble clean
142  {
143  if (it->getLength() > 32) { fValid = false; it->SetLength(32); }
144  else if (it->getLength() < 2) { fValid = false; it->Clear(); }
145  else if (*it->getData() != '#' && *it->getData() != '+') { fValid = false; it->InsertChar('#', 0); }
146  if (it->ReplaceChar(' ', '_')) fValid = false;
147  rsString.Append(*it);
148  if(it+1 < chans.end() && it->getLength() > 0) rsString.Append(",");
149  }
150  if(rsString.getLength() < 2) rsString.Copy("#openclonk");
151  break;
152  }
153 
154  case VAL_Comment: // comment - just limit length
155  if (rsString.getLength() > C4MaxComment) { fValid = false; rsString.SetLength(C4MaxComment); }
156  break;
157 
158  default:
159  assert(!"not yet implemented");
160  }
161  // issue warning for invalid adjustments
162  if (false) if (!fValid)
163  {
164  const char *szOption = "unknown";
165  switch (eOption)
166  {
167  case VAL_Filename: szOption = "filename"; break;
168  case VAL_SubPathFilename: szOption = "(sub-)filename"; break;
169  case VAL_FullPath: szOption = "free filename"; break;
170  case VAL_NameNoEmpty: szOption = "strict name"; break;
171  case VAL_NameExNoEmpty: szOption = "name"; break;
172  case VAL_NameAllowEmpty: szOption = "strict name*"; break;
173  case VAL_NameExAllowEmpty: szOption = "name*"; break;
174  case VAL_IRCName: szOption = "IRC nick"; break;
175  case VAL_IRCPass: szOption = "IRC password"; break;
176  case VAL_IRCChannel: szOption = "IRC channel"; break;
177  case VAL_Comment: szOption = "Comment"; break;
178  }
179  LogF(R"(WARNING: Adjusted invalid user input for "%s" to "%s")", szOption, rsString.getData());
180  }
181  return !fValid;
182  }
183 
184  bool ValidateInt(int32_t &riVal, int32_t iMinVal, int32_t iMaxVal)
185  {
186  if (riVal < iMinVal) { riVal = iMinVal; return false; }
187  else if (riVal > iMaxVal) { riVal = iMaxVal; return false; }
188  else return true;
189  }
190 }
const unsigned int C4MaxComment
const unsigned int C4MaxLongName
const unsigned int C4MaxName
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:262
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:213
static bool StripMarkup(char *szText)
Definition: C4Markup.cpp:148
void SetLength(size_t iLength)
Definition: StdBuf.h:509
int Replace(const char *szOld, const char *szNew, size_t iStartSearch=0)
Definition: StdBuf.cpp:284
const char * getData() const
Definition: StdBuf.h:442
char * getMData()
Definition: StdBuf.h:443
bool SplitAtChar(char cSplit, StdStrBuf *psSplit)
Definition: StdBuf.h:619
bool ValidateChars(const char *szInitialChars, const char *szMidChars)
Definition: StdBuf.cpp:360
void Copy()
Definition: StdBuf.h:467
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
int ReplaceChar(char cOld, char cNew)
Definition: StdBuf.cpp:336
bool TrimSpaces()
Definition: StdBuf.cpp:470
const char * getPtr(size_t i) const
Definition: StdBuf.h:448
char * getMPtr(size_t i)
Definition: StdBuf.h:449
void Clear()
Definition: StdBuf.h:466
size_t getLength() const
Definition: StdBuf.h:445
bool ValidateString(char *szString, ValidationOption eOption, size_t iMaxSize)
@ VAL_NameExAllowEmpty
bool ValidateInt(int32_t &riVal, int32_t iMinVal, int32_t iMaxVal)