]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/helperfuncs.cpp
Move IsValidDuration into the core.
[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 int duration_multi[] =
358 {
359         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
360         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
361         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
362         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
363         1, 1, 1, 1, 1, 1, 1, 1, 86400, 1, 1, 1, 3600,
364         1, 1, 1, 1, 60, 1, 1, 1, 1, 1, 1, 1, 1, 1,
365         604800, 1, 31557600, 1, 1, 1, 1, 1, 1, 1, 1,
366         1, 1, 86400, 1, 1, 1, 3600, 1, 1, 1, 1, 60,
367         1, 1, 1, 1, 1, 1, 1, 1, 1, 604800, 1, 31557600,
368         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
369         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
370         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
371         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
372         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
373         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
374         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
375         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
376         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
377 };
378
379 unsigned long InspIRCd::Duration(const std::string &str)
380 {
381         unsigned char multiplier = 0;
382         long total = 0;
383         long times = 1;
384         long subtotal = 0;
385
386         /* Iterate each item in the string, looking for number or multiplier */
387         for (std::string::const_reverse_iterator i = str.rbegin(); i != str.rend(); ++i)
388         {
389                 /* Found a number, queue it onto the current number */
390                 if ((*i >= '0') && (*i <= '9'))
391                 {
392                         subtotal = subtotal + ((*i - '0') * times);
393                         times = times * 10;
394                 }
395                 else
396                 {
397                         /* Found something thats not a number, find out how much
398                          * it multiplies the built up number by, multiply the total
399                          * and reset the built up number.
400                          */
401                         if (subtotal)
402                                 total += subtotal * duration_multi[multiplier];
403
404                         /* Next subtotal please */
405                         subtotal = 0;
406                         multiplier = *i;
407                         times = 1;
408                 }
409         }
410         if (multiplier)
411         {
412                 total += subtotal * duration_multi[multiplier];
413                 subtotal = 0;
414         }
415         /* Any trailing values built up are treated as raw seconds */
416         return total + subtotal;
417 }
418
419 bool InspIRCd::IsValidDuration(const std::string& duration)
420 {
421         for (std::string::const_iterator i = duration.begin(); i != duration.end(); ++i)
422         {
423                 unsigned char c = *i;
424                 if (((c >= '0') && (c <= '9')) || (c == 's') || (c == 'S'))
425                         continue;
426
427                 if (duration_multi[c] == 1)
428                         return false;
429         }
430         return true;
431 }
432
433 std::string InspIRCd::Format(va_list& vaList, const char* formatString)
434 {
435         static std::vector<char> formatBuffer(1024);
436
437         while (true)
438         {
439                 va_list dst;
440                 va_copy(dst, vaList);
441
442                 int vsnret = vsnprintf(&formatBuffer[0], formatBuffer.size(), formatString, dst);
443                 va_end(dst);
444
445                 if (vsnret > 0 && static_cast<unsigned>(vsnret) < formatBuffer.size())
446                 {
447                         break;
448                 }
449
450                 formatBuffer.resize(formatBuffer.size() * 2);
451         }
452
453         return std::string(&formatBuffer[0]);
454 }
455
456 std::string InspIRCd::Format(const char* formatString, ...)
457 {
458         std::string ret;
459         VAFORMAT(ret, formatString, formatString);
460         return ret;
461 }
462
463 std::string InspIRCd::TimeString(time_t curtime, const char* format, bool utc)
464 {
465 #ifdef _WIN32
466         if (curtime < 0)
467                 curtime = 0;
468 #endif
469
470         struct tm* timeinfo = utc ? gmtime(&curtime) : localtime(&curtime);
471         if (!timeinfo)
472         {
473                 curtime = 0;
474                 timeinfo = localtime(&curtime);
475         }
476
477         // If the calculated year exceeds four digits or is less than the year 1000,
478         // the behavior of asctime() is undefined
479         if (timeinfo->tm_year + 1900 > 9999)
480                 timeinfo->tm_year = 9999 - 1900;
481         else if (timeinfo->tm_year + 1900 < 1000)
482                 timeinfo->tm_year = 0;
483
484         // This is the default format used by asctime without the terminating new line.
485         if (!format)
486                 format = "%a %b %d %Y %H:%M:%S";
487
488         char buffer[512];
489         if (!strftime(buffer, sizeof(buffer), format, timeinfo))
490                 buffer[0] = '\0';
491
492         return buffer;
493 }
494
495 std::string InspIRCd::GenRandomStr(unsigned int length, bool printable)
496 {
497         char* buf = new char[length];
498         GenRandom(buf, length);
499         std::string rv;
500         rv.resize(length);
501         for(size_t i = 0; i < length; i++)
502                 rv[i] = printable ? 0x3F + (buf[i] & 0x3F) : buf[i];
503         delete[] buf;
504         return rv;
505 }
506
507 // NOTE: this has a slight bias for lower values if max is not a power of 2.
508 // Don't use it if that matters.
509 unsigned long InspIRCd::GenRandomInt(unsigned long max)
510 {
511         unsigned long rv;
512         GenRandom((char*)&rv, sizeof(rv));
513         return rv % max;
514 }
515
516 // This is overridden by a higher-quality algorithm when SSL support is loaded
517 void InspIRCd::DefaultGenRandom(char* output, size_t max)
518 {
519 #if defined HAS_ARC4RANDOM_BUF
520         arc4random_buf(output, max);
521 #else
522         for (unsigned int i = 0; i < max; ++i)
523 # ifdef _WIN32
524         {
525                 unsigned int uTemp;
526                 if(rand_s(&uTemp) != 0)
527                         output[i] = rand();
528                 else
529                         output[i] = uTemp;
530         }
531 # else
532                 output[i] = random();
533 # endif
534 #endif
535 }