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