]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/helperfuncs.cpp
Only parse valid durations, don't treat invalid multipliers as seconds (#1538)
[user/henk/code/inspircd.git] / src / helperfuncs.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5  *   Copyright (C) 2006-2008 Robin Burchell <robin+git@viroteck.net>
6  *   Copyright (C) 2005-2008 Craig Edwards <craigedwards@brainbox.cc>
7  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
8  *   Copyright (C) 2006-2007 Oliver Lupton <oliverlupton@gmail.com>
9  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
10  *
11  * This file is part of InspIRCd.  InspIRCd is free software: you can
12  * redistribute it and/or modify it under the terms of the GNU General Public
13  * License as published by the Free Software Foundation, version 2.
14  *
15  * This program is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24
25 #ifdef _WIN32
26 #define _CRT_RAND_S
27 #include <stdlib.h>
28 #endif
29
30 #include "inspircd.h"
31 #include "xline.h"
32 #include "exitcodes.h"
33 #include <iostream>
34
35 /* Find a user record by nickname and return a pointer to it */
36 User* InspIRCd::FindNick(const std::string &nick)
37 {
38         if (!nick.empty() && isdigit(*nick.begin()))
39                 return FindUUID(nick);
40         return FindNickOnly(nick);
41 }
42
43 User* InspIRCd::FindNickOnly(const std::string &nick)
44 {
45         user_hash::iterator iter = this->Users->clientlist.find(nick);
46
47         if (iter == this->Users->clientlist.end())
48                 return NULL;
49
50         return iter->second;
51 }
52
53 User *InspIRCd::FindUUID(const std::string &uid)
54 {
55         user_hash::iterator finduuid = this->Users->uuidlist.find(uid);
56
57         if (finduuid == this->Users->uuidlist.end())
58                 return NULL;
59
60         return finduuid->second;
61 }
62 /* find a channel record by channel name and return a pointer to it */
63
64 Channel* InspIRCd::FindChan(const std::string &chan)
65 {
66         chan_hash::iterator iter = chanlist.find(chan);
67
68         if (iter == chanlist.end())
69                 /* Couldn't find it */
70                 return NULL;
71
72         return iter->second;
73 }
74
75 bool InspIRCd::IsValidMask(const std::string &mask)
76 {
77         const char* dest = mask.c_str();
78         int exclamation = 0;
79         int atsign = 0;
80
81         for (const char* i = dest; *i; i++)
82         {
83                 /* out of range character, bad mask */
84                 if (*i < 32 || *i > 126)
85                 {
86                         return false;
87                 }
88
89                 switch (*i)
90                 {
91                         case '!':
92                                 exclamation++;
93                                 break;
94                         case '@':
95                                 atsign++;
96                                 break;
97                 }
98         }
99
100         /* valid masks only have 1 ! and @ */
101         if (exclamation != 1 || atsign != 1)
102                 return false;
103
104         if (mask.length() > ServerInstance->Config->Limits.GetMaxMask())
105                 return false;
106
107         return true;
108 }
109
110 void InspIRCd::StripColor(std::string &sentence)
111 {
112         /* refactor this completely due to SQUIT bug since the old code would strip last char and replace with \0 --peavey */
113         int seq = 0;
114
115         for (std::string::iterator i = sentence.begin(); i != sentence.end();)
116         {
117                 if (*i == 3)
118                         seq = 1;
119                 else if (seq && (( ((*i >= '0') && (*i <= '9')) || (*i == ',') ) ))
120                 {
121                         seq++;
122                         if ( (seq <= 4) && (*i == ',') )
123                                 seq = 1;
124                         else if (seq > 3)
125                                 seq = 0;
126                 }
127                 else
128                         seq = 0;
129
130                 // Strip all control codes too except \001 for CTCP
131                 if (seq || ((*i >= 0) && (*i < 32) && (*i != 1)))
132                         i = sentence.erase(i);
133                 else
134                         ++i;
135         }
136 }
137
138 void InspIRCd::ProcessColors(file_cache& input)
139 {
140         /*
141          * Replace all color codes from the special[] array to actual
142          * color code chars using C++ style escape sequences. You
143          * can append other chars to replace if you like -- Justasic
144          */
145         static struct special_chars
146         {
147                 std::string character;
148                 std::string replace;
149                 special_chars(const std::string& c, const std::string& r)
150                         : character(c)
151                         , replace(r)
152                 {
153                 }
154         } special[] = {
155                 special_chars("\\b", "\x02"), // Bold
156                 special_chars("\\c", "\x03"), // Color
157                 special_chars("\\i", "\x1D"), // Italic
158                 special_chars("\\m", "\x11"), // Monospace
159                 special_chars("\\r", "\x16"), // Reverse
160                 special_chars("\\s", "\x1E"), // Strikethrough
161                 special_chars("\\u", "\x1F"), // Underline
162                 special_chars("\\x", "\x0F"), // Reset
163                 special_chars("", "")
164         };
165
166         for(file_cache::iterator it = input.begin(), it_end = input.end(); it != it_end; it++)
167         {
168                 std::string ret = *it;
169                 for(int i = 0; special[i].character.empty() == false; ++i)
170                 {
171                         std::string::size_type pos = ret.find(special[i].character);
172                         if(pos == std::string::npos) // Couldn't find the character, skip this line
173                                 continue;
174
175                         if((pos > 0) && (ret[pos-1] == '\\') && (ret[pos] == '\\'))
176                                 continue; // Skip double slashes.
177
178                         // Replace all our characters in the array
179                         while(pos != std::string::npos)
180                         {
181                                 ret = ret.substr(0, pos) + special[i].replace + ret.substr(pos + special[i].character.size());
182                                 pos = ret.find(special[i].character, pos + special[i].replace.size());
183                         }
184                 }
185
186                 // Replace double slashes with a single slash before we return
187                 std::string::size_type pos = ret.find("\\\\");
188                 while(pos != std::string::npos)
189                 {
190                         ret = ret.substr(0, pos) + "\\" + ret.substr(pos + 2);
191                         pos = ret.find("\\\\", pos + 1);
192                 }
193                 *it = ret;
194         }
195 }
196
197 /* true for valid channel name, false else */
198 bool InspIRCd::DefaultIsChannel(const std::string& chname)
199 {
200         if (chname.empty() || chname.length() > ServerInstance->Config->Limits.ChanMax)
201                 return false;
202
203         if (chname[0] != '#')
204                 return false;
205
206         for (std::string::const_iterator i = chname.begin()+1; i != chname.end(); ++i)
207         {
208                 switch (*i)
209                 {
210                         case ' ':
211                         case ',':
212                         case 7:
213                                 return false;
214                 }
215         }
216
217         return true;
218 }
219
220 /* true for valid nickname, false else */
221 bool InspIRCd::DefaultIsNick(const std::string& n)
222 {
223         if (n.empty() || n.length() > ServerInstance->Config->Limits.NickMax)
224                 return false;
225
226         for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
227         {
228                 if ((*i >= 'A') && (*i <= '}'))
229                 {
230                         /* "A"-"}" can occur anywhere in a nickname */
231                         continue;
232                 }
233
234                 if ((((*i >= '0') && (*i <= '9')) || (*i == '-')) && (i != n.begin()))
235                 {
236                         /* "0"-"9", "-" can occur anywhere BUT the first char of a nickname */
237                         continue;
238                 }
239
240                 /* invalid character! abort */
241                 return false;
242         }
243
244         return true;
245 }
246
247 /* return true for good ident, false else */
248 bool InspIRCd::DefaultIsIdent(const std::string& n)
249 {
250         if (n.empty())
251                 return false;
252
253         for (std::string::const_iterator i = n.begin(); i != n.end(); ++i)
254         {
255                 if ((*i >= 'A') && (*i <= '}'))
256                 {
257                         continue;
258                 }
259
260                 if (((*i >= '0') && (*i <= '9')) || (*i == '-') || (*i == '.'))
261                 {
262                         continue;
263                 }
264
265                 return false;
266         }
267
268         return true;
269 }
270
271 bool InspIRCd::IsHost(const std::string& host)
272 {
273         // Hostnames must be non-empty and shorter than the maximum hostname length.
274         if (host.empty() || host.length() > ServerInstance->Config->Limits.MaxHost)
275                 return false;
276
277         unsigned int numdashes = 0;
278         unsigned int numdots = 0;
279         bool seendot = false;
280         const std::string::const_iterator hostend = host.end() - 1;
281         for (std::string::const_iterator iter = host.begin(); iter != host.end(); ++iter)
282         {
283                 unsigned char chr = static_cast<unsigned char>(*iter);
284
285                 // If the current character is a label separator.
286                 if (chr == '.')
287                 {
288                         numdots++;
289
290                         // Consecutive separators are not allowed and dashes can not exist at the start or end
291                         // of labels and separators must only exist between labels.
292                         if (seendot || numdashes || iter == host.begin() || iter == hostend)
293                                 return false;
294
295                         seendot = true;
296                         continue;
297                 }
298
299                 // If this point is reached then the character is not a dot.
300                 seendot = false;
301
302                 // If the current character is a dash.
303                 if (chr == '-')
304                 {
305                         // Consecutive separators are not allowed and dashes can not exist at the start or end
306                         // of labels and separators must only exist between labels.
307                         if (seendot || numdashes >= 2 || iter == host.begin() || iter == hostend)
308                                 return false;
309
310                         numdashes += 1;
311                         continue;
312                 }
313
314                 // If this point is reached then the character is not a dash.
315                 numdashes = 0;
316
317                 // Alphanumeric characters are allowed at any position.
318                 if ((chr >= '0' && chr <= '9') || (chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z'))
319                         continue;
320
321                 return false;
322         }
323
324         // Whilst simple hostnames (e.g. localhost) are valid we do not allow the server to use
325         // them to prevent issues with clients that differentiate between short client and server
326         // prefixes by checking whether the nickname contains a dot.
327         return numdots;
328 }
329
330 bool InspIRCd::IsSID(const std::string &str)
331 {
332         /* Returns true if the string given is exactly 3 characters long,
333          * starts with a digit, and the other two characters are A-Z or digits
334          */
335         return ((str.length() == 3) && isdigit(str[0]) &&
336                         ((str[1] >= 'A' && str[1] <= 'Z') || isdigit(str[1])) &&
337                          ((str[2] >= 'A' && str[2] <= 'Z') || isdigit(str[2])));
338 }
339
340 void InspIRCd::CheckRoot()
341 {
342 #ifndef _WIN32
343         if (geteuid() == 0)
344         {
345                 std::cout << "ERROR: You are running an irc server as root! DO NOT DO THIS!" << std::endl << std::endl;
346                 this->Logs->Log("STARTUP", LOG_DEFAULT, "Can't start as root");
347                 Exit(EXIT_STATUS_ROOT);
348         }
349 #endif
350 }
351
352 /** A lookup table of values for multiplier characters used by
353  * InspIRCd::Duration(). In this lookup table, the indexes for
354  * the ascii values 'm' and 'M' have the value '60', the indexes
355  * for the ascii values 'D' and 'd' have a value of '86400', etc.
356  */
357 static const unsigned int duration_multi[] =
358 {
359         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
360         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
361         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
362         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
363         0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
364         0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 0, 0, 0, 0, 0, 0,
365         0, 0, 0, 0, 86400, 0, 0, 0, 3600, 0, 0, 0, 0, 60, 0, 0,
366         0, 0, 0, 1, 0, 0, 0, 604800, 0, 31557600, 0, 0, 0, 0, 0, 0,
367         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
368         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
369         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
370         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
371         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
372         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
373         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
374         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
375 };
376
377 bool InspIRCd::Duration(const std::string& str, unsigned long& duration)
378 {
379         unsigned long total = 0;
380         unsigned long subtotal = 0;
381
382         /* Iterate each item in the string, looking for number or multiplier */
383         for (std::string::const_iterator i = str.begin(); i != str.end(); ++i)
384         {
385                 /* Found a number, queue it onto the current number */
386                 if ((*i >= '0') && (*i <= '9'))
387                 {
388                         subtotal = (subtotal * 10) + (*i - '0');
389                 }
390                 else
391                 {
392                         /* Found something thats not a number, find out how much
393                          * it multiplies the built up number by, multiply the total
394                          * and reset the built up number.
395                          */
396                         unsigned int multiplier = duration_multi[static_cast<unsigned char>(*i)];
397                         if (multiplier == 0)
398                                 return false;
399
400                         total += subtotal * multiplier;
401
402                         /* Next subtotal please */
403                         subtotal = 0;
404                 }
405         }
406         /* Any trailing values built up are treated as raw seconds */
407         duration = total + subtotal;
408         return true;
409 }
410
411 unsigned long InspIRCd::Duration(const std::string& str)
412 {
413         unsigned long out = 0;
414         InspIRCd::Duration(str, out);
415         return out;
416 }
417
418 bool InspIRCd::IsValidDuration(const std::string& duration)
419 {
420         for (std::string::const_iterator i = duration.begin(); i != duration.end(); ++i)
421         {
422                 unsigned char c = *i;
423                 if (((c >= '0') && (c <= '9')))
424                         continue;
425
426                 if (!duration_multi[c])
427                         return false;
428         }
429         return true;
430 }
431
432 std::string InspIRCd::Format(va_list& vaList, const char* formatString)
433 {
434         static std::vector<char> formatBuffer(1024);
435
436         while (true)
437         {
438                 va_list dst;
439                 va_copy(dst, vaList);
440
441                 int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
442                 va_end(dst);
443
444                 if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
445                 {
446                         break;
447                 }
448
449                 formatBuffer.resize(formatBuffer.size() * 2);
450         }
451
452         return std::string(&formatBuffer[0]);
453 }
454
455 std::string InspIRCd::Format(const char* formatString, ...)
456 {
457         std::string ret;
458         VAFORMAT(ret, formatString, formatString);
459         return ret;
460 }
461
462 std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
463 {
464 #ifdef _WIN32
465         if (curtime < 0)
466                 curtime = 0;
467 #endif
468
469         struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
470         if (!timeinfo)
471         {
472                 curtime = 0;
473                 timeinfo = localtime(&curtime);
474         }
475
476         // If the calculated year exceeds four digits or is less than the year 1000,
477         // the behavior of asctime() is undefined
478         if (timeinfo->tm_year + 1900 > 9999)
479                 timeinfo->tm_year = 9999 - 1900;
480         else if (timeinfo->tm_year + 1900 < 1000)
481                 timeinfo->tm_year = 0;
482
483         // This is the default format used by asctime without the terminating new line.
484         if (!format)
485                 format = "%a %b %d %Y %H:%M:%S";
486
487         char buffer[512];
488         if (!strftime(buffer, sizeof(buffer), format, timeinfo))
489                 buffer[0] = '\0';
490
491         return buffer;
492 }
493
494 std::string InspIRCd::GenRandomStr(unsigned int length, bool printable)
495 {
496         char* buf = new char[length];
497         GenRandom(buf, length);
498         std::string rv;
499         rv.resize(length);
500         for(size_t i = 0; i < length; i++)
501                 rv[i] = printable ? 0x3F + (buf[i] & 0x3F) : buf[i];
502         delete[] buf;
503         return rv;
504 }
505
506 // NOTE: this has a slight bias for lower values if max is not a power of 2.
507 // Don't use it if that matters.
508 unsigned long InspIRCd::GenRandomInt(unsigned long max)
509 {
510         unsigned long rv;
511         GenRandom((char*)&rv, sizeof(rv));
512         return rv % max;
513 }
514
515 // This is overridden by a higher-quality algorithm when SSL support is loaded
516 void InspIRCd::DefaultGenRandom(char* output, size_t max)
517 {
518 #if defined HAS_ARC4RANDOM_BUF
519         arc4random_buf(output, max);
520 #else
521         for (unsigned int i = 0; i < max; ++i)
522 # ifdef _WIN32
523         {
524                 unsigned int uTemp;
525                 if(rand_s(&uTemp) != 0)
526                         output[i] = rand();
527                 else
528                         output[i] = uTemp;
529         }
530 # else
531                 output[i] = random();
532 # endif
533 #endif
534 }